Esempio n. 1
0
def config_oauth(app):
    authorization.init_app(
        app,
        query_client=query_client,
        save_token=save_token
    )

    # support all grants
    authorization.register_grant(grants.ImplicitGrant)
    authorization.register_grant(grants.ClientCredentialsGrant)
    authorization.register_grant(AuthorizationCodeGrant, [
        CodeChallenge(required=True),
        OpenIDCode(require_nonce=True)
    ])
    authorization.register_grant(PasswordGrant)
    authorization.register_grant(RefreshTokenGrant)
    authorization.register_grant(OpenIDImplicitGrant)
    authorization.register_grant(OpenIDHybridGrant)

    # OAuth2 server configurations
    with app.app_context():
        AuthorizationCodeGrant.TOKEN_ENDPOINT_AUTH_METHODS = app.config.get('TOKEN_ENDPOINT_AUTH_METHODS', [])
        RefreshTokenGrant.INCLUDE_NEW_REFRESH_TOKEN = app.config.get('INCLUDE_NEW_REFRESH_TOKEN', False)

    # support revocation
    revocation_cls = create_revocation_endpoint(db.session, OAuth2Token)
    authorization.register_endpoint(revocation_cls)
Esempio n. 2
0
 def __init__(
         self,
         jwt_config: JWTConfiguration,
         cognito_version: int = 2,
         now: typing.Callable[[], datetime] = utcnow,
         uuidgen: typing.Callable[[], str] = lambda: str(uuid.uuid4()),
 ):
     self.jwt_config = jwt_config
     self.cognito_version = cognito_version
     self.authz_server = AuthorizationServer(
         query_client=self._get_client_by_id,
         save_token=self._save_token,
         generate_token=self._generate_token,
         jwt_config=self.jwt_config,
         now=now,
         uuidgen=uuidgen,
     )
     self.authz_server.register_grant(
         AuthorizationCodeGrant,
         [OpenIDCode(False),
          CodeChallenge(required=False)])
     self.authz_server.register_grant(OpenIDImplicitGrant)
     self.jwt_builder = JsonWebToken()
     self.now = now  # type: ignore
     self.uuidgen = uuidgen  # type: ignore
Esempio n. 3
0
 def __init__(self):
     self.db = AuthDB()  # place to store session information
     self.log = sLog
     self.idps = IdProviderFactory()
     self.proxyCli = ProxyManagerClient()  # take care about proxies
     self.tokenCli = TokenManagerClient()  # take care about tokens
     # The authorization server has its own settings, but they are standardized
     self.metadata = collectMetadata()
     self.metadata.validate()
     # Initialize AuthorizationServer
     _AuthorizationServer.__init__(self, scopes_supported=self.metadata["scopes_supported"])
     # authlib requires the following methods:
     # The following `save_token` method is called when requesting a new access token to save it after it is generated.
     # Let's skip this step, because getting tokens and saving them if necessary has already taken place in `generate_token` method.
     self.save_token = lambda x, y: None
     # Framework integration can re-implement this method to support signal system.
     # But in this implementation, this system is not used.
     self.send_signal = lambda *x, **y: None
     # The main method that will return an access token to the user (this can be a proxy)
     self.generate_token = self.generateProxyOrToken
     # Register configured grants
     self.register_grant(RefreshTokenGrant)  # Enable refreshing tokens
     # Enable device code flow
     self.register_grant(DeviceCodeGrant)
     self.register_endpoint(DeviceAuthorizationEndpoint)
     self.register_endpoint(RevocationEndpoint)  # Enable revokation tokens
     self.register_grant(AuthorizationCodeGrant, [CodeChallenge(required=True)])  # Enable authorization code flow
