def __init__(self, client_id=None, client_secret=None, request_token_url=None, request_token_params=None, access_token_url=None, access_token_params=None, refresh_token_url=None, refresh_token_params=None, authorize_url=None, api_base_url=None, client_kwargs=None, compliance_fix=None, **kwargs): if not client_id and 'client_key' in kwargs: client_id = kwargs.get('client_key') deprecate('"client_key" has been renamed to "client_id".', '0.6') self.client_id = client_id self.client_secret = client_secret self.request_token_url = request_token_url self.request_token_params = request_token_params self.access_token_url = access_token_url self.access_token_params = access_token_params self.refresh_token_url = refresh_token_url self.refresh_token_params = refresh_token_params self.authorize_url = authorize_url self.api_base_url = api_base_url self.client_kwargs = client_kwargs or {} self.compliance_fix = compliance_fix self._kwargs = kwargs self._sess = None
def validate_authorization_request(self): # pragma: no cover deprecate( 'use `server.validate_consent_request(end_user=current_user)` instead', '0.8', link_uid='vAAUK', link_file='VAR') grant = self.get_authorization_grant(_create_oauth2_request()) grant.validate_authorization_request() return grant
def create_token_response(self): """If valid and authorized, the authorization server issues an access token as described in Section 5.1. If the request failed verification or is invalid, the authorization server returns an error response as described in Section 5.2. """ scope = self.request.scope credential = self.request.credential if not scope: scope = credential.get_scope() client = self.request.client expires_in = credential.get_expires_in() token = self.generate_token( client, self.GRANT_TYPE, expires_in=expires_in, scope=scope, ) log.debug('Issue token {!r} to {!r}'.format(token, client)) if self.server.save_token: user = self.authenticate_user(credential) if not user: raise InvalidRequestError('There is no "user" for this token.') self.request.user = user self.server.save_token(token, self.request) token = self.process_token(token, self.request) else: deprecate('"create_access_token" deprecated', '0.8', 'vAAUK', 'gt') self.create_access_token(token, client, credential) # pragma: no cover return 200, token, self.TOKEN_RESPONSE_HEADER
def create_authorization_response(self, request=None, grant_user=None): """Create the HTTP response for authorization. If resource owner granted the authorization, pass the resource owner as the user parameter, otherwise None:: @app.route('/authorize', methods=['POST']) def confirm_authorize(): if request.form['confirm'] == 'ok': grant_user = current_user else: grant_user = None return server.create_authorization_response(grant_user=grant_user) """ if request and not grant_user: # pragma: no cover grant_user = request # prepare for next upgrade deprecate( 'Use "create_authorization_response(grant_user=grant_user)" instead', '0.8', 'vAAUK', 'car' ) status, body, headers = self.create_valid_authorization_response( _create_oauth2_request(), grant_user=grant_user ) if isinstance(body, dict): body = json.dumps(body) return Response(body, status=status, headers=headers)
def expires_in(client, grant_type): conf_key = 'OAUTH2_EXPIRES_{}'.format(grant_type.upper()) expires = app.config.get(conf_key) if expires: deprecate('Deprecate "{}".'.format(conf_key), '0.10', 'vpCH5', 'as') return expires return expires_conf.get(grant_type, BearerToken.DEFAULT_EXPIRES_IN)
def verify_id_token(response, key, response_type='code', issuers=None, client_id=None, nonce=None, max_age=None, now=None): deprecate('"verify_id_token" is deprecated, use JWT instead.', 0.8) if 'id_token' not in response: raise ValueError('Invalid OpenID response') claims_cls = get_claim_cls_by_response_type(response_type) claims_request = { 'access_token': response.get('access_token'), 'client_id': client_id, 'nonce': nonce, 'max_age': max_age } claims_options = { 'iss': { 'values': issuers, } } claims = jwt.decode( response['id_token'], key, claims_cls, claims_options=claims_options, claims_params=claims_request, ) claims.validate(now=now) return claims
def create_authorization_response(self, redirect_uri, grant_user): """If the resource owner grants the access request, the authorization server issues an authorization code and delivers it to the client by adding the following parameters to the query component of the redirection URI using the "application/x-www-form-urlencoded" format. Per `Section 4.1.2`_. code REQUIRED. The authorization code generated by the authorization server. The authorization code MUST expire shortly after it is issued to mitigate the risk of leaks. A maximum authorization code lifetime of 10 minutes is RECOMMENDED. The client MUST NOT use the authorization code more than once. If an authorization code is used more than once, the authorization server MUST deny the request and SHOULD revoke (when possible) all tokens previously issued based on that authorization code. The authorization code is bound to the client identifier and redirection URI. state REQUIRED if the "state" parameter was present in the client authorization request. The exact value received from the client. For example, the authorization server redirects the user-agent by sending the following HTTP response. .. code-block:: http HTTP/1.1 302 Found Location: https://client.example.com/cb?code=SplxlOBeZQQYbYS6WxSbIA &state=xyz .. _`Section 4.1.2`: https://tools.ietf.org/html/rfc6749#section-4.1.2 :param redirect_uri: Redirect to the given URI for the authorization :param grant_user: if resource owner granted the request, pass this resource owner, otherwise pass None. :returns: (status_code, body, headers) """ state = self.request.state if grant_user: self.request.user = grant_user if hasattr(self, 'create_authorization_code'): deprecate('Use "generate_authorization_code" instead', '1.0') code = self.create_authorization_code(self.request.client, grant_user, self.request) else: code = self.generate_authorization_code() self.save_authorization_code(code, self.request) params = [('code', code)] if state: params.append(('state', state)) uri = add_params_to_uri(redirect_uri, params) headers = [('Location', uri)] return 302, '', headers else: raise AccessDeniedError(state=state, redirect_uri=redirect_uri)
def create_token_expires_in_generator(self, app): """Create a generator function for generating ``expires_in`` value. Developers can re-implement this method with a subclass if other means required. The default expires_in value is defined by ``grant_type``, different ``grant_type`` has different value. It can be configured with:: OAUTH2_TOKEN_EXPIRES_IN = { 'authorization_code': 864000, 'urn:ietf:params:oauth:grant-type:jwt-bearer': 3600, } """ expires_conf = {} expires_conf.update(GRANT_TYPES_EXPIRES) _old_expires = app.config.get('OAUTH2_EXPIRES_IN') if _old_expires: # pragma: no cover deprecate('Deprecate "OAUTH2_EXPIRES_IN".', '0.11', 'vhL75', 'ae') expires_conf.update(_old_expires) expires_conf.update(app.config.get('OAUTH2_TOKEN_EXPIRES_IN', {})) def expires_in(client, grant_type): return expires_conf.get(grant_type, BearerToken.DEFAULT_EXPIRES_IN) return expires_in
def create_granted_params(self, grant_user): self.request.user = grant_user client = self.request.client if hasattr(self, 'create_authorization_code'): # pragma: no cover deprecate('Use "generate_authorization_code" instead', '1.0') code = self.create_authorization_code(client, grant_user, self.request) else: code = self.generate_authorization_code() self.save_authorization_code(code, self.request) params = [('code', code)] token = self.generate_token(grant_type='implicit', user=grant_user, scope=self.request.scope, include_refresh_token=False) response_types = self.request.response_type.split() if 'token' in response_types: log.debug('Grant token %r to %r', token, client) self.server.save_token(token, self.request) if 'id_token' in response_types: token = self.process_implicit_token(token, code) else: # response_type is "code id_token" token = { 'expires_in': token['expires_in'], 'scope': token['scope'] } token = self.process_implicit_token(token, code) params.extend([(k, token[k]) for k in token]) return params
def _compatible_query_client(query_client): if query_client and hasattr(query_client, 'get_by_client_id'): message = ( 'client_model is deprecated.\n\n' 'Please read: <https://github.com/lepture/authlib/issues/27>') deprecate(message, '0.7') query_client = query_client.get_by_client_id return query_client
def __init__(self, *args, **kwargs): super(AuthorizationCodeGrant, self).__init__(*args, **kwargs) deprecate('Use CodeChallenge as an extension instead.', '0.12', 'fAmW1', 'CC') challenge = CodeChallenge(required=True) challenge.get_authorization_code_challenge = self.get_authorization_code_challenge challenge.get_authorization_code_challenge_method = self.get_authorization_code_challenge_method self.challenge = challenge
def create_authorization_response(self, request=None, grant_user=None): if request and not grant_user: # pragma: no cover deprecate( 'Use "create_authorization_response(grant_user=user)" instead', '0.11') grant_user = request request = None return super(AuthorizationServer, self)\ .create_authorization_response(request, grant_user)
def get_jwt_config(self): # pragma: no cover # TODO: developers MUST re-implement this method deprecate('Missing "OpenIDImplicitGrant.get_jwt_config"', '1.0', 'fjPsV', 'oi') config = self.server.config key = config['jwt_key'] alg = config['jwt_alg'] iss = config['jwt_iss'] exp = config['jwt_exp'] return dict(key=key, alg=alg, iss=iss, exp=exp)
def __init__(self, algorithms, private_headers=None): self._algorithms = {} self._private_headers = private_headers if isinstance(algorithms, dict): # pragma: no cover deprecate('Pass list of algorithms to JWS instead', '0.10') self._algorithms = algorithms elif isinstance(algorithms, list): for algorithm in algorithms: self.register_algorithm(algorithm)
def __init__(self, require_nonce=False, **kwargs): self.require_nonce = require_nonce if 'required_nonce' in kwargs: deprecate('Use "require_nonce" instead', '1.0') if kwargs['required_nonce']: self.require_nonce = True self.key = kwargs.get('key') self.alg = kwargs.get('alg') self.iss = kwargs.get('iss') self.exp = kwargs.get('exp') self._exists_nonce = kwargs.get('exists_nonce')
def register_error_uri(error, error_uri): """Register ``error_uri`` for each error code. When raise an OAuth2Error without ``error_uri``, it will use the URI in this registry:: register_error_uri('invalid_client', 'https://example.com/errors#invalid-client') :param error: OAuth 2 error code. :param error_uri: A human-readable web page with information about the error. """ global _error_uris _error_uris[error] = error_uri deprecate('This function is deprecated.', version='0.10')
def create_token_response(self): """If the access token request is valid and authorized, the authorization server issues an access token and optional refresh token as described in Section 5.1. If the request client authentication failed or is invalid, the authorization server returns an error response as described in Section 5.2. Per `Section 4.1.4`_. An example successful response: .. code-block:: http HTTP/1.1 200 OK Content-Type: application/json Cache-Control: no-store Pragma: no-cache { "access_token":"2YotnFZFEjr1zCsicMWpAA", "token_type":"example", "expires_in":3600, "refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA", "example_parameter":"example_value" } :returns: (status_code, body, headers) .. _`Section 4.1.4`: https://tools.ietf.org/html/rfc6749#section-4.1.4 """ client = self.request.client authorization_code = self.request.credential scope = authorization_code.get_scope() token = self.generate_token( client, self.GRANT_TYPE, scope=scope, include_refresh_token=client.has_client_secret(), ) log.debug('Issue token {!r} to {!r}'.format(token, client)) if self.server.save_token: user = self.authenticate_user(authorization_code) if not user: raise InvalidRequestError('There is no "user" for this code.') self.request.user = user self.server.save_token(token, self.request) token = self.process_token(token, self.request) else: deprecate('"create_access_token" deprecated', '0.8', 'vAAUK', 'gt') self.create_access_token(token, client, authorization_code) # pragma: no cover self.delete_authorization_code(authorization_code) return 200, token, self.TOKEN_RESPONSE_HEADER
def create_client(self, name): if not self.app: raise RuntimeError('OAuth is not init with Flask app.') if name in self._clients: return self._clients[name] keys = ( 'client_id', 'client_secret', 'request_token_url', 'request_token_params', 'access_token_url', 'access_token_params', 'refresh_token_url', 'refresh_token_params', 'authorize_url', 'api_base_url', 'client_kwargs', ) kwargs = self._registry[name] compliance_fix = kwargs.pop('compliance_fix', None) for k in keys: if k not in kwargs: conf_key = '{}_{}'.format(name, k).upper() v = self.app.config.get(conf_key, None) kwargs[k] = v if not kwargs['client_id']: conf_key = '{}_client_key'.format(name).upper() kwargs['client_id'] = self.app.config.get(conf_key, None) deprecate( 'Use "{}" instead of "{}".'.format( conf_key.replace('CLIENT_KEY', 'CLIENT_ID'), conf_key), '0.6') fetch_token = kwargs.pop('fetch_token', None) if fetch_token is None and self.fetch_token: fetch_token = functools.partial(self.fetch_token, name=name) update_token = kwargs.pop('update_token', None) if update_token is None and self.update_token: update_token = functools.partial(self.update_token, name=name) client = RemoteApp(name, self.cache, fetch_token, update_token, **kwargs) if compliance_fix: client.compliance_fix = compliance_fix self._clients[name] = client return client
def __init__(self, *args, **kwargs): deprecate('Deprecate "OpenIDCodeGrant".', '1.0', 'fjPsV', 'oi') super(OpenIDCodeGrant, self).__init__(*args, **kwargs) config = self.server.config extension = OpenIDCode( key=config['jwt_key'], alg=config['jwt_alg'], iss=config['jwt_iss'], exp=config['jwt_exp'], exists_nonce=self.exists_nonce, ) extension(self)
def verify(self, s, key): # pragma: no cover deprecate('Method "verify" is deprecated. Use "deserialize" instead.', '0.9') rv = self.deserialize_compact(s, None) header, payload = rv['header'], rv['payload'] algorithm, key = self._prepare_algorithm_key(header, payload, key) key = algorithm.prepare_verify_key(key) signing_input, signature = rv['signing_input'], rv['signature'] verified = algorithm.verify(signing_input, key, signature) # Note that the payload can be any content and need not # be a representation of a JSON object return header, payload, verified
def query_authorization_code(self, code, client): """Get authorization_code from previously savings. Developers MUST implement it in subclass:: def query_authorization_code(self, code, client): return Authorization.get(code=code, client_id=client.client_id) :param code: a string represent the code. :param client: client related to this code. :return: authorization_code object """ if hasattr(self, 'parse_authorization_code'): deprecate('Use "query_authorization_code" instead', '1.0') return self.parse_authorization_code(code, client) raise NotImplementedError()
def _gen_token(func, client, grant_type, user, scope): if hasattr(inspect, 'getfullargspec'): spec = inspect.getfullargspec(func) no_args = not spec.args and not spec.varkw else: spec = inspect.getargspec(func) no_args = not spec.args and not spec.keywords if no_args: deprecate('Token generator now accepts parameters', '0.11', 'vhL75', 'tg') return func() return func( client=client, grant_type=grant_type, user=user, scope=scope, )
def __init__(self, token_endpoint, issuer, subject, audience=None, grant_type=None, claims=None, token_placement='header', scope=None, **kwargs): Session.__init__(self) token_url = kwargs.pop('token_url', None) if token_url: deprecate('Use "token_endpoint" instead of "token_url"', '1.0') token_endpoint = token_url AssertionClient.__init__( self, session=self, token_endpoint=token_endpoint, issuer=issuer, subject=subject, audience=audience, grant_type=grant_type, claims=claims, token_placement=token_placement, scope=scope, **kwargs )
def exists_nonce(self, nonce, request): # pragma: no cover """Check if the given nonce is existing in your database. Developers MUST implement this method in subclass, e.g.:: def exists_nonce(self, nonce, request): exists = AuthorizationCode.query.filter_by( client_id=request.client_id, nonce=nonce ).first() return bool(exists) :param nonce: A string of "nonce" parameter in request :param request: OAuth2Request instance :return: Boolean """ deprecate('Missing "OpenIDCode.exists_nonce"', '1.0', 'fjPsV', 'oi') return self._exists_nonce(nonce, request)
def get_jwt_config(self, grant): # pragma: no cover """Get the JWT configuration for OpenIDCode extension. The JWT configuration will be used to generate ``id_token``. Developers MUST implement this method in subclass, e.g.:: def get_jwt_config(self, grant): return { 'key': read_private_key_file(key_path), 'alg': 'RS512', 'iss': 'issuer-identity', 'exp': 3600 } :param grant: AuthorizationCodeGrant instance :return: dict """ deprecate('Missing "OpenIDCode.get_jwt_config"', '1.0', 'fjPsV', 'oi') return dict(key=self.key, alg=self.alg, iss=self.iss, exp=self.exp)
def register_session_client_auth_method(session, token_url=None, **kwargs): # pragma: no cover """Register "client_secret_jwt" or "private_key_jwt" token endpoint auth method to OAuth2Session. :param session: OAuth2Session instance. :param token_url: Optional token endpoint url. """ deprecate('Use `ClientSecretJWT` and `PrivateKeyJWT` instead', '1.0', 'Jeclj', 'ca') if session.token_endpoint_auth_method == 'client_secret_jwt': cls = ClientSecretJWT elif session.token_endpoint_auth_method == 'private_key_jwt': cls = PrivateKeyJWT else: raise ValueError('Invalid token_endpoint_auth_method') session.register_client_auth_method(cls(token_url))
def generate_user_info(self, user, scope): # pragma: no cover """Provide user information for the given scope. Developers MUST implement this method in subclass, e.g.:: from authlib.oidc.core import UserInfo def generate_user_info(self, user, scope): user_info = UserInfo(sub=user.id, name=user.name) if 'email' in scope: user_info['email'] = user.email return user_info :param user: user instance :param scope: scope of the token :return: ``authlib.oidc.core.UserInfo`` instance """ deprecate('Missing "OpenIDCode.generate_user_info"', '1.0', 'fjPsV', 'oi') scopes = scope_to_list(scope) return _generate_user_info(user, scopes)
def create_token_response(self): """If the access token request is valid and authorized, the authorization server issues an access token and optional refresh token as described in Section 5.1. If the request failed client authentication or is invalid, the authorization server returns an error response as described in Section 5.2. An example successful response: .. code-block:: http HTTP/1.1 200 OK Content-Type: application/json Cache-Control: no-store Pragma: no-cache { "access_token":"2YotnFZFEjr1zCsicMWpAA", "token_type":"example", "expires_in":3600, "refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA", "example_parameter":"example_value" } :returns: (status_code, body, headers) """ client = self.request.client user = self.request.user token = self.generate_token( client, self.GRANT_TYPE, scope=self.request.scope, ) log.debug('Issue token {!r} to {!r}'.format(token, client)) if self.server.save_token: self.server.save_token(token, self.request) token = self.process_token(token, self.request) else: deprecate('"create_access_token" deprecated', '0.8', 'vAAUK', 'gt') self.create_access_token(token, client, user) # pragma: no cover return 200, token, self.TOKEN_RESPONSE_HEADER
def init_app(self, app, query_client=None, save_token=None): """Initialize later with Flask app instance.""" if query_client is not None: self.query_client = query_client if save_token is not None: self.save_token = save_token self.generate_token = self.create_bearer_token_generator(app.config) metadata_file = app.config.get('OAUTH2_METADATA_FILE') if metadata_file: with open(metadata_file) as f: metadata = self.metadata_class(json.load(f)) metadata.validate() self.metadata = metadata self.config.setdefault('error_uris', app.config.get('OAUTH2_ERROR_URIS')) if app.config.get('OAUTH2_JWT_ENABLED'): deprecate('Define "get_jwt_config" in OpenID Connect grants', '1.0') self.init_jwt_config(app.config)
def __init__(self, app, config_prefix='AUTHLIB', **kwargs): deprecate(DEPRECATE_MESSAGE, 0.7) self.config_prefix = config_prefix self.config = app.config cache_type = self._config('type') kwargs.update( dict(default_timeout=self._config('DEFAULT_TIMEOUT', 100))) if cache_type == 'null': self.cache = NullCache() elif cache_type == 'simple': kwargs.update(dict(threshold=self._config('threshold', 500))) self.cache = SimpleCache(**kwargs) elif cache_type == 'memcache': kwargs.update( dict( servers=self._config('MEMCACHED_SERVERS'), key_prefix=self._config('KEY_PREFIX', None), )) self.cache = MemcachedCache(**kwargs) elif cache_type == 'redis': kwargs.update( dict( host=self._config('REDIS_HOST', 'localhost'), port=self._config('REDIS_PORT', 6379), password=self._config('REDIS_PASSWORD', None), db=self._config('REDIS_DB', 0), key_prefix=self._config('KEY_PREFIX', None), )) self.cache = RedisCache(**kwargs) elif cache_type == 'filesystem': kwargs.update(dict(threshold=self._config('threshold', 500), )) self.cache = FileSystemCache(self._config('DIR'), **kwargs) else: raise RuntimeError('`%s` is not a valid cache type!' % cache_type) app.extensions[config_prefix.lower() + '_cache'] = self.cache