コード例 #1
0
    def validate_token_request(self, request):
        # We need to set these at None by default otherwise
        # we are going to get some AttributeError later
        request._params.setdefault("backend", None)
        request._params.setdefault("client_secret", None)

        if request.grant_type != 'convert_token':
            raise errors.UnsupportedGrantTypeError(request=request)

        # We check that a token parameter is present.
        # It should contain the social token to be used with the backend
        if request.token is None:
            raise errors.InvalidRequestError(
                description='Missing token parameter.', request=request)

        # We check that a provider parameter is present.
        # It should contain the name of the social provider to be used
        if request.provider is None:
            raise errors.InvalidRequestError(
                description='Missing provider parameter.', request=request)

        if not request.client_id:
            raise errors.MissingClientIdError(request=request)

        if not self.request_validator.validate_client_id(
                request.client_id, request):
            raise errors.InvalidClientIdError(request=request)

        # Existing code to retrieve the application instance from the client id
        if self.request_validator.client_authentication_required(request):
            logger.debug('Authenticating client, %r.', request)
            if not self.request_validator.authenticate_client(request):
                logger.debug('Invalid client (%r), denying access.', request)
                raise errors.InvalidClientError(request=request)
        elif not self.request_validator.authenticate_client_id(
                request.client_id, request):
            logger.debug('Client authentication failed, %r.', request)
            raise errors.InvalidClientError(request=request)

        # Ensure client is authorized use of this grant type
        # We chose refresh_token as a grant_type
        # as we don't want to modify all the codebase.
        # It is also the most permissive and logical grant for our needs.
        request.grant_type = "refresh_token"
        self.validate_grant_type(request)
        self.validate_scopes(request)

        # TODO: Implement logic to decide whether or not to grant the access_token
        # Decoded body is a list of tuple, dict is better
        body = {}
        for t in request.decoded_body:
            body[t[0]] = t[1]

        user = get_user_by_access_token(token=body['token'],
                                        provider=body['provider'],
                                        nonce=body['nonce'])
        if user is None:
            raise errors.InvalidClientError(
                "Authentication token for the given provider is invalid")
        request.user = user
コード例 #2
0
    def create_token_response(self, request, token_handler):
        """
        Follows the same routine as password token grant in oauthlib
        """
        headers = self._get_default_headers()

        try:
            if self.request_validator.client_authentication_required(request):
                log.debug('Authenticating client, %r.', request)
                if not self.request_validator.authenticate_client(request):
                    log.debug('Client authentication failed, %r.', request)
                    raise errors.InvalidClientError(request=request)
            elif not self.request_validator.authenticate_client_id(
                    request.client_id, request):
                log.debug('Client authentication failed, %r.', request)
                raise errors.InvalidClientError(request=request)
            log.debug('Validating access token request, %r.', request)
            self.validate_token_request(request)
        except errors.OAuth2Error as e:
            log.debug('Client error in token request, %s.', e)
            headers.update(e.headers)
            return headers, e.json, e.status_code

        token = token_handler.create_token(request, self.refresh_token)

        for modifier in self._token_modifiers:
            token = modifier(token)

        self.request_validator.save_token(token, request)

        log.debug('Issuing new token to client id %r (%r), %r.',
                  request.client_id, request.client, token)
        return headers, json.dumps(token), 200
コード例 #3
0
    def validate_token_request(self, request):
        """
        Same as original method (of AuthorizationCodeGrant) except for
        letting out the request_uri and request_uri validations
        :param request:
        :return:
        """
        if request.grant_type != 'authorization_code_push':
            raise errors.UnsupportedGrantTypeError(request=request)

        if request.code is None:
            raise errors.InvalidRequestError(
                description='Missing code parameter.', request=request)

        for param in ('client_id', 'grant_type'):
            if param in request.duplicate_params:
                raise errors.InvalidRequestError(description='Duplicate %s parameter.' % param,
                                                 request=request)

        if self.request_validator.client_authentication_required(request):
            if not self.request_validator.authenticate_client(request):
                log.debug('Client authentication failed, %r.', request)
                raise errors.InvalidClientError(request=request)
        elif not self.request_validator.authenticate_client_id(request.client_id, request):
            log.debug('Client authentication failed, %r.', request)
            raise errors.InvalidClientError(request=request)

        if not hasattr(request.client, 'client_id'):
            raise NotImplementedError('Authenticate client must set the '
                                      'request.client.client_id attribute '
                                      'in authenticate_client.')

        self.validate_grant_type(request)

        if not self.request_validator.validate_code(request.client_id,
                                                    request.code, request.client, request):
            log.debug('Client, %r (%r), is not allowed access to scopes %r.',
                      request.client_id, request.client, request.scopes)
            raise errors.InvalidGrantError(request=request)

        for attr in ('user', 'scopes'):
            if getattr(request, attr, None) is None:
                log.debug('request.%s was not set on code validation.', attr)
