Update oauthlib-3.1.1

This commit is contained in:
JonnyWong16 2021-10-14 22:34:45 -07:00
parent e58aa40099
commit d76838a607
No known key found for this signature in database
GPG key ID: B1F1F9807184697A
64 changed files with 4329 additions and 1421 deletions

View file

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
"""
oauthlib.oauth2.rfc6749
~~~~~~~~~~~~~~~~~~~~~~~
@ -6,14 +5,13 @@ oauthlib.oauth2.rfc6749
This module is an implementation of various logic needed
for consuming and providing OAuth 2.0 RFC6749.
"""
from __future__ import absolute_import, unicode_literals
from .authorization import AuthorizationEndpoint
from .token import TokenEndpoint
from .introspect import IntrospectEndpoint
from .metadata import MetadataEndpoint
from .pre_configured import (
BackendApplicationServer, LegacyApplicationServer, MobileApplicationServer,
Server, WebApplicationServer,
)
from .resource import ResourceEndpoint
from .revocation import RevocationEndpoint
from .pre_configured import Server
from .pre_configured import WebApplicationServer
from .pre_configured import MobileApplicationServer
from .pre_configured import LegacyApplicationServer
from .pre_configured import BackendApplicationServer
from .token import TokenEndpoint

View file

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
"""
oauthlib.oauth2.rfc6749
~~~~~~~~~~~~~~~~~~~~~~~
@ -6,11 +5,10 @@ oauthlib.oauth2.rfc6749
This module is an implementation of various logic needed
for consuming and providing OAuth 2.0 RFC6749.
"""
from __future__ import absolute_import, unicode_literals
import logging
from oauthlib.common import Request
from oauthlib.oauth2.rfc6749 import utils
from .base import BaseEndpoint, catch_errors_and_unavailability
@ -58,7 +56,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,
@ -108,7 +106,9 @@ class AuthorizationEndpoint(BaseEndpoint):
"""Extract response_type and route to the designated handler."""
request = Request(
uri, http_method=http_method, body=body, headers=headers)
request.scopes = None
request.scopes = utils.scope_to_list(request.scope)
response_type_handler = self.response_types.get(
request.response_type, self.default_response_type_handler)
return response_type_handler.validate_authorization_request(request)

View file

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
"""
oauthlib.oauth2.rfc6749
~~~~~~~~~~~~~~~~~~~~~~~
@ -6,22 +5,34 @@ oauthlib.oauth2.rfc6749
This module is an implementation of various logic needed
for consuming and providing OAuth 2.0 RFC6749.
"""
from __future__ import absolute_import, unicode_literals
import functools
import logging
from ..errors import TemporarilyUnavailableError, ServerError
from ..errors import FatalClientError, OAuth2Error
from ..errors import (
FatalClientError, InvalidClientError, InvalidRequestError, OAuth2Error,
ServerError, TemporarilyUnavailableError, UnsupportedTokenTypeError,
)
log = logging.getLogger(__name__)
class BaseEndpoint(object):
class BaseEndpoint:
def __init__(self):
self._available = True
self._catch_errors = False
self._valid_request_methods = None
@property
def valid_request_methods(self):
return self._valid_request_methods
@valid_request_methods.setter
def valid_request_methods(self, valid_request_methods):
if valid_request_methods is not None:
valid_request_methods = [x.upper() for x in valid_request_methods]
self._valid_request_methods = valid_request_methods
@property
def available(self):
@ -29,7 +40,7 @@ class BaseEndpoint(object):
@available.setter
def available(self, available):
self._available = available
self._available = available
@property
def catch_errors(self):
@ -39,6 +50,43 @@ class BaseEndpoint(object):
def catch_errors(self, catch_errors):
self._catch_errors = catch_errors
def _raise_on_missing_token(self, request):
"""Raise error on missing token."""
if not request.token:
raise InvalidRequestError(request=request,
description='Missing token parameter.')
def _raise_on_invalid_client(self, request):
"""Raise on failed client authentication."""
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)
def _raise_on_unsupported_token(self, request):
"""Raise on unsupported tokens."""
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)
def _raise_on_bad_method(self, request):
if self.valid_request_methods is None:
raise ValueError('Configure "valid_request_methods" property first')
if request.http_method.upper() not in self.valid_request_methods:
raise InvalidRequestError(request=request,
description=('Unsupported request method %s' % request.http_method.upper()))
def _raise_on_bad_post_request(self, request):
"""Raise if invalid POST request received
"""
if request.http_method.upper() == 'POST':
query_params = request.uri_query or ""
if query_params:
raise InvalidRequestError(request=request,
description=('URL query parameters are not allowed'))
def catch_errors_and_unavailability(f):
@functools.wraps(f)

