def generate_id_token( token, user_info, key, iss, aud, alg='RS256', exp=3600, nonce=None, auth_time=None, code=None): now = int(time.time()) if auth_time is None: auth_time = now payload = { 'iss': iss, 'aud': aud, 'iat': now, 'exp': now + exp, 'auth_time': auth_time, } if nonce: payload['nonce'] = nonce if code: payload['c_hash'] = to_native(create_half_hash(code, alg)) access_token = token.get('access_token') if access_token: payload['at_hash'] = to_native(create_half_hash(access_token, alg)) payload.update(user_info) return to_native(jwt.encode({'alg': alg}, payload, key))
def _generate_id_token_payload(alg, iss, aud, exp, nonce=None, auth_time=None, code=None, access_token=None): now = int(time.time()) if auth_time is None: auth_time = now payload = { 'iss': iss, 'aud': aud, 'iat': now, 'exp': now + exp, 'auth_time': auth_time, } if nonce: payload['nonce'] = nonce if code: payload['c_hash'] = to_native(create_half_hash(code, alg)) if access_token: payload['at_hash'] = to_native(create_half_hash(access_token, alg)) return payload
def generate_id_token(self, token, request, nonce=None, auth_time=None, code=None): scopes = scope_to_list(token['scope']) if not scopes or scopes[0] != 'openid': return None # TODO: merge scopes and claims user_info = self.generate_user_info(request.user, scopes) now = int(time.time()) if auth_time is None: auth_time = now config = self.server.config payload = { 'iss': config['jwt_iss'], 'aud': [request.client.client_id], 'iat': now, 'exp': now + config['jwt_exp'], 'auth_time': auth_time, } if nonce: payload['nonce'] = nonce # calculate at_hash alg = config['jwt_alg'] access_token = token.get('access_token') if access_token: payload['at_hash'] = to_native(create_half_hash(access_token, alg)) # calculate c_hash if code: payload['c_hash'] = to_native(create_half_hash(code, alg)) payload.update(user_info) jwt = JWT(algorithms=alg) header = {'alg': alg} key = config['jwt_key'] if isinstance(key, dict): # JWK set format if 'keys' in key: key = random.choice(key['keys']) header['kid'] = key['kid'] elif 'kid' in key: header['kid'] = key['kid'] return to_native(jwt.encode(header, payload, key))
def generate(self, grant_type, client, user=None, scope=None, expires_in=None): """Generate a bearer token for OAuth 2.0 authorization token endpoint. :param client: the client that making the request. :param grant_type: current requested grant_type. :param user: current authorized user. :param expires_in: if provided, use this value as expires_in. :param scope: current requested scope. :return: Token dict """ token_data = self.get_token_data(grant_type, client, user, scope, expires_in) access_token = jwt.encode({'alg': self.alg}, token_data, key=self.secret_key, check=False) token = { 'token_type': 'Bearer', 'access_token': to_native(access_token), 'expires_in': expires_in } if scope: token['scope'] = scope return token
def __call__(self, req): """Add OAuth parameters to the request. Parameters may be included from the body if the content-type is urlencoded, if no content type is set a guess is made. """ # Overwriting url is safe here as request will not modify it past # this point. content_type = to_native(req.headers.get('Content-Type', '')) if self.signature_type == SIGNATURE_TYPE_BODY: content_type = CONTENT_TYPE_FORM_URLENCODED elif not content_type and extract_params(req.body): content_type = CONTENT_TYPE_FORM_URLENCODED if CONTENT_TYPE_FORM_URLENCODED in content_type: req.headers['Content-Type'] = CONTENT_TYPE_FORM_URLENCODED req.url, headers, req.body = self.sign_request(req) elif self.force_include_body: # To allow custom clients to work on non form encoded bodies. req.url, headers, req.body = self.sign_request(req) else: # Omit body data in the signing of non form-encoded requests req.url, headers, _ = self.sign( req.method, req.url, '', req.headers) req.prepare_headers(headers) req.url = to_native_string(req.url) return req
def wrap(self, cek, headers, key): self._check_key(key) #: https://tools.ietf.org/html/rfc7518#section-4.7.1.1 #: The "iv" (initialization vector) Header Parameter value is the #: base64url-encoded representation of the 96-bit IV value iv_size = 96 iv = os.urandom(iv_size // 8) cipher = Cipher(AES(key), GCM(iv), backend=default_backend()) enc = cipher.encryptor() ek = enc.update(cek) + enc.finalize() h = { 'iv': to_native(urlsafe_b64encode(iv)), 'tag': to_native(urlsafe_b64encode(enc.tag)) } return {'ek': ek, 'header': h}
def __call__(self, req): url, headers, body = self.prepare(req.method, req.url, req.headers, req.body) req.url = to_native(url) req.prepare_headers(headers) if body: req.body = body return req
def generate_id_token(self, token, request, nonce=None, auth_time=None, code=None): scopes = scope_to_list(token['scope']) if not scopes or scopes[0] != 'openid': return None # TODO: merge scopes and claims user_info = self.generate_user_info(request.user, scopes) now = int(time.time()) if auth_time is None: auth_time = now config = self.server.config payload = { 'iss': config['jwt_iss'], 'aud': [request.client.client_id], 'iat': now, 'exp': now + token['expires_in'], 'auth_time': auth_time, } if nonce: payload['nonce'] = nonce # calculate at_hash alg = config.get('jwt_alg', 'HS256') access_token = token.get('access_token') if access_token: at_hash = to_native(create_half_hash(access_token, alg)) payload['at_hash'] = at_hash # calculate c_hash if code: payload['c_hash'] = to_native(create_half_hash(code, alg)) payload.update(user_info) jwt = JWT(algorithms=alg) header = {'alg': alg} key = config['jwt_key'] id_token = jwt.encode(header, payload, key) return to_native(id_token)
def _jwt_encode(alg, payload, key): jwt = JWT(algorithms=alg) header = {'alg': alg} if isinstance(key, dict): # JWK set format if 'keys' in key: key = random.choice(key['keys']) header['kid'] = key['kid'] elif 'kid' in key: header['kid'] = key['kid'] return to_native(jwt.encode(header, payload, key))
def wrap(self, enc_alg, headers, key, preset=None): if preset and 'cek' in preset: cek = preset['cek'] else: cek = enc_alg.generate_cek() op_key = key.get_op_key('wrapKey') self._check_key(op_key) #: https://tools.ietf.org/html/rfc7518#section-4.7.1.1 #: The "iv" (initialization vector) Header Parameter value is the #: base64url-encoded representation of the 96-bit IV value iv_size = 96 iv = os.urandom(iv_size // 8) cipher = Cipher(AES(op_key), GCM(iv), backend=default_backend()) enc = cipher.encryptor() ek = enc.update(cek) + enc.finalize() h = { 'iv': to_native(urlsafe_b64encode(iv)), 'tag': to_native(urlsafe_b64encode(enc.tag)) } return {'ek': ek, 'cek': cek, 'header': h}
def refresh_token(self): """Using Assertions as Authorization Grants to refresh token as described in `Section 4.1`_. .. _`Section 4.1`: https://tools.ietf.org/html/rfc7521#section-4.1 """ generate_assertion = self.ASSERTION_METHODS[self.grant_type] assertion = generate_assertion(issuer=self.issuer, subject=self.subject, audience=self.audience, claims=self.claims, **self._kwargs) data = { 'assertion': to_native(assertion), 'grant_type': self.grant_type, } if self.scope: data['scope'] = self.scope return self._refresh_token(data)
def prepare(self, method, uri, headers, body): """Add OAuth parameters to the request. Parameters may be included from the body if the content-type is urlencoded, if no content type is set, a guess is made. """ content_type = to_native(headers.get('Content-Type', '')) if self.signature_type == SIGNATURE_TYPE_BODY: content_type = CONTENT_TYPE_FORM_URLENCODED elif not content_type and extract_params(body): content_type = CONTENT_TYPE_FORM_URLENCODED if CONTENT_TYPE_FORM_URLENCODED in content_type: headers['Content-Type'] = CONTENT_TYPE_FORM_URLENCODED uri, headers, body = self.sign(method, uri, headers, body) elif self.force_include_body: # To allow custom clients to work on non form encoded bodies. uri, headers, body = self.sign(method, uri, headers, body) else: # Omit body data in the signing of non form-encoded requests uri, headers, _ = self.sign(method, uri, headers, '') body = '' return uri, headers, body
def get_oauth_params(self, body, headers, nonce, timestamp): oauth_params = [ ('oauth_nonce', nonce), ('oauth_timestamp', timestamp), ('oauth_version', '1.0'), ('oauth_signature_method', self.signature_method), ('oauth_consumer_key', self.client_id), ] if self.token: oauth_params.append(('oauth_token', self.token)) if self.redirect_uri: oauth_params.append(('oauth_callback', self.redirect_uri)) if self.verifier: oauth_params.append(('oauth_verifier', self.verifier)) # https://tools.ietf.org/id/draft-eaton-oauth-bodyhash-00.html content_type = headers.get('Content-Type', '') not_form = not content_type.startswith(CONTENT_TYPE_FORM_URLENCODED) if body and content_type and not_form: sig = base64.b64encode(hashlib.sha1(to_bytes(body)).digest()) oauth_params.append(('oauth_body_hash', to_native(sig))) return oauth_params
def encode_client_secret_basic(client, method, uri, headers, body): text = '{}:{}'.format(client.client_id, client.client_secret) auth = to_native(base64.urlsafe_b64encode(to_bytes(text, 'latin1'))) headers['Authorization'] = 'Basic {}'.format(auth) return uri, headers, body