コード例 #4
0
ファイル: base.py プロジェクト: Maikflow/freelancer_jobs
 def authenticate_client(self, request):
     log.debug('Authenticating client, %r.', request)
     if not self.request_validator.authenticate_client(request):
         log.debug('Client authentication failed, %r.', request)
         raise errors.InvalidClientError(request=request)
     else:
         if not hasattr(request.client, 'client_id'):
             raise NotImplementedError('Authenticate client must set the '
                                       'request.client.client_id attribute '
                                       'in authenticate_client.')
コード例 #5
0
    def create_token_response(self, request, token_handler):
        """Return token or error in json format.

        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`_.

        .. _`Section 5.1`: http://tools.ietf.org/html/rfc6749#section-5.1
        .. _`Section 5.2`: http://tools.ietf.org/html/rfc6749#section-5.2
        """
        headers = {
            'Content-Type': 'application/json',
            'Cache-Control': 'no-store',
            'Pragma': 'no-cache',
        }
        try:
            if self.request_validator.client_authentication_required(request):
                log.debug('Authenticating client, %r.', request)
                if not self.request_validator.authenticate_client(request):
                    log.debug('Client authentication failed, %r.', request)
                    raise errors.InvalidClientError(request=request)
            elif not self.request_validator.authenticate_client_id(
                    request.client_id, request):
                log.debug('Client authentication failed, %r.', request)
                raise errors.InvalidClientError(request=request)
            log.debug('Validating access token request, %r.', request)
            self.validate_token_request(request)
        except errors.OAuth2Error as e:
            log.debug('Client error in token request, %s.', e)
            return headers, e.json, e.status_code

        token = token_handler.create_token(request, self.refresh_token)
        log.debug('Issuing token %r to client id %r (%r) and username %s.',
                  token, request.client_id, request.client, request.username)
        return headers, json.dumps(token), 200
コード例 #6
0
ファイル: jwt_grant.py プロジェクト: fuelpress/i.fuel.press
    def validate_token_request(self, request):
        """
        Validates a token request.

        Sets the ``client_id`` property on the passed-in request to the JWT
        issuer, and finds the user based on the JWT subject and sets it as
        the ``user`` property.

        Raises subclasses of ``oauthlib.oauth2.rfc6749.OAuth2Error`` when
        validation fails.

        :param request: the oauthlib request
        :type request: oauthlib.common.Request
        """

        try:
            assertion = request.assertion
        except AttributeError:
            raise errors.InvalidRequestFatalError("Missing assertion.")

        token = JWTGrantToken(assertion)

        # Update client_id in oauthlib request
        request.client_id = token.issuer

        if not self.request_validator.authenticate_client_id(
            request.client_id, request
        ):
            raise errors.InvalidClientError(request=request)

        # Ensure client is authorized use of this grant type
        self.validate_grant_type(request)

        authclient = request.client.authclient

        verified_token = token.verified(key=authclient.secret, audience=self.domain)

        user = self.user_svc.fetch(verified_token.subject)
        if user is None:
            raise errors.InvalidGrantError(
                "Grant token subject (sub) could not be found."
            )

        if user.authority != authclient.authority:
            raise errors.InvalidGrantError(
                "Grant token subject (sub) does not match issuer (iss)."
            )

        request.user = user
コード例 #7
0
    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
コード例 #8
0
    def validate_token_request(self, request):
        # This method's code is based on the parent method's code
        # We removed the original comments to replace with ours
        # explaining our modifications.

        # We need to set these at None by default otherwise
        # we are going to get some AttributeError later
        request._params.setdefault("backend", None)
        request._params.setdefault("client_secret", None)

        if request.grant_type != 'convert_token':
            raise errors.UnsupportedGrantTypeError(request=request)

        # We check that a token parameter is present.
        # It should contain the social token to be used with the backend
        if request.token is None:
            raise errors.InvalidRequestError(
                description='Missing token parameter.', request=request)

        # We check that a backend parameter is present.
        # It should contain the name of the social backend to be used
        if request.backend is None:
            raise errors.InvalidRequestError(
                description='Missing backend parameter.', request=request)

        if not request.client_id:
            raise errors.MissingClientIdError(request=request)

        if not self.request_validator.validate_client_id(
                request.client_id, request):
            raise errors.InvalidClientIdError(request=request)

        # Existing code to retrieve the application instance from the client id
        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)
        elif not self.request_validator.authenticate_client_id(
                request.client_id, request):
            log.debug('Client authentication failed, %r.', request)
            raise errors.InvalidClientError(request=request)

        # Ensure client is authorized use of this grant type
        # We chose refresh_token as a grant_type
        # as we don't want to modify all the codebase.
        # It is also the most permissive and logical grant for our needs.
        request.grant_type = "refresh_token"
        self.validate_grant_type(request)

        self.validate_scopes(request)

        # TODO: Find a better way to pass the django request object
        strategy = load_strategy(request=request.django_request)

        try:
            backend = load_backend(
                strategy, request.backend,
                reverse(NAMESPACE + ":complete", args=(request.backend, )))
        except MissingBackend:
            raise errors.InvalidRequestError(
                description='Invalid backend parameter.', request=request)

        try:
            user = backend.do_auth(access_token=request.token)
        except requests.HTTPError as e:
            raise errors.InvalidRequestError(
                description="Backend responded with HTTP{0}: {1}.".format(
                    e.response.status_code, e.response.text),
                request=request)
        except SocialAuthBaseException as e:
            raise errors.AccessDeniedError(description=str(e), request=request)

        if not user:
            raise errors.InvalidGrantError('Invalid credentials given.',
                                           request=request)

        if not user.is_active:
            raise errors.InvalidGrantError('User inactive or deleted.',
                                           request=request)

        request.user = user
        log.debug('Authorizing access to user %r.', request.user)
