summaryrefslogtreecommitdiff
path: root/oauthlib/oauth2
diff options
context:
space:
mode:
authorJonathan Huot <JonathanHuot@users.noreply.github.com>2018-03-29 18:39:54 +0200
committerGitHub <noreply@github.com>2018-03-29 18:39:54 +0200
commit8130cdc06fbf17d8cf2805a454eea3019e45c593 (patch)
tree683d48b9f9017885553a8a0800bebbeb13cc2748 /oauthlib/oauth2
parent17593fdeb4f3528a146da9a434e18fcc9f973e1d (diff)
parent46f79e027a3fda7b012e3d786cbb1ee945af508f (diff)
downloadoauthlib-8130cdc06fbf17d8cf2805a454eea3019e45c593.tar.gz
Merge branch 'master' into master
Diffstat (limited to 'oauthlib/oauth2')
-rw-r--r--oauthlib/oauth2/__init__.py2
-rw-r--r--oauthlib/oauth2/rfc6749/clients/__init__.py2
-rw-r--r--oauthlib/oauth2/rfc6749/clients/backend_application.py9
-rw-r--r--oauthlib/oauth2/rfc6749/clients/base.py24
-rw-r--r--oauthlib/oauth2/rfc6749/clients/legacy_application.py9
-rw-r--r--oauthlib/oauth2/rfc6749/clients/mobile_application.py17
-rw-r--r--oauthlib/oauth2/rfc6749/clients/service_application.py5
-rw-r--r--oauthlib/oauth2/rfc6749/clients/web_application.py22
-rw-r--r--oauthlib/oauth2/rfc6749/endpoints/authorization.py3
-rw-r--r--oauthlib/oauth2/rfc6749/endpoints/base.py4
-rw-r--r--oauthlib/oauth2/rfc6749/endpoints/pre_configured.py44
-rw-r--r--oauthlib/oauth2/rfc6749/endpoints/resource.py2
-rw-r--r--oauthlib/oauth2/rfc6749/endpoints/revocation.py16
-rw-r--r--oauthlib/oauth2/rfc6749/endpoints/token.py3
-rw-r--r--oauthlib/oauth2/rfc6749/errors.py23
-rw-r--r--oauthlib/oauth2/rfc6749/grant_types/__init__.py2
-rw-r--r--oauthlib/oauth2/rfc6749/grant_types/authorization_code.py32
-rw-r--r--oauthlib/oauth2/rfc6749/grant_types/base.py5
-rw-r--r--oauthlib/oauth2/rfc6749/grant_types/client_credentials.py10
-rw-r--r--oauthlib/oauth2/rfc6749/grant_types/implicit.py70
-rw-r--r--oauthlib/oauth2/rfc6749/grant_types/openid_connect.py90
-rw-r--r--oauthlib/oauth2/rfc6749/grant_types/refresh_token.py12
-rw-r--r--oauthlib/oauth2/rfc6749/grant_types/resource_owner_password_credentials.py14
-rw-r--r--oauthlib/oauth2/rfc6749/parameters.py49
-rw-r--r--oauthlib/oauth2/rfc6749/request_validator.py98
-rw-r--r--oauthlib/oauth2/rfc6749/tokens.py71
-rw-r--r--oauthlib/oauth2/rfc6749/utils.py6
27 files changed, 433 insertions, 211 deletions
diff --git a/oauthlib/oauth2/__init__.py b/oauthlib/oauth2/__init__.py
index a13e484..c8d934e 100644
--- a/oauthlib/oauth2/__init__.py
+++ b/oauthlib/oauth2/__init__.py
@@ -23,7 +23,7 @@ from .rfc6749.endpoints import WebApplicationServer
from .rfc6749.endpoints import MobileApplicationServer
from .rfc6749.endpoints import LegacyApplicationServer
from .rfc6749.endpoints import BackendApplicationServer
-from .rfc6749.errors import *
+from .rfc6749.errors import AccessDeniedError, AccountSelectionRequired, ConsentRequired, FatalClientError, FatalOpenIDClientError, InsecureTransportError, InteractionRequired, InvalidClientError, InvalidClientIdError, InvalidGrantError, InvalidRedirectURIError, InvalidRequestError, InvalidRequestFatalError, InvalidScopeError, LoginRequired, MismatchingRedirectURIError, MismatchingStateError, MissingClientIdError, MissingCodeError, MissingRedirectURIError, MissingResponseTypeError, MissingTokenError, MissingTokenTypeError, OAuth2Error, OpenIDClientError, ServerError, TemporarilyUnavailableError, TokenExpiredError, UnauthorizedClientError, UnsupportedGrantTypeError, UnsupportedResponseTypeError, UnsupportedTokenTypeError
from .rfc6749.grant_types import AuthorizationCodeGrant
from .rfc6749.grant_types import ImplicitGrant
from .rfc6749.grant_types import ResourceOwnerPasswordCredentialsGrant
diff --git a/oauthlib/oauth2/rfc6749/clients/__init__.py b/oauthlib/oauth2/rfc6749/clients/__init__.py
index 65bea50..17d0023 100644
--- a/oauthlib/oauth2/rfc6749/clients/__init__.py
+++ b/oauthlib/oauth2/rfc6749/clients/__init__.py
@@ -8,7 +8,7 @@ for consuming OAuth 2.0 RFC6749.
"""
from __future__ import absolute_import, unicode_literals
-from .base import *
+from .base import Client, AUTH_HEADER, URI_QUERY, BODY
from .web_application import WebApplicationClient
from .mobile_application import MobileApplicationClient
from .legacy_application import LegacyApplicationClient
diff --git a/oauthlib/oauth2/rfc6749/clients/backend_application.py b/oauthlib/oauth2/rfc6749/clients/backend_application.py
index 445bdd5..cbad8b7 100644
--- a/oauthlib/oauth2/rfc6749/clients/backend_application.py
+++ b/oauthlib/oauth2/rfc6749/clients/backend_application.py
@@ -8,9 +8,8 @@ for consuming and providing OAuth 2.0 RFC6749.
"""
from __future__ import absolute_import, unicode_literals
+from ..parameters import parse_token_response, prepare_token_request
from .base import Client
-from ..parameters import prepare_token_request
-from ..parameters import parse_token_response
class BackendApplicationClient(Client):
@@ -53,9 +52,9 @@ class BackendApplicationClient(Client):
>>> client.prepare_request_body(scope=['hello', 'world'])
'grant_type=client_credentials&scope=hello+world'
- .. _`Appendix B`: http://tools.ietf.org/html/rfc6749#appendix-B
- .. _`Section 3.3`: http://tools.ietf.org/html/rfc6749#section-3.3
- .. _`Section 3.2.1`: http://tools.ietf.org/html/rfc6749#section-3.2.1
+ .. _`Appendix B`: https://tools.ietf.org/html/rfc6749#appendix-B
+ .. _`Section 3.3`: https://tools.ietf.org/html/rfc6749#section-3.3
+ .. _`Section 3.2.1`: https://tools.ietf.org/html/rfc6749#section-3.2.1
"""
return prepare_token_request('client_credentials', body=body,
scope=scope, **kwargs)
diff --git a/oauthlib/oauth2/rfc6749/clients/base.py b/oauthlib/oauth2/rfc6749/clients/base.py
index e804b9a..a07a5c9 100644
--- a/oauthlib/oauth2/rfc6749/clients/base.py
+++ b/oauthlib/oauth2/rfc6749/clients/base.py
@@ -12,14 +12,13 @@ import time
from oauthlib.common import generate_token
from oauthlib.oauth2.rfc6749 import tokens
-from oauthlib.oauth2.rfc6749.parameters import parse_token_response
-from oauthlib.oauth2.rfc6749.parameters import prepare_token_request
-from oauthlib.oauth2.rfc6749.parameters import prepare_token_revocation_request
-from oauthlib.oauth2.rfc6749.errors import TokenExpiredError
-from oauthlib.oauth2.rfc6749.errors import InsecureTransportError
+from oauthlib.oauth2.rfc6749.errors import (InsecureTransportError,
+ TokenExpiredError)
+from oauthlib.oauth2.rfc6749.parameters import (parse_token_response,
+ prepare_token_request,
+ prepare_token_revocation_request)
from oauthlib.oauth2.rfc6749.utils import is_secure_transport
-
AUTH_HEADER = 'auth_header'
URI_QUERY = 'query'
BODY = 'body'
@@ -174,8 +173,8 @@ class Client(object):
nonce="274312:dj83hs9s",
mac="kDZvddkndxvhGRXZhvuDjEWhGeE="
- .. _`I-D.ietf-oauth-v2-bearer`: http://tools.ietf.org/html/rfc6749#section-12.2
- .. _`I-D.ietf-oauth-v2-http-mac`: http://tools.ietf.org/html/rfc6749#section-12.2
+ .. _`I-D.ietf-oauth-v2-bearer`: https://tools.ietf.org/html/rfc6749#section-12.2
+ .. _`I-D.ietf-oauth-v2-http-mac`: https://tools.ietf.org/html/rfc6749#section-12.2
"""
if not is_secure_transport(uri):
raise InsecureTransportError()
@@ -187,7 +186,7 @@ class Client(object):
if not self.token_type.lower() in case_insensitive_token_types:
raise ValueError("Unsupported token type: %s" % self.token_type)
- if not self.access_token:
+ if not (self.access_token or self.token.get('access_token')):
raise ValueError("Missing access token.")
if self._expires_at and self._expires_at < time.time():
@@ -402,9 +401,9 @@ class Client(object):
Providers may supply this in all responses but are required to only
if it has changed since the authorization request.
- .. _`Section 5.1`: http://tools.ietf.org/html/rfc6749#section-5.1
- .. _`Section 5.2`: http://tools.ietf.org/html/rfc6749#section-5.2
- .. _`Section 7.1`: http://tools.ietf.org/html/rfc6749#section-7.1
+ .. _`Section 5.1`: https://tools.ietf.org/html/rfc6749#section-5.1
+ .. _`Section 5.2`: https://tools.ietf.org/html/rfc6749#section-5.2
+ .. _`Section 7.1`: https://tools.ietf.org/html/rfc6749#section-7.1
"""
self.token = parse_token_response(body, scope=scope)
self._populate_attributes(self.token)
@@ -487,4 +486,3 @@ class Client(object):
if 'mac_algorithm' in response:
self.mac_algorithm = response.get('mac_algorithm')
-
diff --git a/oauthlib/oauth2/rfc6749/clients/legacy_application.py b/oauthlib/oauth2/rfc6749/clients/legacy_application.py
index e4e522a..b16fc9f 100644
--- a/oauthlib/oauth2/rfc6749/clients/legacy_application.py
+++ b/oauthlib/oauth2/rfc6749/clients/legacy_application.py
@@ -8,9 +8,8 @@ for consuming and providing OAuth 2.0 RFC6749.
"""
from __future__ import absolute_import, unicode_literals
+from ..parameters import parse_token_response, prepare_token_request
from .base import Client
-from ..parameters import prepare_token_request
-from ..parameters import parse_token_response
class LegacyApplicationClient(Client):
@@ -65,9 +64,9 @@ class LegacyApplicationClient(Client):
>>> client.prepare_request_body(username='foo', password='bar', scope=['hello', 'world'])
'grant_type=password&username=foo&scope=hello+world&password=bar'
- .. _`Appendix B`: http://tools.ietf.org/html/rfc6749#appendix-B
- .. _`Section 3.3`: http://tools.ietf.org/html/rfc6749#section-3.3
- .. _`Section 3.2.1`: http://tools.ietf.org/html/rfc6749#section-3.2.1
+ .. _`Appendix B`: https://tools.ietf.org/html/rfc6749#appendix-B
+ .. _`Section 3.3`: https://tools.ietf.org/html/rfc6749#section-3.3
+ .. _`Section 3.2.1`: https://tools.ietf.org/html/rfc6749#section-3.2.1
"""
return prepare_token_request('password', 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 9e3ef21..311aacf 100644
--- a/oauthlib/oauth2/rfc6749/clients/mobile_application.py
+++ b/oauthlib/oauth2/rfc6749/clients/mobile_application.py
@@ -8,9 +8,8 @@ for consuming and providing OAuth 2.0 RFC6749.
"""
from __future__ import absolute_import, unicode_literals
+from ..parameters import parse_implicit_response, prepare_grant_uri
from .base import Client
-from ..parameters import prepare_grant_uri
-from ..parameters import parse_implicit_response
class MobileApplicationClient(Client):
@@ -86,11 +85,11 @@ class MobileApplicationClient(Client):
>>> client.prepare_request_uri('https://example.com', foo='bar')
'https://example.com?client_id=your_id&response_type=token&foo=bar'
- .. _`Appendix B`: http://tools.ietf.org/html/rfc6749#appendix-B
- .. _`Section 2.2`: http://tools.ietf.org/html/rfc6749#section-2.2
- .. _`Section 3.1.2`: http://tools.ietf.org/html/rfc6749#section-3.1.2
- .. _`Section 3.3`: http://tools.ietf.org/html/rfc6749#section-3.3
- .. _`Section 10.12`: http://tools.ietf.org/html/rfc6749#section-10.12
+ .. _`Appendix B`: https://tools.ietf.org/html/rfc6749#appendix-B
+ .. _`Section 2.2`: https://tools.ietf.org/html/rfc6749#section-2.2
+ .. _`Section 3.1.2`: https://tools.ietf.org/html/rfc6749#section-3.1.2
+ .. _`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',
redirect_uri=redirect_uri, state=state, scope=scope, **kwargs)
@@ -165,8 +164,8 @@ class MobileApplicationClient(Client):
>>> client.parse_request_body_response(response_body, scope=['other'])
('Scope has changed from "other" to "hello world".', ['other'], ['hello', 'world'])
- .. _`Section 7.1`: http://tools.ietf.org/html/rfc6749#section-7.1
- .. _`Section 3.3`: http://tools.ietf.org/html/rfc6749#section-3.3
+ .. _`Section 7.1`: https://tools.ietf.org/html/rfc6749#section-7.1
+ .. _`Section 3.3`: https://tools.ietf.org/html/rfc6749#section-3.3
"""
self.token = parse_implicit_response(uri, state=state, scope=scope)
self._populate_attributes(self.token)
diff --git a/oauthlib/oauth2/rfc6749/clients/service_application.py b/oauthlib/oauth2/rfc6749/clients/service_application.py
index 36da98b..84ea0e9 100644
--- a/oauthlib/oauth2/rfc6749/clients/service_application.py
+++ b/oauthlib/oauth2/rfc6749/clients/service_application.py
@@ -12,9 +12,8 @@ import time
from oauthlib.common import to_unicode
+from ..parameters import parse_token_response, prepare_token_request
from .base import Client
-from ..parameters import prepare_token_request
-from ..parameters import parse_token_response
class ServiceApplicationClient(Client):
@@ -137,7 +136,7 @@ class ServiceApplicationClient(Client):
eyJpc3Mi[...omitted for brevity...].
J9l-ZhwP[...omitted for brevity...]
- .. _`Section 3.2.1`: http://tools.ietf.org/html/rfc6749#section-3.2.1
+ .. _`Section 3.2.1`: https://tools.ietf.org/html/rfc6749#section-3.2.1
"""
import jwt
diff --git a/oauthlib/oauth2/rfc6749/clients/web_application.py b/oauthlib/oauth2/rfc6749/clients/web_application.py
index c685d3c..14b5265 100644
--- a/oauthlib/oauth2/rfc6749/clients/web_application.py
+++ b/oauthlib/oauth2/rfc6749/clients/web_application.py
@@ -8,10 +8,10 @@ for consuming and providing OAuth 2.0 RFC6749.
"""
from __future__ import absolute_import, unicode_literals
+from ..parameters import (parse_authorization_code_response,
+ parse_token_response, prepare_grant_uri,
+ prepare_token_request)
from .base import Client
-from ..parameters import prepare_grant_uri, prepare_token_request
-from ..parameters import parse_authorization_code_response
-from ..parameters import parse_token_response
class WebApplicationClient(Client):
@@ -76,11 +76,11 @@ class WebApplicationClient(Client):
>>> client.prepare_request_uri('https://example.com', foo='bar')
'https://example.com?client_id=your_id&response_type=code&foo=bar'
- .. _`Appendix B`: http://tools.ietf.org/html/rfc6749#appendix-B
- .. _`Section 2.2`: http://tools.ietf.org/html/rfc6749#section-2.2
- .. _`Section 3.1.2`: http://tools.ietf.org/html/rfc6749#section-3.1.2
- .. _`Section 3.3`: http://tools.ietf.org/html/rfc6749#section-3.3
- .. _`Section 10.12`: http://tools.ietf.org/html/rfc6749#section-10.12
+ .. _`Appendix B`: https://tools.ietf.org/html/rfc6749#appendix-B
+ .. _`Section 2.2`: https://tools.ietf.org/html/rfc6749#section-2.2
+ .. _`Section 3.1.2`: https://tools.ietf.org/html/rfc6749#section-3.1.2
+ .. _`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, 'code',
redirect_uri=redirect_uri, scope=scope, state=state, **kwargs)
@@ -120,12 +120,12 @@ class WebApplicationClient(Client):
>>> client.prepare_request_body(code='sh35ksdf09sf', foo='bar')
'grant_type=authorization_code&code=sh35ksdf09sf&foo=bar'
- .. _`Section 4.1.1`: http://tools.ietf.org/html/rfc6749#section-4.1.1
- .. _`Section 3.2.1`: http://tools.ietf.org/html/rfc6749#section-3.2.1
+ .. _`Section 4.1.1`: https://tools.ietf.org/html/rfc6749#section-4.1.1
+ .. _`Section 3.2.1`: https://tools.ietf.org/html/rfc6749#section-3.2.1
"""
code = code or self.code
return prepare_token_request('authorization_code', code=code, body=body,
- client_id=self.client_id, redirect_uri=redirect_uri, **kwargs)
+ client_id=client_id, redirect_uri=redirect_uri, **kwargs)
def parse_request_uri_response(self, uri, state=None):
"""Parse the URI query for code and state.
diff --git a/oauthlib/oauth2/rfc6749/endpoints/authorization.py b/oauthlib/oauth2/rfc6749/endpoints/authorization.py
index de100b1..92cde34 100644
--- a/oauthlib/oauth2/rfc6749/endpoints/authorization.py
+++ b/oauthlib/oauth2/rfc6749/endpoints/authorization.py
@@ -7,6 +7,7 @@ This module is an implementation of various logic needed
for consuming and providing OAuth 2.0 RFC6749.
"""
from __future__ import absolute_import, unicode_literals
+
import logging
from oauthlib.common import Request
@@ -58,7 +59,7 @@ class AuthorizationEndpoint(BaseEndpoint):
# Enforced through the design of oauthlib.common.Request
- .. _`Appendix B`: http://tools.ietf.org/html/rfc6749#appendix-B
+ .. _`Appendix B`: https://tools.ietf.org/html/rfc6749#appendix-B
"""
def __init__(self, default_response_type, default_token_type,
diff --git a/oauthlib/oauth2/rfc6749/endpoints/base.py b/oauthlib/oauth2/rfc6749/endpoints/base.py
index 759c6c8..cdb015f 100644
--- a/oauthlib/oauth2/rfc6749/endpoints/base.py
+++ b/oauthlib/oauth2/rfc6749/endpoints/base.py
@@ -11,8 +11,8 @@ from __future__ import absolute_import, unicode_literals
import functools
import logging
-from ..errors import TemporarilyUnavailableError, ServerError
-from ..errors import FatalClientError, OAuth2Error
+from ..errors import (FatalClientError, OAuth2Error, ServerError,
+ TemporarilyUnavailableError)
log = logging.getLogger(__name__)
diff --git a/oauthlib/oauth2/rfc6749/endpoints/pre_configured.py b/oauthlib/oauth2/rfc6749/endpoints/pre_configured.py
index 9e7667c..0c26986 100644
--- a/oauthlib/oauth2/rfc6749/endpoints/pre_configured.py
+++ b/oauthlib/oauth2/rfc6749/endpoints/pre_configured.py
@@ -8,20 +8,19 @@ for consuming and providing OAuth 2.0 RFC6749.
"""
from __future__ import absolute_import, unicode_literals
-from ..grant_types import OpenIDConnectAuthCode
-from ..tokens import BearerToken
-from ..grant_types import AuthorizationCodeGrant
-from ..grant_types import ImplicitGrant
-from ..grant_types import ResourceOwnerPasswordCredentialsGrant
-from ..grant_types import ClientCredentialsGrant
-from ..grant_types import RefreshTokenGrant
-from ..grant_types import OpenIDConnectImplicit
-from ..grant_types import AuthCodeGrantDispatcher
-
+from ..grant_types import (AuthCodeGrantDispatcher, AuthorizationCodeGrant,
+ AuthTokenGrantDispatcher,
+ ClientCredentialsGrant,
+ ImplicitTokenGrantDispatcher, ImplicitGrant,
+ OpenIDConnectAuthCode, OpenIDConnectImplicit,
+ OpenIDConnectHybrid,
+ RefreshTokenGrant,
+ ResourceOwnerPasswordCredentialsGrant)
+from ..tokens import BearerToken, JWTToken
from .authorization import AuthorizationEndpoint
-from .token import TokenEndpoint
from .resource import ResourceEndpoint
from .revocation import RevocationEndpoint
+from .token import TokenEndpoint
class Server(AuthorizationEndpoint, TokenEndpoint, ResourceEndpoint,
@@ -53,37 +52,44 @@ class Server(AuthorizationEndpoint, TokenEndpoint, ResourceEndpoint,
refresh_grant = RefreshTokenGrant(request_validator)
openid_connect_auth = OpenIDConnectAuthCode(request_validator)
openid_connect_implicit = OpenIDConnectImplicit(request_validator)
+ openid_connect_hybrid = OpenIDConnectHybrid(request_validator)
bearer = BearerToken(request_validator, token_generator,
token_expires_in, refresh_token_generator)
- auth_grant_choice = AuthCodeGrantDispatcher( default_auth_grant=auth_grant, oidc_auth_grant=openid_connect_auth)
+ jwt = JWTToken(request_validator, token_generator,
+ token_expires_in, refresh_token_generator)
+
+ auth_grant_choice = AuthCodeGrantDispatcher(default_auth_grant=auth_grant, oidc_auth_grant=openid_connect_auth)
+ implicit_grant_choice = ImplicitTokenGrantDispatcher(default_implicit_grant=implicit_grant, oidc_implicit_grant=openid_connect_implicit)
# See http://openid.net/specs/oauth-v2-multiple-response-types-1_0.html#Combinations for valid combinations
# internally our AuthorizationEndpoint will ensure they can appear in any order for any valid combination
AuthorizationEndpoint.__init__(self, default_response_type='code',
response_types={
'code': auth_grant_choice,
- 'token': implicit_grant,
+ 'token': implicit_grant_choice,
'id_token': openid_connect_implicit,
'id_token token': openid_connect_implicit,
- 'code token': openid_connect_auth,
- 'code id_token': openid_connect_auth,
- 'code token id_token': openid_connect_auth,
+ 'code token': openid_connect_hybrid,
+ 'code id_token': openid_connect_hybrid,
+ 'code id_token token': openid_connect_hybrid,
'none': auth_grant
},
default_token_type=bearer)
+
+ token_grant_choice = AuthTokenGrantDispatcher(request_validator, default_token_grant=auth_grant, oidc_token_grant=openid_connect_auth)
+
TokenEndpoint.__init__(self, default_grant_type='authorization_code',
grant_types={
- 'authorization_code': auth_grant,
+ 'authorization_code': token_grant_choice,
'password': password_grant,
'client_credentials': credentials_grant,
'refresh_token': refresh_grant,
- 'openid': openid_connect_auth
},
default_token_type=bearer)
ResourceEndpoint.__init__(self, default_token='Bearer',
- token_types={'Bearer': bearer})
+ token_types={'Bearer': bearer, 'JWT': jwt})
RevocationEndpoint.__init__(self, request_validator)
diff --git a/oauthlib/oauth2/rfc6749/endpoints/resource.py b/oauthlib/oauth2/rfc6749/endpoints/resource.py
index d03ed21..f19c60c 100644
--- a/oauthlib/oauth2/rfc6749/endpoints/resource.py
+++ b/oauthlib/oauth2/rfc6749/endpoints/resource.py
@@ -83,5 +83,5 @@ class ResourceEndpoint(BaseEndpoint):
to give an estimation based on the request.
"""
estimates = sorted(((t.estimate_type(request), n)
- for n, t in self.tokens.items()))
+ for n, t in self.tokens.items()), reverse=True)
return estimates[0][1] if len(estimates) else None
diff --git a/oauthlib/oauth2/rfc6749/endpoints/revocation.py b/oauthlib/oauth2/rfc6749/endpoints/revocation.py
index 6a5c408..d5b5b78 100644
--- a/oauthlib/oauth2/rfc6749/endpoints/revocation.py
+++ b/oauthlib/oauth2/rfc6749/endpoints/revocation.py
@@ -5,7 +5,7 @@ oauthlib.oauth2.rfc6749.endpoint.revocation
An implementation of the OAuth 2 `Token Revocation`_ spec (draft 11).
-.. _`Token Revocation`: http://tools.ietf.org/html/draft-ietf-oauth-revocation-11
+.. _`Token Revocation`: https://tools.ietf.org/html/draft-ietf-oauth-revocation-11
"""
from __future__ import absolute_import, unicode_literals
@@ -13,9 +13,9 @@ import logging
from oauthlib.common import Request
+from ..errors import (InvalidClientError, InvalidRequestError, OAuth2Error,
+ UnsupportedTokenTypeError)
from .base import BaseEndpoint, catch_errors_and_unavailability
-from ..errors import InvalidClientError, UnsupportedTokenTypeError
-from ..errors import InvalidRequestError, OAuth2Error
log = logging.getLogger(__name__)
@@ -110,11 +110,11 @@ class RevocationEndpoint(BaseEndpoint):
The client also includes its authentication credentials as described in
`Section 2.3`_. of [`RFC6749`_].
- .. _`section 1.4`: http://tools.ietf.org/html/rfc6749#section-1.4
- .. _`section 1.5`: http://tools.ietf.org/html/rfc6749#section-1.5
- .. _`section 2.3`: http://tools.ietf.org/html/rfc6749#section-2.3
- .. _`Section 4.1.2`: http://tools.ietf.org/html/draft-ietf-oauth-revocation-11#section-4.1.2
- .. _`RFC6749`: http://tools.ietf.org/html/rfc6749
+ .. _`section 1.4`: https://tools.ietf.org/html/rfc6749#section-1.4
+ .. _`section 1.5`: https://tools.ietf.org/html/rfc6749#section-1.5
+ .. _`section 2.3`: https://tools.ietf.org/html/rfc6749#section-2.3
+ .. _`Section 4.1.2`: https://tools.ietf.org/html/draft-ietf-oauth-revocation-11#section-4.1.2
+ .. _`RFC6749`: https://tools.ietf.org/html/rfc6749
"""
if not request.token:
raise InvalidRequestError(request=request,
diff --git a/oauthlib/oauth2/rfc6749/endpoints/token.py b/oauthlib/oauth2/rfc6749/endpoints/token.py
index 96cfbc7..90fb16f 100644
--- a/oauthlib/oauth2/rfc6749/endpoints/token.py
+++ b/oauthlib/oauth2/rfc6749/endpoints/token.py
@@ -15,7 +15,6 @@ from oauthlib.oauth2.rfc6749 import utils
from .base import BaseEndpoint, catch_errors_and_unavailability
-
log = logging.getLogger(__name__)
@@ -60,7 +59,7 @@ class TokenEndpoint(BaseEndpoint):
# Delegated to each grant type.
- .. _`Appendix B`: http://tools.ietf.org/html/rfc6749#appendix-B
+ .. _`Appendix B`: https://tools.ietf.org/html/rfc6749#appendix-B
"""
def __init__(self, default_grant_type, default_token_type, grant_types):
diff --git a/oauthlib/oauth2/rfc6749/errors.py b/oauthlib/oauth2/rfc6749/errors.py
index d33c6ff..180f636 100644
--- a/oauthlib/oauth2/rfc6749/errors.py
+++ b/oauthlib/oauth2/rfc6749/errors.py
@@ -7,8 +7,10 @@ Error used both by OAuth 2 clients and providers to represent the spec
defined error responses for all four core grant types.
"""
from __future__ import unicode_literals
+
import json
-from oauthlib.common import urlencode, add_params_to_uri
+
+from oauthlib.common import add_params_to_uri, urlencode
class OAuth2Error(Exception):
@@ -16,8 +18,8 @@ class OAuth2Error(Exception):
status_code = 400
description = ''
- def __init__(self, description=None, uri=None, state=None, status_code=None,
- request=None):
+ def __init__(self, description=None, uri=None, state=None,
+ status_code=None, request=None):
"""
description: A human-readable ASCII [USASCII] text providing
additional information, used to assist the client
@@ -37,7 +39,9 @@ class OAuth2Error(Exception):
request: Oauthlib Request object
"""
- self.description = description or self.description
+ if description is not None:
+ self.description = description
+
message = '(%s) %s' % (self.error, self.description)
if request:
message += ' ' + repr(request)
@@ -58,10 +62,17 @@ class OAuth2Error(Exception):
self.grant_type = request.grant_type
if not state:
self.state = request.state
+ else:
+ self.redirect_uri = None
+ self.client_id = None
+ self.scopes = None
+ self.response_type = None
+ self.response_mode = None
+ self.grant_type = None
def in_uri(self, uri):
- return add_params_to_uri(uri, self.twotuples,
- fragment=self.response_mode == "fragment")
+ fragment = self.response_mode == "fragment"
+ return add_params_to_uri(uri, self.twotuples, fragment)
@property
def twotuples(self):
diff --git a/oauthlib/oauth2/rfc6749/grant_types/__init__.py b/oauthlib/oauth2/rfc6749/grant_types/__init__.py
index 1da1281..2e4bfe4 100644
--- a/oauthlib/oauth2/rfc6749/grant_types/__init__.py
+++ b/oauthlib/oauth2/rfc6749/grant_types/__init__.py
@@ -16,3 +16,5 @@ from .openid_connect import OpenIDConnectImplicit
from .openid_connect import OpenIDConnectHybrid
from .openid_connect import OIDCNoPrompt
from .openid_connect import AuthCodeGrantDispatcher
+from .openid_connect import AuthTokenGrantDispatcher
+from .openid_connect import ImplicitTokenGrantDispatcher
diff --git a/oauthlib/oauth2/rfc6749/grant_types/authorization_code.py b/oauthlib/oauth2/rfc6749/grant_types/authorization_code.py
index 8a9d705..7bea650 100644
--- a/oauthlib/oauth2/rfc6749/grant_types/authorization_code.py
+++ b/oauthlib/oauth2/rfc6749/grant_types/authorization_code.py
@@ -3,7 +3,7 @@
oauthlib.oauth2.rfc6749.grant_types
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
"""
-from __future__ import unicode_literals, absolute_import
+from __future__ import absolute_import, unicode_literals
import json
import logging
@@ -11,8 +11,8 @@ import logging
from oauthlib import common
from oauthlib.uri_validate import is_absolute_uri
-from .base import GrantTypeBase
from .. import errors
+from .base import GrantTypeBase
log = logging.getLogger(__name__)
@@ -91,7 +91,7 @@ class AuthorizationCodeGrant(GrantTypeBase):
step (C). If valid, the authorization server responds back with
an access token and, optionally, a refresh token.
- .. _`Authorization Code Grant`: http://tools.ietf.org/html/rfc6749#section-4.1
+ .. _`Authorization Code Grant`: https://tools.ietf.org/html/rfc6749#section-4.1
"""
default_response_mode = 'query'
@@ -175,11 +175,11 @@ class AuthorizationCodeGrant(GrantTypeBase):
File "oauthlib/oauth2/rfc6749/grant_types.py", line 591, in validate_authorization_request
oauthlib.oauth2.rfc6749.errors.InvalidClientIdError
- .. _`Appendix B`: http://tools.ietf.org/html/rfc6749#appendix-B
- .. _`Section 2.2`: http://tools.ietf.org/html/rfc6749#section-2.2
- .. _`Section 3.1.2`: http://tools.ietf.org/html/rfc6749#section-3.1.2
- .. _`Section 3.3`: http://tools.ietf.org/html/rfc6749#section-3.3
- .. _`Section 10.12`: http://tools.ietf.org/html/rfc6749#section-10.12
+ .. _`Appendix B`: https://tools.ietf.org/html/rfc6749#appendix-B
+ .. _`Section 2.2`: https://tools.ietf.org/html/rfc6749#section-2.2
+ .. _`Section 3.1.2`: https://tools.ietf.org/html/rfc6749#section-3.1.2
+ .. _`Section 3.3`: https://tools.ietf.org/html/rfc6749#section-3.3
+ .. _`Section 10.12`: https://tools.ietf.org/html/rfc6749#section-10.12
"""
try:
# request.scopes is only mandated in post auth and both pre and
@@ -206,7 +206,7 @@ class AuthorizationCodeGrant(GrantTypeBase):
# the authorization server informs the client by adding the following
# parameters to the query component of the redirection URI using the
# "application/x-www-form-urlencoded" format, per Appendix B:
- # http://tools.ietf.org/html/rfc6749#appendix-B
+ # https://tools.ietf.org/html/rfc6749#appendix-B
except errors.OAuth2Error as e:
log.debug('Client error during validation of %r. %r.', request, e)
request.redirect_uri = request.redirect_uri or self.error_uri
@@ -285,7 +285,7 @@ class AuthorizationCodeGrant(GrantTypeBase):
raise errors.InvalidRequestFatalError(description='Duplicate %s parameter.' % param, request=request)
# REQUIRED. The client identifier as described in Section 2.2.
- # http://tools.ietf.org/html/rfc6749#section-2.2
+ # https://tools.ietf.org/html/rfc6749#section-2.2
if not request.client_id:
raise errors.MissingClientIdError(request=request)
@@ -293,7 +293,7 @@ class AuthorizationCodeGrant(GrantTypeBase):
raise errors.InvalidClientIdError(request=request)
# OPTIONAL. As described in Section 3.1.2.
- # http://tools.ietf.org/html/rfc6749#section-3.1.2
+ # https://tools.ietf.org/html/rfc6749#section-3.1.2
log.debug('Validating redirection uri %s for client %s.',
request.redirect_uri, request.client_id)
if request.redirect_uri is not None:
@@ -320,7 +320,7 @@ class AuthorizationCodeGrant(GrantTypeBase):
# the authorization server informs the client by adding the following
# parameters to the query component of the redirection URI using the
# "application/x-www-form-urlencoded" format, per Appendix B.
- # http://tools.ietf.org/html/rfc6749#appendix-B
+ # https://tools.ietf.org/html/rfc6749#appendix-B
# Note that the correct parameters to be added are automatically
# populated through the use of specific exceptions.
@@ -346,7 +346,7 @@ class AuthorizationCodeGrant(GrantTypeBase):
raise errors.UnauthorizedClientError(request=request)
# OPTIONAL. The scope of the access request as described by Section 3.3
- # http://tools.ietf.org/html/rfc6749#section-3.3
+ # https://tools.ietf.org/html/rfc6749#section-3.3
self.validate_scopes(request)
request_info.update({
@@ -384,14 +384,14 @@ class AuthorizationCodeGrant(GrantTypeBase):
# credentials (or assigned other authentication requirements), the
# client MUST authenticate with the authorization server as described
# in Section 3.2.1.
- # http://tools.ietf.org/html/rfc6749#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.
- # http://tools.ietf.org/html/rfc6749#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)
@@ -424,7 +424,7 @@ class AuthorizationCodeGrant(GrantTypeBase):
request.redirect_uri, request.client):
log.debug('Redirect_uri (%r) invalid for client %r (%r).',
request.redirect_uri, request.client_id, request.client)
- raise errors.AccessDeniedError(request=request)
+ raise errors.MismatchingRedirectURIError(request=request)
for validator in self.custom_validators.post_token:
validator(request)
diff --git a/oauthlib/oauth2/rfc6749/grant_types/base.py b/oauthlib/oauth2/rfc6749/grant_types/base.py
index 1128388..e5d8ddd 100644
--- a/oauthlib/oauth2/rfc6749/grant_types/base.py
+++ b/oauthlib/oauth2/rfc6749/grant_types/base.py
@@ -3,13 +3,14 @@
oauthlib.oauth2.rfc6749.grant_types
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
"""
-from __future__ import unicode_literals, absolute_import
-from itertools import chain
+from __future__ import absolute_import, unicode_literals
import logging
+from itertools import chain
from oauthlib.common import add_params_to_uri
from oauthlib.oauth2.rfc6749 import errors, utils
+
from ..request_validator import RequestValidator
log = logging.getLogger(__name__)
diff --git a/oauthlib/oauth2/rfc6749/grant_types/client_credentials.py b/oauthlib/oauth2/rfc6749/grant_types/client_credentials.py
index dd0a39a..4c50a78 100644
--- a/oauthlib/oauth2/rfc6749/grant_types/client_credentials.py
+++ b/oauthlib/oauth2/rfc6749/grant_types/client_credentials.py
@@ -3,14 +3,14 @@
oauthlib.oauth2.rfc6749.grant_types
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
"""
-from __future__ import unicode_literals, absolute_import
+from __future__ import absolute_import, unicode_literals
import json
import logging
-from .base import GrantTypeBase
from .. import errors
from ..request_validator import RequestValidator
+from .base import GrantTypeBase
log = logging.getLogger(__name__)
@@ -47,7 +47,7 @@ class ClientCredentialsGrant(GrantTypeBase):
(B) The authorization server authenticates the client, and if valid,
issues an access token.
- .. _`Client Credentials Grant`: http://tools.ietf.org/html/rfc6749#section-4.4
+ .. _`Client Credentials Grant`: https://tools.ietf.org/html/rfc6749#section-4.4
"""
def create_token_response(self, request, token_handler):
@@ -59,8 +59,8 @@ class ClientCredentialsGrant(GrantTypeBase):
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
+ .. _`Section 5.1`: https://tools.ietf.org/html/rfc6749#section-5.1
+ .. _`Section 5.2`: https://tools.ietf.org/html/rfc6749#section-5.2
"""
headers = {
'Content-Type': 'application/json',
diff --git a/oauthlib/oauth2/rfc6749/grant_types/implicit.py b/oauthlib/oauth2/rfc6749/grant_types/implicit.py
index 7ffed8d..3a5c058 100644
--- a/oauthlib/oauth2/rfc6749/grant_types/implicit.py
+++ b/oauthlib/oauth2/rfc6749/grant_types/implicit.py
@@ -3,16 +3,15 @@
oauthlib.oauth2.rfc6749.grant_types
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
"""
-from __future__ import unicode_literals, absolute_import
+from __future__ import absolute_import, unicode_literals
import logging
from oauthlib import common
from oauthlib.uri_validate import is_absolute_uri
-from .base import GrantTypeBase
from .. import errors
-from ..request_validator import RequestValidator
+from .base import GrantTypeBase
log = logging.getLogger(__name__)
@@ -112,9 +111,9 @@ class ImplicitGrant(GrantTypeBase):
See `Section 10.3`_ and `Section 10.16`_ for important security considerations
when using the implicit grant.
- .. _`Implicit Grant`: http://tools.ietf.org/html/rfc6749#section-4.2
- .. _`Section 10.3`: http://tools.ietf.org/html/rfc6749#section-10.3
- .. _`Section 10.16`: http://tools.ietf.org/html/rfc6749#section-10.16
+ .. _`Implicit Grant`: https://tools.ietf.org/html/rfc6749#section-4.2
+ .. _`Section 10.3`: https://tools.ietf.org/html/rfc6749#section-10.3
+ .. _`Section 10.16`: https://tools.ietf.org/html/rfc6749#section-10.16
"""
response_types = ['token']
@@ -153,11 +152,11 @@ class ImplicitGrant(GrantTypeBase):
access token matches a redirection URI registered by the client as
described in `Section 3.1.2`_.
- .. _`Section 2.2`: http://tools.ietf.org/html/rfc6749#section-2.2
- .. _`Section 3.1.2`: http://tools.ietf.org/html/rfc6749#section-3.1.2
- .. _`Section 3.3`: http://tools.ietf.org/html/rfc6749#section-3.3
- .. _`Section 10.12`: http://tools.ietf.org/html/rfc6749#section-10.12
- .. _`Appendix B`: http://tools.ietf.org/html/rfc6749#appendix-B
+ .. _`Section 2.2`: https://tools.ietf.org/html/rfc6749#section-2.2
+ .. _`Section 3.1.2`: https://tools.ietf.org/html/rfc6749#section-3.1.2
+ .. _`Section 3.3`: https://tools.ietf.org/html/rfc6749#section-3.3
+ .. _`Section 10.12`: https://tools.ietf.org/html/rfc6749#section-10.12
+ .. _`Appendix B`: https://tools.ietf.org/html/rfc6749#appendix-B
"""
return self.create_token_response(request, token_handler)
@@ -196,9 +195,9 @@ class ImplicitGrant(GrantTypeBase):
The authorization server MUST NOT issue a refresh token.
- .. _`Appendix B`: http://tools.ietf.org/html/rfc6749#appendix-B
- .. _`Section 3.3`: http://tools.ietf.org/html/rfc6749#section-3.3
- .. _`Section 7.1`: http://tools.ietf.org/html/rfc6749#section-7.1
+ .. _`Appendix B`: https://tools.ietf.org/html/rfc6749#appendix-B
+ .. _`Section 3.3`: https://tools.ietf.org/html/rfc6749#section-3.3
+ .. _`Section 7.1`: https://tools.ietf.org/html/rfc6749#section-7.1
"""
try:
self.validate_token_request(request)
@@ -218,13 +217,13 @@ class ImplicitGrant(GrantTypeBase):
# the authorization server informs the client by adding the following
# parameters to the fragment component of the redirection URI using the
# "application/x-www-form-urlencoded" format, per Appendix B:
- # http://tools.ietf.org/html/rfc6749#appendix-B
+ # https://tools.ietf.org/html/rfc6749#appendix-B
except errors.OAuth2Error as e:
log.debug('Client error during validation of %r. %r.', request, e)
return {'Location': common.add_params_to_uri(request.redirect_uri, e.twotuples,
fragment=True)}, None, 302
- # In OIDC implicit flow it is possible to have a request_type that does not include the access token!
+ # In OIDC implicit flow it is possible to have a request_type that does not include the access_token!
# "id_token token" - return the access token and the id token
# "id_token" - don't return the access token
if "token" in request.response_type.split():
@@ -234,7 +233,12 @@ class ImplicitGrant(GrantTypeBase):
for modifier in self._token_modifiers:
token = modifier(token, token_handler, request)
- self.request_validator.save_token(token, request)
+
+ # In OIDC implicit flow it is possible to have a request_type that does
+ # not include the access_token! In this case there is no need to save a token.
+ if "token" in request.response_type.split():
+ self.request_validator.save_token(token, request)
+
return self.prepare_authorization_response(
request, token, {}, None, 302)
@@ -276,7 +280,7 @@ class ImplicitGrant(GrantTypeBase):
raise errors.InvalidRequestFatalError(description='Duplicate %s parameter.' % param, request=request)
# REQUIRED. The client identifier as described in Section 2.2.
- # http://tools.ietf.org/html/rfc6749#section-2.2
+ # https://tools.ietf.org/html/rfc6749#section-2.2
if not request.client_id:
raise errors.MissingClientIdError(request=request)
@@ -284,7 +288,7 @@ class ImplicitGrant(GrantTypeBase):
raise errors.InvalidClientIdError(request=request)
# OPTIONAL. As described in Section 3.1.2.
- # http://tools.ietf.org/html/rfc6749#section-3.1.2
+ # https://tools.ietf.org/html/rfc6749#section-3.1.2
if request.redirect_uri is not None:
request.using_default_redirect_uri = False
log.debug('Using provided redirect_uri %s', request.redirect_uri)
@@ -295,7 +299,7 @@ class ImplicitGrant(GrantTypeBase):
# to which it will redirect the access token matches a
# redirection URI registered by the client as described in
# Section 3.1.2.
- # http://tools.ietf.org/html/rfc6749#section-3.1.2
+ # https://tools.ietf.org/html/rfc6749#section-3.1.2
if not self.request_validator.validate_redirect_uri(
request.client_id, request.redirect_uri, request):
raise errors.MismatchingRedirectURIError(request=request)
@@ -312,15 +316,14 @@ class ImplicitGrant(GrantTypeBase):
# Then check for normal errors.
request_info = self._run_custom_validators(request,
- self.custom_validators.all_pre)
-
+ self.custom_validators.all_pre)
# If the resource owner denies the access request or if the request
# fails for reasons other than a missing or invalid redirection URI,
# the authorization server informs the client by adding the following
# parameters to the fragment component of the redirection URI using the
# "application/x-www-form-urlencoded" format, per Appendix B.
- # http://tools.ietf.org/html/rfc6749#appendix-B
+ # https://tools.ietf.org/html/rfc6749#appendix-B
# Note that the correct parameters to be added are automatically
# populated through the use of specific exceptions
@@ -343,24 +346,25 @@ class ImplicitGrant(GrantTypeBase):
raise errors.UnauthorizedClientError(request=request)
# OPTIONAL. The scope of the access request as described by Section 3.3
- # http://tools.ietf.org/html/rfc6749#section-3.3
+ # https://tools.ietf.org/html/rfc6749#section-3.3
self.validate_scopes(request)
request_info.update({
- 'client_id': request.client_id,
- 'redirect_uri': request.redirect_uri,
- 'response_type': request.response_type,
- 'state': request.state,
- 'request': request,
+ 'client_id': request.client_id,
+ 'redirect_uri': request.redirect_uri,
+ 'response_type': request.response_type,
+ 'state': request.state,
+ 'request': request,
})
- request_info = self._run_custom_validators(request,
- self.custom_validators.all_post,
- request_info)
+ request_info = self._run_custom_validators(
+ request,
+ self.custom_validators.all_post,
+ request_info
+ )
return request.scopes, request_info
-
def _run_custom_validators(self,
request,
validations,
diff --git a/oauthlib/oauth2/rfc6749/grant_types/openid_connect.py b/oauthlib/oauth2/rfc6749/grant_types/openid_connect.py
index 12d4a86..4371b28 100644
--- a/oauthlib/oauth2/rfc6749/grant_types/openid_connect.py
+++ b/oauthlib/oauth2/rfc6749/grant_types/openid_connect.py
@@ -3,21 +3,20 @@
oauthlib.oauth2.rfc6749.grant_types.openid_connect
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
"""
-from __future__ import unicode_literals, absolute_import
-
-from json import loads
-import logging
+from __future__ import absolute_import, unicode_literals
import datetime
+import logging
+from json import loads
-from .base import GrantTypeBase
+from ..errors import ConsentRequired, InvalidRequestError, LoginRequired
+from ..request_validator import RequestValidator
from .authorization_code import AuthorizationCodeGrant
from .implicit import ImplicitGrant
-from ..errors import InvalidRequestError, LoginRequired, ConsentRequired
-from ..request_validator import RequestValidator
log = logging.getLogger(__name__)
+
class OIDCNoPrompt(Exception):
"""Exception used to inform users that no explicit authorization is needed.
@@ -77,6 +76,65 @@ class AuthCodeGrantDispatcher(object):
return self._handler_for_request(request).validate_authorization_request(request)
+class ImplicitTokenGrantDispatcher(object):
+ """
+ This is an adapter class that will route simple Authorization Code requests, those that have response_type=code and a scope
+ including 'openid' to either the default_auth_grant or the oidc_auth_grant based on the scopes requested.
+ """
+ def __init__(self, default_implicit_grant=None, oidc_implicit_grant=None):
+ self.default_implicit_grant = default_implicit_grant
+ self.oidc_implicit_grant = oidc_implicit_grant
+
+ def _handler_for_request(self, request):
+ handler = self.default_implicit_grant
+
+ if request.scopes and "openid" in request.scopes and 'id_token' in request.response_type:
+ handler = self.oidc_implicit_grant
+
+ log.debug('Selecting handler for request %r.', handler)
+ return handler
+
+ def create_authorization_response(self, request, token_handler):
+ return self._handler_for_request(request).create_authorization_response(request, token_handler)
+
+ def validate_authorization_request(self, request):
+ return self._handler_for_request(request).validate_authorization_request(request)
+
+
+class AuthTokenGrantDispatcher(object):
+ """
+ This is an adapter class that will route simple Token requests, those that authorization_code have a scope
+ including 'openid' to either the default_token_grant or the oidc_token_grant based on the scopes requested.
+ """
+ def __init__(self, request_validator, default_token_grant=None, oidc_token_grant=None):
+ self.default_token_grant = default_token_grant
+ self.oidc_token_grant = oidc_token_grant
+ self.request_validator = request_validator
+
+ def _handler_for_request(self, request):
+ handler = self.default_token_grant
+ scopes = ()
+ parameters = dict(request.decoded_body)
+ client_id = parameters.get('client_id', None)
+ code = parameters.get('code', None)
+ redirect_uri = parameters.get('redirect_uri', None)
+
+ # If code is not pressent fallback to `default_token_grant` wich will
+ # raise an error for the missing `code` in `create_token_response` step.
+ if code:
+ scopes = self.request_validator.get_authorization_code_scopes(client_id, code, redirect_uri, request)
+
+ if 'openid' in scopes:
+ handler = self.oidc_token_grant
+
+ log.debug('Selecting handler for request %r.', handler)
+ return handler
+
+ def create_token_response(self, request, token_handler):
+ handler = self._handler_for_request(request)
+ return handler.create_token_response(request, token_handler)
+
+
class OpenIDConnectBase(object):
# Just proxy the majority of method calls through to the
@@ -142,6 +200,13 @@ class OpenIDConnectBase(object):
def openid_authorization_validator(self, request):
"""Perform OpenID Connect specific authorization request validation.
+ nonce
+ OPTIONAL. String value used to associate a Client session with
+ an ID Token, and to mitigate replay attacks. The value is
+ passed through unmodified from the Authentication Request to
+ the ID Token. Sufficient entropy MUST be present in the nonce
+ values used to prevent attackers from guessing values
+
display
OPTIONAL. ASCII string value that specifies how the
Authorization Server displays the authentication and consent
@@ -301,12 +366,13 @@ class OpenIDConnectBase(object):
self._inflate_claims(request)
if not self.request_validator.validate_user_match(
- request.id_token_hint, request.scopes, request.claims, request):
+ request.id_token_hint, request.scopes, request.claims, request):
msg = "Session user does not match client supplied user."
raise LoginRequired(request=request, description=msg)
request_info = {
'display': request.display,
+ 'nonce': request.nonce,
'prompt': prompt,
'ui_locales': request.ui_locales.split() if request.ui_locales else [],
'id_token_hint': request.id_token_hint,
@@ -337,9 +403,7 @@ class OpenIDConnectBase(object):
desc = 'Request is missing mandatory nonce parameter.'
raise InvalidRequestError(request=request, description=desc)
- self._inflate_claims(request)
-
- return {'nonce': request.nonce, 'claims': request.claims}
+ return {}
class OpenIDConnectAuthCode(OpenIDConnectBase):
@@ -351,6 +415,7 @@ class OpenIDConnectAuthCode(OpenIDConnectBase):
self.openid_authorization_validator)
self.register_token_modifier(self.add_id_token)
+
class OpenIDConnectImplicit(OpenIDConnectBase):
def __init__(self, request_validator=None, **kwargs):
@@ -364,6 +429,7 @@ class OpenIDConnectImplicit(OpenIDConnectBase):
self.openid_implicit_authorization_validator)
self.register_token_modifier(self.add_id_token)
+
class OpenIDConnectHybrid(OpenIDConnectBase):
def __init__(self, request_validator=None, **kwargs):
@@ -371,6 +437,8 @@ class OpenIDConnectHybrid(OpenIDConnectBase):
self.proxy_target = AuthorizationCodeGrant(
request_validator=request_validator, **kwargs)
+ # All hybrid response types should be fragment-encoded.
+ self.proxy_target.default_response_mode = "fragment"
self.register_response_type('code id_token')
self.register_response_type('code token')
self.register_response_type('code id_token token')
diff --git a/oauthlib/oauth2/rfc6749/grant_types/refresh_token.py b/oauthlib/oauth2/rfc6749/grant_types/refresh_token.py
index 396668b..c2d86f7 100644
--- a/oauthlib/oauth2/rfc6749/grant_types/refresh_token.py
+++ b/oauthlib/oauth2/rfc6749/grant_types/refresh_token.py
@@ -3,14 +3,14 @@
oauthlib.oauth2.rfc6749.grant_types
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
"""
-from __future__ import unicode_literals, absolute_import
+from __future__ import absolute_import, unicode_literals
import json
import logging
-from .base import GrantTypeBase
from .. import errors, utils
from ..request_validator import RequestValidator
+from .base import GrantTypeBase
log = logging.getLogger(__name__)
@@ -19,7 +19,7 @@ class RefreshTokenGrant(GrantTypeBase):
"""`Refresh token grant`_
- .. _`Refresh token grant`: http://tools.ietf.org/html/rfc6749#section-6
+ .. _`Refresh token grant`: https://tools.ietf.org/html/rfc6749#section-6
"""
def __init__(self, request_validator=None,
@@ -46,8 +46,8 @@ class RefreshTokenGrant(GrantTypeBase):
identical to that of the refresh token included by the client in the
request.
- .. _`Section 5.1`: http://tools.ietf.org/html/rfc6749#section-5.1
- .. _`Section 5.2`: http://tools.ietf.org/html/rfc6749#section-5.2
+ .. _`Section 5.1`: https://tools.ietf.org/html/rfc6749#section-5.1
+ .. _`Section 5.2`: https://tools.ietf.org/html/rfc6749#section-5.2
"""
headers = {
'Content-Type': 'application/json',
@@ -90,7 +90,7 @@ class RefreshTokenGrant(GrantTypeBase):
# 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.
- # http://tools.ietf.org/html/rfc6749#section-3.2.1
+ # https://tools.ietf.org/html/rfc6749#section-3.2.1
if self.request_validator.client_authentication_required(request):
log.debug('Authenticating client, %r.', request)
if not self.request_validator.authenticate_client(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 f755240..e5f04af 100644
--- a/oauthlib/oauth2/rfc6749/grant_types/resource_owner_password_credentials.py
+++ b/oauthlib/oauth2/rfc6749/grant_types/resource_owner_password_credentials.py
@@ -3,14 +3,14 @@
oauthlib.oauth2.rfc6749.grant_types
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
"""
-from __future__ import unicode_literals, absolute_import
+from __future__ import absolute_import, unicode_literals
import json
import logging
-from .base import GrantTypeBase
from .. import errors
from ..request_validator import RequestValidator
+from .base import GrantTypeBase
log = logging.getLogger(__name__)
@@ -67,7 +67,7 @@ class ResourceOwnerPasswordCredentialsGrant(GrantTypeBase):
the resource owner credentials, and if valid, issues an access
token.
- .. _`Resource Owner Password Credentials Grant`: http://tools.ietf.org/html/rfc6749#section-4.3
+ .. _`Resource Owner Password Credentials Grant`: https://tools.ietf.org/html/rfc6749#section-4.3
"""
def create_token_response(self, request, token_handler):
@@ -79,8 +79,8 @@ class ResourceOwnerPasswordCredentialsGrant(GrantTypeBase):
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
+ .. _`Section 5.1`: https://tools.ietf.org/html/rfc6749#section-5.1
+ .. _`Section 5.2`: https://tools.ietf.org/html/rfc6749#section-5.2
"""
headers = {
'Content-Type': 'application/json',
@@ -153,8 +153,8 @@ class ResourceOwnerPasswordCredentialsGrant(GrantTypeBase):
brute force attacks (e.g., using rate-limitation or generating
alerts).
- .. _`Section 3.3`: http://tools.ietf.org/html/rfc6749#section-3.3
- .. _`Section 3.2.1`: http://tools.ietf.org/html/rfc6749#section-3.2.1
+ .. _`Section 3.3`: https://tools.ietf.org/html/rfc6749#section-3.3
+ .. _`Section 3.2.1`: https://tools.ietf.org/html/rfc6749#section-3.2.1
"""
for validator in self.custom_validators.pre_token:
validator(request)
diff --git a/oauthlib/oauth2/rfc6749/parameters.py b/oauthlib/oauth2/rfc6749/parameters.py
index b46889c..0107933 100644
--- a/oauthlib/oauth2/rfc6749/parameters.py
+++ b/oauthlib/oauth2/rfc6749/parameters.py
@@ -5,24 +5,27 @@ oauthlib.oauth2.rfc6749.parameters
This module contains methods related to `Section 4`_ of the OAuth 2 RFC.
-.. _`Section 4`: http://tools.ietf.org/html/rfc6749#section-4
+.. _`Section 4`: https://tools.ietf.org/html/rfc6749#section-4
"""
from __future__ import absolute_import, unicode_literals
import json
import os
import time
+
+from oauthlib.common import add_params_to_qs, add_params_to_uri, unicode_type
+from oauthlib.signals import scope_changed
+
+from .errors import (InsecureTransportError, MismatchingStateError,
+ MissingCodeError, MissingTokenError,
+ MissingTokenTypeError, raise_from_error)
+from .tokens import OAuth2Token
+from .utils import is_secure_transport, list_to_scope, scope_to_list
+
try:
import urlparse
except ImportError:
import urllib.parse as urlparse
-from oauthlib.common import add_params_to_uri, add_params_to_qs, unicode_type
-from oauthlib.signals import scope_changed
-from .errors import raise_from_error, MissingTokenError, MissingTokenTypeError
-from .errors import MismatchingStateError, MissingCodeError
-from .errors import InsecureTransportError
-from .tokens import OAuth2Token
-from .utils import list_to_scope, scope_to_list, is_secure_transport
def prepare_grant_uri(uri, client_id, response_type, redirect_uri=None,
@@ -58,11 +61,11 @@ def prepare_grant_uri(uri, client_id, response_type, redirect_uri=None,
&redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb HTTP/1.1
Host: server.example.com
- .. _`W3C.REC-html401-19991224`: http://tools.ietf.org/html/rfc6749#ref-W3C.REC-html401-19991224
- .. _`Section 2.2`: http://tools.ietf.org/html/rfc6749#section-2.2
- .. _`Section 3.1.2`: http://tools.ietf.org/html/rfc6749#section-3.1.2
- .. _`Section 3.3`: http://tools.ietf.org/html/rfc6749#section-3.3
- .. _`section 10.12`: http://tools.ietf.org/html/rfc6749#section-10.12
+ .. _`W3C.REC-html401-19991224`: https://tools.ietf.org/html/rfc6749#ref-W3C.REC-html401-19991224
+ .. _`Section 2.2`: https://tools.ietf.org/html/rfc6749#section-2.2
+ .. _`Section 3.1.2`: https://tools.ietf.org/html/rfc6749#section-3.1.2
+ .. _`Section 3.3`: https://tools.ietf.org/html/rfc6749#section-3.3
+ .. _`section 10.12`: https://tools.ietf.org/html/rfc6749#section-10.12
"""
if not is_secure_transport(uri):
raise InsecureTransportError()
@@ -108,7 +111,7 @@ def prepare_token_request(grant_type, body='', **kwargs):
grant_type=authorization_code&code=SplxlOBeZQQYbYS6WxSbIA
&redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb
- .. _`Section 4.1.1`: http://tools.ietf.org/html/rfc6749#section-4.1.1
+ .. _`Section 4.1.1`: https://tools.ietf.org/html/rfc6749#section-4.1.1
"""
params = [('grant_type', grant_type)]
@@ -150,9 +153,9 @@ def prepare_token_revocation_request(url, token, token_type_hint="access_token",
specification MAY define other values for this parameter using the
registry defined in `Section 4.1.2`_.
- .. _`Section 1.4`: http://tools.ietf.org/html/rfc6749#section-1.4
- .. _`Section 1.5`: http://tools.ietf.org/html/rfc6749#section-1.5
- .. _`Section 4.1.2`: http://tools.ietf.org/html/rfc7009#section-4.1.2
+ .. _`Section 1.4`: https://tools.ietf.org/html/rfc6749#section-1.4
+ .. _`Section 1.5`: https://tools.ietf.org/html/rfc6749#section-1.5
+ .. _`Section 4.1.2`: https://tools.ietf.org/html/rfc7009#section-4.1.2
"""
if not is_secure_transport(url):
@@ -345,10 +348,10 @@ def parse_token_response(body, scope=None):
"example_parameter":"example_value"
}
- .. _`Section 7.1`: http://tools.ietf.org/html/rfc6749#section-7.1
- .. _`Section 6`: http://tools.ietf.org/html/rfc6749#section-6
- .. _`Section 3.3`: http://tools.ietf.org/html/rfc6749#section-3.3
- .. _`RFC4627`: http://tools.ietf.org/html/rfc4627
+ .. _`Section 7.1`: https://tools.ietf.org/html/rfc6749#section-7.1
+ .. _`Section 6`: https://tools.ietf.org/html/rfc6749#section-6
+ .. _`Section 3.3`: https://tools.ietf.org/html/rfc6749#section-3.3
+ .. _`RFC4627`: https://tools.ietf.org/html/rfc4627
"""
try:
params = json.loads(body)
@@ -356,7 +359,7 @@ def parse_token_response(body, scope=None):
# Fall back to URL-encoded string, to support old implementations,
# including (at time of writing) Facebook. See:
- # https://github.com/idan/oauthlib/issues/267
+ # https://github.com/oauthlib/oauthlib/issues/267
params = dict(urlparse.parse_qsl(body))
for key in ('expires_in', 'expires'):
@@ -392,7 +395,7 @@ def validate_token_parameters(params):
# If the issued access token scope is different from the one requested by
# the client, the authorization server MUST include the "scope" response
# parameter to inform the client of the actual scope granted.
- # http://tools.ietf.org/html/rfc6749#section-3.3
+ # https://tools.ietf.org/html/rfc6749#section-3.3
if params.scope_changed:
message = 'Scope has changed from "{old}" to "{new}".'.format(
old=params.old_scope, new=params.scope,
diff --git a/oauthlib/oauth2/rfc6749/request_validator.py b/oauthlib/oauth2/rfc6749/request_validator.py
index 2acd8ec..182642e 100644
--- a/oauthlib/oauth2/rfc6749/request_validator.py
+++ b/oauthlib/oauth2/rfc6749/request_validator.py
@@ -3,7 +3,7 @@
oauthlib.oauth2.rfc6749.grant_types
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
"""
-from __future__ import unicode_literals, absolute_import
+from __future__ import absolute_import, unicode_literals
import logging
@@ -34,9 +34,9 @@ class RequestValidator(object):
- Resource Owner Password Credentials Grant
- Refresh Token Grant
- .. _`Section 4.3.2`: http://tools.ietf.org/html/rfc6749#section-4.3.2
- .. _`Section 4.1.3`: http://tools.ietf.org/html/rfc6749#section-4.1.3
- .. _`Section 6`: http://tools.ietf.org/html/rfc6749#section-6
+ .. _`Section 4.3.2`: https://tools.ietf.org/html/rfc6749#section-4.3.2
+ .. _`Section 4.1.3`: https://tools.ietf.org/html/rfc6749#section-4.1.3
+ .. _`Section 6`: https://tools.ietf.org/html/rfc6749#section-6
"""
return True
@@ -60,7 +60,7 @@ class RequestValidator(object):
- Client Credentials Grant
- Refresh Token Grant
- .. _`HTTP Basic Authentication Scheme`: http://tools.ietf.org/html/rfc1945#section-11.1
+ .. _`HTTP Basic Authentication Scheme`: https://tools.ietf.org/html/rfc1945#section-11.1
"""
raise NotImplementedError('Subclasses must implement this method.')
@@ -238,6 +238,30 @@ class RequestValidator(object):
"""
raise NotImplementedError('Subclasses must implement this method.')
+ def get_authorization_code_scopes(self, client_id, code, redirect_uri, request):
+ """ Extracts scopes from saved authorization code.
+
+ The scopes returned by this method is used to route token requests
+ based on scopes passed to Authorization Code requests.
+
+ With that the token endpoint knows when to include OpenIDConnect
+ id_token in token response only based on authorization code scopes.
+
+ Only code param should be sufficient to retrieve grant code from
+ any storage you are using, `client_id` and `redirect_uri` can gave a
+ blank value `""` don't forget to check it before using those values
+ in a select query if a database is used.
+
+ :param client_id: Unicode client identifier
+ :param code: Unicode authorization code grant
+ :param redirect_uri: Unicode absolute URI
+ :return: A list of scope
+
+ Method is used by:
+ - Authorization Token Grant Dispatcher
+ """
+ raise NotImplementedError('Subclasses must implement this method.')
+
def save_token(self, token, request, *args, **kwargs):
"""Persist the token with a token type specific method.
@@ -288,8 +312,24 @@ class RequestValidator(object):
"""
raise NotImplementedError('Subclasses must implement this method.')
- def get_id_token(self, token, token_handler, request):
+ def get_jwt_bearer_token(self, token, token_handler, request):
+ """Get JWT Bearer token or OpenID Connect ID token
+
+ If using OpenID Connect this SHOULD call `oauthlib.oauth2.RequestValidator.get_id_token`
+
+ :param token: A Bearer token dict
+ :param token_handler: the token handler (BearerToken class)
+ :param request: the HTTP Request (oauthlib.common.Request)
+ :return: The JWT Bearer token or OpenID Connect ID token (a JWS signed JWT)
+
+ Method is used by JWT Bearer and OpenID Connect tokens:
+ - JWTToken.create_token
"""
+ raise NotImplementedError('Subclasses must implement this method.')
+
+ def get_id_token(self, token, token_handler, request):
+ """Get OpenID Connect ID token
+
In the OpenID Connect workflows when an ID Token is requested this method is called.
Subclasses should implement the construction, signing and optional encryption of the
ID Token as described in the OpenID Connect spec.
@@ -320,6 +360,52 @@ class RequestValidator(object):
# the request.scope should be used by the get_id_token() method to determine which claims to include in the resulting id_token
raise NotImplementedError('Subclasses must implement this method.')
+ def validate_jwt_bearer_token(self, token, scopes, request):
+ """Ensure the JWT Bearer token or OpenID Connect ID token are valids and authorized access to scopes.
+
+ If using OpenID Connect this SHOULD call `oauthlib.oauth2.RequestValidator.get_id_token`
+
+ If not using OpenID Connect this can `return None` to avoid 5xx rather 401/3 response.
+
+ OpenID connect core 1.0 describe how to validate an id_token:
+ - http://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation
+ - http://openid.net/specs/openid-connect-core-1_0.html#ImplicitIDTValidation
+ - http://openid.net/specs/openid-connect-core-1_0.html#HybridIDTValidation
+ - http://openid.net/specs/openid-connect-core-1_0.html#HybridIDTValidation2
+
+ :param token: Unicode Bearer token
+ :param scopes: List of scopes (defined by you)
+ :param request: The HTTP Request (oauthlib.common.Request)
+ :rtype: True or False
+
+ Method is indirectly used by all core OpenID connect JWT token issuing grant types:
+ - Authorization Code Grant
+ - Implicit Grant
+ - Hybrid Grant
+ """
+ raise NotImplementedError('Subclasses must implement this method.')
+
+ def validate_id_token(self, token, scopes, request):
+ """Ensure the id token is valid and authorized access to scopes.
+
+ OpenID connect core 1.0 describe how to validate an id_token:
+ - http://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation
+ - http://openid.net/specs/openid-connect-core-1_0.html#ImplicitIDTValidation
+ - http://openid.net/specs/openid-connect-core-1_0.html#HybridIDTValidation
+ - http://openid.net/specs/openid-connect-core-1_0.html#HybridIDTValidation2
+
+ :param token: Unicode Bearer token
+ :param scopes: List of scopes (defined by you)
+ :param request: The HTTP Request (oauthlib.common.Request)
+ :rtype: True or False
+
+ Method is indirectly used by all core OpenID connect JWT token issuing grant types:
+ - Authorization Code Grant
+ - Implicit Grant
+ - Hybrid Grant
+ """
+ raise NotImplementedError('Subclasses must implement this method.')
+
def validate_bearer_token(self, token, scopes, request):
"""Ensure the Bearer token is valid and authorized access to scopes.
diff --git a/oauthlib/oauth2/rfc6749/tokens.py b/oauthlib/oauth2/rfc6749/tokens.py
index 2060de1..4ae20e0 100644
--- a/oauthlib/oauth2/rfc6749/tokens.py
+++ b/oauthlib/oauth2/rfc6749/tokens.py
@@ -4,24 +4,25 @@ oauthlib.oauth2.rfc6749.tokens
This module contains methods for adding two types of access tokens to requests.
-- Bearer http://tools.ietf.org/html/rfc6750
-- MAC http://tools.ietf.org/html/draft-ietf-oauth-v2-http-mac-01
+- Bearer https://tools.ietf.org/html/rfc6750
+- MAC https://tools.ietf.org/html/draft-ietf-oauth-v2-http-mac-01
"""
from __future__ import absolute_import, unicode_literals
-from binascii import b2a_base64
import hashlib
import hmac
-try:
- from urlparse import urlparse
-except ImportError:
- from urllib.parse import urlparse
+from binascii import b2a_base64
-from oauthlib.common import add_params_to_uri, add_params_to_qs, unicode_type
from oauthlib import common
+from oauthlib.common import add_params_to_qs, add_params_to_uri, unicode_type
from . import utils
+try:
+ from urlparse import urlparse
+except ImportError:
+ from urllib.parse import urlparse
+
class OAuth2Token(dict):
@@ -92,8 +93,8 @@ def prepare_mac_header(token, uri, key, http_method,
nonce="1336363200:dj83hs9s",
mac="bhCQXTVyfj5cmA9uKkPFx1zeOXM="
- .. _`MAC Access Authentication`: http://tools.ietf.org/html/draft-ietf-oauth-v2-http-mac-01
- .. _`extension algorithms`: http://tools.ietf.org/html/draft-ietf-oauth-v2-http-mac-01#section-7.1
+ .. _`MAC Access Authentication`: https://tools.ietf.org/html/draft-ietf-oauth-v2-http-mac-01
+ .. _`extension algorithms`: https://tools.ietf.org/html/draft-ietf-oauth-v2-http-mac-01#section-7.1
:param uri: Request URI.
:param headers: Request headers as a dictionary.
@@ -179,7 +180,7 @@ def prepare_bearer_uri(token, uri):
http://www.example.com/path?access_token=h480djs93hd8
- .. _`Bearer Token`: http://tools.ietf.org/html/rfc6750
+ .. _`Bearer Token`: https://tools.ietf.org/html/rfc6750
"""
return add_params_to_uri(uri, [(('access_token', token))])
@@ -190,7 +191,7 @@ def prepare_bearer_headers(token, headers=None):
Authorization: Bearer h480djs93hd8
- .. _`Bearer Token`: http://tools.ietf.org/html/rfc6750
+ .. _`Bearer Token`: https://tools.ietf.org/html/rfc6750
"""
headers = headers or {}
headers['Authorization'] = 'Bearer %s' % token
@@ -202,7 +203,7 @@ def prepare_bearer_body(token, body=''):
access_token=h480djs93hd8
- .. _`Bearer Token`: http://tools.ietf.org/html/rfc6750
+ .. _`Bearer Token`: https://tools.ietf.org/html/rfc6750
"""
return add_params_to_qs(body, [(('access_token', token))])
@@ -300,3 +301,47 @@ class BearerToken(TokenBase):
return 5
else:
return 0
+
+
+class JWTToken(TokenBase):
+ __slots__ = (
+ 'request_validator', 'token_generator',
+ 'refresh_token_generator', 'expires_in'
+ )
+
+ def __init__(self, request_validator=None, token_generator=None,
+ expires_in=None, refresh_token_generator=None):
+ self.request_validator = request_validator
+ self.token_generator = token_generator or random_token_generator
+ self.refresh_token_generator = (
+ refresh_token_generator or self.token_generator
+ )
+ self.expires_in = expires_in or 3600
+
+ def create_token(self, request, refresh_token=False, save_token=False):
+ """Create a JWT Token, using requestvalidator method."""
+
+ if callable(self.expires_in):
+ expires_in = self.expires_in(request)
+ else:
+ expires_in = self.expires_in
+
+ request.expires_in = expires_in
+
+ return self.request_validator.get_jwt_bearer_token(None, None, request)
+
+ def validate_request(self, request):
+ token = None
+ if 'Authorization' in request.headers:
+ token = request.headers.get('Authorization')[7:]
+ else:
+ token = request.access_token
+ return self.request_validator.validate_jwt_bearer_token(
+ token, request.scopes, request)
+
+ def estimate_type(self, request):
+ token = request.headers.get('Authorization', '')[7:]
+ if token.startswith('ey') and token.count('.') in (2, 4):
+ return 10
+ else:
+ return 0
diff --git a/oauthlib/oauth2/rfc6749/utils.py b/oauthlib/oauth2/rfc6749/utils.py
index 870ac32..f67019d 100644
--- a/oauthlib/oauth2/rfc6749/utils.py
+++ b/oauthlib/oauth2/rfc6749/utils.py
@@ -7,8 +7,11 @@ This module contains utility methods used by various parts of the OAuth 2 spec.
"""
from __future__ import absolute_import, unicode_literals
-import os
import datetime
+import os
+
+from oauthlib.common import unicode_type, urldecode
+
try:
from urllib import quote
except ImportError:
@@ -17,7 +20,6 @@ try:
from urlparse import urlparse
except ImportError:
from urllib.parse import urlparse
-from oauthlib.common import unicode_type, urldecode
def list_to_scope(scope):