View file

@ -0,0 +1,122 @@
"""
oauthlib.oauth2.rfc6749.endpoint.introspect
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
An implementation of the OAuth 2.0 `Token Introspection`.
.. _`Token Introspection`: https://tools.ietf.org/html/rfc7662
"""
import json
import logging
from oauthlib.common import Request
from ..errors import OAuth2Error
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')
valid_request_methods = ('POST',)
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.
"""
resp_headers = {
'Content-Type': 'application/json',
'Cache-Control': 'no-store',
'Pragma': 'no-cache',
}
request = Request(uri, http_method, body, headers)
try:
self.validate_introspect_request(request)
log.debug('Token introspect valid for %r.', request)
except OAuth2Error as e:
log.debug('Client error during validation of %r. %r.', request, e)
resp_headers.update(e.headers)
return resp_headers, e.json, e.status_code
claims = self.request_validator.introspect_token(
request.token,
request.token_type_hint,
request
)
if claims is None:
return resp_headers, json.dumps(dict(active=False)), 200
if "active" in claims:
claims.pop("active")
return resp_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
"""
self._raise_on_bad_method(request)
self._raise_on_bad_post_request(request)
self._raise_on_missing_token(request)
self._raise_on_invalid_client(request)
self._raise_on_unsupported_token(request)

View file

