????
Current Path : C:/opt/pgsql/pgAdmin 4/python/Lib/site-packages/authlib/oauth2/rfc7523/ |
Current File : C:/opt/pgsql/pgAdmin 4/python/Lib/site-packages/authlib/oauth2/rfc7523/jwt_bearer.py |
import logging from authlib.jose import jwt, JoseError from ..rfc6749 import BaseGrant, TokenEndpointMixin from ..rfc6749 import ( UnauthorizedClientError, InvalidRequestError, InvalidGrantError, InvalidClientError, ) from .assertion import sign_jwt_bearer_assertion log = logging.getLogger(__name__) JWT_BEARER_GRANT_TYPE = 'urn:ietf:params:oauth:grant-type:jwt-bearer' class JWTBearerGrant(BaseGrant, TokenEndpointMixin): GRANT_TYPE = JWT_BEARER_GRANT_TYPE #: Options for verifying JWT payload claims. Developers MAY #: overwrite this constant to create a more strict options. CLAIMS_OPTIONS = { 'iss': {'essential': True}, 'aud': {'essential': True}, 'exp': {'essential': True}, } @staticmethod def sign(key, issuer, audience, subject=None, issued_at=None, expires_at=None, claims=None, **kwargs): return sign_jwt_bearer_assertion( key, issuer, audience, subject, issued_at, expires_at, claims, **kwargs) def process_assertion_claims(self, assertion): """Extract JWT payload claims from request "assertion", per `Section 3.1`_. :param assertion: assertion string value in the request :return: JWTClaims :raise: InvalidGrantError .. _`Section 3.1`: https://tools.ietf.org/html/rfc7523#section-3.1 """ try: claims = jwt.decode( assertion, self.resolve_public_key, claims_options=self.CLAIMS_OPTIONS) claims.validate() except JoseError as e: log.debug('Assertion Error: %r', e) raise InvalidGrantError(description=e.description) return claims def resolve_public_key(self, headers, payload): client = self.resolve_issuer_client(payload['iss']) return self.resolve_client_key(client, headers, payload) def validate_token_request(self): """The client makes a request to the token endpoint by sending the following parameters using the "application/x-www-form-urlencoded" format per `Section 2.1`_: grant_type REQUIRED. Value MUST be set to "urn:ietf:params:oauth:grant-type:jwt-bearer". assertion REQUIRED. Value MUST contain a single JWT. scope OPTIONAL. The following example demonstrates an access token request with a JWT as an authorization grant: .. code-block:: http POST /token.oauth2 HTTP/1.1 Host: as.example.com Content-Type: application/x-www-form-urlencoded grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer &assertion=eyJhbGciOiJFUzI1NiIsImtpZCI6IjE2In0. eyJpc3Mi[...omitted for brevity...]. J9l-ZhwP[...omitted for brevity...] .. _`Section 2.1`: https://tools.ietf.org/html/rfc7523#section-2.1 """ assertion = self.request.form.get('assertion') if not assertion: raise InvalidRequestError('Missing "assertion" in request') claims = self.process_assertion_claims(assertion) client = self.resolve_issuer_client(claims['iss']) log.debug('Validate token request of %s', client) if not client.check_grant_type(self.GRANT_TYPE): raise UnauthorizedClientError() self.request.client = client self.validate_requested_scope() subject = claims.get('sub') if subject: user = self.authenticate_user(subject) if not user: raise InvalidGrantError(description='Invalid "sub" value in assertion') log.debug('Check client(%s) permission to User(%s)', client, user) if not self.has_granted_permission(client, user): raise InvalidClientError( description='Client has no permission to access user data') self.request.user = user def create_token_response(self): """If valid and authorized, the authorization server issues an access token. """ token = self.generate_token( scope=self.request.scope, user=self.request.user, include_refresh_token=False, ) log.debug('Issue token %r to %r', token, self.request.client) self.save_token(token) return 200, token, self.TOKEN_RESPONSE_HEADER def resolve_issuer_client(self, issuer): """Fetch client via "iss" in assertion claims. Developers MUST implement this method in subclass, e.g.:: def resolve_issuer_client(self, issuer): return Client.query_by_iss(issuer) :param issuer: "iss" value in assertion :return: Client instance """ raise NotImplementedError() def resolve_client_key(self, client, headers, payload): """Resolve client key to decode assertion data. Developers MUST implement this method in subclass. For instance, there is a "jwks" column on client table, e.g.:: def resolve_client_key(self, client, headers, payload): # from authlib.jose import JsonWebKey key_set = JsonWebKey.import_key_set(client.jwks) return key_set.find_by_kid(headers['kid']) :param client: instance of OAuth client model :param headers: headers part of the JWT :param payload: payload part of the JWT :return: ``authlib.jose.Key`` instance """ raise NotImplementedError() def authenticate_user(self, subject): """Authenticate user with the given assertion claims. Developers MUST implement it in subclass, e.g.:: def authenticate_user(self, subject): return User.get_by_sub(subject) :param subject: "sub" value in claims :return: User instance """ raise NotImplementedError() def has_granted_permission(self, client, user): """Check if the client has permission to access the given user's resource. Developers MUST implement it in subclass, e.g.:: def has_granted_permission(self, client, user): permission = ClientUserGrant.query(client=client, user=user) return permission.granted :param client: instance of OAuth client model :param user: instance of User model :return: bool """ raise NotImplementedError()