Esempio n. 4
0
def config_oauth(app):
    authorization.init_app(app)

    authorization.register_grant(AuthorizationCodeGrant,
                                 [CodeChallenge(required=True)])

    # support revocation
    revocation_cls = create_revocation_endpoint(db.session, OAuth2Token)
    authorization.register_endpoint(revocation_cls)

    # protect resource
    bearer_cls = create_bearer_token_validator(db.session, OAuth2Token)
    require_oauth.register_token_validator(bearer_cls())
Esempio n. 5
0
def config_oauth(app):
    authorization.init_app(app)

    authorization.register_grant(grants.ImplicitGrant)
    authorization.register_grant(grants.ClientCredentialsGrant)
    authorization.register_grant(AuthorizationCodeGrant,
                                 [CodeChallenge(required=True)])
    authorization.register_grant(PasswordGrant)
    authorization.register_grant(RefreshTokenGrant)

    revocation_cls = create_revocation_endpoint(db.session, OAuth2TokenModel)
    authorization.register_endpoint(revocation_cls)

    bearer_cls = create_bearer_token_validator(db.session, OAuth2TokenModel)
    require_oauth.register_token_validator(bearer_cls())
Esempio n. 6
0
def init_app(app):
    oauth.init_app(app, query_client=query_client, save_token=save_token)

    # support all grants
    oauth.register_grant(AuthorizationCodeGrant,
                         [CodeChallenge(required=True)])
    oauth.register_grant(PasswordGrant)
    oauth.register_grant(RefreshTokenGrant)
    oauth.register_grant(grants.ClientCredentialsGrant)

    # support revocation endpoint
    oauth.register_endpoint(RevokeToken)

    require_oauth.register_token_validator(BearerToken())
    app.register_blueprint(blueprint)
Esempio n. 7
0
def config_oauth(app):
    authorization.init_app(app)
    authorization.register_client_auth_method(
        'client_secret_json', authenticate_client_secret_json)

    # supported grant types
    authorization.register_grant(ClientCredentialsGrant)
    authorization.register_grant(AuthorizationCodeGrant, [CodeChallenge(required=False)])

    # support revocation
    revocation_cls = create_revocation_endpoint(db.session, OAuth2Token)
    authorization.register_endpoint(revocation_cls)

    # protect resource
    bearer_cls = create_bearer_token_validator(db.session, OAuth2Token)
    require_oauth.register_token_validator(bearer_cls())
Esempio n. 8
0
 def __init__(self, app=None):
     super().__init__(app,
                      query_client=create_query_client_func(db.session, Client),
                      save_token=create_save_token_func(db.session, Token))
     # register it to grant endpoint
     self.register_grant(AuthorizationCodeGrant, [
         OpenIDCode(require_nonce=True),
         CodeChallenge(required=True)
     ])
     # register it to grant endpoint
     self.register_grant(grants.ImplicitGrant)
     # register it to grant endpoint
     self.register_grant(PasswordGrant)
     # register it to grant endpoint
     self.register_grant(ClientCredentialsGrant)
     # register it to grant endpoint
     self.register_grant(RefreshTokenGrant)
Esempio n. 9
0
def config_oauth(app):
    authorization.init_app(app,
                           query_client=query_client,
                           save_token=save_token)

    # support all grants
    authorization.register_grant(grants.ClientCredentialsGrant)
    authorization.register_grant(AuthorizationCodeGrant,
                                 [CodeChallenge(required=True)])
    authorization.register_grant(RefreshTokenGrant)

    # support revocation
    revocation_cls = create_revocation_endpoint(db.session, OAuth2Token)
    authorization.register_endpoint(revocation_cls)

    # protect resource
    bearer_cls = create_bearer_token_validator(db.session, OAuth2Token)
    require_oauth.register_token_validator(bearer_cls())
Esempio n. 10
0
def config_oauth(app):
    app.config['OAUTH2_ACCESS_TOKEN_GENERATOR'] = gen_access_token
    authorization.init_app(app)
    authorization.register_grant(AuthorizationCodeGrant,
                                 [CodeChallenge(required=True)])
        credential.revoked = True
        db.session.add(credential)
        db.session.commit()


