From e575cca3e5d18b1e7051c64f435f2cdea71a29ab Mon Sep 17 00:00:00 2001 From: Wiliam Souza Date: Sun, 1 Oct 2017 03:07:11 -0300 Subject: OpenID connect improvements (#484) * Change create_token_response to only save access_token when it's present in request.response_type * Remove unused import, fix indentation and improve comment * Fix AuthorizationEndpoint response_type for OpenID Connect hybrid flow * Add new ImplicitTokenGrantDispatcher Changes AuthorizationEndpoint response_type `'token'`, `'id_token'` and `'id_token token'` to work with OpenID Connect and OAuth2 implicit flow in a transparent way * Add new AuthTokenGrantDispatcher Change AuthorizationEndpoint grant_types `'authorization_code'` to work with OpenID Connect and OAuth2 authorization flow in a transparent way * Change tests to include required client_id and redirect_uri * Remove AuthorizationEndpoint grant_types `'openid'` Now OpenID Connect and OAuth2 authorization flow can use `authorization_code` in a transparent way * Add sone blank lines and fix indentation * Change AuthorizationEndpoint grant type id_token and id_token token to use openid_connect_implicit direct * Change default empty value to None and fix a typo * Add assert called to AuthTokenGrantDispatcher tests * Add request to get_authorization_code_scopes --- .../oauth2/rfc6749/endpoints/pre_configured.py | 23 +++++--- oauthlib/oauth2/rfc6749/grant_types/__init__.py | 2 + oauthlib/oauth2/rfc6749/grant_types/implicit.py | 32 ++++++----- .../oauth2/rfc6749/grant_types/openid_connect.py | 65 +++++++++++++++++++++- oauthlib/oauth2/rfc6749/request_validator.py | 24 ++++++++ 5 files changed, 122 insertions(+), 24 deletions(-) (limited to 'oauthlib/oauth2') diff --git a/oauthlib/oauth2/rfc6749/endpoints/pre_configured.py b/oauthlib/oauth2/rfc6749/endpoints/pre_configured.py index 6428b8d..07c3715 100644 --- a/oauthlib/oauth2/rfc6749/endpoints/pre_configured.py +++ b/oauthlib/oauth2/rfc6749/endpoints/pre_configured.py @@ -9,8 +9,11 @@ for consuming and providing OAuth 2.0 RFC6749. from __future__ import absolute_import, unicode_literals from ..grant_types import (AuthCodeGrantDispatcher, AuthorizationCodeGrant, - ClientCredentialsGrant, ImplicitGrant, + AuthTokenGrantDispatcher, + ClientCredentialsGrant, + ImplicitTokenGrantDispatcher, ImplicitGrant, OpenIDConnectAuthCode, OpenIDConnectImplicit, + OpenIDConnectHybrid, RefreshTokenGrant, ResourceOwnerPasswordCredentialsGrant) from ..tokens import BearerToken @@ -49,33 +52,37 @@ 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) + 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', 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/implicit.py b/oauthlib/oauth2/rfc6749/grant_types/implicit.py index 858ef77..2b9c49d 100644 --- a/oauthlib/oauth2/rfc6749/grant_types/implicit.py +++ b/oauthlib/oauth2/rfc6749/grant_types/implicit.py @@ -11,7 +11,6 @@ from oauthlib import common from oauthlib.uri_validate import is_absolute_uri from .. import errors -from ..request_validator import RequestValidator from .base import GrantTypeBase log = logging.getLogger(__name__) @@ -229,7 +228,7 @@ class ImplicitGrant(GrantTypeBase): 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(): @@ -239,7 +238,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) @@ -317,8 +321,7 @@ 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, @@ -352,20 +355,21 @@ class ImplicitGrant(GrantTypeBase): 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 4c98864..4371b28 100644 --- a/oauthlib/oauth2/rfc6749/grant_types/openid_connect.py +++ b/oauthlib/oauth2/rfc6749/grant_types/openid_connect.py @@ -12,11 +12,11 @@ from json import loads from ..errors import ConsentRequired, InvalidRequestError, LoginRequired from ..request_validator import RequestValidator from .authorization_code import AuthorizationCodeGrant -from .base import GrantTypeBase from .implicit import ImplicitGrant log = logging.getLogger(__name__) + class OIDCNoPrompt(Exception): """Exception used to inform users that no explicit authorization is needed. @@ -76,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 @@ -307,7 +366,7 @@ 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) @@ -356,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): @@ -369,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): diff --git a/oauthlib/oauth2/rfc6749/request_validator.py b/oauthlib/oauth2/rfc6749/request_validator.py index 0adfa1b..ba129d5 100644 --- a/oauthlib/oauth2/rfc6749/request_validator.py +++ b/oauthlib/oauth2/rfc6749/request_validator.py @@ -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. -- cgit v1.2.1 From c6b11373648af4b81367b0424b65b15ee8b58261 Mon Sep 17 00:00:00 2001 From: Hsiaoming Yang Date: Wed, 18 Oct 2017 23:40:32 +0900 Subject: Refactor OAuth2Error --- oauthlib/oauth2/rfc6749/errors.py | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) (limited to 'oauthlib/oauth2') diff --git a/oauthlib/oauth2/rfc6749/errors.py b/oauthlib/oauth2/rfc6749/errors.py index e0c29a0..180f636 100644 --- a/oauthlib/oauth2/rfc6749/errors.py +++ b/oauthlib/oauth2/rfc6749/errors.py @@ -18,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 @@ -39,8 +39,9 @@ class OAuth2Error(Exception): request: Oauthlib Request object """ - self.response_mode = None - 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) @@ -61,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): -- cgit v1.2.1 From fa0b63cfaced831d8b916c5a125128f582acf044 Mon Sep 17 00:00:00 2001 From: Grey Li Date: Tue, 14 Nov 2017 23:38:33 +0800 Subject: Check access token in self.token dict (#500) * Check access token in self.token dict * fix typo --- oauthlib/oauth2/rfc6749/clients/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'oauthlib/oauth2') diff --git a/oauthlib/oauth2/rfc6749/clients/base.py b/oauthlib/oauth2/rfc6749/clients/base.py index c2f8809..5c5acee 100644 --- a/oauthlib/oauth2/rfc6749/clients/base.py +++ b/oauthlib/oauth2/rfc6749/clients/base.py @@ -186,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(): -- cgit v1.2.1 From 9b95e4e8f094d78abe577203ad1ef53aecfdb270 Mon Sep 17 00:00:00 2001 From: Jonathan Huot Date: Wed, 8 Nov 2017 09:55:03 +0100 Subject: Added initial introspect support --- oauthlib/oauth2/__init__.py | 1 + oauthlib/oauth2/rfc6749/endpoints/__init__.py | 1 + oauthlib/oauth2/rfc6749/endpoints/introspect.py | 135 +++++++++++++++++++++ .../oauth2/rfc6749/endpoints/pre_configured.py | 27 +++-- oauthlib/oauth2/rfc6749/errors.py | 2 +- oauthlib/oauth2/rfc6749/request_validator.py | 20 +++ 6 files changed, 175 insertions(+), 11 deletions(-) create mode 100644 oauthlib/oauth2/rfc6749/endpoints/introspect.py (limited to 'oauthlib/oauth2') diff --git a/oauthlib/oauth2/__init__.py b/oauthlib/oauth2/__init__.py index c8d934e..dc7b431 100644 --- a/oauthlib/oauth2/__init__.py +++ b/oauthlib/oauth2/__init__.py @@ -15,6 +15,7 @@ from .rfc6749.clients import LegacyApplicationClient from .rfc6749.clients import BackendApplicationClient from .rfc6749.clients import ServiceApplicationClient from .rfc6749.endpoints import AuthorizationEndpoint +from .rfc6749.endpoints import IntrospectEndpoint from .rfc6749.endpoints import TokenEndpoint from .rfc6749.endpoints import ResourceEndpoint from .rfc6749.endpoints import RevocationEndpoint diff --git a/oauthlib/oauth2/rfc6749/endpoints/__init__.py b/oauthlib/oauth2/rfc6749/endpoints/__init__.py index 848bec6..9557f92 100644 --- a/oauthlib/oauth2/rfc6749/endpoints/__init__.py +++ b/oauthlib/oauth2/rfc6749/endpoints/__init__.py @@ -9,6 +9,7 @@ for consuming and providing OAuth 2.0 RFC6749. from __future__ import absolute_import, unicode_literals from .authorization import AuthorizationEndpoint +from .introspect import IntrospectEndpoint from .token import TokenEndpoint from .resource import ResourceEndpoint from .revocation import RevocationEndpoint diff --git a/oauthlib/oauth2/rfc6749/endpoints/introspect.py b/oauthlib/oauth2/rfc6749/endpoints/introspect.py new file mode 100644 index 0000000..7613acc --- /dev/null +++ b/oauthlib/oauth2/rfc6749/endpoints/introspect.py @@ -0,0 +1,135 @@ +# -*- coding: utf-8 -*- +""" +oauthlib.oauth2.rfc6749.endpoint.introspect +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +An implementation of the OAuth 2.0 `Token Introspection`. + +.. _`Token Introspection`: https://tools.ietf.org/html/rfc7662 +""" +from __future__ import absolute_import, unicode_literals + +import json +import logging + +from oauthlib.common import Request + +from ..errors import (InvalidClientError, InvalidRequestError, OAuth2Error, + UnsupportedTokenTypeError) +from .base import BaseEndpoint, catch_errors_and_unavailability + +log = logging.getLogger(__name__) + + +class IntrospectEndpoint(BaseEndpoint): + + """Introspect token endpoint. + + This endpoint defines a method to query an OAuth 2.0 authorization + server to determine the active state of an OAuth 2.0 token and to + determine meta-information about this token. OAuth 2.0 deployments + can use this method to convey information about the authorization + context of the token from the authorization server to the protected + resource. + + To prevent the values of access tokens from leaking into + server-side logs via query parameters, an authorization server + offering token introspection MAY disallow the use of HTTP GET on + the introspection endpoint and instead require the HTTP POST method + to be used at the introspection endpoint. + """ + + valid_token_types = ('access_token', 'refresh_token') + + def __init__(self, request_validator, supported_token_types=None): + BaseEndpoint.__init__(self) + self.request_validator = request_validator + self.supported_token_types = ( + supported_token_types or self.valid_token_types) + + @catch_errors_and_unavailability + def create_introspect_response(self, uri, http_method='POST', body=None, + headers=None): + """Create introspect valid or invalid response + + If the authorization server is unable to determine the state + of the token without additional information, it SHOULD return + an introspection response indicating the token is not active + as described in Section 2.2. + """ + 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 + + 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: + claims.pop("active") + return headers, json.dumps(dict(active=True, **claims)), 200 + + def validate_introspect_request(self, request): + """Ensure the request is valid. + + The protected resource calls the introspection endpoint using + an HTTP POST request with parameters sent as + "application/x-www-form-urlencoded". + + token REQUIRED. The string value of the token. + + token_type_hint OPTIONAL. + A hint about the type of the token submitted for + introspection. The protected resource MAY pass this parameter to + help the authorization server optimize the token lookup. If the + server is unable to locate the token using the given hint, it MUST + extend its search across all of its supported token types. An + authorization server MAY ignore this parameter, particularly if it + is able to detect the token type automatically. + * access_token: An Access Token as defined in [`RFC6749`], + `section 1.4`_ + + * refresh_token: A Refresh Token as defined in [`RFC6749`], + `section 1.5`_ + + The introspection endpoint MAY accept other OPTIONAL + parameters to provide further context to the query. For + instance, an authorization server may desire to know the IP + address of the client accessing the protected resource to + determine if the correct client is likely to be presenting the + token. The definition of this or any other parameters are + outside the scope of this specification, to be defined by + service documentation or extensions to this specification. + + .. _`section 1.4`: http://tools.ietf.org/html/rfc6749#section-1.4 + .. _`section 1.5`: http://tools.ietf.org/html/rfc6749#section-1.5 + .. _`RFC6749`: http://tools.ietf.org/html/rfc6749 + """ + if not request.token: + raise InvalidRequestError(request=request, + description='Missing token parameter.') + + if self.request_validator.client_authentication_required(request): + if not self.request_validator.authenticate_client(request): + log.debug('Client authentication failed, %r.', request) + raise InvalidClientError(request=request) + elif not self.request_validator.authenticate_client_id(request.client_id, request): + log.debug('Client authentication failed, %r.', request) + raise InvalidClientError(request=request) + + if (request.token_type_hint and + request.token_type_hint in self.valid_token_types and + request.token_type_hint not in self.supported_token_types): + raise UnsupportedTokenTypeError(request=request) diff --git a/oauthlib/oauth2/rfc6749/endpoints/pre_configured.py b/oauthlib/oauth2/rfc6749/endpoints/pre_configured.py index 07c3715..f1dfead 100644 --- a/oauthlib/oauth2/rfc6749/endpoints/pre_configured.py +++ b/oauthlib/oauth2/rfc6749/endpoints/pre_configured.py @@ -18,13 +18,14 @@ from ..grant_types import (AuthCodeGrantDispatcher, AuthorizationCodeGrant, ResourceOwnerPasswordCredentialsGrant) from ..tokens import BearerToken from .authorization import AuthorizationEndpoint +from .introspect import IntrospectEndpoint from .resource import ResourceEndpoint from .revocation import RevocationEndpoint from .token import TokenEndpoint -class Server(AuthorizationEndpoint, TokenEndpoint, ResourceEndpoint, - RevocationEndpoint): +class Server(AuthorizationEndpoint, IntrospectEndpoint, TokenEndpoint, + ResourceEndpoint, RevocationEndpoint): """An all-in-one endpoint featuring all four major grant types.""" @@ -88,10 +89,11 @@ class Server(AuthorizationEndpoint, TokenEndpoint, ResourceEndpoint, ResourceEndpoint.__init__(self, default_token='Bearer', token_types={'Bearer': bearer}) RevocationEndpoint.__init__(self, request_validator) + IntrospectEndpoint.__init__(self, request_validator) -class WebApplicationServer(AuthorizationEndpoint, TokenEndpoint, ResourceEndpoint, - RevocationEndpoint): +class WebApplicationServer(AuthorizationEndpoint, IntrospectEndpoint, TokenEndpoint, + ResourceEndpoint, RevocationEndpoint): """An all-in-one endpoint featuring Authorization code grant and Bearer tokens.""" @@ -126,10 +128,11 @@ class WebApplicationServer(AuthorizationEndpoint, TokenEndpoint, ResourceEndpoin ResourceEndpoint.__init__(self, default_token='Bearer', token_types={'Bearer': bearer}) RevocationEndpoint.__init__(self, request_validator) + IntrospectEndpoint.__init__(self, request_validator) -class MobileApplicationServer(AuthorizationEndpoint, ResourceEndpoint, - RevocationEndpoint): +class MobileApplicationServer(AuthorizationEndpoint, IntrospectEndpoint, + ResourceEndpoint, RevocationEndpoint): """An all-in-one endpoint featuring Implicit code grant and Bearer tokens.""" @@ -159,10 +162,11 @@ class MobileApplicationServer(AuthorizationEndpoint, ResourceEndpoint, token_types={'Bearer': bearer}) RevocationEndpoint.__init__(self, request_validator, supported_token_types=['access_token']) + IntrospectEndpoint.__init__(self, request_validator) -class LegacyApplicationServer(TokenEndpoint, ResourceEndpoint, - RevocationEndpoint): +class LegacyApplicationServer(TokenEndpoint, IntrospectEndpoint, + ResourceEndpoint, RevocationEndpoint): """An all-in-one endpoint featuring Resource Owner Password Credentials grant and Bearer tokens.""" @@ -195,10 +199,11 @@ class LegacyApplicationServer(TokenEndpoint, ResourceEndpoint, ResourceEndpoint.__init__(self, default_token='Bearer', token_types={'Bearer': bearer}) RevocationEndpoint.__init__(self, request_validator) + IntrospectEndpoint.__init__(self, request_validator) -class BackendApplicationServer(TokenEndpoint, ResourceEndpoint, - RevocationEndpoint): +class BackendApplicationServer(TokenEndpoint, IntrospectEndpoint, + ResourceEndpoint, RevocationEndpoint): """An all-in-one endpoint featuring Client Credentials grant and Bearer tokens.""" @@ -228,3 +233,5 @@ class BackendApplicationServer(TokenEndpoint, ResourceEndpoint, token_types={'Bearer': bearer}) RevocationEndpoint.__init__(self, request_validator, supported_token_types=['access_token']) + IntrospectEndpoint.__init__(self, request_validator, + supported_token_types=['access_token']) diff --git a/oauthlib/oauth2/rfc6749/errors.py b/oauthlib/oauth2/rfc6749/errors.py index 180f636..1d5e98d 100644 --- a/oauthlib/oauth2/rfc6749/errors.py +++ b/oauthlib/oauth2/rfc6749/errors.py @@ -267,7 +267,7 @@ class UnsupportedGrantTypeError(OAuth2Error): class UnsupportedTokenTypeError(OAuth2Error): """ - The authorization server does not support the revocation of the + The authorization server does not support the hint of the presented token type. I.e. the client tried to revoke an access token on a server not supporting this feature. """ diff --git a/oauthlib/oauth2/rfc6749/request_validator.py b/oauthlib/oauth2/rfc6749/request_validator.py index ba129d5..525ba33 100644 --- a/oauthlib/oauth2/rfc6749/request_validator.py +++ b/oauthlib/oauth2/rfc6749/request_validator.py @@ -166,6 +166,26 @@ class RequestValidator(object): """ return False + def introspect_token(self, token, token_type_hint, request, *args, **kwargs): + """Introspect an access or refresh token. + + Called once introspect token request is validated. This method + should return a dictionary with any desired claims associated + with the *token*. The implementation can use *token_type_hint* + to lookup this type first, but then it must fallback to other + types known, to be compliant with RFC. + + The dict of claims is added to request.token after this method. + + :param token: The token string. + :param token_type_hint: access_token or refresh_token. + :param request: The HTTP Request (oauthlib.common.Request) + + Method is used by: + - Introspect Endpoint (all grants are compatible) + """ + raise NotImplementedError('Subclasses must implement this method.') + def invalidate_authorization_code(self, client_id, code, request, *args, **kwargs): """Invalidate an authorization code after use. -- cgit v1.2.1 From ef8a3b47305b23b278310c1f21106c677a748434 Mon Sep 17 00:00:00 2001 From: Jonathan Huot Date: Tue, 19 Dec 2017 15:18:48 +0100 Subject: Added default supported_token_types for Mobile --- oauthlib/oauth2/rfc6749/endpoints/pre_configured.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'oauthlib/oauth2') diff --git a/oauthlib/oauth2/rfc6749/endpoints/pre_configured.py b/oauthlib/oauth2/rfc6749/endpoints/pre_configured.py index f1dfead..378339a 100644 --- a/oauthlib/oauth2/rfc6749/endpoints/pre_configured.py +++ b/oauthlib/oauth2/rfc6749/endpoints/pre_configured.py @@ -162,7 +162,8 @@ class MobileApplicationServer(AuthorizationEndpoint, IntrospectEndpoint, token_types={'Bearer': bearer}) RevocationEndpoint.__init__(self, request_validator, supported_token_types=['access_token']) - IntrospectEndpoint.__init__(self, request_validator) + IntrospectEndpoint.__init__(self, request_validator, + supported_token_types=['access_token']) class LegacyApplicationServer(TokenEndpoint, IntrospectEndpoint, -- cgit v1.2.1 From 296c6bc5931c95f631c1a496dacc523959fc50e9 Mon Sep 17 00:00:00 2001 From: Jonathan Huot Date: Tue, 19 Dec 2017 15:19:09 +0100 Subject: Improved doc by adding links to RFC and list of claims. --- oauthlib/oauth2/rfc6749/request_validator.py | 30 +++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) (limited to 'oauthlib/oauth2') diff --git a/oauthlib/oauth2/rfc6749/request_validator.py b/oauthlib/oauth2/rfc6749/request_validator.py index 525ba33..4b76b7a 100644 --- a/oauthlib/oauth2/rfc6749/request_validator.py +++ b/oauthlib/oauth2/rfc6749/request_validator.py @@ -169,11 +169,28 @@ class RequestValidator(object): def introspect_token(self, token, token_type_hint, request, *args, **kwargs): """Introspect an access or refresh token. - Called once introspect token request is validated. This method - should return a dictionary with any desired claims associated - with the *token*. The implementation can use *token_type_hint* - to lookup this type first, but then it must fallback to other - types known, to be compliant with RFC. + Called once the introspect request is validated. This method should + verify the *token* and either return a dictionary with the list of + claims associated, or `None` in case the token is unknown. + + Below the list of registered claims you should be interested in: + - scope : space-separated list of scopes + - client_id : client identifier + - username : human-readable identifier for the resource owner + - token_type : type of the token + - exp : integer timestamp indicating when this token will expire + - iat : integer timestamp indicating when this token was issued + - nbf : integer timestamp indicating when it can be "not-before" used + - sub : subject of the token - identifier of the resource owner + - aud : list of string identifiers representing the intended audience + - iss : string representing issuer of this token + - jti : string identifier for the token + + Note that most of them are coming directly from JWT RFC. More details + can be found in `Introspect Claims`_ or `_JWT Claims`_. + + The implementation can use *token_type_hint* to improve lookup + efficency, but must fallback to other types to be compliant with RFC. The dict of claims is added to request.token after this method. @@ -183,6 +200,9 @@ class RequestValidator(object): Method is used by: - Introspect Endpoint (all grants are compatible) + + .. _`Introspect Claims`: https://tools.ietf.org/html/rfc7662#section-2.2 + .. _`JWT Claims`: https://tools.ietf.org/html/rfc7519#section-4 """ raise NotImplementedError('Subclasses must implement this method.') -- cgit v1.2.1 From d7fc1336d81b39f3d2193eb3155ff66da6caadd9 Mon Sep 17 00:00:00 2001 From: Antoine Bertin Date: Mon, 29 Jan 2018 10:17:54 +0100 Subject: Fix cliend_id in web request body (#505) Previously, cliend_id was always included in the request body in the Authorization Code flow and the client_id parameter was ignored in contradiction with the docs. Fixes #495 --- oauthlib/oauth2/rfc6749/clients/web_application.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'oauthlib/oauth2') diff --git a/oauthlib/oauth2/rfc6749/clients/web_application.py b/oauthlib/oauth2/rfc6749/clients/web_application.py index c099d99..bc62c8f 100644 --- a/oauthlib/oauth2/rfc6749/clients/web_application.py +++ b/oauthlib/oauth2/rfc6749/clients/web_application.py @@ -125,7 +125,7 @@ class WebApplicationClient(Client): """ 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. -- cgit v1.2.1 From 2fe1cdb88e076f624824496c4aba6a8665e991d9 Mon Sep 17 00:00:00 2001 From: Wiliam Souza Date: Tue, 30 Jan 2018 17:30:26 -0200 Subject: Openid connect jwt (#488) * Add JWT token with it the server knows how to validate this new type of token in resource requests * Change find_token_type sorted function to reverse result and choose the valued estimated token handler * Add validate_id_token method to RequestValidator * Added unittest for JWTToken model * Updated version of Mock * Add get_jwt_bearer_token and validate_jwt_bearer_token oauthlib.oauth2.RequestValidator and change oauthlib.oauth2.tokens JWTToken to use it * Change to improve token type estimate test * Add a note in RequestValidator.validate_jwt_bearer_token about error 5xx rather 4xx --- .../oauth2/rfc6749/endpoints/pre_configured.py | 7 ++- oauthlib/oauth2/rfc6749/endpoints/resource.py | 2 +- oauthlib/oauth2/rfc6749/request_validator.py | 64 +++++++++++++++++++++- oauthlib/oauth2/rfc6749/tokens.py | 46 +++++++++++++++- 4 files changed, 113 insertions(+), 6 deletions(-) (limited to 'oauthlib/oauth2') diff --git a/oauthlib/oauth2/rfc6749/endpoints/pre_configured.py b/oauthlib/oauth2/rfc6749/endpoints/pre_configured.py index 07c3715..0c26986 100644 --- a/oauthlib/oauth2/rfc6749/endpoints/pre_configured.py +++ b/oauthlib/oauth2/rfc6749/endpoints/pre_configured.py @@ -16,7 +16,7 @@ from ..grant_types import (AuthCodeGrantDispatcher, AuthorizationCodeGrant, OpenIDConnectHybrid, RefreshTokenGrant, ResourceOwnerPasswordCredentialsGrant) -from ..tokens import BearerToken +from ..tokens import BearerToken, JWTToken from .authorization import AuthorizationEndpoint from .resource import ResourceEndpoint from .revocation import RevocationEndpoint @@ -57,6 +57,9 @@ class Server(AuthorizationEndpoint, TokenEndpoint, ResourceEndpoint, bearer = BearerToken(request_validator, token_generator, token_expires_in, refresh_token_generator) + 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) @@ -86,7 +89,7 @@ class Server(AuthorizationEndpoint, TokenEndpoint, ResourceEndpoint, }, 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/request_validator.py b/oauthlib/oauth2/rfc6749/request_validator.py index ba129d5..d25a6e0 100644 --- a/oauthlib/oauth2/rfc6749/request_validator.py +++ b/oauthlib/oauth2/rfc6749/request_validator.py @@ -312,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. @@ -344,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 e0ac431..e68ba59 100644 --- a/oauthlib/oauth2/rfc6749/tokens.py +++ b/oauthlib/oauth2/rfc6749/tokens.py @@ -24,8 +24,6 @@ except ImportError: from urllib.parse import urlparse - - class OAuth2Token(dict): def __init__(self, params, old_scope=None): @@ -303,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 -- cgit v1.2.1 From 32e5ad1509a8d46fa402776f54fbabef4b1ded63 Mon Sep 17 00:00:00 2001 From: Jonathan Huot Date: Wed, 28 Feb 2018 15:00:08 +0100 Subject: Rtd docs fix (#515) * Added sphinx build for developers Rationale is to build docs locally to prevent RTD to break later. * Replace manual sphinx into make * Renamed idan URL to oauthlib community * Renamed http into https URLs since http is returning 302 * python requests library renamed its home URL * Add ignore list for "make linkcheck" linkcheck is doing requests to github with anonymous access, however creating an issue require an logged-in account * virtualenv changed its homepage and website. * Fixed broken link --- .../oauth2/rfc6749/clients/backend_application.py | 6 ++-- oauthlib/oauth2/rfc6749/clients/base.py | 10 +++---- .../oauth2/rfc6749/clients/legacy_application.py | 6 ++-- .../oauth2/rfc6749/clients/mobile_application.py | 14 ++++----- .../oauth2/rfc6749/clients/service_application.py | 2 +- oauthlib/oauth2/rfc6749/clients/web_application.py | 14 ++++----- oauthlib/oauth2/rfc6749/endpoints/authorization.py | 2 +- oauthlib/oauth2/rfc6749/endpoints/revocation.py | 12 ++++---- oauthlib/oauth2/rfc6749/endpoints/token.py | 2 +- .../rfc6749/grant_types/authorization_code.py | 26 ++++++++--------- .../rfc6749/grant_types/client_credentials.py | 6 ++-- oauthlib/oauth2/rfc6749/grant_types/implicit.py | 34 +++++++++++----------- .../oauth2/rfc6749/grant_types/refresh_token.py | 8 ++--- .../resource_owner_password_credentials.py | 10 +++---- oauthlib/oauth2/rfc6749/parameters.py | 32 ++++++++++---------- oauthlib/oauth2/rfc6749/request_validator.py | 8 ++--- oauthlib/oauth2/rfc6749/tokens.py | 14 ++++----- 17 files changed, 103 insertions(+), 103 deletions(-) (limited to 'oauthlib/oauth2') diff --git a/oauthlib/oauth2/rfc6749/clients/backend_application.py b/oauthlib/oauth2/rfc6749/clients/backend_application.py index 7505b0d..cbad8b7 100644 --- a/oauthlib/oauth2/rfc6749/clients/backend_application.py +++ b/oauthlib/oauth2/rfc6749/clients/backend_application.py @@ -52,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 5c5acee..a07a5c9 100644 --- a/oauthlib/oauth2/rfc6749/clients/base.py +++ b/oauthlib/oauth2/rfc6749/clients/base.py @@ -173,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() @@ -401,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) diff --git a/oauthlib/oauth2/rfc6749/clients/legacy_application.py b/oauthlib/oauth2/rfc6749/clients/legacy_application.py index 57fe99e..b16fc9f 100644 --- a/oauthlib/oauth2/rfc6749/clients/legacy_application.py +++ b/oauthlib/oauth2/rfc6749/clients/legacy_application.py @@ -64,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 490efcd..311aacf 100644 --- a/oauthlib/oauth2/rfc6749/clients/mobile_application.py +++ b/oauthlib/oauth2/rfc6749/clients/mobile_application.py @@ -85,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) @@ -164,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 e6c3270..84ea0e9 100644 --- a/oauthlib/oauth2/rfc6749/clients/service_application.py +++ b/oauthlib/oauth2/rfc6749/clients/service_application.py @@ -136,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 bc62c8f..14b5265 100644 --- a/oauthlib/oauth2/rfc6749/clients/web_application.py +++ b/oauthlib/oauth2/rfc6749/clients/web_application.py @@ -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,8 +120,8 @@ 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, diff --git a/oauthlib/oauth2/rfc6749/endpoints/authorization.py b/oauthlib/oauth2/rfc6749/endpoints/authorization.py index b6e0734..92cde34 100644 --- a/oauthlib/oauth2/rfc6749/endpoints/authorization.py +++ b/oauthlib/oauth2/rfc6749/endpoints/authorization.py @@ -59,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/revocation.py b/oauthlib/oauth2/rfc6749/endpoints/revocation.py index 4364b81..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 @@ -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 ece6325..90fb16f 100644 --- a/oauthlib/oauth2/rfc6749/endpoints/token.py +++ b/oauthlib/oauth2/rfc6749/endpoints/token.py @@ -59,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/grant_types/authorization_code.py b/oauthlib/oauth2/rfc6749/grant_types/authorization_code.py index 8661c35..7bea650 100644 --- a/oauthlib/oauth2/rfc6749/grant_types/authorization_code.py +++ b/oauthlib/oauth2/rfc6749/grant_types/authorization_code.py @@ -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) diff --git a/oauthlib/oauth2/rfc6749/grant_types/client_credentials.py b/oauthlib/oauth2/rfc6749/grant_types/client_credentials.py index bf6c87f..4c50a78 100644 --- a/oauthlib/oauth2/rfc6749/grant_types/client_credentials.py +++ b/oauthlib/oauth2/rfc6749/grant_types/client_credentials.py @@ -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 2b9c49d..bdab814 100644 --- a/oauthlib/oauth2/rfc6749/grant_types/implicit.py +++ b/oauthlib/oauth2/rfc6749/grant_types/implicit.py @@ -111,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'] @@ -152,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) @@ -195,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: # request.scopes is only mandated in post auth and both pre and @@ -222,7 +222,7 @@ 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, @@ -285,7 +285,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) @@ -293,7 +293,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) @@ -304,7 +304,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) @@ -328,7 +328,7 @@ 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 # Note that the correct parameters to be added are automatically # populated through the use of specific exceptions @@ -351,7 +351,7 @@ 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({ diff --git a/oauthlib/oauth2/rfc6749/grant_types/refresh_token.py b/oauthlib/oauth2/rfc6749/grant_types/refresh_token.py index 6233e7c..c2d86f7 100644 --- a/oauthlib/oauth2/rfc6749/grant_types/refresh_token.py +++ b/oauthlib/oauth2/rfc6749/grant_types/refresh_token.py @@ -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 ede779a..e5f04af 100644 --- a/oauthlib/oauth2/rfc6749/grant_types/resource_owner_password_credentials.py +++ b/oauthlib/oauth2/rfc6749/grant_types/resource_owner_password_credentials.py @@ -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 b87b146..0107933 100644 --- a/oauthlib/oauth2/rfc6749/parameters.py +++ b/oauthlib/oauth2/rfc6749/parameters.py @@ -5,7 +5,7 @@ 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 @@ -61,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() @@ -111,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)] @@ -153,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): @@ -348,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) @@ -359,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'): @@ -395,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 d25a6e0..182642e 100644 --- a/oauthlib/oauth2/rfc6749/request_validator.py +++ b/oauthlib/oauth2/rfc6749/request_validator.py @@ -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.') diff --git a/oauthlib/oauth2/rfc6749/tokens.py b/oauthlib/oauth2/rfc6749/tokens.py index e68ba59..4ae20e0 100644 --- a/oauthlib/oauth2/rfc6749/tokens.py +++ b/oauthlib/oauth2/rfc6749/tokens.py @@ -4,8 +4,8 @@ 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 @@ -93,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. @@ -180,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))]) @@ -191,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 @@ -203,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))]) -- cgit v1.2.1 From d49b9f02a821dca920c89b24540485da3b96bf1e Mon Sep 17 00:00:00 2001 From: Jimmy Thrasibule Date: Fri, 13 Apr 2018 04:27:01 -0400 Subject: Add request argument to confirm_redirect_uri (#504) (#504) --- oauthlib/oauth2/rfc6749/grant_types/authorization_code.py | 3 ++- oauthlib/oauth2/rfc6749/request_validator.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'oauthlib/oauth2') diff --git a/oauthlib/oauth2/rfc6749/grant_types/authorization_code.py b/oauthlib/oauth2/rfc6749/grant_types/authorization_code.py index 7bea650..0660263 100644 --- a/oauthlib/oauth2/rfc6749/grant_types/authorization_code.py +++ b/oauthlib/oauth2/rfc6749/grant_types/authorization_code.py @@ -421,7 +421,8 @@ class AuthorizationCodeGrant(GrantTypeBase): # authorization request as described in Section 4.1.1, and their # values MUST be identical. if not self.request_validator.confirm_redirect_uri(request.client_id, request.code, - request.redirect_uri, request.client): + request.redirect_uri, request.client, + request): log.debug('Redirect_uri (%r) invalid for client %r (%r).', request.redirect_uri, request.client_id, request.client) raise errors.MismatchingRedirectURIError(request=request) diff --git a/oauthlib/oauth2/rfc6749/request_validator.py b/oauthlib/oauth2/rfc6749/request_validator.py index 182642e..c0b69a1 100644 --- a/oauthlib/oauth2/rfc6749/request_validator.py +++ b/oauthlib/oauth2/rfc6749/request_validator.py @@ -82,7 +82,7 @@ class RequestValidator(object): """ raise NotImplementedError('Subclasses must implement this method.') - def confirm_redirect_uri(self, client_id, code, redirect_uri, client, + def confirm_redirect_uri(self, client_id, code, redirect_uri, client, request, *args, **kwargs): """Ensure that the authorization process represented by this authorization code began with this 'redirect_uri'. -- cgit v1.2.1 From 1b3498aeac6f4c57156283e59d340746595d6329 Mon Sep 17 00:00:00 2001 From: paulie4 Date: Fri, 13 Apr 2018 04:39:07 -0400 Subject: Fixed some copy and paste typos (#535) Fixed some copy and paste typos, see issue #532. --- oauthlib/oauth2/rfc6749/clients/service_application.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'oauthlib/oauth2') diff --git a/oauthlib/oauth2/rfc6749/clients/service_application.py b/oauthlib/oauth2/rfc6749/clients/service_application.py index 84ea0e9..7f336bb 100644 --- a/oauthlib/oauth2/rfc6749/clients/service_application.py +++ b/oauthlib/oauth2/rfc6749/clients/service_application.py @@ -146,8 +146,8 @@ class ServiceApplicationClient(Client): ' token requests.') claim = { 'iss': issuer or self.issuer, - 'aud': audience or self.issuer, - 'sub': subject or self.issuer, + 'aud': audience or self.audience, + 'sub': subject or self.subject, 'exp': int(expires_at or time.time() + 3600), 'iat': int(issued_at or time.time()), } -- cgit v1.2.1 From 657065d76d59a100ffcacd0954fb2091552dfaa2 Mon Sep 17 00:00:00 2001 From: Pieter Ennes Date: Tue, 8 May 2018 21:14:35 +0100 Subject: Avoid populating spurious token credentials (#542) --- oauthlib/oauth2/rfc6749/clients/base.py | 19 ++++++++++++------- oauthlib/oauth2/rfc6749/clients/mobile_application.py | 2 +- oauthlib/oauth2/rfc6749/clients/web_application.py | 2 +- 3 files changed, 14 insertions(+), 9 deletions(-) (limited to 'oauthlib/oauth2') diff --git a/oauthlib/oauth2/rfc6749/clients/base.py b/oauthlib/oauth2/rfc6749/clients/base.py index a07a5c9..3c5372c 100644 --- a/oauthlib/oauth2/rfc6749/clients/base.py +++ b/oauthlib/oauth2/rfc6749/clients/base.py @@ -111,8 +111,10 @@ class Client(object): self.state_generator = state_generator self.state = state self.redirect_url = redirect_url + self.code = None + self.expires_in = None self._expires_at = None - self._populate_attributes(self.token) + self._populate_token_attributes(self.token) @property def token_types(self): @@ -406,7 +408,7 @@ class Client(object): .. _`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) + self._populate_token_attributes(self.token) return self.token def prepare_refresh_body(self, body='', refresh_token=None, scope=None, **kwargs): @@ -459,8 +461,14 @@ class Client(object): hash_algorithm=self.mac_algorithm, **kwargs) return uri, headers, body - def _populate_attributes(self, response): - """Add commonly used values such as access_token to self.""" + def _populate_code_attributes(self, response): + """Add attributes from an auth code response to self.""" + + if 'code' in response: + self.code = response.get('code') + + def _populate_token_attributes(self, response): + """Add attributes from a token exchange response to self.""" if 'access_token' in response: self.access_token = response.get('access_token') @@ -478,9 +486,6 @@ class Client(object): if 'expires_at' in response: self._expires_at = int(response.get('expires_at')) - if 'code' in response: - self.code = response.get('code') - if 'mac_key' in response: self.mac_key = response.get('mac_key') diff --git a/oauthlib/oauth2/rfc6749/clients/mobile_application.py b/oauthlib/oauth2/rfc6749/clients/mobile_application.py index 311aacf..965185d 100644 --- a/oauthlib/oauth2/rfc6749/clients/mobile_application.py +++ b/oauthlib/oauth2/rfc6749/clients/mobile_application.py @@ -168,5 +168,5 @@ class MobileApplicationClient(Client): .. _`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) + self._populate_token_attributes(self.token) return self.token diff --git a/oauthlib/oauth2/rfc6749/clients/web_application.py b/oauthlib/oauth2/rfc6749/clients/web_application.py index 14b5265..435c0b1 100644 --- a/oauthlib/oauth2/rfc6749/clients/web_application.py +++ b/oauthlib/oauth2/rfc6749/clients/web_application.py @@ -172,5 +172,5 @@ class WebApplicationClient(Client): oauthlib.oauth2.rfc6749.errors.MismatchingStateError """ response = parse_authorization_code_response(uri, state=state) - self._populate_attributes(response) + self._populate_code_attributes(response) return response -- cgit v1.2.1 From a9d9ba17a0fe04cec5afa1c6ede96f1984ae7334 Mon Sep 17 00:00:00 2001 From: Pieter Ennes Date: Fri, 18 May 2018 19:04:06 +0100 Subject: Backward compatibility fix for requests-oauthlib. (#546) --- oauthlib/oauth2/rfc6749/clients/base.py | 14 ++++++++++---- oauthlib/oauth2/rfc6749/clients/mobile_application.py | 2 +- oauthlib/oauth2/rfc6749/clients/web_application.py | 2 +- 3 files changed, 12 insertions(+), 6 deletions(-) (limited to 'oauthlib/oauth2') diff --git a/oauthlib/oauth2/rfc6749/clients/base.py b/oauthlib/oauth2/rfc6749/clients/base.py index 3c5372c..07ef894 100644 --- a/oauthlib/oauth2/rfc6749/clients/base.py +++ b/oauthlib/oauth2/rfc6749/clients/base.py @@ -9,6 +9,7 @@ for consuming OAuth 2.0 RFC6749. from __future__ import absolute_import, unicode_literals import time +import warnings from oauthlib.common import generate_token from oauthlib.oauth2.rfc6749 import tokens @@ -114,7 +115,7 @@ class Client(object): self.code = None self.expires_in = None self._expires_at = None - self._populate_token_attributes(self.token) + self.populate_token_attributes(self.token) @property def token_types(self): @@ -408,7 +409,7 @@ class Client(object): .. _`Section 7.1`: https://tools.ietf.org/html/rfc6749#section-7.1 """ self.token = parse_token_response(body, scope=scope) - self._populate_token_attributes(self.token) + self.populate_token_attributes(self.token) return self.token def prepare_refresh_body(self, body='', refresh_token=None, scope=None, **kwargs): @@ -461,13 +462,18 @@ class Client(object): hash_algorithm=self.mac_algorithm, **kwargs) return uri, headers, body - def _populate_code_attributes(self, response): + def _populate_attributes(self, response): + warnings.warn("Please switch to the public method " + "populate_token_attributes.", DeprecationWarning) + return self.populate_token_attributes(response) + + def populate_code_attributes(self, response): """Add attributes from an auth code response to self.""" if 'code' in response: self.code = response.get('code') - def _populate_token_attributes(self, response): + def populate_token_attributes(self, response): """Add attributes from a token exchange response to self.""" if 'access_token' in response: diff --git a/oauthlib/oauth2/rfc6749/clients/mobile_application.py b/oauthlib/oauth2/rfc6749/clients/mobile_application.py index 965185d..aa20daa 100644 --- a/oauthlib/oauth2/rfc6749/clients/mobile_application.py +++ b/oauthlib/oauth2/rfc6749/clients/mobile_application.py @@ -168,5 +168,5 @@ class MobileApplicationClient(Client): .. _`Section 3.3`: https://tools.ietf.org/html/rfc6749#section-3.3 """ self.token = parse_implicit_response(uri, state=state, scope=scope) - self._populate_token_attributes(self.token) + self.populate_token_attributes(self.token) return self.token diff --git a/oauthlib/oauth2/rfc6749/clients/web_application.py b/oauthlib/oauth2/rfc6749/clients/web_application.py index 435c0b1..c14a5f8 100644 --- a/oauthlib/oauth2/rfc6749/clients/web_application.py +++ b/oauthlib/oauth2/rfc6749/clients/web_application.py @@ -172,5 +172,5 @@ class WebApplicationClient(Client): oauthlib.oauth2.rfc6749.errors.MismatchingStateError """ response = parse_authorization_code_response(uri, state=state) - self._populate_code_attributes(response) + self.populate_code_attributes(response) return response -- cgit v1.2.1 From 27702f40753f88fc5bbf15128dac15758d4bc29a Mon Sep 17 00:00:00 2001 From: Mattia Procopio Date: Sat, 26 May 2018 21:33:41 +0200 Subject: Check that the Bearer header is properly formatted (#491) --- oauthlib/oauth2/rfc6749/tokens.py | 40 ++++++++++++++++++++++++--------------- 1 file changed, 25 insertions(+), 15 deletions(-) (limited to 'oauthlib/oauth2') diff --git a/oauthlib/oauth2/rfc6749/tokens.py b/oauthlib/oauth2/rfc6749/tokens.py index 4ae20e0..a7491f4 100644 --- a/oauthlib/oauth2/rfc6749/tokens.py +++ b/oauthlib/oauth2/rfc6749/tokens.py @@ -220,6 +220,24 @@ def signed_token_generator(private_pem, **kwargs): return signed_token_generator +def get_token_from_header(request): + """ + Helper function to extract a token from the request header. + :param request: The request object + :return: Return the token or None if the Authorization header is malformed. + """ + token = None + + if 'Authorization' in request.headers: + split_header = request.headers.get('Authorization').split() + if len(split_header) == 2 and split_header[0] == 'Bearer': + token = split_header[1] + else: + token = request.access_token + + return token + + class TokenBase(object): def __call__(self, request, refresh_token=False): @@ -286,16 +304,12 @@ class BearerToken(TokenBase): return token def validate_request(self, request): - token = None - if 'Authorization' in request.headers: - token = request.headers.get('Authorization')[7:] - else: - token = request.access_token + token = get_token_from_header(request) return self.request_validator.validate_bearer_token( token, request.scopes, request) def estimate_type(self, request): - if request.headers.get('Authorization', '').startswith('Bearer'): + if request.headers.get('Authorization', '').split(' ')[0] == 'Bearer': return 9 elif request.access_token is not None: return 5 @@ -331,17 +345,13 @@ class JWTToken(TokenBase): 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 + token = get_token_from_header(request) 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): + split_header = request.headers.get('Authorization', '').split() + + if len(split_header) == 2 and split_header[0] == 'Bearer' and split_header[1].startswith('ey') and split_header[1].count('.') in (2, 4): return 10 - else: - return 0 + return 0 -- cgit v1.2.1 From fedc1d1b740a0407ec59152750bbbd9dc736b51d Mon Sep 17 00:00:00 2001 From: Grey Li Date: Sun, 27 May 2018 03:38:05 +0800 Subject: Add missing NotImplementedError (#499) --- oauthlib/oauth2/rfc6749/clients/base.py | 1 + 1 file changed, 1 insertion(+) (limited to 'oauthlib/oauth2') diff --git a/oauthlib/oauth2/rfc6749/clients/base.py b/oauthlib/oauth2/rfc6749/clients/base.py index 07ef894..406832d 100644 --- a/oauthlib/oauth2/rfc6749/clients/base.py +++ b/oauthlib/oauth2/rfc6749/clients/base.py @@ -143,6 +143,7 @@ class Client(object): def parse_request_uri_response(self, *args, **kwargs): """Abstract method used to parse redirection responses.""" + raise NotImplementedError("Must be implemented by inheriting classes.") def add_token(self, uri, http_method='GET', body=None, headers=None, token_placement=None, **kwargs): -- cgit v1.2.1 From d5a4d5ea0eab04ddddefac7d1e7a4902fc469286 Mon Sep 17 00:00:00 2001 From: Wiliam Souza Date: Tue, 5 Jun 2018 11:33:21 -0300 Subject: OpenID Connect split (#525) * Add command to clean up builds to makefile * Fix docs strings for endpoints pre_configured * Chnage grant_types.openid_connect to include a deprecation warning be a backward compatible * Fix doc string for rfc6749.request_validator * Remove unused import * Change import to be explicity * Move JWTTokenTestCase to openid.connect.core.test_token * Move JWTToken to oauthlib.openid.connect.core.tokens * Move to openid connect test * Move openid connect exceptions to its own file * Remove openid connect from oauth2 server * Remove JWTToken from oauth tokens * Remove grant_types.openid_connect file * Add oauthlib/openid estructure and tests --- oauthlib/oauth2/__init__.py | 2 +- .../oauth2/rfc6749/endpoints/pre_configured.py | 43 +- oauthlib/oauth2/rfc6749/errors.py | 123 ++---- oauthlib/oauth2/rfc6749/grant_types/__init__.py | 8 - .../oauth2/rfc6749/grant_types/openid_connect.py | 451 --------------------- oauthlib/oauth2/rfc6749/request_validator.py | 4 +- oauthlib/oauth2/rfc6749/tokens.py | 40 -- 7 files changed, 37 insertions(+), 634 deletions(-) delete mode 100644 oauthlib/oauth2/rfc6749/grant_types/openid_connect.py (limited to 'oauthlib/oauth2') diff --git a/oauthlib/oauth2/__init__.py b/oauthlib/oauth2/__init__.py index dc7b431..303c6a1 100644 --- a/oauthlib/oauth2/__init__.py +++ b/oauthlib/oauth2/__init__.py @@ -24,7 +24,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 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.errors import AccessDeniedError, OAuth2Error, FatalClientError, InsecureTransportError, InvalidClientError, InvalidClientIdError, InvalidGrantError, InvalidRedirectURIError, InvalidRequestError, InvalidRequestFatalError, InvalidScopeError, MismatchingRedirectURIError, MismatchingStateError, MissingClientIdError, MissingCodeError, MissingRedirectURIError, MissingResponseTypeError, MissingTokenError, MissingTokenTypeError, 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/endpoints/pre_configured.py b/oauthlib/oauth2/rfc6749/endpoints/pre_configured.py index 66af516..e2cc9db 100644 --- a/oauthlib/oauth2/rfc6749/endpoints/pre_configured.py +++ b/oauthlib/oauth2/rfc6749/endpoints/pre_configured.py @@ -1,22 +1,19 @@ # -*- coding: utf-8 -*- """ -oauthlib.oauth2.rfc6749 -~~~~~~~~~~~~~~~~~~~~~~~ +oauthlib.oauth2.rfc6749.endpoints.pre_configured +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -This module is an implementation of various logic needed -for consuming and providing OAuth 2.0 RFC6749. +This module is an implementation of various endpoints needed +for providing OAuth 2.0 RFC6749 servers. """ from __future__ import absolute_import, unicode_literals -from ..grant_types import (AuthCodeGrantDispatcher, AuthorizationCodeGrant, - AuthTokenGrantDispatcher, +from ..grant_types import (AuthorizationCodeGrant, ClientCredentialsGrant, - ImplicitTokenGrantDispatcher, ImplicitGrant, - OpenIDConnectAuthCode, OpenIDConnectImplicit, - OpenIDConnectHybrid, + ImplicitGrant, RefreshTokenGrant, ResourceOwnerPasswordCredentialsGrant) -from ..tokens import BearerToken, JWTToken +from ..tokens import BearerToken from .authorization import AuthorizationEndpoint from .introspect import IntrospectEndpoint from .resource import ResourceEndpoint @@ -51,46 +48,28 @@ class Server(AuthorizationEndpoint, IntrospectEndpoint, TokenEndpoint, request_validator) credentials_grant = ClientCredentialsGrant(request_validator) 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) - 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_choice, - 'id_token': openid_connect_implicit, - 'id_token token': openid_connect_implicit, - 'code token': openid_connect_hybrid, - 'code id_token': openid_connect_hybrid, - 'code id_token token': openid_connect_hybrid, + 'code': auth_grant, + 'token': implicit_grant, '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': token_grant_choice, + 'authorization_code': auth_grant, 'password': password_grant, 'client_credentials': credentials_grant, 'refresh_token': refresh_grant, }, default_token_type=bearer) ResourceEndpoint.__init__(self, default_token='Bearer', - token_types={'Bearer': bearer, 'JWT': jwt}) + token_types={'Bearer': bearer}) RevocationEndpoint.__init__(self, request_validator) IntrospectEndpoint.__init__(self, request_validator) diff --git a/oauthlib/oauth2/rfc6749/errors.py b/oauthlib/oauth2/rfc6749/errors.py index 1d5e98d..5a0cca2 100644 --- a/oauthlib/oauth2/rfc6749/errors.py +++ b/oauthlib/oauth2/rfc6749/errors.py @@ -274,106 +274,6 @@ class UnsupportedTokenTypeError(OAuth2Error): error = 'unsupported_token_type' -class FatalOpenIDClientError(FatalClientError): - pass - - -class OpenIDClientError(OAuth2Error): - pass - - -class InteractionRequired(OpenIDClientError): - """ - The Authorization Server requires End-User interaction to proceed. - - This error MAY be returned when the prompt parameter value in the - Authentication Request is none, but the Authentication Request cannot be - completed without displaying a user interface for End-User interaction. - """ - error = 'interaction_required' - status_code = 401 - - -class LoginRequired(OpenIDClientError): - """ - The Authorization Server requires End-User authentication. - - This error MAY be returned when the prompt parameter value in the - Authentication Request is none, but the Authentication Request cannot be - completed without displaying a user interface for End-User authentication. - """ - error = 'login_required' - status_code = 401 - - -class AccountSelectionRequired(OpenIDClientError): - """ - The End-User is REQUIRED to select a session at the Authorization Server. - - The End-User MAY be authenticated at the Authorization Server with - different associated accounts, but the End-User did not select a session. - This error MAY be returned when the prompt parameter value in the - Authentication Request is none, but the Authentication Request cannot be - completed without displaying a user interface to prompt for a session to - use. - """ - error = 'account_selection_required' - - -class ConsentRequired(OpenIDClientError): - """ - The Authorization Server requires End-User consent. - - This error MAY be returned when the prompt parameter value in the - Authentication Request is none, but the Authentication Request cannot be - completed without displaying a user interface for End-User consent. - """ - error = 'consent_required' - status_code = 401 - - -class InvalidRequestURI(OpenIDClientError): - """ - The request_uri in the Authorization Request returns an error or - contains invalid data. - """ - error = 'invalid_request_uri' - description = 'The request_uri in the Authorization Request returns an ' \ - 'error or contains invalid data.' - - -class InvalidRequestObject(OpenIDClientError): - """ - The request parameter contains an invalid Request Object. - """ - error = 'invalid_request_object' - description = 'The request parameter contains an invalid Request Object.' - - -class RequestNotSupported(OpenIDClientError): - """ - The OP does not support use of the request parameter. - """ - error = 'request_not_supported' - description = 'The request parameter is not supported.' - - -class RequestURINotSupported(OpenIDClientError): - """ - The OP does not support use of the request_uri parameter. - """ - error = 'request_uri_not_supported' - description = 'The request_uri parameter is not supported.' - - -class RegistrationNotSupported(OpenIDClientError): - """ - The OP does not support use of the registration parameter. - """ - error = 'registration_not_supported' - description = 'The registration parameter is not supported.' - - class InvalidTokenError(OAuth2Error): """ The access token provided is expired, revoked, malformed, or @@ -402,6 +302,29 @@ class InsufficientScopeError(OAuth2Error): "the access token.") +class ConsentRequired(OAuth2Error): + """ + The Authorization Server requires End-User consent. + + This error MAY be returned when the prompt parameter value in the + Authentication Request is none, but the Authentication Request cannot be + completed without displaying a user interface for End-User consent. + """ + error = 'consent_required' + status_code = 401 + +class LoginRequired(OAuth2Error): + """ + The Authorization Server requires End-User authentication. + + This error MAY be returned when the prompt parameter value in the + Authentication Request is none, but the Authentication Request cannot be + completed without displaying a user interface for End-User authentication. + """ + error = 'login_required' + status_code = 401 + + def raise_from_error(error, params=None): import inspect import sys diff --git a/oauthlib/oauth2/rfc6749/grant_types/__init__.py b/oauthlib/oauth2/rfc6749/grant_types/__init__.py index 2e4bfe4..2ec8e4f 100644 --- a/oauthlib/oauth2/rfc6749/grant_types/__init__.py +++ b/oauthlib/oauth2/rfc6749/grant_types/__init__.py @@ -10,11 +10,3 @@ from .implicit import ImplicitGrant from .resource_owner_password_credentials import ResourceOwnerPasswordCredentialsGrant from .client_credentials import ClientCredentialsGrant from .refresh_token import RefreshTokenGrant -from .openid_connect import OpenIDConnectBase -from .openid_connect import OpenIDConnectAuthCode -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/openid_connect.py b/oauthlib/oauth2/rfc6749/grant_types/openid_connect.py deleted file mode 100644 index 4371b28..0000000 --- a/oauthlib/oauth2/rfc6749/grant_types/openid_connect.py +++ /dev/null @@ -1,451 +0,0 @@ -# -*- coding: utf-8 -*- -""" -oauthlib.oauth2.rfc6749.grant_types.openid_connect -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -""" -from __future__ import absolute_import, unicode_literals - -import datetime -import logging -from json import loads - -from ..errors import ConsentRequired, InvalidRequestError, LoginRequired -from ..request_validator import RequestValidator -from .authorization_code import AuthorizationCodeGrant -from .implicit import ImplicitGrant - -log = logging.getLogger(__name__) - - -class OIDCNoPrompt(Exception): - """Exception used to inform users that no explicit authorization is needed. - - Normally users authorize requests after validation of the request is done. - Then post-authorization validation is again made and a response containing - an auth code or token is created. However, when OIDC clients request - no prompting of user authorization the final response is created directly. - - Example (without the shortcut for no prompt) - - scopes, req_info = endpoint.validate_authorization_request(url, ...) - authorization_view = create_fancy_auth_form(scopes, req_info) - return authorization_view - - Example (with the no prompt shortcut) - try: - scopes, req_info = endpoint.validate_authorization_request(url, ...) - authorization_view = create_fancy_auth_form(scopes, req_info) - return authorization_view - except OIDCNoPrompt: - # Note: Location will be set for you - headers, body, status = endpoint.create_authorization_response(url, ...) - redirect_view = create_redirect(headers, body, status) - return redirect_view - """ - - def __init__(self): - msg = ("OIDC request for no user interaction received. Do not ask user " - "for authorization, it should been done using silent " - "authentication through create_authorization_response. " - "See OIDCNoPrompt.__doc__ for more details.") - super(OIDCNoPrompt, self).__init__(msg) - - -class AuthCodeGrantDispatcher(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_auth_grant=None, oidc_auth_grant=None): - self.default_auth_grant = default_auth_grant - self.oidc_auth_grant = oidc_auth_grant - - def _handler_for_request(self, request): - handler = self.default_auth_grant - - if request.scopes and "openid" in request.scopes: - handler = self.oidc_auth_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 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 - # proxy_target grant type handler, which will usually be either - # the standard OAuth2 AuthCode or Implicit grant types. - def __getattr__(self, attr): - return getattr(self.proxy_target, attr) - - def __setattr__(self, attr, value): - proxied_attrs = set(('refresh_token', 'response_types')) - if attr in proxied_attrs: - setattr(self.proxy_target, attr, value) - else: - super(OpenIDConnectBase, self).__setattr__(attr, value) - - def validate_authorization_request(self, request): - """Validates the OpenID Connect authorization request parameters. - - :returns: (list of scopes, dict of request info) - """ - # If request.prompt is 'none' then no login/authorization form should - # be presented to the user. Instead, a silent login/authorization - # should be performed. - if request.prompt == 'none': - raise OIDCNoPrompt() - else: - return self.proxy_target.validate_authorization_request(request) - - def _inflate_claims(self, request): - # this may be called multiple times in a single request so make sure we only de-serialize the claims once - if request.claims and not isinstance(request.claims, dict): - # specific claims are requested during the Authorization Request and may be requested for inclusion - # in either the id_token or the UserInfo endpoint response - # see http://openid.net/specs/openid-connect-core-1_0.html#ClaimsParameter - try: - request.claims = loads(request.claims) - except Exception as ex: - raise InvalidRequestError(description="Malformed claims parameter", - uri="http://openid.net/specs/openid-connect-core-1_0.html#ClaimsParameter") - - def add_id_token(self, token, token_handler, request): - # Treat it as normal OAuth 2 auth code request if openid is not present - if not request.scopes or 'openid' not in request.scopes: - return token - - # Only add an id token on auth/token step if asked for. - if request.response_type and 'id_token' not in request.response_type: - return token - - if 'state' not in token: - token['state'] = request.state - - if request.max_age: - d = datetime.datetime.utcnow() - token['auth_time'] = d.isoformat("T") + "Z" - - # TODO: acr claims (probably better handled by server code using oauthlib in get_id_token) - - token['id_token'] = self.request_validator.get_id_token(token, token_handler, request) - - return token - - 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 - user interface pages to the End-User. The defined values are: - - page - The Authorization Server SHOULD display the - authentication and consent UI consistent with a full User - Agent page view. If the display parameter is not specified, - this is the default display mode. - - popup - The Authorization Server SHOULD display the - authentication and consent UI consistent with a popup User - Agent window. The popup User Agent window should be of an - appropriate size for a login-focused dialog and should not - obscure the entire window that it is popping up over. - - touch - The Authorization Server SHOULD display the - authentication and consent UI consistent with a device that - leverages a touch interface. - - wap - The Authorization Server SHOULD display the - authentication and consent UI consistent with a "feature - phone" type display. - - The Authorization Server MAY also attempt to detect the - capabilities of the User Agent and present an appropriate - display. - - prompt - OPTIONAL. Space delimited, case sensitive list of ASCII string - values that specifies whether the Authorization Server prompts - the End-User for reauthentication and consent. The defined - values are: - - none - The Authorization Server MUST NOT display any - authentication or consent user interface pages. An error is - returned if an End-User is not already authenticated or the - Client does not have pre-configured consent for the - requested Claims or does not fulfill other conditions for - processing the request. The error code will typically be - login_required, interaction_required, or another code - defined in Section 3.1.2.6. This can be used as a method to - check for existing authentication and/or consent. - - login - The Authorization Server SHOULD prompt the End-User - for reauthentication. If it cannot reauthenticate the - End-User, it MUST return an error, typically - login_required. - - consent - The Authorization Server SHOULD prompt the - End-User for consent before returning information to the - Client. If it cannot obtain consent, it MUST return an - error, typically consent_required. - - select_account - The Authorization Server SHOULD prompt the - End-User to select a user account. This enables an End-User - who has multiple accounts at the Authorization Server to - select amongst the multiple accounts that they might have - current sessions for. If it cannot obtain an account - selection choice made by the End-User, it MUST return an - error, typically account_selection_required. - - The prompt parameter can be used by the Client to make sure - that the End-User is still present for the current session or - to bring attention to the request. If this parameter contains - none with any other value, an error is returned. - - max_age - OPTIONAL. Maximum Authentication Age. Specifies the allowable - elapsed time in seconds since the last time the End-User was - actively authenticated by the OP. If the elapsed time is - greater than this value, the OP MUST attempt to actively - re-authenticate the End-User. (The max_age request parameter - corresponds to the OpenID 2.0 PAPE [OpenID.PAPE] max_auth_age - request parameter.) When max_age is used, the ID Token returned - MUST include an auth_time Claim Value. - - ui_locales - OPTIONAL. End-User's preferred languages and scripts for the - user interface, represented as a space-separated list of BCP47 - [RFC5646] language tag values, ordered by preference. For - instance, the value "fr-CA fr en" represents a preference for - French as spoken in Canada, then French (without a region - designation), followed by English (without a region - designation). An error SHOULD NOT result if some or all of the - requested locales are not supported by the OpenID Provider. - - id_token_hint - OPTIONAL. ID Token previously issued by the Authorization - Server being passed as a hint about the End-User's current or - past authenticated session with the Client. If the End-User - identified by the ID Token is logged in or is logged in by the - request, then the Authorization Server returns a positive - response; otherwise, it SHOULD return an error, such as - login_required. When possible, an id_token_hint SHOULD be - present when prompt=none is used and an invalid_request error - MAY be returned if it is not; however, the server SHOULD - respond successfully when possible, even if it is not present. - The Authorization Server need not be listed as an audience of - the ID Token when it is used as an id_token_hint value. If the - ID Token received by the RP from the OP is encrypted, to use it - as an id_token_hint, the Client MUST decrypt the signed ID - Token contained within the encrypted ID Token. The Client MAY - re-encrypt the signed ID token to the Authentication Server - using a key that enables the server to decrypt the ID Token, - and use the re-encrypted ID token as the id_token_hint value. - - login_hint - OPTIONAL. Hint to the Authorization Server about the login - identifier the End-User might use to log in (if necessary). - This hint can be used by an RP if it first asks the End-User - for their e-mail address (or other identifier) and then wants - to pass that value as a hint to the discovered authorization - service. It is RECOMMENDED that the hint value match the value - used for discovery. This value MAY also be a phone number in - the format specified for the phone_number Claim. The use of - this parameter is left to the OP's discretion. - - acr_values - OPTIONAL. Requested Authentication Context Class Reference - values. Space-separated string that specifies the acr values - that the Authorization Server is being requested to use for - processing this Authentication Request, with the values - appearing in order of preference. The Authentication Context - Class satisfied by the authentication performed is returned as - the acr Claim Value, as specified in Section 2. The acr Claim - is requested as a Voluntary Claim by this parameter. - """ - - # Treat it as normal OAuth 2 auth code request if openid is not present - if not request.scopes or 'openid' not in request.scopes: - return {} - - prompt = request.prompt if request.prompt else [] - if hasattr(prompt, 'split'): - prompt = prompt.strip().split() - prompt = set(prompt) - - if 'none' in prompt: - - if len(prompt) > 1: - msg = "Prompt none is mutually exclusive with other values." - raise InvalidRequestError(request=request, description=msg) - - # prompt other than 'none' should be handled by the server code that - # uses oauthlib - if not request.id_token_hint: - msg = "Prompt is set to none yet id_token_hint is missing." - raise InvalidRequestError(request=request, description=msg) - - if not self.request_validator.validate_silent_login(request): - raise LoginRequired(request=request) - - if not self.request_validator.validate_silent_authorization(request): - raise ConsentRequired(request=request) - - self._inflate_claims(request) - - if not self.request_validator.validate_user_match( - 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, - 'login_hint': request.login_hint, - 'claims': request.claims - } - - return request_info - - def openid_implicit_authorization_validator(self, request): - """Additional validation when following the implicit flow. - """ - # Undefined in OpenID Connect, fall back to OAuth2 definition. - if request.response_type == 'token': - return {} - - # Treat it as normal OAuth 2 auth code request if openid is not present - if not request.scopes or 'openid' not in request.scopes: - return {} - - # REQUIRED. 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. For implementation notes, see - # Section 15.5.2. - if not request.nonce: - desc = 'Request is missing mandatory nonce parameter.' - raise InvalidRequestError(request=request, description=desc) - - return {} - - -class OpenIDConnectAuthCode(OpenIDConnectBase): - - def __init__(self, request_validator=None, **kwargs): - self.proxy_target = AuthorizationCodeGrant( - request_validator=request_validator, **kwargs) - self.custom_validators.post_auth.append( - self.openid_authorization_validator) - self.register_token_modifier(self.add_id_token) - - -class OpenIDConnectImplicit(OpenIDConnectBase): - - def __init__(self, request_validator=None, **kwargs): - self.proxy_target = ImplicitGrant( - request_validator=request_validator, **kwargs) - self.register_response_type('id_token') - self.register_response_type('id_token token') - self.custom_validators.post_auth.append( - self.openid_authorization_validator) - self.custom_validators.post_auth.append( - self.openid_implicit_authorization_validator) - self.register_token_modifier(self.add_id_token) - - -class OpenIDConnectHybrid(OpenIDConnectBase): - - def __init__(self, request_validator=None, **kwargs): - self.request_validator = request_validator or RequestValidator() - - 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') - self.custom_validators.post_auth.append( - self.openid_authorization_validator) - # Hybrid flows can return the id_token from the authorization - # endpoint as part of the 'code' response - self.register_code_modifier(self.add_token) - self.register_code_modifier(self.add_id_token) - self.register_token_modifier(self.add_id_token) diff --git a/oauthlib/oauth2/rfc6749/request_validator.py b/oauthlib/oauth2/rfc6749/request_validator.py index 56ecc3d..92edba6 100644 --- a/oauthlib/oauth2/rfc6749/request_validator.py +++ b/oauthlib/oauth2/rfc6749/request_validator.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- """ -oauthlib.oauth2.rfc6749.grant_types -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +oauthlib.oauth2.rfc6749.request_validator +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ """ from __future__ import absolute_import, unicode_literals diff --git a/oauthlib/oauth2/rfc6749/tokens.py b/oauthlib/oauth2/rfc6749/tokens.py index a7491f4..1d2b5eb 100644 --- a/oauthlib/oauth2/rfc6749/tokens.py +++ b/oauthlib/oauth2/rfc6749/tokens.py @@ -315,43 +315,3 @@ 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 = get_token_from_header(request) - return self.request_validator.validate_jwt_bearer_token( - token, request.scopes, request) - - def estimate_type(self, request): - split_header = request.headers.get('Authorization', '').split() - - if len(split_header) == 2 and split_header[0] == 'Bearer' and split_header[1].startswith('ey') and split_header[1].count('.') in (2, 4): - return 10 - return 0 -- cgit v1.2.1 From 481a4ec2e29530541ff8985cce938ece7a661562 Mon Sep 17 00:00:00 2001 From: claweyenuk <39317519+claweyenuk@users.noreply.github.com> Date: Sat, 30 Jun 2018 15:04:02 -0700 Subject: Update save_bearer_token docs to mention how the token is passed in as a reference (#556) --- oauthlib/oauth2/rfc6749/request_validator.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'oauthlib/oauth2') diff --git a/oauthlib/oauth2/rfc6749/request_validator.py b/oauthlib/oauth2/rfc6749/request_validator.py index 92edba6..bf1515d 100644 --- a/oauthlib/oauth2/rfc6749/request_validator.py +++ b/oauthlib/oauth2/rfc6749/request_validator.py @@ -332,7 +332,14 @@ class RequestValidator(object): } Note that while "scope" is a string-separated list of authorized scopes, - the original list is still available in request.scopes + the original list is still available in request.scopes. + + The token dict is passed as a reference so any changes made to the dictionary + will go back to the user. If additional information must return to the client + user, and it is only possible to get this information after writing the token + to storage, it should be added to the token dictionary. If the token + dictionary must be modified but the changes should not go back to the user, + a copy of the dictionary must be made before making the changes. Also note that if an Authorization Code grant request included a valid claims parameter (for OpenID Connect) then the request.claims property will contain -- cgit v1.2.1 From 3eaf962311dfbc566dbfa66a988e0331b91184be Mon Sep 17 00:00:00 2001 From: Seth Davis Date: Sat, 30 Jun 2018 18:09:26 -0400 Subject: Remove handling of nonstandard parameter "expires" (#506) --- oauthlib/oauth2/rfc6749/parameters.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'oauthlib/oauth2') diff --git a/oauthlib/oauth2/rfc6749/parameters.py b/oauthlib/oauth2/rfc6749/parameters.py index 0107933..9ea8c44 100644 --- a/oauthlib/oauth2/rfc6749/parameters.py +++ b/oauthlib/oauth2/rfc6749/parameters.py @@ -362,16 +362,13 @@ def parse_token_response(body, scope=None): # https://github.com/oauthlib/oauthlib/issues/267 params = dict(urlparse.parse_qsl(body)) - for key in ('expires_in', 'expires'): - if key in params: # cast a couple things to int + for key in ('expires_in',): + if key in params: # cast things to int params[key] = int(params[key]) if 'scope' in params: params['scope'] = scope_to_list(params['scope']) - if 'expires' in params: - params['expires_in'] = params.pop('expires') - if 'expires_in' in params: params['expires_at'] = time.time() + int(params['expires_in']) -- cgit v1.2.1