about summary refs log tree commit diff
path: root/.venv/lib/python3.12/site-packages/oauthlib/oauth2/rfc6749/endpoints
diff options
context:
space:
mode:
Diffstat (limited to '.venv/lib/python3.12/site-packages/oauthlib/oauth2/rfc6749/endpoints')
-rw-r--r--.venv/lib/python3.12/site-packages/oauthlib/oauth2/rfc6749/endpoints/__init__.py17
-rw-r--r--.venv/lib/python3.12/site-packages/oauthlib/oauth2/rfc6749/endpoints/authorization.py114
-rw-r--r--.venv/lib/python3.12/site-packages/oauthlib/oauth2/rfc6749/endpoints/base.py113
-rw-r--r--.venv/lib/python3.12/site-packages/oauthlib/oauth2/rfc6749/endpoints/introspect.py120
-rw-r--r--.venv/lib/python3.12/site-packages/oauthlib/oauth2/rfc6749/endpoints/metadata.py238
-rw-r--r--.venv/lib/python3.12/site-packages/oauthlib/oauth2/rfc6749/endpoints/pre_configured.py216
-rw-r--r--.venv/lib/python3.12/site-packages/oauthlib/oauth2/rfc6749/endpoints/resource.py84
-rw-r--r--.venv/lib/python3.12/site-packages/oauthlib/oauth2/rfc6749/endpoints/revocation.py126
-rw-r--r--.venv/lib/python3.12/site-packages/oauthlib/oauth2/rfc6749/endpoints/token.py119
9 files changed, 1147 insertions, 0 deletions
diff --git a/.venv/lib/python3.12/site-packages/oauthlib/oauth2/rfc6749/endpoints/__init__.py b/.venv/lib/python3.12/site-packages/oauthlib/oauth2/rfc6749/endpoints/__init__.py
new file mode 100644
index 00000000..1695b41b
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/oauthlib/oauth2/rfc6749/endpoints/__init__.py
@@ -0,0 +1,17 @@
+"""
+oauthlib.oauth2.rfc6749
+~~~~~~~~~~~~~~~~~~~~~~~
+
+This module is an implementation of various logic needed
+for consuming and providing OAuth 2.0 RFC6749.
+"""
+from .authorization import AuthorizationEndpoint
+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 .token import TokenEndpoint
diff --git a/.venv/lib/python3.12/site-packages/oauthlib/oauth2/rfc6749/endpoints/authorization.py b/.venv/lib/python3.12/site-packages/oauthlib/oauth2/rfc6749/endpoints/authorization.py
new file mode 100644
index 00000000..71967865
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/oauthlib/oauth2/rfc6749/endpoints/authorization.py
@@ -0,0 +1,114 @@
+"""
+oauthlib.oauth2.rfc6749
+~~~~~~~~~~~~~~~~~~~~~~~
+
+This module is an implementation of various logic needed
+for consuming and providing OAuth 2.0 RFC6749.
+"""
+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__)
+
+
+class AuthorizationEndpoint(BaseEndpoint):
+
+    """Authorization endpoint - used by the client to obtain authorization
+    from the resource owner via user-agent redirection.
+
+    The authorization endpoint is used to interact with the resource
+    owner and obtain an authorization grant.  The authorization server
+    MUST first verify the identity of the resource owner.  The way in
+    which the authorization server authenticates the resource owner (e.g.
+    username and password login, session cookies) is beyond the scope of
+    this specification.
+
+    The endpoint URI MAY include an "application/x-www-form-urlencoded"
+    formatted (per `Appendix B`_) query component,
+    which MUST be retained when adding additional query parameters.  The
+    endpoint URI MUST NOT include a fragment component::
+
+        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
+    authentication and the transmission of clear-text credentials (in the
+    HTTP response), the authorization server MUST require the use of TLS
+    as described in Section 1.6 when sending requests to the
+    authorization endpoint::
+
+        # We will deny any request which URI schema is not with https
+
+    The authorization server MUST support the use of the HTTP "GET"
+    method [RFC2616] for the authorization endpoint, and MAY support the
+    use of the "POST" method as well::
+
+        # HTTP method is currently not enforced
+
+    Parameters sent without a value MUST be treated as if they were
+    omitted from the request.  The authorization server MUST ignore
+    unrecognized request parameters.  Request and response parameters
+    MUST NOT be included more than once::
+
+        # Enforced through the design of oauthlib.common.Request
+
+    .. _`Appendix B`: https://tools.ietf.org/html/rfc6749#appendix-B
+    """
+
+    def __init__(self, default_response_type, default_token_type,
+                 response_types):
+        BaseEndpoint.__init__(self)
+        self._response_types = response_types
+        self._default_response_type = default_response_type
+        self._default_token_type = default_token_type
+
+    @property
+    def response_types(self):
+        return self._response_types
+
+    @property
+    def default_response_type(self):
+        return self._default_response_type
+
+    @property
+    def default_response_type_handler(self):
+        return self.response_types.get(self.default_response_type)
+
+    @property
+    def default_token_type(self):
+        return self._default_token_type
+
+    @catch_errors_and_unavailability
+    def create_authorization_response(self, uri, http_method='GET', body=None,
+                                      headers=None, scopes=None, credentials=None):
+        """Extract response_type and route to the designated handler."""
+        request = Request(
+            uri, http_method=http_method, body=body, headers=headers)
+        request.scopes = scopes
+        # TODO: decide whether this should be a required argument
+        request.user = None     # TODO: explain this in docs
+        for k, v in (credentials or {}).items():
+            setattr(request, k, v)
+        response_type_handler = self.response_types.get(
+            request.response_type, self.default_response_type_handler)
+        log.debug('Dispatching response_type %s request to %r.',
+                  request.response_type, response_type_handler)
+        return response_type_handler.create_authorization_response(
+            request, self.default_token_type)
+
+    @catch_errors_and_unavailability
+    def validate_authorization_request(self, uri, http_method='GET', body=None,
+                                       headers=None):
+        """Extract response_type and route to the designated handler."""
+        request = Request(
+            uri, http_method=http_method, body=body, headers=headers)
+
+        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)
diff --git a/.venv/lib/python3.12/site-packages/oauthlib/oauth2/rfc6749/endpoints/base.py b/.venv/lib/python3.12/site-packages/oauthlib/oauth2/rfc6749/endpoints/base.py
new file mode 100644
index 00000000..3f239917
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/oauthlib/oauth2/rfc6749/endpoints/base.py
@@ -0,0 +1,113 @@
+"""
+oauthlib.oauth2.rfc6749
+~~~~~~~~~~~~~~~~~~~~~~~
+
+This module is an implementation of various logic needed
+for consuming and providing OAuth 2.0 RFC6749.
+"""
+import functools
+import logging
+
+from ..errors import (
+    FatalClientError, InvalidClientError, InvalidRequestError, OAuth2Error,
+    ServerError, TemporarilyUnavailableError, UnsupportedTokenTypeError,
+)
+
+log = logging.getLogger(__name__)
+
+
+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):
+        return self._available
+
+    @available.setter
+    def available(self, available):
+        self._available = available       
+
+    @property
+    def catch_errors(self):
+        return self._catch_errors
+
+    @catch_errors.setter
+    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)
+    def wrapper(endpoint, uri, *args, **kwargs):
+        if not endpoint.available:
+            e = TemporarilyUnavailableError()
+            log.info('Endpoint unavailable, ignoring request %s.' % uri)
+            return {}, e.json, 503
+
+        if endpoint.catch_errors:
+            try:
+                return f(endpoint, uri, *args, **kwargs)
+            except OAuth2Error:
+                raise
+            except FatalClientError:
+                raise
+            except Exception as e:
+                error = ServerError()
+                log.warning(
+                    'Exception caught while processing request, %s.' % e)
+                return {}, error.json, 500
+        else:
+            return f(endpoint, uri, *args, **kwargs)
+    return wrapper
diff --git a/.venv/lib/python3.12/site-packages/oauthlib/oauth2/rfc6749/endpoints/introspect.py b/.venv/lib/python3.12/site-packages/oauthlib/oauth2/rfc6749/endpoints/introspect.py
new file mode 100644
index 00000000..3cc61e66
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/oauthlib/oauth2/rfc6749/endpoints/introspect.py
@@ -0,0 +1,120 @@
+"""
+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)
diff --git a/.venv/lib/python3.12/site-packages/oauthlib/oauth2/rfc6749/endpoints/metadata.py b/.venv/lib/python3.12/site-packages/oauthlib/oauth2/rfc6749/endpoints/metadata.py
new file mode 100644
index 00000000..a2820f28
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/oauthlib/oauth2/rfc6749/endpoints/metadata.py
@@ -0,0 +1,238 @@
+"""
+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, utils
+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',
+            'Access-Control-Allow-Origin': '*',
+        }
+        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 utils.is_secure_transport(array[key]):
+                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
diff --git a/.venv/lib/python3.12/site-packages/oauthlib/oauth2/rfc6749/endpoints/pre_configured.py b/.venv/lib/python3.12/site-packages/oauthlib/oauth2/rfc6749/endpoints/pre_configured.py
new file mode 100644
index 00000000..d64a1663
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/oauthlib/oauth2/rfc6749/endpoints/pre_configured.py
@@ -0,0 +1,216 @@
+"""
+oauthlib.oauth2.rfc6749.endpoints.pre_configured
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This module is an implementation of various endpoints needed
+for providing OAuth 2.0 RFC6749 servers.
+"""
+from ..grant_types import (
+    AuthorizationCodeGrant, ClientCredentialsGrant, ImplicitGrant,
+    RefreshTokenGrant, 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, IntrospectEndpoint, TokenEndpoint,
+             ResourceEndpoint, RevocationEndpoint):
+
+    """An all-in-one endpoint featuring all four major grant types."""
+
+    def __init__(self, request_validator, token_expires_in=None,
+                 token_generator=None, refresh_token_generator=None,
+                 *args, **kwargs):
+        """Construct a new all-grants-in-one server.
+
+        :param request_validator: An implementation of
+                                  oauthlib.oauth2.RequestValidator.
+        :param token_expires_in: An int or a function to generate a token
+                                 expiration offset (in seconds) given a
+                                 oauthlib.common.Request object.
+        :param token_generator: A function to generate a token from a request.
+        :param refresh_token_generator: A function to generate a token from a
+                                        request for the refresh token.
+        :param kwargs: Extra parameters to pass to authorization-,
+                       token-, resource-, and revocation-endpoint constructors.
+        """
+        self.auth_grant = AuthorizationCodeGrant(request_validator)
+        self.implicit_grant = ImplicitGrant(request_validator)
+        self.password_grant = ResourceOwnerPasswordCredentialsGrant(
+            request_validator)
+        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': self.auth_grant,
+                                           'token': self.implicit_grant,
+                                           'none': self.auth_grant
+                                       },
+                                       default_token_type=self.bearer)
+
+        TokenEndpoint.__init__(self, default_grant_type='authorization_code',
+                               grant_types={
+                                   'authorization_code': self.auth_grant,
+                                   'password': self.password_grant,
+                                   'client_credentials': self.credentials_grant,
+                                   'refresh_token': self.refresh_grant,
+                               },
+                               default_token_type=self.bearer)
+        ResourceEndpoint.__init__(self, default_token='Bearer',
+                                  token_types={'Bearer': self.bearer})
+        RevocationEndpoint.__init__(self, request_validator)
+        IntrospectEndpoint.__init__(self, request_validator)
+
+
+class WebApplicationServer(AuthorizationEndpoint, IntrospectEndpoint, TokenEndpoint,
+                           ResourceEndpoint, RevocationEndpoint):
+
+    """An all-in-one endpoint featuring Authorization code grant and Bearer tokens."""
+
+    def __init__(self, request_validator, token_generator=None,
+                 token_expires_in=None, refresh_token_generator=None, **kwargs):
+        """Construct a new web application server.
+
+        :param request_validator: An implementation of
+                                  oauthlib.oauth2.RequestValidator.
+        :param token_expires_in: An int or a function to generate a token
+                                 expiration offset (in seconds) given a
+                                 oauthlib.common.Request object.
+        :param token_generator: A function to generate a token from a request.
+        :param refresh_token_generator: A function to generate a token from a
+                                        request for the refresh token.
+        :param kwargs: Extra parameters to pass to authorization-,
+                       token-, resource-, and revocation-endpoint constructors.
+        """
+        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': self.auth_grant},
+                                       default_token_type=self.bearer)
+        TokenEndpoint.__init__(self, default_grant_type='authorization_code',
+                               grant_types={
+                                   'authorization_code': self.auth_grant,
+                                   'refresh_token': self.refresh_grant,
+                               },
+                               default_token_type=self.bearer)
+        ResourceEndpoint.__init__(self, default_token='Bearer',
+                                  token_types={'Bearer': self.bearer})
+        RevocationEndpoint.__init__(self, request_validator)
+        IntrospectEndpoint.__init__(self, request_validator)
+
+
+class MobileApplicationServer(AuthorizationEndpoint, IntrospectEndpoint,
+                              ResourceEndpoint, RevocationEndpoint):
+
+    """An all-in-one endpoint featuring Implicit code grant and Bearer tokens."""
+
+    def __init__(self, request_validator, token_generator=None,
+                 token_expires_in=None, refresh_token_generator=None, **kwargs):
+        """Construct a new implicit grant server.
+
+        :param request_validator: An implementation of
+                                  oauthlib.oauth2.RequestValidator.
+        :param token_expires_in: An int or a function to generate a token
+                                 expiration offset (in seconds) given a
+                                 oauthlib.common.Request object.
+        :param token_generator: A function to generate a token from a request.
+        :param refresh_token_generator: A function to generate a token from a
+                                        request for the refresh token.
+        :param kwargs: Extra parameters to pass to authorization-,
+                       token-, resource-, and revocation-endpoint constructors.
+        """
+        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': self.implicit_grant},
+                                       default_token_type=self.bearer)
+        ResourceEndpoint.__init__(self, default_token='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, IntrospectEndpoint,
+                              ResourceEndpoint, RevocationEndpoint):
+
+    """An all-in-one endpoint featuring Resource Owner Password Credentials grant and Bearer tokens."""
+
+    def __init__(self, request_validator, token_generator=None,
+                 token_expires_in=None, refresh_token_generator=None, **kwargs):
+        """Construct a resource owner password credentials grant server.
+
+        :param request_validator: An implementation of
+                                  oauthlib.oauth2.RequestValidator.
+        :param token_expires_in: An int or a function to generate a token
+                                 expiration offset (in seconds) given a
+                                 oauthlib.common.Request object.
+        :param token_generator: A function to generate a token from a request.
+        :param refresh_token_generator: A function to generate a token from a
+                                        request for the refresh token.
+        :param kwargs: Extra parameters to pass to authorization-,
+                       token-, resource-, and revocation-endpoint constructors.
+        """
+        self.password_grant = ResourceOwnerPasswordCredentialsGrant(
+            request_validator)
+        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': self.password_grant,
+                                   'refresh_token': self.refresh_grant,
+                               },
+                               default_token_type=self.bearer)
+        ResourceEndpoint.__init__(self, default_token='Bearer',
+                                  token_types={'Bearer': self.bearer})
+        RevocationEndpoint.__init__(self, request_validator)
+        IntrospectEndpoint.__init__(self, request_validator)
+
+
+class BackendApplicationServer(TokenEndpoint, IntrospectEndpoint,
+                               ResourceEndpoint, RevocationEndpoint):
+
+    """An all-in-one endpoint featuring Client Credentials grant and Bearer tokens."""
+
+    def __init__(self, request_validator, token_generator=None,
+                 token_expires_in=None, refresh_token_generator=None, **kwargs):
+        """Construct a client credentials grant server.
+
+        :param request_validator: An implementation of
+                                  oauthlib.oauth2.RequestValidator.
+        :param token_expires_in: An int or a function to generate a token
+                                 expiration offset (in seconds) given a
+                                 oauthlib.common.Request object.
+        :param token_generator: A function to generate a token from a request.
+        :param refresh_token_generator: A function to generate a token from a
+                                        request for the refresh token.
+        :param kwargs: Extra parameters to pass to authorization-,
+                       token-, resource-, and revocation-endpoint constructors.
+        """
+        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': self.credentials_grant},
+                               default_token_type=self.bearer)
+        ResourceEndpoint.__init__(self, default_token='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'])
diff --git a/.venv/lib/python3.12/site-packages/oauthlib/oauth2/rfc6749/endpoints/resource.py b/.venv/lib/python3.12/site-packages/oauthlib/oauth2/rfc6749/endpoints/resource.py
new file mode 100644
index 00000000..f7562255
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/oauthlib/oauth2/rfc6749/endpoints/resource.py
@@ -0,0 +1,84 @@
+"""
+oauthlib.oauth2.rfc6749
+~~~~~~~~~~~~~~~~~~~~~~~
+
+This module is an implementation of various logic needed
+for consuming and providing OAuth 2.0 RFC6749.
+"""
+import logging
+
+from oauthlib.common import Request
+
+from .base import BaseEndpoint, catch_errors_and_unavailability
+
+log = logging.getLogger(__name__)
+
+
+class ResourceEndpoint(BaseEndpoint):
+
+    """Authorizes access to protected resources.
+
+    The client accesses protected resources by presenting the access
+    token to the resource server.  The resource server MUST validate the
+    access token and ensure that it has not expired and that its scope
+    covers the requested resource.  The methods used by the resource
+    server to validate the access token (as well as any error responses)
+    are beyond the scope of this specification but generally involve an
+    interaction or coordination between the resource server and the
+    authorization server::
+
+        # For most cases, returning a 403 should suffice.
+
+    The method in which the client utilizes the access token to
+    authenticate with the resource server depends on the type of access
+    token issued by the authorization server.  Typically, it involves
+    using the HTTP "Authorization" request header field [RFC2617] with an
+    authentication scheme defined by the specification of the access
+    token type used, such as [RFC6750]::
+
+        # Access tokens may also be provided in query and body
+        https://example.com/protected?access_token=kjfch2345sdf   # Query
+        access_token=sdf23409df   # Body
+    """
+
+    def __init__(self, default_token, token_types):
+        BaseEndpoint.__init__(self)
+        self._tokens = token_types
+        self._default_token = default_token
+
+    @property
+    def default_token(self):
+        return self._default_token
+
+    @property
+    def default_token_type_handler(self):
+        return self.tokens.get(self.default_token)
+
+    @property
+    def tokens(self):
+        return self._tokens
+
+    @catch_errors_and_unavailability
+    def verify_request(self, uri, http_method='GET', body=None, headers=None,
+                       scopes=None):
+        """Validate client, code etc, return body + headers"""
+        request = Request(uri, http_method, body, headers)
+        request.token_type = self.find_token_type(request)
+        request.scopes = scopes
+        token_type_handler = self.tokens.get(request.token_type,
+                                             self.default_token_type_handler)
+        log.debug('Dispatching token_type %s request to %r.',
+                  request.token_type, token_type_handler)
+        return token_type_handler.validate_request(request), request
+
+    def find_token_type(self, request):
+        """Token type identification.
+
+        RFC 6749 does not provide a method for easily differentiating between
+        different token types during protected resource access. We estimate
+        the most likely token type (if any) by asking each known token type
+        to give an estimation based on the request.
+        """
+        estimates = sorted(((t.estimate_type(request), n)
+                            for n, t in self.tokens.items()), reverse=True)
+        return estimates[0][1] if len(estimates) else None
diff --git a/.venv/lib/python3.12/site-packages/oauthlib/oauth2/rfc6749/endpoints/revocation.py b/.venv/lib/python3.12/site-packages/oauthlib/oauth2/rfc6749/endpoints/revocation.py
new file mode 100644
index 00000000..596d0860
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/oauthlib/oauth2/rfc6749/endpoints/revocation.py
@@ -0,0 +1,126 @@
+"""
+oauthlib.oauth2.rfc6749.endpoint.revocation
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+An implementation of the OAuth 2 `Token Revocation`_ spec (draft 11).
+
+.. _`Token Revocation`: https://tools.ietf.org/html/draft-ietf-oauth-revocation-11
+"""
+import logging
+
+from oauthlib.common import Request
+
+from ..errors import OAuth2Error
+from .base import BaseEndpoint, catch_errors_and_unavailability
+
+log = logging.getLogger(__name__)
+
+
+class RevocationEndpoint(BaseEndpoint):
+
+    """Token revocation endpoint.
+
+    Endpoint used by authenticated clients to revoke access and refresh tokens.
+    Commonly this will be part of the Authorization Endpoint.
+    """
+
+    valid_token_types = ('access_token', 'refresh_token')
+    valid_request_methods = ('POST',)
+
+    def __init__(self, request_validator, supported_token_types=None,
+            enable_jsonp=False):
+        BaseEndpoint.__init__(self)
+        self.request_validator = request_validator
+        self.supported_token_types = (
+            supported_token_types or self.valid_token_types)
+        self.enable_jsonp = enable_jsonp
+
+    @catch_errors_and_unavailability
+    def create_revocation_response(self, uri, http_method='POST', body=None,
+                                   headers=None):
+        """Revoke supplied access or refresh token.
+
+
+        The authorization server responds with HTTP status code 200 if the
+        token has been revoked successfully or if the client submitted an
+        invalid token.
+
+        Note: invalid tokens do not cause an error response since the client
+        cannot handle such an error in a reasonable way.  Moreover, the purpose
+        of the revocation request, invalidating the particular token, is
+        already achieved.
+
+        The content of the response body is ignored by the client as all
+        necessary information is conveyed in the response code.
+
+        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:
+            self.validate_revocation_request(request)
+            log.debug('Token revocation valid for %r.', request)
+        except OAuth2Error as e:
+            log.debug('Client error during validation of %r. %r.', request, e)
+            response_body = e.json
+            if self.enable_jsonp and request.callback:
+                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)
+
+        response_body = ''
+        if self.enable_jsonp and request.callback:
+            response_body = request.callback + '();'
+        return {}, response_body, 200
+
+    def validate_revocation_request(self, request):
+        """Ensure the request is valid.
+
+        The client constructs the request by including the following parameters
+        using the "application/x-www-form-urlencoded" format in the HTTP
+        request entity-body:
+
+        token (REQUIRED).  The token that the client wants to get revoked.
+
+        token_type_hint (OPTIONAL).  A hint about the type of the token
+        submitted for revocation.  Clients MAY pass this parameter in order to
+        help the authorization server to 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.  This specification
+        defines two such values:
+
+                *  access_token: An Access Token as defined in [RFC6749],
+                    `section 1.4`_
+
+                *  refresh_token: A Refresh Token as defined in [RFC6749],
+                    `section 1.5`_
+
+                Specific implementations, profiles, and extensions of this
+                specification MAY define other values for this parameter using
+                the registry defined in `Section 4.1.2`_.
+
+        The client also includes its authentication credentials as described in
+        `Section 2.3`_. of [`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
+        """
+        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)
diff --git a/.venv/lib/python3.12/site-packages/oauthlib/oauth2/rfc6749/endpoints/token.py b/.venv/lib/python3.12/site-packages/oauthlib/oauth2/rfc6749/endpoints/token.py
new file mode 100644
index 00000000..ab9e0918
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/oauthlib/oauth2/rfc6749/endpoints/token.py
@@ -0,0 +1,119 @@
+"""
+oauthlib.oauth2.rfc6749
+~~~~~~~~~~~~~~~~~~~~~~~
+
+This module is an implementation of various logic needed
+for consuming and providing OAuth 2.0 RFC6749.
+"""
+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__)
+
+
+class TokenEndpoint(BaseEndpoint):
+
+    """Token issuing endpoint.
+
+    The token endpoint is used by the client to obtain an access token by
+    presenting its authorization grant or refresh token.  The token
+    endpoint is used with every authorization grant except for the
+    implicit grant type (since an access token is issued directly).
+
+    The means through which the client obtains the location of the token
+    endpoint are beyond the scope of this specification, but the location
+    is typically provided in the service documentation.
+
+    The endpoint URI MAY include an "application/x-www-form-urlencoded"
+    formatted (per `Appendix B`_) query component,
+    which MUST be retained when adding additional query parameters.  The
+    endpoint URI MUST NOT include a fragment component::
+
+        https://example.com/path?query=component             # OK
+        https://example.com/path?query=component#fragment    # Not OK
+
+    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
+    Section 1.6 when sending requests to the token endpoint::
+
+        # We will deny any request which URI schema is not with https
+
+    The client MUST use the HTTP "POST" method when making access token
+    requests::
+
+        # HTTP method is currently not enforced
+
+    Parameters sent without a value MUST be treated as if they were
+    omitted from the request.  The authorization server MUST ignore
+    unrecognized request parameters.  Request and response parameters
+    MUST NOT be included more than once::
+
+        # Delegated to each grant type.
+
+    .. _`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
+        self._default_token_type = default_token_type
+        self._default_grant_type = default_grant_type
+
+    @property
+    def grant_types(self):
+        return self._grant_types
+
+    @property
+    def default_grant_type(self):
+        return self._default_grant_type
+
+    @property
+    def default_grant_type_handler(self):
+        return self.grant_types.get(self.default_grant_type)
+
+    @property
+    def default_token_type(self):
+        return self._default_token_type
+
+    @catch_errors_and_unavailability
+    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)
+        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)