query_client = create_query_client_func(db.session, OAuth2Client)
save_token = create_save_token_func(db.session, OAuth2Token)
authorization = AuthorizationServer(
    query_client=query_client,
    save_token=save_token,
)
require_oauth = ResourceProtector()

authorization.init_app(app)

# support all grants
# authorization.register_grant(grants.ImplicitGrant)
authorization.register_grant(grants.ClientCredentialsGrant)
authorization.register_grant(AuthorizationCodeGrant,
                             [CodeChallenge(required=True)])
authorization.register_grant(PasswordGrant)
authorization.register_grant(RefreshTokenGrant)

# support revocation
revocation_cls = create_revocation_endpoint(db.session, OAuth2Token)
authorization.register_endpoint(revocation_cls)

# protect resource
bearer_cls = create_bearer_token_validator(db.session, OAuth2Token)
require_oauth.register_token_validator(bearer_cls())
Esempio n. 12
0
    id_token_signing_alg_values_supported=[config.JWT_ALGORITHM],
)
auth = AuthorizationServer(Client.get, save_token, metadata, ClientAuthentication)
metadata["token_endpoint_auth_methods_supported"] = auth.auth_methods
if config.DEBUG:
    from unittest.mock import patch

    mock = {}
    for key in ("issuer", "authorization_endpoint", "token_endpoint", "jwks_uri"):
        mock[key] = metadata[key].replace("http://", "https://")
    with patch.dict(metadata, mock):
        metadata.validate()
else:
    metadata.validate()
auth.register_grant(
    AuthorizationCodeGrant, [OpenIDCode(require_nonce=True), CodeChallenge()],
)
auth.register_grant(RefreshTokenGrant)


def access_token(raise_on_error=True):
    def _access_token(
        security_scopes: SecurityScopes,
        token: Tuple[bool, Union[dict, HTTPException]] = Depends(oidc_scheme),
    ) -> Optional[dict]:
        success, payload = token
        if not success:
            if raise_on_error:
                raise payload
            else:
                return None
