def validate_access_token_request(self): """ Override the parent method from authlib to not fail immediately for public clients. """ client = self.authenticate_token_endpoint_client() if not client.check_grant_type(self.GRANT_TYPE): raise UnauthorizedClientError(uri=self.uri) self._authenticated_client = client refresh_token = self.params.get("refresh_token") if refresh_token is None: raise InvalidRequestError('Missing "refresh_token" in request.', uri=self.uri) refresh_claims = self.authenticate_refresh_token(refresh_token) if not refresh_claims: raise InvalidRequestError('Invalid "refresh_token" in request.', uri=self.uri) scope = self.params.get("scope") if scope: original_scope = refresh_claims["scope"] if not original_scope: raise InvalidScopeError(uri=self.uri) original_scope = set(scope_to_list(original_scope)) if not original_scope.issuperset(set(scope_to_list(scope))): raise InvalidScopeError(uri=self.uri) self._authenticated_token = refresh_claims
def scope_insufficient(self, token, scope, operator='AND'): if not scope: return False token_scopes = set(scope_to_list(token['scope'])) resource_scopes = set(scope_to_list(scope)) if operator == 'AND': return not token_scopes.issuperset(resource_scopes) if operator == 'OR': return not token_scopes & resource_scopes if callable(operator): return not operator(token_scopes, resource_scopes) raise ValueError('Invalid operator value')
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) config = self.server.config alg = config['jwt_alg'] payload = self.generate_id_token_payload( alg, config['jwt_iss'], [request.client.client_id], config['jwt_exp'], nonce=nonce, auth_time=auth_time, code=code, access_token=token.get('access_token'), ) payload.update(user_info) return _jwt_encode(alg, payload, config['jwt_key'])
def generate_id_token(key, token, request, alg, iss, exp, nonce=None, auth_time=None, code=None): scopes = scope_to_list(token['scope']) # TODO: merge scopes and claims user_info = _generate_user_info(request.user, scopes) payload = _generate_id_token_payload( alg, iss, [request.client.client_id], exp=exp, nonce=nonce, auth_time=auth_time, code=code, access_token=token.get('access_token'), ) payload.update(user_info) return _jwt_encode(alg, payload, key)
def _validate_token_scope(self, token): """ OVERRIDES method from authlib. Why? Becuase our "token" is not a class with `get_scope` method. So we just need to treat it like a dictionary. """ scope = self.request.scope if not scope: return # token is dict so just get the scope, don't use get_scope() original_scope = token.get("aud") if not original_scope: raise InvalidScopeError() original_scope = set(scope_to_list(original_scope)) if not original_scope.issuperset(set(scope_to_list(scope))): raise InvalidScopeError()
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_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_unicode(create_half_hash(access_token, alg)) payload['at_hash'] = at_hash # calculate c_hash if code: payload['c_hash'] = to_unicode(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_unicode(id_token)
def create_authorization_code(self, client, grant_user, request): # instead of creating a shared-secret auth code bound to the client in the DB, # we already create the id token info but send it to the client encrypted. token_info = { 'sub': grant_user.generate_user_info(scope_to_list(request.scope))['sub'], 'scope': request.scope, 'auth_time': int(time.time()), 'redirect_uri': client.redirect_uri, 'client_id': client.client_id, 'nonce': request.data.get('nonce'), } return encryption.encrypt_and_serialize(json.dumps(token_info))
def is_openid_request(request): scopes = scope_to_list(request.scope) # openid should be the first scope return scopes and scopes[0] == 'openid'
def is_openid_scope(scope): scopes = scope_to_list(scope) return scopes and scopes[0] == 'openid'