コード例 #9
0
    def validate_token_request(self, request):
        """
        :param request: OAuthlib request.
        :type request: oauthlib.common.Request
        """
        # REQUIRED. Value MUST be set to "authorization_code".
        if request.grant_type not in ('authorization_code', 'openid'):
            raise errors.UnsupportedGrantTypeError(request=request)

        for validator in self.custom_validators.pre_token:
            validator(request)

        if request.code is None:
            raise errors.InvalidRequestError(
                description='Missing code parameter.', request=request)

        for param in ('client_id', 'grant_type', 'redirect_uri'):
            if param in request.duplicate_params:
                raise errors.InvalidRequestError(
                    description='Duplicate %s parameter.' % param,
                    request=request)

        if self.request_validator.client_authentication_required(request):
            # If the client type is confidential or the client was issued client
            # credentials (or assigned other authentication requirements), the
            # client MUST authenticate with the authorization server as described
            # in Section 3.2.1.
            # https://tools.ietf.org/html/rfc6749#section-3.2.1
            if not self.request_validator.authenticate_client(request):
                log.debug('Client authentication failed, %r.', request)
                raise errors.InvalidClientError(request=request)
        elif not self.request_validator.authenticate_client_id(
                request.client_id, request):
            # REQUIRED, if the client is not authenticating with the
            # authorization server as described in Section 3.2.1.
            # https://tools.ietf.org/html/rfc6749#section-3.2.1
            log.debug('Client authentication failed, %r.', request)
            raise errors.InvalidClientError(request=request)

        if not hasattr(request.client, 'client_id'):
            raise NotImplementedError('Authenticate client must set the '
                                      'request.client.client_id attribute '
                                      'in authenticate_client.')

        request.client_id = request.client_id or request.client.client_id

        # Ensure client is authorized use of this grant type
        self.validate_grant_type(request)

        # REQUIRED. The authorization code received from the
        # authorization server.
        if not self.request_validator.validate_code(
                request.client_id, request.code, request.client, request):
            log.debug('Client, %r (%r), is not allowed access to scopes %r.',
                      request.client_id, request.client, request.scopes)
            raise errors.InvalidGrantError(request=request)

        # OPTIONAL. Validate PKCE code_verifier
        challenge = self.request_validator.get_code_challenge(
            request.code, request)

        if challenge is not None:
            if request.code_verifier is None:
                raise errors.MissingCodeVerifierError(request=request)

            challenge_method = self.request_validator.get_code_challenge_method(
                request.code, request)
            if challenge_method is None:
                raise errors.InvalidGrantError(
                    request=request, description="Challenge method not found")

            if challenge_method not in self._code_challenge_methods:
                raise errors.ServerError(
                    description="code_challenge_method {} is not supported.".
                    format(challenge_method),
                    request=request)

            if not self.validate_code_challenge(challenge, challenge_method,
                                                request.code_verifier):
                log.debug('request provided a invalid code_verifier.')
                raise errors.InvalidGrantError(request=request)
        elif self.request_validator.is_pkce_required(request.client_id,
                                                     request) is True:
            if request.code_verifier is None:
                raise errors.MissingCodeVerifierError(request=request)
            raise errors.InvalidGrantError(request=request,
                                           description="Challenge not found")

        for attr in ('user', 'scopes'):
            if getattr(request, attr, None) is None:
                log.debug('request.%s was not set on code validation.', attr)

        # REQUIRED, if the "redirect_uri" parameter was included in the
        # authorization request as described in Section 4.1.1, and their
        # values MUST be identical.
        if request.redirect_uri is None:
            request.using_default_redirect_uri = True
            request.redirect_uri = self.request_validator.get_default_redirect_uri(
                request.client_id, request)
            log.debug('Using default redirect_uri %s.', request.redirect_uri)
            if not request.redirect_uri:
                raise errors.MissingRedirectURIError(request=request)
        else:
            request.using_default_redirect_uri = False
            log.debug('Using provided redirect_uri %s', request.redirect_uri)

        if not self.request_validator.confirm_redirect_uri(
                request.client_id, request.code, request.redirect_uri,
                request.client, request):
            log.debug('Redirect_uri (%r) invalid for client %r (%r).',
                      request.redirect_uri, request.client_id, request.client)
            raise errors.MismatchingRedirectURIError(request=request)

        for validator in self.custom_validators.post_token:
            validator(request)
コード例 #10
0
    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)