Esempio n. 13
0
def _init_oauth():
    """
    Initialize Afterglow OAuth2 server

    :return: None
    """
    from authlib.oauth2.rfc6749 import ClientMixin
    from authlib.oauth2.rfc6749 import grants
    from authlib.oauth2.rfc7636 import CodeChallenge
    from authlib.integrations.sqla_oauth2 import (OAuth2AuthorizationCodeMixin,
                                                  OAuth2TokenMixin,
                                                  create_save_token_func)
    from authlib.integrations.flask_oauth2 import AuthorizationServer
    from .resources.users import DbUser, db

    global Token, oauth_server

    class OAuth2Client(ClientMixin):
        """
        OAuth2 client definition class
        """
        name = None
        description = None
        client_id = None
        client_secret = None
        redirect_uris = None
        default_scopes = ('email', )
        token_endpoint_auth_method = 'client_secret_basic'
        allowed_grant_types = (
            'authorization_code',
            'refresh_token',
        )

        def __init__(self, **kwargs):
            """
            Initialize OAuth2 client from a dictionary of attributes

            :param kwargs: client attributes; the following are required::
                    - name: client name
                    - client_id: a random string
                    - client_secret: a random string
                    - redirect_uris: a list of redirect uris; the first one
                        is used by default
                    - consent_uri: redirect URI of the user consent page

                Optional attributes::
                    - description: client description
                    - default_scopes: list of default scopes of the client
                    - token_endpoint_auth_method: RFC7591 token endpoint
                        authentication method: "none" (public client),
                        "client_secret_post" (client uses the HTTP POST
                        parameters), or "client_secret_basic" (client uses
                        basic HTTP auth)
                    - allowed_grant_types: list of allowed grant types,
                        including "authorization_code", "implicit",
                        "client_credentials", and "password"
            """
            for name, val in kwargs.items():
                setattr(self, name, val)

            if self.name is None:
                raise ValueError('Missing OAuth client name')
            if self.client_id is None:
                raise ValueError('Missing OAuth client ID')

            if not self.redirect_uris:
                raise ValueError('Missing OAuth redirect URIs')

            if self.token_endpoint_auth_method not in ('none',
                                                       'client_secret_post',
                                                       'client_secret_basic'):
                raise ValueError('Invalid token endpoint auth method')

            if self.token_endpoint_auth_method != 'none' and \
                    self.client_secret is None:
                raise ValueError('Missing OAuth client secret')

            if self.description is None:
                self.description = self.name

        def get_client_id(self) -> str:
            """Return ID of the client"""
            return self.client_id

        def get_default_redirect_uri(self) -> str:
            """Return client default redirect_uri"""
            return self.redirect_uris[0]

        def get_allowed_scope(self, scope: str) -> str:
            """
            Return requested scopes which are supported by this client

            :param scope: requested scope(s), multiple scopes are separated
                by spaces
            """
            if scope is None:
                scope = ''
            return ' '.join(
                {s
                 for s in scope.split() if s in self.default_scopes})

        def check_redirect_uri(self, redirect_uri: str) -> bool:
            """Validate redirect_uri parameter in authorization endpoints

            :param redirect_uri: URL string for redirecting.

            :return: True if valid redirect URI
            """
            return redirect_uri in self.redirect_uris

        def has_client_secret(self) -> bool:
            """Does the client has a secret?"""
            return bool(self.client_secret)

        def check_client_secret(self, client_secret: str) -> bool:
            """Validate client_secret

            :param client_secret: client secret

            :return: True if client secret matches the stored value
            """
            return client_secret == self.client_secret

        def check_token_endpoint_auth_method(self, method: str) -> bool:
            """Validate token endpoint auth method

            :param method: token endpoint auth method

            :return: True if the given token endpoint auth method matches
                the one for the server
            """
            return method == self.token_endpoint_auth_method

        def check_response_type(self, response_type: str) -> bool:
            """Check that the client can handle the given response_type

            :param response_type: requested response_type

            :return: True if a valid response type
            """
            return response_type in ('code', 'token')

        def check_grant_type(self, grant_type: str) -> bool:
            """Check that the client can handle the given grant_type

            :param grant_type: requested grant type

            :return: True if grant type is supported by client
            """
            return grant_type in self.allowed_grant_types

    class OAuth2AuthorizationCode(db.Model, OAuth2AuthorizationCodeMixin):
        __tablename__ = 'oauth_codes'
        __table_args__ = dict(sqlite_autoincrement=True)

        id = db.Column(db.Integer, primary_key=True)
        user_id = db.Column(db.Integer,
                            db.ForeignKey('users.id', ondelete='CASCADE'),
                            nullable=False)

        user = db.relationship('DbUser', uselist=False, backref='oauth_codes')

    class _Token(db.Model, OAuth2TokenMixin):
        """
        Token object; stored in the memory database
        """
        __tablename__ = 'oauth_tokens'
        __table_args__ = dict(sqlite_autoincrement=True)

        id = db.Column(db.Integer, primary_key=True)
        user_id = db.Column(db.Integer,
                            db.ForeignKey('users.id', ondelete='CASCADE'),
                            nullable=False)
        # override Mixin to set default=oauth2
        token_type = db.Column(db.String(40), default='oauth2')
        note = db.Column(db.Text, default='')

        user = db.relationship('DbUser', uselist=False, backref='oauth_tokens')

        @property
        def active(self) -> bool:
            if self.revoked:
                return False
            if not self.expires_in:
                return True
            return self.issued_at + self.expires_in >= time.time()

        def is_refresh_token_active(self):
            if self.revoked:
                return False
            expires_at = self.issued_at + \
                app.config.get('REFRESH_TOKEN_EXPIRES')
            return expires_at >= time.time()

    Token = _Token

    class AuthorizationCodeGrant(grants.AuthorizationCodeGrant):
        def save_authorization_code(self, code, req) -> None:
            """Save authorization_code for later use"""
            try:
                # noinspection PyArgumentList
                code_challenge = request.data.get('code_challenge')
                code_challenge_method = request.data.get(
                    'code_challenge_method')

                # noinspection PyArgumentList
                db.session.add(
                    OAuth2AuthorizationCode(
                        code=code,
                        client_id=req.client.client_id,
                        redirect_uri=req.redirect_uri,
                        scope=req.scope,
                        user_id=req.user.id,
                        code_challenge=code_challenge,
                        code_challenge_method=code_challenge_method,
                    ))
                db.session.commit()
            except Exception:
                db.session.rollback()
                raise

        def query_authorization_code(self, code, client) \
                -> OAuth2AuthorizationCode:
            item = OAuth2AuthorizationCode.query.filter_by(
                code=code, client_id=client.client_id).first()
            if item and not item.is_expired():
                return item

        def delete_authorization_code(self, authorization_code) -> None:
            try:
                db.session.delete(authorization_code)
                db.session.commit()
            except Exception:
                db.session.rollback()
                raise

        def authenticate_user(self, authorization_code) -> DbUser:
            return DbUser.query.get(authorization_code.user_id)

    class RefreshTokenGrant(grants.RefreshTokenGrant):
        def authenticate_refresh_token(self, refresh_token) -> Token:
            token = Token.query \
                .filter_by(refresh_token=refresh_token) \
                .first()
            if token and token.is_refresh_token_active():
                return token

        def authenticate_user(self, credential) -> DbUser:
            return credential.user

        def revoke_old_credential(self, credential) -> None:
            credential.revoked = True
            try:
                db.session.add(credential)
                db.session.commit()
            except Exception:
                db.session.rollback()
                raise

    for client_def in app.config.get('OAUTH_CLIENTS', []):
        oauth_clients[client_def.get('client_id')] = OAuth2Client(**client_def)

    def access_token_generator(*_):
        return secrets.token_hex(20)

    # Configure Afterglow OAuth2 tokens
    app.config['OAUTH2_ACCESS_TOKEN_GENERATOR'] = \
        app.config['OAUTH2_REFRESH_TOKEN_GENERATOR'] = \
        access_token_generator

    oauth_server = AuthorizationServer(
        app,
        query_client=lambda client_id: oauth_clients.get(client_id),
        save_token=create_save_token_func(db.session, Token),
    )
    oauth_server.register_grant(grants.ImplicitGrant)
    oauth_server.register_grant(grants.ClientCredentialsGrant)
    oauth_server.register_grant(AuthorizationCodeGrant,
                                [CodeChallenge(required=True)])
    oauth_server.register_grant(RefreshTokenGrant)

    app.logger.info('Initialized Afterglow OAuth2 Service')
Esempio n. 14
0
metadata["token_endpoint_auth_methods_supported"] = auth.auth_methods
if config.DEBUG:
    from unittest.mock import patch

    mock = {}
    for key in ("issuer", "authorization_endpoint", "token_endpoint",
                "jwks_uri"):
        mock[key] = metadata[key].replace("http://", "https://")
    with patch.dict(metadata, mock):
        metadata.validate()
else:
    metadata.validate()
auth.register_grant(
    AuthorizationCodeGrant,
    [OpenIDCode(require_nonce=True),
     CodeChallenge()],
)
auth.register_grant(RefreshTokenGrant)


def access_token(raise_on_error=True):
    def _access_token(
        security_scopes: SecurityScopes,
        token: Tuple[bool, Union[dict, HTTPException]] = Depends(oidc_scheme),
    ) -> Optional[dict]:
        success, payload = token
        if not success:
            if raise_on_error:
                raise payload
            else:
                return None