def wrapper(endpoint, uri, *args, **kwargs): if not endpoint.available: e = TemporarilyUnavailableError() log.info('Endpoint unavailable, ignoring request %s.' % uri) return {}, e.json, 503 if endpoint.catch_errors: try: return f(endpoint, uri, *args, **kwargs) except OAuth2Error: raise except FatalClientError: raise except Exception as e: error = ServerError() log.warning('Exception caught while processing request, %s.' % e) return {}, error.json, 500 else: return f(endpoint, uri, *args, **kwargs)
def validate_request(self, uri, http_method='GET', body=None, headers=None): """Validate a signed OAuth request. :param uri: The full URI of the token request. :param http_method: A valid HTTP verb, i.e. GET, POST, PUT, HEAD, etc. :param body: The request body as a string. :param headers: The request headers as a dict. :returns: A tuple of 2 elements. 1. True if valid, False otherwise. 2. An oauthlib.common.Request object. """ try: request = self._create_request(uri, http_method, body, headers) except errors.OAuth1Error: return False, None try: self._check_transport_security(request) self._check_mandatory_parameters(request) except errors.OAuth1Error: return False, request if not self.request_validator.validate_timestamp_and_nonce( request.client_key, request.timestamp, request.nonce, request): return False, request # The server SHOULD return a 401 (Unauthorized) status code when # receiving a request with invalid client credentials. # Note: This is postponed in order to avoid timing attacks, instead # a dummy client is assigned and used to maintain near constant # time request verification. # # Note that early exit would enable client enumeration valid_client = self.request_validator.validate_client_key( request.client_key, request) if not valid_client: request.client_key = self.request_validator.dummy_client valid_signature = self._check_signature(request) # We delay checking validity until the very end, using dummy values for # calculations and fetching secrets/keys to ensure the flow of every # request remains almost identical regardless of whether valid values # have been supplied. This ensures near constant time execution and # prevents malicious users from guessing sensitive information v = all((valid_client, valid_signature)) if not v: log.info("[Failure] request verification failed.") log.info("Valid client: %s", valid_client) log.info("Valid signature: %s", valid_signature) return v, request
def validate_request_token_request(self, request): """Validate a request token request. :param request: An oauthlib.common.Request object. :raises: OAuth1Error if the request is invalid. :returns: True or False """ self._check_transport_security(request) self._check_mandatory_parameters(request) if request.realm: request.realms = request.realm.split(' ') else: request.realms = self.request_validator.get_default_realms( request.client_key, request) if not self.request_validator.check_realms(request.realms): raise errors.InvalidRequestError( description='Invalid realm %s. Allowed are %r.' % ( request.realms, self.request_validator.realms)) if not request.redirect_uri: raise errors.InvalidRequestError( description='Missing callback URI.') if not self.request_validator.validate_timestamp_and_nonce( request.client_key, request.timestamp, request.nonce, request, request_token=request.resource_owner_key): return False, request # The server SHOULD return a 401 (Unauthorized) status code when # receiving a request with invalid client credentials. # Note: This is postponed in order to avoid timing attacks, instead # a dummy client is assigned and used to maintain near constant # time request verification. # # Note that early exit would enable client enumeration valid_client = self.request_validator.validate_client_key( request.client_key, request) if not valid_client: request.client_key = self.request_validator.dummy_client # Note that `realm`_ is only used in authorization headers and how # it should be interepreted is not included in the OAuth spec. # However they could be seen as a scope or realm to which the # client has access and as such every client should be checked # to ensure it is authorized access to that scope or realm. # .. _`realm`: http://tools.ietf.org/html/rfc2617#section-1.2 # # Note that early exit would enable client realm access enumeration. # # The require_realm indicates this is the first step in the OAuth # workflow where a client requests access to a specific realm. # This first step (obtaining request token) need not require a realm # and can then be identified by checking the require_resource_owner # flag and abscence of realm. # # Clients obtaining an access token will not supply a realm and it will # not be checked. Instead the previously requested realm should be # transferred from the request token to the access token. # # Access to protected resources will always validate the realm but note # that the realm is now tied to the access token and not provided by # the client. valid_realm = self.request_validator.validate_requested_realms( request.client_key, request.realms, request) # Callback is normally never required, except for requests for # a Temporary Credential as described in `Section 2.1`_ # .._`Section 2.1`: http://tools.ietf.org/html/rfc5849#section-2.1 valid_redirect = self.request_validator.validate_redirect_uri( request.client_key, request.redirect_uri, request) if not request.redirect_uri: raise NotImplementedError('Redirect URI must either be provided ' 'or set to a default during validation.') valid_signature = self._check_signature(request) # We delay checking validity until the very end, using dummy values for # calculations and fetching secrets/keys to ensure the flow of every # request remains almost identical regardless of whether valid values # have been supplied. This ensures near constant time execution and # prevents malicious users from guessing sensitive information v = all((valid_client, valid_realm, valid_redirect, valid_signature)) if not v: log.info("[Failure] request verification failed.") log.info("Valid client: %s.", valid_client) log.info("Valid realm: %s.", valid_realm) log.info("Valid callback: %s.", valid_redirect) log.info("Valid signature: %s.", valid_signature) return v, request
def validate_access_token_request(self, request): """Validate an access token request. :param request: An oauthlib.common.Request object. :raises: OAuth1Error if the request is invalid. :returns: A tuple of 2 elements. 1. The validation result (True or False). 2. The request object. """ self._check_transport_security(request) self._check_mandatory_parameters(request) if not request.resource_owner_key: raise errors.InvalidRequestError( description='Missing resource owner.') if not self.request_validator.check_request_token( request.resource_owner_key): raise errors.InvalidRequestError( description='Invalid resource owner key format.') if not request.verifier: raise errors.InvalidRequestError(description='Missing verifier.') if not self.request_validator.check_verifier(request.verifier): raise errors.InvalidRequestError( description='Invalid verifier format.') if not self.request_validator.validate_timestamp_and_nonce( request.client_key, request.timestamp, request.nonce, request, request_token=request.resource_owner_key): return False, request # The server SHOULD return a 401 (Unauthorized) status code when # receiving a request with invalid client credentials. # Note: This is postponed in order to avoid timing attacks, instead # a dummy client is assigned and used to maintain near constant # time request verification. # # Note that early exit would enable client enumeration valid_client = self.request_validator.validate_client_key( request.client_key, request) if not valid_client: request.client_key = self.request_validator.dummy_client # The server SHOULD return a 401 (Unauthorized) status code when # receiving a request with invalid or expired token. # Note: This is postponed in order to avoid timing attacks, instead # a dummy token is assigned and used to maintain near constant # time request verification. # # Note that early exit would enable resource owner enumeration valid_resource_owner = self.request_validator.validate_request_token( request.client_key, request.resource_owner_key, request) if not valid_resource_owner: request.resource_owner_key = self.request_validator.dummy_request_token # The server MUST verify (Section 3.2) the validity of the request, # ensure that the resource owner has authorized the provisioning of # token credentials to the client, and ensure that the temporary # credentials have not expired or been used before. The server MUST # also verify the verification code received from the client. # .. _`Section 3.2`: http://tools.ietf.org/html/rfc5849#section-3.2 # # Note that early exit would enable resource owner authorization # verifier enumertion. valid_verifier = self.request_validator.validate_verifier( request.client_key, request.resource_owner_key, request.verifier, request) valid_signature = self._check_signature(request, is_token_request=True) # We delay checking validity until the very end, using dummy values for # calculations and fetching secrets/keys to ensure the flow of every # request remains almost identical regardless of whether valid values # have been supplied. This ensures near constant time execution and # prevents malicious users from guessing sensitive information v = all((valid_client, valid_resource_owner, valid_verifier, valid_signature)) if not v: log.info("[Failure] request verification failed.") log.info("Valid client:, %s", valid_client) log.info("Valid token:, %s", valid_resource_owner) log.info("Valid verifier:, %s", valid_verifier) log.info("Valid signature:, %s", valid_signature) return v, request
def validate_access_token_request(self, request): """Validate an access token request. :param request: An oauthlib.common.Request object. :raises: OAuth1Error if the request is invalid. :returns: A tuple of 2 elements. 1. The validation result (True or False). 2. The request object. """ self._check_transport_security(request) self._check_mandatory_parameters(request) if not request.resource_owner_key: raise errors.InvalidRequestError( description='Missing resource owner.') if not self.request_validator.check_request_token( request.resource_owner_key): raise errors.InvalidRequestError( description='Invalid resource owner key format.') if not request.verifier: raise errors.InvalidRequestError( description='Missing verifier.') if not self.request_validator.check_verifier(request.verifier): raise errors.InvalidRequestError( description='Invalid verifier format.') if not self.request_validator.validate_timestamp_and_nonce( request.client_key, request.timestamp, request.nonce, request, request_token=request.resource_owner_key): return False, request # The server SHOULD return a 401 (Unauthorized) status code when # receiving a request with invalid client credentials. # Note: This is postponed in order to avoid timing attacks, instead # a dummy client is assigned and used to maintain near constant # time request verification. # # Note that early exit would enable client enumeration valid_client = self.request_validator.validate_client_key( request.client_key, request) if not valid_client: request.client_key = self.request_validator.dummy_client # The server SHOULD return a 401 (Unauthorized) status code when # receiving a request with invalid or expired token. # Note: This is postponed in order to avoid timing attacks, instead # a dummy token is assigned and used to maintain near constant # time request verification. # # Note that early exit would enable resource owner enumeration valid_resource_owner = self.request_validator.validate_request_token( request.client_key, request.resource_owner_key, request) if not valid_resource_owner: request.resource_owner_key = self.request_validator.dummy_request_token # The server MUST verify (Section 3.2) the validity of the request, # ensure that the resource owner has authorized the provisioning of # token credentials to the client, and ensure that the temporary # credentials have not expired or been used before. The server MUST # also verify the verification code received from the client. # .. _`Section 3.2`: http://tools.ietf.org/html/rfc5849#section-3.2 # # Note that early exit would enable resource owner authorization # verifier enumertion. valid_verifier = self.request_validator.validate_verifier( request.client_key, request.resource_owner_key, request.verifier, request) valid_signature = self._check_signature(request, is_token_request=True) # We delay checking validity until the very end, using dummy values for # calculations and fetching secrets/keys to ensure the flow of every # request remains almost identical regardless of whether valid values # have been supplied. This ensures near constant time execution and # prevents malicious users from guessing sensitive information v = all((valid_client, valid_resource_owner, valid_verifier, valid_signature)) if not v: log.info("[Failure] request verification failed.") log.info("Valid client:, %s", valid_client) log.info("Valid token:, %s", valid_resource_owner) log.info("Valid verifier:, %s", valid_verifier) log.info("Valid signature:, %s", valid_signature) return v, request
def validate_protected_resource_request(self, uri, http_method='GET', body=None, headers=None, realms=None): """Create a request token response, with a new request token if valid. :param uri: The full URI of the token request. :param http_method: A valid HTTP verb, i.e. GET, POST, PUT, HEAD, etc. :param body: The request body as a string. :param headers: The request headers as a dict. :param realms: A list of realms the resource is protected under. This will be supplied to the ``validate_realms`` method of the request validator. :returns: A tuple of 2 elements. 1. True if valid, False otherwise. 2. An oauthlib.common.Request object. """ try: request = self._create_request(uri, http_method, body, headers) except errors.OAuth1Error: return False, None try: self._check_transport_security(request) self._check_mandatory_parameters(request) except errors.OAuth1Error: return False, request if not request.resource_owner_key: return False, request if not self.request_validator.check_access_token( request.resource_owner_key): return False, request if not self.request_validator.validate_timestamp_and_nonce( request.client_key, request.timestamp, request.nonce, request, access_token=request.resource_owner_key): return False, request # The server SHOULD return a 401 (Unauthorized) status code when # receiving a request with invalid client credentials. # Note: This is postponed in order to avoid timing attacks, instead # a dummy client is assigned and used to maintain near constant # time request verification. # # Note that early exit would enable client enumeration valid_client = self.request_validator.validate_client_key( request.client_key, request) if not valid_client: request.client_key = self.request_validator.dummy_client # The server SHOULD return a 401 (Unauthorized) status code when # receiving a request with invalid or expired token. # Note: This is postponed in order to avoid timing attacks, instead # a dummy token is assigned and used to maintain near constant # time request verification. # # Note that early exit would enable resource owner enumeration valid_resource_owner = self.request_validator.validate_access_token( request.client_key, request.resource_owner_key, request) if not valid_resource_owner: request.resource_owner_key = self.request_validator.dummy_access_token # Note that `realm`_ is only used in authorization headers and how # it should be interepreted is not included in the OAuth spec. # However they could be seen as a scope or realm to which the # client has access and as such every client should be checked # to ensure it is authorized access to that scope or realm. # .. _`realm`: http://tools.ietf.org/html/rfc2617#section-1.2 # # Note that early exit would enable client realm access enumeration. # # The require_realm indicates this is the first step in the OAuth # workflow where a client requests access to a specific realm. # This first step (obtaining request token) need not require a realm # and can then be identified by checking the require_resource_owner # flag and abscence of realm. # # Clients obtaining an access token will not supply a realm and it will # not be checked. Instead the previously requested realm should be # transferred from the request token to the access token. # # Access to protected resources will always validate the realm but note # that the realm is now tied to the access token and not provided by # the client. valid_realm = self.request_validator.validate_realms( request.client_key, request.resource_owner_key, request, uri=request.uri, realms=realms) valid_signature = self._check_signature(request) # We delay checking validity until the very end, using dummy values for # calculations and fetching secrets/keys to ensure the flow of every # request remains almost identical regardless of whether valid values # have been supplied. This ensures near constant time execution and # prevents malicious users from guessing sensitive information v = all( (valid_client, valid_resource_owner, valid_realm, valid_signature)) if not v: log.info("[Failure] request verification failed.") log.info("Valid client: %s", valid_client) log.info("Valid token: %s", valid_resource_owner) log.info("Valid realm: %s", valid_realm) log.info("Valid signature: %s", valid_signature) return v, request
def validate_protected_resource_request(self, uri, http_method='GET', body=None, headers=None, realms=None): """Create a request token response, with a new request token if valid. :param uri: The full URI of the token request. :param http_method: A valid HTTP verb, i.e. GET, POST, PUT, HEAD, etc. :param body: The request body as a string. :param headers: The request headers as a dict. :param realms: A list of realms the resource is protected under. This will be supplied to the ``validate_realms`` method of the request validator. :returns: A tuple of 2 elements. 1. True if valid, False otherwise. 2. An oauthlib.common.Request object. """ try: request = self._create_request(uri, http_method, body, headers) except errors.OAuth1Error: return False, None try: self._check_transport_security(request) self._check_mandatory_parameters(request) except errors.OAuth1Error: return False, request if not request.resource_owner_key: return False, request if not self.request_validator.check_access_token( request.resource_owner_key): return False, request if not self.request_validator.validate_timestamp_and_nonce( request.client_key, request.timestamp, request.nonce, request, access_token=request.resource_owner_key): return False, request # The server SHOULD return a 401 (Unauthorized) status code when # receiving a request with invalid client credentials. # Note: This is postponed in order to avoid timing attacks, instead # a dummy client is assigned and used to maintain near constant # time request verification. # # Note that early exit would enable client enumeration valid_client = self.request_validator.validate_client_key( request.client_key, request) if not valid_client: request.client_key = self.request_validator.dummy_client # The server SHOULD return a 401 (Unauthorized) status code when # receiving a request with invalid or expired token. # Note: This is postponed in order to avoid timing attacks, instead # a dummy token is assigned and used to maintain near constant # time request verification. # # Note that early exit would enable resource owner enumeration valid_resource_owner = self.request_validator.validate_access_token( request.client_key, request.resource_owner_key, request) if not valid_resource_owner: request.resource_owner_key = self.request_validator.dummy_access_token # Note that `realm`_ is only used in authorization headers and how # it should be interepreted is not included in the OAuth spec. # However they could be seen as a scope or realm to which the # client has access and as such every client should be checked # to ensure it is authorized access to that scope or realm. # .. _`realm`: http://tools.ietf.org/html/rfc2617#section-1.2 # # Note that early exit would enable client realm access enumeration. # # The require_realm indicates this is the first step in the OAuth # workflow where a client requests access to a specific realm. # This first step (obtaining request token) need not require a realm # and can then be identified by checking the require_resource_owner # flag and abscence of realm. # # Clients obtaining an access token will not supply a realm and it will # not be checked. Instead the previously requested realm should be # transferred from the request token to the access token. # # Access to protected resources will always validate the realm but note # that the realm is now tied to the access token and not provided by # the client. valid_realm = self.request_validator.validate_realms(request.client_key, request.resource_owner_key, request, uri=request.uri, realms=realms) valid_signature = self._check_signature(request) # We delay checking validity until the very end, using dummy values for # calculations and fetching secrets/keys to ensure the flow of every # request remains almost identical regardless of whether valid values # have been supplied. This ensures near constant time execution and # prevents malicious users from guessing sensitive information v = all((valid_client, valid_resource_owner, valid_realm, valid_signature)) if not v: log.info("[Failure] request verification failed.") log.info("Valid client: %s", valid_client) log.info("Valid token: %s", valid_resource_owner) log.info("Valid realm: %s", valid_realm) log.info("Valid signature: %s", valid_signature) return v, request