@ -0,0 +1,237 @@
"""
oauthlib.oauth2.rfc6749.endpoint.metadata
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
An implementation of the `OAuth 2.0 Authorization Server Metadata`.
.. _`OAuth 2.0 Authorization Server Metadata`: https://tools.ietf.org/html/rfc8414
"""
import copy
import json
import logging
from .. import grant_types
from .authorization import AuthorizationEndpoint
from .base import BaseEndpoint, catch_errors_and_unavailability
from .introspect import IntrospectEndpoint
from .revocation import RevocationEndpoint
from .token import TokenEndpoint
log = logging.getLogger(__name__)
class MetadataEndpoint(BaseEndpoint):
"""OAuth2.0 Authorization Server Metadata endpoint.
This specification generalizes the metadata format defined by
`OpenID Connect Discovery 1.0` in a way that is compatible
with OpenID Connect Discovery while being applicable to a wider set
of OAuth 2.0 use cases. This is intentionally parallel to the way
that OAuth 2.0 Dynamic Client Registration Protocol [`RFC7591`_]
generalized the dynamic client registration mechanisms defined by
OpenID Connect Dynamic Client Registration 1.0
in a way that is compatible with it.
.. _`OpenID Connect Discovery 1.0`: https://openid.net/specs/openid-connect-discovery-1_0.html
.. _`RFC7591`: https://tools.ietf.org/html/rfc7591
"""
def __init__(self, endpoints, claims={}, raise_errors=True):
assert isinstance(claims, dict)
for endpoint in endpoints:
assert isinstance(endpoint, BaseEndpoint)
BaseEndpoint.__init__(self)
self.raise_errors = raise_errors
self.endpoints = endpoints
self.initial_claims = claims
self.claims = self.validate_metadata_server()
@catch_errors_and_unavailability
def create_metadata_response(self, uri, http_method='GET', body=None,
headers=None):
"""Create metadata response
"""
headers = {
'Content-Type': 'application/json'
}
return headers, json.dumps(self.claims), 200
def validate_metadata(self, array, key, is_required=False, is_list=False, is_url=False, is_issuer=False):
if not self.raise_errors:
return
if key not in array:
if is_required:
raise ValueError("key {} is a mandatory metadata.".format(key))
elif is_issuer:
if not array[key].startswith("https"):
raise ValueError("key {}: {} must be an HTTPS URL".format(key, array[key]))
if "?" in array[key] or "&" in array[key] or "#" in array[key]:
raise ValueError("key {}: {} must not contain query or fragment components".format(key, array[key]))
elif is_url:
if not array[key].startswith("http"):
raise ValueError("key {}: {} must be an URL".format(key, array[key]))
elif is_list:
if not isinstance(array[key], list):
raise ValueError("key {}: {} must be an Array".format(key, array[key]))
for elem in array[key]:
if not isinstance(elem, str):
raise ValueError("array {}: {} must contains only string (not {})".format(key, array[key], elem))
def validate_metadata_token(self, claims, endpoint):
"""
If the token endpoint is used in the grant type, the value of this
parameter MUST be the same as the value of the "grant_type"
parameter passed to the token endpoint defined in the grant type
definition.
"""
self._grant_types.extend(endpoint._grant_types.keys())
claims.setdefault("token_endpoint_auth_methods_supported", ["client_secret_post", "client_secret_basic"])
self.validate_metadata(claims, "token_endpoint_auth_methods_supported", is_list=True)
self.validate_metadata(claims, "token_endpoint_auth_signing_alg_values_supported", is_list=True)
self.validate_metadata(claims, "token_endpoint", is_required=True, is_url=True)
def validate_metadata_authorization(self, claims, endpoint):
claims.setdefault("response_types_supported",
list(filter(lambda x: x != "none", endpoint._response_types.keys())))
claims.setdefault("response_modes_supported", ["query", "fragment"])
# The OAuth2.0 Implicit flow is defined as a "grant type" but it is not
# using the "token" endpoint, as such, we have to add it explicitly to
# the list of "grant_types_supported" when enabled.
if "token" in claims["response_types_supported"]:
self._grant_types.append("implicit")
self.validate_metadata(claims, "response_types_supported", is_required=True, is_list=True)
self.validate_metadata(claims, "response_modes_supported", is_list=True)
if "code" in claims["response_types_supported"]:
code_grant = endpoint._response_types["code"]
if not isinstance(code_grant, grant_types.AuthorizationCodeGrant) and hasattr(code_grant, "default_grant"):
code_grant = code_grant.default_grant
claims.setdefault("code_challenge_methods_supported",
list(code_grant._code_challenge_methods.keys()))
self.validate_metadata(claims, "code_challenge_methods_supported", is_list=True)
self.validate_metadata(claims, "authorization_endpoint", is_required=True, is_url=True)
def validate_metadata_revocation(self, claims, endpoint):
claims.setdefault("revocation_endpoint_auth_methods_supported",
["client_secret_post", "client_secret_basic"])
self.validate_metadata(claims, "revocation_endpoint_auth_methods_supported", is_list=True)
self.validate_metadata(claims, "revocation_endpoint_auth_signing_alg_values_supported", is_list=True)
self.validate_metadata(claims, "revocation_endpoint", is_required=True, is_url=True)
def validate_metadata_introspection(self, claims, endpoint):
claims.setdefault("introspection_endpoint_auth_methods_supported",
["client_secret_post", "client_secret_basic"])
self.validate_metadata(claims, "introspection_endpoint_auth_methods_supported", is_list=True)
self.validate_metadata(claims, "introspection_endpoint_auth_signing_alg_values_supported", is_list=True)
self.validate_metadata(claims, "introspection_endpoint", is_required=True, is_url=True)
def validate_metadata_server(self):
"""
Authorization servers can have metadata describing their
configuration. The following authorization server metadata values
are used by this specification. More details can be found in
`RFC8414 section 2`_ :
issuer
REQUIRED
authorization_endpoint
URL of the authorization server's authorization endpoint
[`RFC6749#Authorization`_]. This is REQUIRED unless no grant types are supported
that use the authorization endpoint.
token_endpoint
URL of the authorization server's token endpoint [`RFC6749#Token`_]. This
is REQUIRED unless only the implicit grant type is supported.
scopes_supported
RECOMMENDED.
response_types_supported
REQUIRED.
Other OPTIONAL fields:
jwks_uri,
registration_endpoint,
response_modes_supported
grant_types_supported
OPTIONAL. JSON array containing a list of the OAuth 2.0 grant
type values that this authorization server supports. The array
values used are the same as those used with the "grant_types"
parameter defined by "OAuth 2.0 Dynamic Client Registration
Protocol" [`RFC7591`_]. If omitted, the default value is
"["authorization_code", "implicit"]".
token_endpoint_auth_methods_supported
token_endpoint_auth_signing_alg_values_supported
service_documentation
ui_locales_supported
op_policy_uri
op_tos_uri
revocation_endpoint
revocation_endpoint_auth_methods_supported
revocation_endpoint_auth_signing_alg_values_supported
introspection_endpoint
introspection_endpoint_auth_methods_supported
introspection_endpoint_auth_signing_alg_values_supported
code_challenge_methods_supported
Additional authorization server metadata parameters MAY also be used.
Some are defined by other specifications, such as OpenID Connect
Discovery 1.0 [`OpenID.Discovery`_].
.. _`RFC8414 section 2`: https://tools.ietf.org/html/rfc8414#section-2
.. _`RFC6749#Authorization`: https://tools.ietf.org/html/rfc6749#section-3.1
.. _`RFC6749#Token`: https://tools.ietf.org/html/rfc6749#section-3.2
.. _`RFC7591`: https://tools.ietf.org/html/rfc7591
.. _`OpenID.Discovery`: https://openid.net/specs/openid-connect-discovery-1_0.html
"""
claims = copy.deepcopy(self.initial_claims)
self.validate_metadata(claims, "issuer", is_required=True, is_issuer=True)
self.validate_metadata(claims, "jwks_uri", is_url=True)
self.validate_metadata(claims, "scopes_supported", is_list=True)
self.validate_metadata(claims, "service_documentation", is_url=True)
self.validate_metadata(claims, "ui_locales_supported", is_list=True)
self.validate_metadata(claims, "op_policy_uri", is_url=True)
self.validate_metadata(claims, "op_tos_uri", is_url=True)
self._grant_types = []
for endpoint in self.endpoints:
if isinstance(endpoint, TokenEndpoint):
self.validate_metadata_token(claims, endpoint)
if isinstance(endpoint, AuthorizationEndpoint):
self.validate_metadata_authorization(claims, endpoint)
if isinstance(endpoint, RevocationEndpoint):
self.validate_metadata_revocation(claims, endpoint)
if isinstance(endpoint, IntrospectEndpoint):
self.validate_metadata_introspection(claims, endpoint)
# "grant_types_supported" is a combination of all OAuth2 grant types
# allowed in the current provider implementation.
claims.setdefault("grant_types_supported", self._grant_types)
self.validate_metadata(claims, "grant_types_supported", is_list=True)
return claims

