summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJonathan Huot <JonathanHuot@users.noreply.github.com>2018-12-13 16:32:45 +0100
committerGitHub <noreply@github.com>2018-12-13 16:32:45 +0100
commite69486499cadcda3f4fdf6dfaf8ec72b04f02128 (patch)
treefa8743c649fca6a8ff1c3b948307da196b78ca30
parent6dcde73a81d6cbc718ca9ca7f9170a28fc1b5e34 (diff)
parent5d9a9c90ba04f85477c7859a3cc7b13577fc24f9 (diff)
downloadoauthlib-e69486499cadcda3f4fdf6dfaf8ec72b04f02128.tar.gz
Merge branch 'master' into 601-pkce-support
-rw-r--r--oauthlib/oauth2/rfc6749/clients/backend_application.py6
-rw-r--r--oauthlib/oauth2/rfc6749/clients/base.py3
-rw-r--r--oauthlib/oauth2/rfc6749/clients/legacy_application.py4
-rw-r--r--oauthlib/oauth2/rfc6749/clients/mobile_application.py4
-rw-r--r--oauthlib/oauth2/rfc6749/clients/web_application.py4
-rw-r--r--oauthlib/oauth2/rfc6749/endpoints/introspect.py13
-rw-r--r--oauthlib/oauth2/rfc6749/endpoints/revocation.py8
-rw-r--r--oauthlib/oauth2/rfc6749/errors.py29
-rw-r--r--oauthlib/oauth2/rfc6749/grant_types/authorization_code.py1
-rw-r--r--oauthlib/oauth2/rfc6749/grant_types/client_credentials.py1
-rw-r--r--oauthlib/oauth2/rfc6749/grant_types/refresh_token.py2
-rw-r--r--oauthlib/oauth2/rfc6749/grant_types/resource_owner_password_credentials.py1
-rw-r--r--tests/oauth2/rfc6749/endpoints/test_introspect_endpoint.py18
-rw-r--r--tests/oauth2/rfc6749/endpoints/test_revocation_endpoint.py23
14 files changed, 90 insertions, 27 deletions
diff --git a/oauthlib/oauth2/rfc6749/clients/backend_application.py b/oauthlib/oauth2/rfc6749/clients/backend_application.py
index cd46f12..a000ecf 100644
--- a/oauthlib/oauth2/rfc6749/clients/backend_application.py
+++ b/oauthlib/oauth2/rfc6749/clients/backend_application.py
@@ -29,7 +29,9 @@ class BackendApplicationClient(Client):
Since the client authentication is used as the authorization grant,
no additional authorization request is needed.
"""
-
+
+ grant_type = 'client_credentials'
+
def prepare_request_body(self, body='', scope=None,
include_client_id=None, **kwargs):
"""Add the client credentials to the request body.
@@ -69,5 +71,5 @@ class BackendApplicationClient(Client):
"""
kwargs['client_id'] = self.client_id
kwargs['include_client_id'] = include_client_id
- return prepare_token_request('client_credentials', body=body,
+ return prepare_token_request(self.grant_type, body=body,
scope=scope, **kwargs)
diff --git a/oauthlib/oauth2/rfc6749/clients/base.py b/oauthlib/oauth2/rfc6749/clients/base.py
index d8ded50..1a50644 100644
--- a/oauthlib/oauth2/rfc6749/clients/base.py
+++ b/oauthlib/oauth2/rfc6749/clients/base.py
@@ -47,6 +47,7 @@ class Client(object):
Python, this is usually :py:class:`oauthlib.oauth2.WebApplicationClient`.
"""
+ refresh_token_key = 'refresh_token'
def __init__(self, client_id,
default_token_placement=AUTH_HEADER,
@@ -435,7 +436,7 @@ class Client(object):
resource owner.
"""
refresh_token = refresh_token or self.refresh_token
- return prepare_token_request('refresh_token', body=body, scope=scope,
+ return prepare_token_request(self.refresh_token_key, body=body, scope=scope,
refresh_token=refresh_token, **kwargs)
def _add_bearer_token(self, uri, http_method='GET', body=None,
diff --git a/oauthlib/oauth2/rfc6749/clients/legacy_application.py b/oauthlib/oauth2/rfc6749/clients/legacy_application.py
index a13927a..2449363 100644
--- a/oauthlib/oauth2/rfc6749/clients/legacy_application.py
+++ b/oauthlib/oauth2/rfc6749/clients/legacy_application.py
@@ -34,6 +34,8 @@ class LegacyApplicationClient(Client):
credentials is beyond the scope of this specification. The client
MUST discard the credentials once an access token has been obtained.
"""
+
+ grant_type = 'password'
def __init__(self, client_id, **kwargs):
super(LegacyApplicationClient, self).__init__(client_id, **kwargs)
@@ -79,5 +81,5 @@ class LegacyApplicationClient(Client):
"""
kwargs['client_id'] = self.client_id
kwargs['include_client_id'] = include_client_id
- return prepare_token_request('password', body=body, username=username,
+ return prepare_token_request(self.grant_type, body=body, username=username,
password=password, scope=scope, **kwargs)
diff --git a/oauthlib/oauth2/rfc6749/clients/mobile_application.py b/oauthlib/oauth2/rfc6749/clients/mobile_application.py
index aa20daa..11c6c51 100644
--- a/oauthlib/oauth2/rfc6749/clients/mobile_application.py
+++ b/oauthlib/oauth2/rfc6749/clients/mobile_application.py
@@ -45,6 +45,8 @@ class MobileApplicationClient(Client):
redirection URI, it may be exposed to the resource owner and other
applications residing on the same device.
"""
+
+ response_type = 'token'
def prepare_request_uri(self, uri, redirect_uri=None, scope=None,
state=None, **kwargs):
@@ -91,7 +93,7 @@ class MobileApplicationClient(Client):
.. _`Section 3.3`: https://tools.ietf.org/html/rfc6749#section-3.3
.. _`Section 10.12`: https://tools.ietf.org/html/rfc6749#section-10.12
"""
- return prepare_grant_uri(uri, self.client_id, 'token',
+ return prepare_grant_uri(uri, self.client_id, self.response_type,
redirect_uri=redirect_uri, state=state, scope=scope, **kwargs)
def parse_request_uri_response(self, uri, state=None, scope=None):
diff --git a/oauthlib/oauth2/rfc6749/clients/web_application.py b/oauthlib/oauth2/rfc6749/clients/web_application.py
index 487e3a0..0cd39ce 100644
--- a/oauthlib/oauth2/rfc6749/clients/web_application.py
+++ b/oauthlib/oauth2/rfc6749/clients/web_application.py
@@ -34,6 +34,8 @@ class WebApplicationClient(Client):
browser) and capable of receiving incoming requests (via redirection)
from the authorization server.
"""
+
+ grant_type = 'authorization_code'
def __init__(self, client_id, code=None, **kwargs):
super(WebApplicationClient, self).__init__(client_id, **kwargs)
@@ -151,7 +153,7 @@ class WebApplicationClient(Client):
kwargs['client_id'] = self.client_id
kwargs['include_client_id'] = include_client_id
- return prepare_token_request('authorization_code', code=code, body=body,
+ return prepare_token_request(self.grant_type, code=code, body=body,
redirect_uri=redirect_uri, **kwargs)
def parse_request_uri_response(self, uri, state=None):
diff --git a/oauthlib/oauth2/rfc6749/endpoints/introspect.py b/oauthlib/oauth2/rfc6749/endpoints/introspect.py
index 7613acc..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.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 d5b5b78..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 {}, 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/oauthlib/oauth2/rfc6749/errors.py b/oauthlib/oauth2/rfc6749/errors.py
index bee9e77..d2a1402 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'
@@ -205,7 +226,6 @@ class AccessDeniedError(OAuth2Error):
The resource owner or authorization server denied the request.
"""
error = 'access_denied'
- status_code = 401
class UnsupportedResponseTypeError(OAuth2Error):
@@ -230,12 +250,12 @@ class UnsupportedCodeChallengeMethodError(InvalidRequestError):
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):
@@ -293,7 +313,6 @@ class UnauthorizedClientError(OAuth2Error):
grant type.
"""
error = 'unauthorized_client'
- status_code = 401
class UnsupportedGrantTypeError(OAuth2Error):
@@ -350,7 +369,6 @@ class ConsentRequired(OAuth2Error):
completed without displaying a user interface for End-User consent.
"""
error = 'consent_required'
- status_code = 401
class LoginRequired(OAuth2Error):
@@ -362,7 +380,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 0df7c6c..d56330a 100644
--- a/oauthlib/oauth2/rfc6749/grant_types/authorization_code.py
+++ b/oauthlib/oauth2/rfc6749/grant_types/authorization_code.py
@@ -307,6 +307,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)
+ 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 7d4f74c..0e4f545 100644
--- a/oauthlib/oauth2/rfc6749/grant_types/client_credentials.py
+++ b/oauthlib/oauth2/rfc6749/grant_types/client_credentials.py
@@ -77,6 +77,7 @@ class ClientCredentialsGrant(GrantTypeBase):
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, 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..67d65a7 100644
--- a/oauthlib/oauth2/rfc6749/grant_types/refresh_token.py
+++ b/oauthlib/oauth2/rfc6749/grant_types/refresh_token.py
@@ -63,6 +63,8 @@ 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)
+ 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 87e8015..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,6 +105,7 @@ class ResourceOwnerPasswordCredentialsGrant(GrantTypeBase):
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, 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..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, {})
+ 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, {})
+ 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 77f5662..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, {})
+ 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, {})
+ 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)