From ffa87c7ec828e5c0a7c68a2197030f20b15ec621 Mon Sep 17 00:00:00 2001 From: Jonathan Huot Date: Tue, 4 Dec 2018 16:08:13 +0100 Subject: Handle 401 with WWW-Authenticate. Moved wrong 401 into 400. access_denied/unauthorized_client/consent_required/login_required MUST be 400, and not 401. Also, 401 MUST have WWW-Authenticate when set. It could have an impact of processing those in webframeworks. --- oauthlib/oauth2/rfc6749/endpoints/introspect.py | 2 ++ oauthlib/oauth2/rfc6749/endpoints/revocation.py | 2 ++ oauthlib/oauth2/rfc6749/errors.py | 8 ++------ oauthlib/oauth2/rfc6749/grant_types/authorization_code.py | 2 ++ oauthlib/oauth2/rfc6749/grant_types/client_credentials.py | 2 ++ oauthlib/oauth2/rfc6749/grant_types/refresh_token.py | 3 +++ .../rfc6749/grant_types/resource_owner_password_credentials.py | 2 ++ tests/oauth2/rfc6749/endpoints/test_introspect_endpoint.py | 4 ++-- tests/oauth2/rfc6749/endpoints/test_revocation_endpoint.py | 4 ++-- 9 files changed, 19 insertions(+), 10 deletions(-) diff --git a/oauthlib/oauth2/rfc6749/endpoints/introspect.py b/oauthlib/oauth2/rfc6749/endpoints/introspect.py index 7613acc..ac2e328 100644 --- a/oauthlib/oauth2/rfc6749/endpoints/introspect.py +++ b/oauthlib/oauth2/rfc6749/endpoints/introspect.py @@ -63,6 +63,8 @@ class IntrospectEndpoint(BaseEndpoint): log.debug('Token introspect valid for %r.', request) except OAuth2Error as e: log.debug('Client error during validation of %r. %r.', request, e) + if e.status_code == 401: + return {"WWW-Authenticate": "Basic"}, e.json, e.status_code return {}, e.json, e.status_code claims = self.request_validator.introspect_token( diff --git a/oauthlib/oauth2/rfc6749/endpoints/revocation.py b/oauthlib/oauth2/rfc6749/endpoints/revocation.py index d5b5b78..b13faa3 100644 --- a/oauthlib/oauth2/rfc6749/endpoints/revocation.py +++ b/oauthlib/oauth2/rfc6749/endpoints/revocation.py @@ -69,6 +69,8 @@ class RevocationEndpoint(BaseEndpoint): response_body = e.json if self.enable_jsonp and request.callback: response_body = '%s(%s);' % (request.callback, response_body) + if e.status_code == 401: + return {"WWW-Authenticate": "Basic"}, response_body, e.status_code return {}, response_body, e.status_code self.request_validator.revoke_token(request.token, diff --git a/oauthlib/oauth2/rfc6749/errors.py b/oauthlib/oauth2/rfc6749/errors.py index 678fcff..addcb6d 100644 --- a/oauthlib/oauth2/rfc6749/errors.py +++ b/oauthlib/oauth2/rfc6749/errors.py @@ -185,7 +185,6 @@ class AccessDeniedError(OAuth2Error): The resource owner or authorization server denied the request. """ error = 'access_denied' - status_code = 401 class UnsupportedResponseTypeError(OAuth2Error): @@ -198,12 +197,12 @@ class UnsupportedResponseTypeError(OAuth2Error): class InvalidScopeError(OAuth2Error): """ - The requested scope is invalid, unknown, or malformed. + The requested scope is invalid, unknown, or malformed, or + exceeds the scope granted by the resource owner. https://tools.ietf.org/html/rfc6749#section-5.2 """ error = 'invalid_scope' - status_code = 400 class ServerError(OAuth2Error): @@ -261,7 +260,6 @@ class UnauthorizedClientError(OAuth2Error): grant type. """ error = 'unauthorized_client' - status_code = 401 class UnsupportedGrantTypeError(OAuth2Error): @@ -318,7 +316,6 @@ class ConsentRequired(OAuth2Error): completed without displaying a user interface for End-User consent. """ error = 'consent_required' - status_code = 401 class LoginRequired(OAuth2Error): @@ -330,7 +327,6 @@ class LoginRequired(OAuth2Error): completed without displaying a user interface for End-User authentication. """ error = 'login_required' - status_code = 401 class CustomOAuth2Error(OAuth2Error): diff --git a/oauthlib/oauth2/rfc6749/grant_types/authorization_code.py b/oauthlib/oauth2/rfc6749/grant_types/authorization_code.py index 8ebae49..334ed56 100644 --- a/oauthlib/oauth2/rfc6749/grant_types/authorization_code.py +++ b/oauthlib/oauth2/rfc6749/grant_types/authorization_code.py @@ -243,6 +243,8 @@ class AuthorizationCodeGrant(GrantTypeBase): log.debug('Token request validation ok for %r.', request) except errors.OAuth2Error as e: log.debug('Client error during validation of %r. %r.', request, e) + if e.status_code == 401: + headers.update({"WWW-Authenticate": "Basic"}) return headers, e.json, e.status_code token = token_handler.create_token(request, refresh_token=self.refresh_token, save_token=False) diff --git a/oauthlib/oauth2/rfc6749/grant_types/client_credentials.py b/oauthlib/oauth2/rfc6749/grant_types/client_credentials.py index 7d4f74c..54dbebc 100644 --- a/oauthlib/oauth2/rfc6749/grant_types/client_credentials.py +++ b/oauthlib/oauth2/rfc6749/grant_types/client_credentials.py @@ -77,6 +77,8 @@ class ClientCredentialsGrant(GrantTypeBase): self.validate_token_request(request) except errors.OAuth2Error as e: log.debug('Client error in token request. %s.', e) + if e.status_code == 401: + headers.update({"WWW-Authenticate": "Basic"}) return headers, e.json, e.status_code token = token_handler.create_token(request, refresh_token=False, save_token=False) diff --git a/oauthlib/oauth2/rfc6749/grant_types/refresh_token.py b/oauthlib/oauth2/rfc6749/grant_types/refresh_token.py index 5f7382a..d2b3f6f 100644 --- a/oauthlib/oauth2/rfc6749/grant_types/refresh_token.py +++ b/oauthlib/oauth2/rfc6749/grant_types/refresh_token.py @@ -63,6 +63,9 @@ class RefreshTokenGrant(GrantTypeBase): log.debug('Validating refresh token request, %r.', request) self.validate_token_request(request) except errors.OAuth2Error as e: + log.debug('Client error in token request, %s.', e) + if e.status_code == 401: + headers.update({"WWW-Authenticate": "Basic"}) return headers, e.json, e.status_code token = token_handler.create_token(request, diff --git a/oauthlib/oauth2/rfc6749/grant_types/resource_owner_password_credentials.py b/oauthlib/oauth2/rfc6749/grant_types/resource_owner_password_credentials.py index 87e8015..931d76c 100644 --- a/oauthlib/oauth2/rfc6749/grant_types/resource_owner_password_credentials.py +++ b/oauthlib/oauth2/rfc6749/grant_types/resource_owner_password_credentials.py @@ -105,6 +105,8 @@ class ResourceOwnerPasswordCredentialsGrant(GrantTypeBase): self.validate_token_request(request) except errors.OAuth2Error as e: log.debug('Client error in token request, %s.', e) + if e.status_code == 401: + headers.update({"WWW-Authenticate": "Basic"}) return headers, e.json, e.status_code token = token_handler.create_token(request, self.refresh_token, save_token=False) diff --git a/tests/oauth2/rfc6749/endpoints/test_introspect_endpoint.py b/tests/oauth2/rfc6749/endpoints/test_introspect_endpoint.py index 7ec8190..f7c8033 100644 --- a/tests/oauth2/rfc6749/endpoints/test_introspect_endpoint.py +++ b/tests/oauth2/rfc6749/endpoints/test_introspect_endpoint.py @@ -86,7 +86,7 @@ class IntrospectEndpointTest(TestCase): ('token_type_hint', 'access_token')]) h, b, s = self.endpoint.create_introspect_response(self.uri, headers=self.headers, body=body) - self.assertEqual(h, {}) + self.assertEqual(h, {"WWW-Authenticate": "Basic"}) self.assertEqual(loads(b)['error'], 'invalid_client') self.assertEqual(s, 401) @@ -109,7 +109,7 @@ class IntrospectEndpointTest(TestCase): ('token_type_hint', 'access_token')]) h, b, s = self.endpoint.create_introspect_response(self.uri, headers=self.headers, body=body) - self.assertEqual(h, {}) + self.assertEqual(h, {"WWW-Authenticate": "Basic"}) self.assertEqual(loads(b)['error'], 'invalid_client') self.assertEqual(s, 401) diff --git a/tests/oauth2/rfc6749/endpoints/test_revocation_endpoint.py b/tests/oauth2/rfc6749/endpoints/test_revocation_endpoint.py index 77f5662..db562c8 100644 --- a/tests/oauth2/rfc6749/endpoints/test_revocation_endpoint.py +++ b/tests/oauth2/rfc6749/endpoints/test_revocation_endpoint.py @@ -49,7 +49,7 @@ class RevocationEndpointTest(TestCase): ('token_type_hint', 'access_token')]) h, b, s = self.endpoint.create_revocation_response(self.uri, headers=self.headers, body=body) - self.assertEqual(h, {}) + self.assertEqual(h, {"WWW-Authenticate": "Basic"}) self.assertEqual(loads(b)['error'], 'invalid_client') self.assertEqual(s, 401) @@ -72,7 +72,7 @@ class RevocationEndpointTest(TestCase): ('token_type_hint', 'access_token')]) h, b, s = self.endpoint.create_revocation_response(self.uri, headers=self.headers, body=body) - self.assertEqual(h, {}) + self.assertEqual(h, {"WWW-Authenticate": "Basic"}) self.assertEqual(loads(b)['error'], 'invalid_client') self.assertEqual(s, 401) -- cgit v1.2.1 From 5f629b5dce3fc6aafb5908480ed241c6f5b4cfbb Mon Sep 17 00:00:00 2001 From: Jonathan Huot Date: Wed, 12 Dec 2018 17:58:45 +0100 Subject: Used WWW-Authenticate and auth-param values as RFC6750 described it. It misses the possibility to add scope= and realm= at the moment, but it should be a step forward into the right direction. --- oauthlib/oauth2/rfc6749/endpoints/introspect.py | 4 +--- oauthlib/oauth2/rfc6749/endpoints/revocation.py | 4 +--- oauthlib/oauth2/rfc6749/errors.py | 21 +++++++++++++++++++++ .../rfc6749/grant_types/authorization_code.py | 3 +-- .../rfc6749/grant_types/client_credentials.py | 3 +-- .../oauth2/rfc6749/grant_types/refresh_token.py | 3 +-- .../resource_owner_password_credentials.py | 3 +-- .../rfc6749/endpoints/test_introspect_endpoint.py | 4 ++-- .../rfc6749/endpoints/test_revocation_endpoint.py | 4 ++-- 9 files changed, 31 insertions(+), 18 deletions(-) diff --git a/oauthlib/oauth2/rfc6749/endpoints/introspect.py b/oauthlib/oauth2/rfc6749/endpoints/introspect.py index ac2e328..4db1bdc 100644 --- a/oauthlib/oauth2/rfc6749/endpoints/introspect.py +++ b/oauthlib/oauth2/rfc6749/endpoints/introspect.py @@ -63,9 +63,7 @@ class IntrospectEndpoint(BaseEndpoint): log.debug('Token introspect valid for %r.', request) except OAuth2Error as e: log.debug('Client error during validation of %r. %r.', request, e) - if e.status_code == 401: - return {"WWW-Authenticate": "Basic"}, e.json, e.status_code - return {}, e.json, e.status_code + return e.headers, e.json, e.status_code claims = self.request_validator.introspect_token( request.token, diff --git a/oauthlib/oauth2/rfc6749/endpoints/revocation.py b/oauthlib/oauth2/rfc6749/endpoints/revocation.py index b13faa3..6c59a1e 100644 --- a/oauthlib/oauth2/rfc6749/endpoints/revocation.py +++ b/oauthlib/oauth2/rfc6749/endpoints/revocation.py @@ -69,9 +69,7 @@ class RevocationEndpoint(BaseEndpoint): response_body = e.json if self.enable_jsonp and request.callback: response_body = '%s(%s);' % (request.callback, response_body) - if e.status_code == 401: - return {"WWW-Authenticate": "Basic"}, response_body, e.status_code - return {}, response_body, e.status_code + return e.headers, response_body, e.status_code self.request_validator.revoke_token(request.token, request.token_type_hint, request) diff --git a/oauthlib/oauth2/rfc6749/errors.py b/oauthlib/oauth2/rfc6749/errors.py index addcb6d..e5543b5 100644 --- a/oauthlib/oauth2/rfc6749/errors.py +++ b/oauthlib/oauth2/rfc6749/errors.py @@ -96,6 +96,27 @@ class OAuth2Error(Exception): def json(self): return json.dumps(dict(self.twotuples)) + @property + def headers(self): + if self.status_code == 401: + """ + https://tools.ietf.org/html/rfc6750#section-3 + + All challenges defined by this specification MUST use the auth-scheme + value "Bearer". This scheme MUST be followed by one or more + auth-param values. + """ + authvalues = [ + "Bearer", + "error={}".format(self.error) + ] + if self.description: + authvalues.append("error_description={}".format(self.description)) + if self.uri: + authvalues.append("error_uri={}".format(self.uri)) + return {"WWW-Authenticate": ", ".join(authvalues)} + return {} + class TokenExpiredError(OAuth2Error): error = 'token_expired' diff --git a/oauthlib/oauth2/rfc6749/grant_types/authorization_code.py b/oauthlib/oauth2/rfc6749/grant_types/authorization_code.py index 334ed56..850d70a 100644 --- a/oauthlib/oauth2/rfc6749/grant_types/authorization_code.py +++ b/oauthlib/oauth2/rfc6749/grant_types/authorization_code.py @@ -243,8 +243,7 @@ class AuthorizationCodeGrant(GrantTypeBase): log.debug('Token request validation ok for %r.', request) except errors.OAuth2Error as e: log.debug('Client error during validation of %r. %r.', request, e) - if e.status_code == 401: - headers.update({"WWW-Authenticate": "Basic"}) + headers.update(e.headers) return headers, e.json, e.status_code token = token_handler.create_token(request, refresh_token=self.refresh_token, save_token=False) diff --git a/oauthlib/oauth2/rfc6749/grant_types/client_credentials.py b/oauthlib/oauth2/rfc6749/grant_types/client_credentials.py index 54dbebc..0e4f545 100644 --- a/oauthlib/oauth2/rfc6749/grant_types/client_credentials.py +++ b/oauthlib/oauth2/rfc6749/grant_types/client_credentials.py @@ -77,8 +77,7 @@ class ClientCredentialsGrant(GrantTypeBase): self.validate_token_request(request) except errors.OAuth2Error as e: log.debug('Client error in token request. %s.', e) - if e.status_code == 401: - headers.update({"WWW-Authenticate": "Basic"}) + headers.update(e.headers) return headers, e.json, e.status_code token = token_handler.create_token(request, refresh_token=False, save_token=False) diff --git a/oauthlib/oauth2/rfc6749/grant_types/refresh_token.py b/oauthlib/oauth2/rfc6749/grant_types/refresh_token.py index d2b3f6f..67d65a7 100644 --- a/oauthlib/oauth2/rfc6749/grant_types/refresh_token.py +++ b/oauthlib/oauth2/rfc6749/grant_types/refresh_token.py @@ -64,8 +64,7 @@ class RefreshTokenGrant(GrantTypeBase): self.validate_token_request(request) except errors.OAuth2Error as e: log.debug('Client error in token request, %s.', e) - if e.status_code == 401: - headers.update({"WWW-Authenticate": "Basic"}) + headers.update(e.headers) return headers, e.json, e.status_code token = token_handler.create_token(request, diff --git a/oauthlib/oauth2/rfc6749/grant_types/resource_owner_password_credentials.py b/oauthlib/oauth2/rfc6749/grant_types/resource_owner_password_credentials.py index 931d76c..cb5a4ca 100644 --- a/oauthlib/oauth2/rfc6749/grant_types/resource_owner_password_credentials.py +++ b/oauthlib/oauth2/rfc6749/grant_types/resource_owner_password_credentials.py @@ -105,8 +105,7 @@ class ResourceOwnerPasswordCredentialsGrant(GrantTypeBase): self.validate_token_request(request) except errors.OAuth2Error as e: log.debug('Client error in token request, %s.', e) - if e.status_code == 401: - headers.update({"WWW-Authenticate": "Basic"}) + headers.update(e.headers) return headers, e.json, e.status_code token = token_handler.create_token(request, self.refresh_token, save_token=False) diff --git a/tests/oauth2/rfc6749/endpoints/test_introspect_endpoint.py b/tests/oauth2/rfc6749/endpoints/test_introspect_endpoint.py index f7c8033..d252a73 100644 --- a/tests/oauth2/rfc6749/endpoints/test_introspect_endpoint.py +++ b/tests/oauth2/rfc6749/endpoints/test_introspect_endpoint.py @@ -86,7 +86,7 @@ class IntrospectEndpointTest(TestCase): ('token_type_hint', 'access_token')]) h, b, s = self.endpoint.create_introspect_response(self.uri, headers=self.headers, body=body) - self.assertEqual(h, {"WWW-Authenticate": "Basic"}) + self.assertEqual(h, {"WWW-Authenticate": "Bearer, error=invalid_client"}) self.assertEqual(loads(b)['error'], 'invalid_client') self.assertEqual(s, 401) @@ -109,7 +109,7 @@ class IntrospectEndpointTest(TestCase): ('token_type_hint', 'access_token')]) h, b, s = self.endpoint.create_introspect_response(self.uri, headers=self.headers, body=body) - self.assertEqual(h, {"WWW-Authenticate": "Basic"}) + self.assertEqual(h, {"WWW-Authenticate": "Bearer, error=invalid_client"}) self.assertEqual(loads(b)['error'], 'invalid_client') self.assertEqual(s, 401) diff --git a/tests/oauth2/rfc6749/endpoints/test_revocation_endpoint.py b/tests/oauth2/rfc6749/endpoints/test_revocation_endpoint.py index db562c8..8a434e2 100644 --- a/tests/oauth2/rfc6749/endpoints/test_revocation_endpoint.py +++ b/tests/oauth2/rfc6749/endpoints/test_revocation_endpoint.py @@ -49,7 +49,7 @@ class RevocationEndpointTest(TestCase): ('token_type_hint', 'access_token')]) h, b, s = self.endpoint.create_revocation_response(self.uri, headers=self.headers, body=body) - self.assertEqual(h, {"WWW-Authenticate": "Basic"}) + self.assertEqual(h, {"WWW-Authenticate": "Bearer, error=invalid_client"}) self.assertEqual(loads(b)['error'], 'invalid_client') self.assertEqual(s, 401) @@ -72,7 +72,7 @@ class RevocationEndpointTest(TestCase): ('token_type_hint', 'access_token')]) h, b, s = self.endpoint.create_revocation_response(self.uri, headers=self.headers, body=body) - self.assertEqual(h, {"WWW-Authenticate": "Basic"}) + self.assertEqual(h, {"WWW-Authenticate": "Bearer, error=invalid_client"}) self.assertEqual(loads(b)['error'], 'invalid_client') self.assertEqual(s, 401) -- cgit v1.2.1 From a9ec83a40477e6b5b460b6f203607199f5f16779 Mon Sep 17 00:00:00 2001 From: Jonathan Huot Date: Wed, 12 Dec 2018 18:08:09 +0100 Subject: Add double-quotes to the key/values in WWW-Authenticate --- oauthlib/oauth2/rfc6749/errors.py | 6 +++--- tests/oauth2/rfc6749/endpoints/test_introspect_endpoint.py | 4 ++-- tests/oauth2/rfc6749/endpoints/test_revocation_endpoint.py | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/oauthlib/oauth2/rfc6749/errors.py b/oauthlib/oauth2/rfc6749/errors.py index e5543b5..ec2b0d1 100644 --- a/oauthlib/oauth2/rfc6749/errors.py +++ b/oauthlib/oauth2/rfc6749/errors.py @@ -108,12 +108,12 @@ class OAuth2Error(Exception): """ authvalues = [ "Bearer", - "error={}".format(self.error) + 'error="{}"'.format(self.error) ] if self.description: - authvalues.append("error_description={}".format(self.description)) + authvalues.append('error_description="{}"'.format(self.description)) if self.uri: - authvalues.append("error_uri={}".format(self.uri)) + authvalues.append('error_uri="{}"'.format(self.uri)) return {"WWW-Authenticate": ", ".join(authvalues)} return {} diff --git a/tests/oauth2/rfc6749/endpoints/test_introspect_endpoint.py b/tests/oauth2/rfc6749/endpoints/test_introspect_endpoint.py index d252a73..e41b83f 100644 --- a/tests/oauth2/rfc6749/endpoints/test_introspect_endpoint.py +++ b/tests/oauth2/rfc6749/endpoints/test_introspect_endpoint.py @@ -86,7 +86,7 @@ class IntrospectEndpointTest(TestCase): ('token_type_hint', 'access_token')]) h, b, s = self.endpoint.create_introspect_response(self.uri, headers=self.headers, body=body) - self.assertEqual(h, {"WWW-Authenticate": "Bearer, error=invalid_client"}) + self.assertEqual(h, {"WWW-Authenticate": 'Bearer, error="invalid_client"'}) self.assertEqual(loads(b)['error'], 'invalid_client') self.assertEqual(s, 401) @@ -109,7 +109,7 @@ class IntrospectEndpointTest(TestCase): ('token_type_hint', 'access_token')]) h, b, s = self.endpoint.create_introspect_response(self.uri, headers=self.headers, body=body) - self.assertEqual(h, {"WWW-Authenticate": "Bearer, error=invalid_client"}) + self.assertEqual(h, {"WWW-Authenticate": 'Bearer, error="invalid_client"'}) self.assertEqual(loads(b)['error'], 'invalid_client') self.assertEqual(s, 401) diff --git a/tests/oauth2/rfc6749/endpoints/test_revocation_endpoint.py b/tests/oauth2/rfc6749/endpoints/test_revocation_endpoint.py index 8a434e2..a6a5cb2 100644 --- a/tests/oauth2/rfc6749/endpoints/test_revocation_endpoint.py +++ b/tests/oauth2/rfc6749/endpoints/test_revocation_endpoint.py @@ -49,7 +49,7 @@ class RevocationEndpointTest(TestCase): ('token_type_hint', 'access_token')]) h, b, s = self.endpoint.create_revocation_response(self.uri, headers=self.headers, body=body) - self.assertEqual(h, {"WWW-Authenticate": "Bearer, error=invalid_client"}) + self.assertEqual(h, {"WWW-Authenticate": 'Bearer, error="invalid_client"'}) self.assertEqual(loads(b)['error'], 'invalid_client') self.assertEqual(s, 401) @@ -72,7 +72,7 @@ class RevocationEndpointTest(TestCase): ('token_type_hint', 'access_token')]) h, b, s = self.endpoint.create_revocation_response(self.uri, headers=self.headers, body=body) - self.assertEqual(h, {"WWW-Authenticate": "Bearer, error=invalid_client"}) + self.assertEqual(h, {"WWW-Authenticate": 'Bearer, error="invalid_client"'}) self.assertEqual(loads(b)['error'], 'invalid_client') self.assertEqual(s, 401) -- cgit v1.2.1 From 61458583d83959a37e56c20eb09546aaa63b4829 Mon Sep 17 00:00:00 2001 From: Jonathan Huot Date: Thu, 13 Dec 2018 10:43:12 +0100 Subject: Add Content-Type and Cache headers to introspect/revocation errors --- oauthlib/oauth2/rfc6749/endpoints/introspect.py | 13 ++++++------ oauthlib/oauth2/rfc6749/endpoints/revocation.py | 8 +++++++- .../rfc6749/endpoints/test_introspect_endpoint.py | 18 +++++++++++++---- .../rfc6749/endpoints/test_revocation_endpoint.py | 23 ++++++++++++++++++---- 4 files changed, 47 insertions(+), 15 deletions(-) diff --git a/oauthlib/oauth2/rfc6749/endpoints/introspect.py b/oauthlib/oauth2/rfc6749/endpoints/introspect.py index 4db1bdc..4a531e4 100644 --- a/oauthlib/oauth2/rfc6749/endpoints/introspect.py +++ b/oauthlib/oauth2/rfc6749/endpoints/introspect.py @@ -57,24 +57,25 @@ class IntrospectEndpoint(BaseEndpoint): an introspection response indicating the token is not active as described in Section 2.2. """ + headers = { + 'Content-Type': 'application/json', + 'Cache-Control': 'no-store', + 'Pragma': 'no-cache', + } request = Request(uri, http_method, body, headers) try: self.validate_introspect_request(request) log.debug('Token introspect valid for %r.', request) except OAuth2Error as e: log.debug('Client error during validation of %r. %r.', request, e) - return e.headers, e.json, e.status_code + headers.update(e.headers) + return headers, e.json, e.status_code claims = self.request_validator.introspect_token( request.token, request.token_type_hint, request ) - headers = { - 'Content-Type': 'application/json', - 'Cache-Control': 'no-store', - 'Pragma': 'no-cache', - } if claims is None: return headers, json.dumps(dict(active=False)), 200 if "active" in claims: diff --git a/oauthlib/oauth2/rfc6749/endpoints/revocation.py b/oauthlib/oauth2/rfc6749/endpoints/revocation.py index 6c59a1e..f7e591d 100644 --- a/oauthlib/oauth2/rfc6749/endpoints/revocation.py +++ b/oauthlib/oauth2/rfc6749/endpoints/revocation.py @@ -59,6 +59,11 @@ class RevocationEndpoint(BaseEndpoint): An invalid token type hint value is ignored by the authorization server and does not influence the revocation response. """ + headers = { + 'Content-Type': 'application/json', + 'Cache-Control': 'no-store', + 'Pragma': 'no-cache', + } request = Request( uri, http_method=http_method, body=body, headers=headers) try: @@ -69,7 +74,8 @@ class RevocationEndpoint(BaseEndpoint): response_body = e.json if self.enable_jsonp and request.callback: response_body = '%s(%s);' % (request.callback, response_body) - return e.headers, response_body, e.status_code + headers.update(e.headers) + return headers, response_body, e.status_code self.request_validator.revoke_token(request.token, request.token_type_hint, request) diff --git a/tests/oauth2/rfc6749/endpoints/test_introspect_endpoint.py b/tests/oauth2/rfc6749/endpoints/test_introspect_endpoint.py index e41b83f..f92652b 100644 --- a/tests/oauth2/rfc6749/endpoints/test_introspect_endpoint.py +++ b/tests/oauth2/rfc6749/endpoints/test_introspect_endpoint.py @@ -86,7 +86,12 @@ class IntrospectEndpointTest(TestCase): ('token_type_hint', 'access_token')]) h, b, s = self.endpoint.create_introspect_response(self.uri, headers=self.headers, body=body) - self.assertEqual(h, {"WWW-Authenticate": 'Bearer, error="invalid_client"'}) + self.assertEqual(h, { + 'Content-Type': 'application/json', + 'Cache-Control': 'no-store', + 'Pragma': 'no-cache', + "WWW-Authenticate": 'Bearer, error="invalid_client"' + }) self.assertEqual(loads(b)['error'], 'invalid_client') self.assertEqual(s, 401) @@ -109,7 +114,12 @@ class IntrospectEndpointTest(TestCase): ('token_type_hint', 'access_token')]) h, b, s = self.endpoint.create_introspect_response(self.uri, headers=self.headers, body=body) - self.assertEqual(h, {"WWW-Authenticate": 'Bearer, error="invalid_client"'}) + self.assertEqual(h, { + 'Content-Type': 'application/json', + 'Cache-Control': 'no-store', + 'Pragma': 'no-cache', + "WWW-Authenticate": 'Bearer, error="invalid_client"' + }) self.assertEqual(loads(b)['error'], 'invalid_client') self.assertEqual(s, 401) @@ -121,12 +131,12 @@ class IntrospectEndpointTest(TestCase): ('token_type_hint', 'refresh_token')]) h, b, s = endpoint.create_introspect_response(self.uri, headers=self.headers, body=body) - self.assertEqual(h, {}) + self.assertEqual(h, self.resp_h) self.assertEqual(loads(b)['error'], 'unsupported_token_type') self.assertEqual(s, 400) h, b, s = endpoint.create_introspect_response(self.uri, headers=self.headers, body='') - self.assertEqual(h, {}) + self.assertEqual(h, self.resp_h) self.assertEqual(loads(b)['error'], 'invalid_request') self.assertEqual(s, 400) diff --git a/tests/oauth2/rfc6749/endpoints/test_revocation_endpoint.py b/tests/oauth2/rfc6749/endpoints/test_revocation_endpoint.py index a6a5cb2..2a24177 100644 --- a/tests/oauth2/rfc6749/endpoints/test_revocation_endpoint.py +++ b/tests/oauth2/rfc6749/endpoints/test_revocation_endpoint.py @@ -24,6 +24,11 @@ class RevocationEndpointTest(TestCase): self.headers = { 'Content-Type': 'application/x-www-form-urlencoded', } + self.resp_h = { + 'Cache-Control': 'no-store', + 'Content-Type': 'application/json', + 'Pragma': 'no-cache' + } def test_revoke_token(self): for token_type in ('access_token', 'refresh_token', 'invalid'): @@ -49,7 +54,12 @@ class RevocationEndpointTest(TestCase): ('token_type_hint', 'access_token')]) h, b, s = self.endpoint.create_revocation_response(self.uri, headers=self.headers, body=body) - self.assertEqual(h, {"WWW-Authenticate": 'Bearer, error="invalid_client"'}) + self.assertEqual(h, { + 'Content-Type': 'application/json', + 'Cache-Control': 'no-store', + 'Pragma': 'no-cache', + "WWW-Authenticate": 'Bearer, error="invalid_client"' + }) self.assertEqual(loads(b)['error'], 'invalid_client') self.assertEqual(s, 401) @@ -72,7 +82,12 @@ class RevocationEndpointTest(TestCase): ('token_type_hint', 'access_token')]) h, b, s = self.endpoint.create_revocation_response(self.uri, headers=self.headers, body=body) - self.assertEqual(h, {"WWW-Authenticate": 'Bearer, error="invalid_client"'}) + self.assertEqual(h, { + 'Content-Type': 'application/json', + 'Cache-Control': 'no-store', + 'Pragma': 'no-cache', + "WWW-Authenticate": 'Bearer, error="invalid_client"' + }) self.assertEqual(loads(b)['error'], 'invalid_client') self.assertEqual(s, 401) @@ -96,12 +111,12 @@ class RevocationEndpointTest(TestCase): ('token_type_hint', 'refresh_token')]) h, b, s = endpoint.create_revocation_response(self.uri, headers=self.headers, body=body) - self.assertEqual(h, {}) + self.assertEqual(h, self.resp_h) self.assertEqual(loads(b)['error'], 'unsupported_token_type') self.assertEqual(s, 400) h, b, s = endpoint.create_revocation_response(self.uri, headers=self.headers, body='') - self.assertEqual(h, {}) + self.assertEqual(h, self.resp_h) self.assertEqual(loads(b)['error'], 'invalid_request') self.assertEqual(s, 400) -- cgit v1.2.1