View file

@ -1,28 +1,24 @@
# -*- 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 (
AuthorizationCodeGrant, ClientCredentialsGrant, ImplicitGrant,
RefreshTokenGrant, ResourceOwnerPasswordCredentialsGrant,
)
from ..tokens import BearerToken
from ..grant_types import AuthorizationCodeGrant
from ..grant_types import ImplicitGrant
from ..grant_types import ResourceOwnerPasswordCredentialsGrant
from ..grant_types import ClientCredentialsGrant
from ..grant_types import RefreshTokenGrant
from .authorization import AuthorizationEndpoint
from .token import TokenEndpoint
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."""
@ -42,35 +38,40 @@ class Server(AuthorizationEndpoint, TokenEndpoint, ResourceEndpoint,
:param kwargs: Extra parameters to pass to authorization-,
token-, resource-, and revocation-endpoint constructors.
"""
auth_grant = AuthorizationCodeGrant(request_validator)
implicit_grant = ImplicitGrant(request_validator)
password_grant = ResourceOwnerPasswordCredentialsGrant(
self.auth_grant = AuthorizationCodeGrant(request_validator)
self.implicit_grant = ImplicitGrant(request_validator)
self.password_grant = ResourceOwnerPasswordCredentialsGrant(
request_validator)
credentials_grant = ClientCredentialsGrant(request_validator)
refresh_grant = RefreshTokenGrant(request_validator)
bearer = BearerToken(request_validator, token_generator,
self.credentials_grant = ClientCredentialsGrant(request_validator)
self.refresh_grant = RefreshTokenGrant(request_validator)
self.bearer = BearerToken(request_validator, token_generator,
token_expires_in, refresh_token_generator)
AuthorizationEndpoint.__init__(self, default_response_type='code',
response_types={
'code': auth_grant,
'token': implicit_grant,
'code': self.auth_grant,
'token': self.implicit_grant,
'none': self.auth_grant
},
default_token_type=bearer)
default_token_type=self.bearer)
TokenEndpoint.__init__(self, default_grant_type='authorization_code',
grant_types={
'authorization_code': auth_grant,
'password': password_grant,
'client_credentials': credentials_grant,
'refresh_token': refresh_grant,
'authorization_code': self.auth_grant,
'password': self.password_grant,
'client_credentials': self.credentials_grant,
'refresh_token': self.refresh_grant,
},
default_token_type=bearer)
default_token_type=self.bearer)
ResourceEndpoint.__init__(self, default_token='Bearer',
token_types={'Bearer': bearer})
token_types={'Bearer': self.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."""
@ -89,26 +90,27 @@ class WebApplicationServer(AuthorizationEndpoint, TokenEndpoint, ResourceEndpoin
:param kwargs: Extra parameters to pass to authorization-,
token-, resource-, and revocation-endpoint constructors.
"""
auth_grant = AuthorizationCodeGrant(request_validator)
refresh_grant = RefreshTokenGrant(request_validator)
bearer = BearerToken(request_validator, token_generator,
self.auth_grant = AuthorizationCodeGrant(request_validator)
self.refresh_grant = RefreshTokenGrant(request_validator)
self.bearer = BearerToken(request_validator, token_generator,
token_expires_in, refresh_token_generator)
AuthorizationEndpoint.__init__(self, default_response_type='code',
response_types={'code': auth_grant},
default_token_type=bearer)
response_types={'code': self.auth_grant},
default_token_type=self.bearer)
TokenEndpoint.__init__(self, default_grant_type='authorization_code',
grant_types={
'authorization_code': auth_grant,
'refresh_token': refresh_grant,
'authorization_code': self.auth_grant,
'refresh_token': self.refresh_grant,
},
default_token_type=bearer)
default_token_type=self.bearer)
ResourceEndpoint.__init__(self, default_token='Bearer',
token_types={'Bearer': bearer})
token_types={'Bearer': self.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."""
@ -127,21 +129,23 @@ class MobileApplicationServer(AuthorizationEndpoint, ResourceEndpoint,
:param kwargs: Extra parameters to pass to authorization-,
token-, resource-, and revocation-endpoint constructors.
"""
implicit_grant = ImplicitGrant(request_validator)
bearer = BearerToken(request_validator, token_generator,
self.implicit_grant = ImplicitGrant(request_validator)
self.bearer = BearerToken(request_validator, token_generator,
token_expires_in, refresh_token_generator)
AuthorizationEndpoint.__init__(self, default_response_type='token',
response_types={
'token': implicit_grant},
default_token_type=bearer)
'token': self.implicit_grant},
default_token_type=self.bearer)
ResourceEndpoint.__init__(self, default_token='Bearer',
token_types={'Bearer': bearer})
token_types={'Bearer': self.bearer})
RevocationEndpoint.__init__(self, request_validator,
supported_token_types=['access_token'])
IntrospectEndpoint.__init__(self, request_validator,
supported_token_types=['access_token'])
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."""
@ -160,24 +164,25 @@ class LegacyApplicationServer(TokenEndpoint, ResourceEndpoint,
:param kwargs: Extra parameters to pass to authorization-,
token-, resource-, and revocation-endpoint constructors.
"""
password_grant = ResourceOwnerPasswordCredentialsGrant(
self.password_grant = ResourceOwnerPasswordCredentialsGrant(
request_validator)
refresh_grant = RefreshTokenGrant(request_validator)
bearer = BearerToken(request_validator, token_generator,
self.refresh_grant = RefreshTokenGrant(request_validator)
self.bearer = BearerToken(request_validator, token_generator,
token_expires_in, refresh_token_generator)
TokenEndpoint.__init__(self, default_grant_type='password',
grant_types={
'password': password_grant,
'refresh_token': refresh_grant,
'password': self.password_grant,
'refresh_token': self.refresh_grant,
},
default_token_type=bearer)
default_token_type=self.bearer)
ResourceEndpoint.__init__(self, default_token='Bearer',
token_types={'Bearer': bearer})
token_types={'Bearer': self.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."""
@ -196,14 +201,16 @@ class BackendApplicationServer(TokenEndpoint, ResourceEndpoint,
:param kwargs: Extra parameters to pass to authorization-,
token-, resource-, and revocation-endpoint constructors.
"""
credentials_grant = ClientCredentialsGrant(request_validator)
bearer = BearerToken(request_validator, token_generator,
self.credentials_grant = ClientCredentialsGrant(request_validator)
self.bearer = BearerToken(request_validator, token_generator,
token_expires_in, refresh_token_generator)
TokenEndpoint.__init__(self, default_grant_type='client_credentials',
grant_types={
'client_credentials': credentials_grant},
default_token_type=bearer)
'client_credentials': self.credentials_grant},
default_token_type=self.bearer)
ResourceEndpoint.__init__(self, default_token='Bearer',
token_types={'Bearer': bearer})
token_types={'Bearer': self.bearer})
RevocationEndpoint.__init__(self, request_validator,
supported_token_types=['access_token'])
IntrospectEndpoint.__init__(self, request_validator,
supported_token_types=['access_token'])

