def validate_scopes(self, request): if not request.scopes: request.scopes = utils.scope_to_list(request.scope) or utils.scope_to_list( self.request_validator.get_default_scopes(request.client_id, request)) log.debug('Validating access to scopes %r for client %r (%r).', request.scopes, request.client_id, request.client) if not self.request_validator.validate_scopes(request.client_id, request.scopes, request.client, request): raise errors.InvalidScopeError(state=request.state, request=request)
def validate_scopes(self, request): """ :param request: OAuthlib request. :type request: oauthlib.common.Request """ if not request.scopes: request.scopes = utils.scope_to_list(request.scope) or utils.scope_to_list( self.request_validator.get_default_scopes(request.client_id, request)) log.debug('Validating access to scopes %r for client %r (%r).', request.scopes, request.client_id, request.client) if not self.request_validator.validate_scopes(request.client_id, request.scopes, request.client, request): raise errors.InvalidScopeError(request=request)
def validate_token_request(self, request): if request.grant_type != JWT_BEARER: raise errors.UnsupportedGrantTypeError(request=request) if request.assertion is None: raise errors.InvalidRequestError('Missing assertion parameter.', request=request) for param in ('grant_type', 'scope'): if param in request.duplicate_params: raise errors.InvalidRequestError('Duplicate %s parameter.' % param, request=request) # Since the JSON Web Token is signed by its issuer client # authentication is not strictly required when the token is used as # an authorization grant. However, if client credentials are provided # they should be validated as describe in Section 3.1. # https://tools.ietf.org/html/draft-ietf-oauth-jwt-bearer-12#section-3.1 if self.request_validator.client_authentication_required(request): log.debug('Authenticating client, %r.', request) if not self.request_validator.authenticate_client(request): log.debug('Invalid client (%r), denying access.', request) raise errors.InvalidClientError(request=request) # REQUIRED. The web token issued by the client. log.debug('Validating assertion %s.', request.assertion) if not self.request_validator.validate_bearer_token( request.assertion, request.scopes, request): log.debug('Invalid assertion, %s, for client %r.', request.assertion, request.client) raise errors.InvalidGrantError('Invalid assertion.', request=request) original_scopes = utils.scope_to_list( self.request_validator.get_original_scopes(request.assertion, request)) if request.scope: request.scopes = utils.scope_to_list(request.scope) if (not all((s in original_scopes for s in request.scopes)) and not self.request_validator.is_within_original_scope( request.scopes, request.refresh_token, request)): log.debug('Refresh token %s lack requested scopes, %r.', request.refresh_token, request.scopes) raise errors.InvalidScopeError(request=request) else: request.scopes = original_scopes
def validate_token_request(self, request): """validate token request by request attributes and jwt claims.""" # pylint: disable-msg=too-many-branches # REQUIRED. Per http://tools.ietf.org/html/rfc7523#section-2.1, value # MUST be set to "urn:ietf:params:oauth:grant-type:jwt-bearer". # Note: support for receiving client authentication jwt requests # following the parameter values specified by rfc7523 section 2.2 is # not yet implemented https://tools.ietf.org/html/rfc7523#section-2.2 if request.grant_type != 'urn:ietf:params:oauth:grant-type:jwt-bearer': raise errors.UnsupportedGrantTypeError(request=request) for validator in self.custom_validators.pre_token: validator(request) if getattr(request, 'assertion', None) is None: raise errors.InvalidRequestError( description='Missing jwt assertion parameter.', request=request) elif len(request.assertion.split(',')) > 1: raise errors.InvalidRequestError( description='Assertion MUST NOT contain more than one JWT', request=request) # Jwt MUST contain exp claim per # https://tools.ietf.org/html/rfc7523#section-3. Signature verification # is postponed for handling in request_validator after retrieval of # public key matching correct client. options = {'verify_signature': False, 'require_exp': True} # The JWT MUST contain an "aud" (audience) claim containing a # value that identifies the authorization server as an intended # audience. audience = self.request_validator.get_audience(request) try: payload = jwt.decode(request.assertion, '', audience=audience, options=options, algorithms=['RS256']) except jwt.ExpiredSignatureError: raise errors.InvalidGrantError( description='JWT request contains an expired signature', request=request) except jwt.ImmatureSignatureError: raise errors.InvalidGrantError( description='JWT is not yet valid (nbf)', request=request) except jwt.InvalidAudienceError: raise errors.InvalidGrantError( description='JWT request contains invalid audience claim', request=request) except jwt.MissingRequiredClaimError: raise errors.InvalidGrantError( description='JWT is missing a required claim', request=request) except jwt.DecodeError: raise errors.InvalidGrantError( description='One of more errors occurred during JWT decode', request=request) # The JWT MUST contain an "iss" (issuer) claim that contains a # unique identifier for the entity that issued the JWT. In the # absence of an application profile specifying otherwise, # compliant applications MUST compare issuer values using the # Simple String Comparison method defined in Section 6.2.1 of RFC # 3986 [RFC3986] https://tools.ietf.org/html/rfc7523#section-3 if not self.request_validator.validate_issuer(request, payload): msg = 'Missing or invalid (iss) claim' log_msg = INVALID_CLAIM.format(msg=msg, payload=payload) log.debug(log_msg) raise errors.InvalidGrantError(description=msg, request=request) # If client_id is not supplied as a param or claim, the issuer must be # client_id. client_id is validated, rather than authenticated, since # authentication is completed by validating token signature. # request.client is set in validate_client_id. if not request.client_id: request.client_id = payload.get('client_id', payload.get('iss')) if not self.request_validator.validate_client_id( request.client_id, request): log.debug('Client authentication failed, %s.', request) raise errors.InvalidClientError(request=request) # A validate_signature method that provides functionality for # retrieving the public key appropriate to the issuer or client_id, # and completes the decoding of the jwt using said key, # MUST be added to the request_validator class. if not self.request_validator.validate_signature( request, request.client, request.assertion): msg = 'Missing or invalid token signature' log_msg = INVALID_TOKEN.format(msg=msg, payload=payload) log.debug(log_msg) raise errors.InvalidGrantError(description=msg, request=request) # Ensure client is authorized use of this grant type. self.validate_grant_type(request) # The request_validator class must include a validate_subject method # that ensures the user ('sub') exists and is active. if not self.request_validator.validate_subject(request, request.client, payload): msg = 'Missing or invalid (sub) claim' log_msg = INVALID_CLAIM.format(msg=msg, payload=payload) log.debug(log_msg) raise errors.InvalidGrantError(description=msg, request=request) # Since a jwt flow acts much like a refresh token flow, giving the # client access to the user's resources without the user present, # server's MAY wish to validate an existing authorization exists # containing explicit offline_access via scope or implied via # refresh token. A validate_offline_access method MUST be added to your # request_validator class; it is recommended that # request.refresh_tokens be set to avoid another query to validate # other scopes against previously authorized scopes. log.debug('Validating offline access for client %s.', request.client) if not self.request_validator.validate_offline_access( request, request.user, request.client): msg = 'Client not authorized for offline_access grants' log_msg = INVALID_CLAIM.format(msg=msg, payload=payload) log.debug(log_msg) raise errors.InvalidGrantError(description=msg, request=request) # A validate_refresh_scopes method must be implemented in the # request_validator class, to either verify that all requested scopes # are within previously authorized scopes, or allow all scopes that # are available to the client. requested_scope = request.scope or payload.get('scope') if not self.request_validator.validate_refresh_scopes( request, getattr(request, 'refresh_tokens', None), requested_scope): log.debug('Client %s lacks requested scopes, %s.', request.client_id, request.scopes) raise errors.InvalidScopeError(request=request) # A jwt MAY contain additional claims. A validate_additional_claims # method should be implemented in the request_validator class to # validate all other jwt claims. if not self.request_validator.validate_additional_claims( request, payload): msg = ("One or more additional claims failed validation. See " "provider for jwt claim requirements") raise errors.InvalidGrantError(description=msg, request=request) for validator in self.custom_validators.post_token: validator(request)