View file

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
"""
oauthlib.oauth2.rfc6749
~~~~~~~~~~~~~~~~~~~~~~~
@ -6,8 +5,6 @@ oauthlib.oauth2.rfc6749
This module is an implementation of various logic needed
for consuming and providing OAuth 2.0 RFC6749.
"""
from __future__ import absolute_import, unicode_literals
import logging
from oauthlib.common import Request
@ -83,5 +80,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

View file

@ -1,21 +1,17 @@
# -*- coding: utf-8 -*-
"""
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
import logging
from oauthlib.common import Request
from ..errors import OAuth2Error
from .base import BaseEndpoint, catch_errors_and_unavailability
from ..errors import InvalidClientError, UnsupportedTokenTypeError
from ..errors import InvalidRequestError, OAuth2Error
log = logging.getLogger(__name__)
@ -29,6 +25,7 @@ class RevocationEndpoint(BaseEndpoint):
"""
valid_token_types = ('access_token', 'refresh_token')
valid_request_methods = ('POST',)
def __init__(self, request_validator, supported_token_types=None,
enable_jsonp=False):
@ -59,6 +56,11 @@ class RevocationEndpoint(BaseEndpoint):
An invalid token type hint value is ignored by the authorization server
and does not influence the revocation response.
"""
resp_headers = {
'Content-Type': 'application/json',
'Cache-Control': 'no-store',
'Pragma': 'no-cache',
}
request = Request(
uri, http_method=http_method, body=body, headers=headers)
try:
@ -68,8 +70,9 @@ class RevocationEndpoint(BaseEndpoint):
log.debug('Client error during validation of %r. %r.', request, e)
response_body = e.json
if self.enable_jsonp and request.callback:
response_body = '%s(%s);' % (request.callback, response_body)
return {}, response_body, e.status_code
response_body = '{}({});'.format(request.callback, response_body)
resp_headers.update(e.headers)
return resp_headers, response_body, e.status_code
self.request_validator.revoke_token(request.token,
request.token_type_hint, request)
@ -110,21 +113,14 @@ 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,
description='Missing token parameter.')
if self.request_validator.client_authentication_required(request):
if not self.request_validator.authenticate_client(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)
self._raise_on_bad_method(request)
self._raise_on_bad_post_request(request)
self._raise_on_missing_token(request)
self._raise_on_invalid_client(request)
self._raise_on_unsupported_token(request)

View file

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
"""
oauthlib.oauth2.rfc6749
~~~~~~~~~~~~~~~~~~~~~~~
@ -6,15 +5,13 @@ oauthlib.oauth2.rfc6749
This module is an implementation of various logic needed
for consuming and providing OAuth 2.0 RFC6749.
"""
from __future__ import absolute_import, unicode_literals
import logging
from oauthlib.common import Request
from oauthlib.oauth2.rfc6749 import utils
from .base import BaseEndpoint, catch_errors_and_unavailability
log = logging.getLogger(__name__)
@ -39,7 +36,6 @@ class TokenEndpoint(BaseEndpoint):
https://example.com/path?query=component # OK
https://example.com/path?query=component#fragment # Not OK
Since requests to the authorization endpoint result in user
Since requests to the token endpoint result in the transmission of
clear-text credentials (in the HTTP request and response), the
authorization server MUST require the use of TLS as described in
@ -59,9 +55,11 @@ 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
"""
valid_request_methods = ('POST',)
def __init__(self, default_grant_type, default_token_type, grant_types):
BaseEndpoint.__init__(self)
self._grant_types = grant_types
@ -85,16 +83,37 @@ class TokenEndpoint(BaseEndpoint):
return self._default_token_type
@catch_errors_and_unavailability
def create_token_response(self, uri, http_method='GET', body=None,
headers=None, credentials=None):
def create_token_response(self, uri, http_method='POST', body=None,
headers=None, credentials=None, grant_type_for_scope=None,
claims=None):
"""Extract grant_type and route to the designated handler."""
request = Request(
uri, http_method=http_method, body=body, headers=headers)
request.scopes = None
self.validate_token_request(request)
# 'scope' is an allowed Token Request param in both the "Resource Owner Password Credentials Grant"
# and "Client Credentials Grant" flows
# https://tools.ietf.org/html/rfc6749#section-4.3.2
# https://tools.ietf.org/html/rfc6749#section-4.4.2
request.scopes = utils.scope_to_list(request.scope)
request.extra_credentials = credentials
if grant_type_for_scope:
request.grant_type = grant_type_for_scope
# OpenID Connect claims, if provided. The server using oauthlib might choose
# to implement the claims parameter of the Authorization Request. In this case
# it should retrieve those claims and pass them via the claims argument here,
# as a dict.
if claims:
request.claims = claims
grant_type_handler = self.grant_types.get(request.grant_type,
self.default_grant_type_handler)
log.debug('Dispatching grant_type %s request to %r.',
request.grant_type, grant_type_handler)
return grant_type_handler.create_token_response(
request, self.default_token_type)
def validate_token_request(self, request):
self._raise_on_bad_method(request)
self._raise_on_bad_post_request(request)