summaryrefslogtreecommitdiff
path: root/game/python-extra/oauthlib/oauth2/rfc6749
diff options
context:
space:
mode:
Diffstat (limited to 'game/python-extra/oauthlib/oauth2/rfc6749')
-rw-r--r--game/python-extra/oauthlib/oauth2/rfc6749/__init__.py16
-rw-r--r--game/python-extra/oauthlib/oauth2/rfc6749/clients/__init__.py14
-rw-r--r--game/python-extra/oauthlib/oauth2/rfc6749/clients/backend_application.py74
-rw-r--r--game/python-extra/oauthlib/oauth2/rfc6749/clients/base.py604
-rw-r--r--game/python-extra/oauthlib/oauth2/rfc6749/clients/legacy_application.py84
-rw-r--r--game/python-extra/oauthlib/oauth2/rfc6749/clients/mobile_application.py174
-rw-r--r--game/python-extra/oauthlib/oauth2/rfc6749/clients/service_application.py189
-rw-r--r--game/python-extra/oauthlib/oauth2/rfc6749/clients/web_application.py222
-rw-r--r--game/python-extra/oauthlib/oauth2/rfc6749/endpoints/__init__.py17
-rw-r--r--game/python-extra/oauthlib/oauth2/rfc6749/endpoints/authorization.py114
-rw-r--r--game/python-extra/oauthlib/oauth2/rfc6749/endpoints/base.py113
-rw-r--r--game/python-extra/oauthlib/oauth2/rfc6749/endpoints/introspect.py120
-rw-r--r--game/python-extra/oauthlib/oauth2/rfc6749/endpoints/metadata.py238
-rw-r--r--game/python-extra/oauthlib/oauth2/rfc6749/endpoints/pre_configured.py216
-rw-r--r--game/python-extra/oauthlib/oauth2/rfc6749/endpoints/resource.py84
-rw-r--r--game/python-extra/oauthlib/oauth2/rfc6749/endpoints/revocation.py126
-rw-r--r--game/python-extra/oauthlib/oauth2/rfc6749/endpoints/token.py119
-rw-r--r--game/python-extra/oauthlib/oauth2/rfc6749/errors.py400
-rw-r--r--game/python-extra/oauthlib/oauth2/rfc6749/grant_types/__init__.py11
-rw-r--r--game/python-extra/oauthlib/oauth2/rfc6749/grant_types/authorization_code.py548
-rw-r--r--game/python-extra/oauthlib/oauth2/rfc6749/grant_types/base.py268
-rw-r--r--game/python-extra/oauthlib/oauth2/rfc6749/grant_types/client_credentials.py123
-rw-r--r--game/python-extra/oauthlib/oauth2/rfc6749/grant_types/implicit.py376
-rw-r--r--game/python-extra/oauthlib/oauth2/rfc6749/grant_types/refresh_token.py136
-rw-r--r--game/python-extra/oauthlib/oauth2/rfc6749/grant_types/resource_owner_password_credentials.py199
-rw-r--r--game/python-extra/oauthlib/oauth2/rfc6749/parameters.py471
-rw-r--r--game/python-extra/oauthlib/oauth2/rfc6749/request_validator.py680
-rw-r--r--game/python-extra/oauthlib/oauth2/rfc6749/tokens.py356
-rw-r--r--game/python-extra/oauthlib/oauth2/rfc6749/utils.py83
29 files changed, 6175 insertions, 0 deletions
diff --git a/game/python-extra/oauthlib/oauth2/rfc6749/__init__.py b/game/python-extra/oauthlib/oauth2/rfc6749/__init__.py
new file mode 100644
index 0000000..4b75a8a
--- /dev/null
+++ b/game/python-extra/oauthlib/oauth2/rfc6749/__init__.py
@@ -0,0 +1,16 @@
+"""
+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 .endpoints.base import BaseEndpoint, catch_errors_and_unavailability
+from .errors import (
+ FatalClientError, OAuth2Error, ServerError, TemporarilyUnavailableError,
+)
+
+log = logging.getLogger(__name__)
diff --git a/game/python-extra/oauthlib/oauth2/rfc6749/clients/__init__.py b/game/python-extra/oauthlib/oauth2/rfc6749/clients/__init__.py
new file mode 100644
index 0000000..8fc6c95
--- /dev/null
+++ b/game/python-extra/oauthlib/oauth2/rfc6749/clients/__init__.py
@@ -0,0 +1,14 @@
+# -*- coding: utf-8 -*-
+"""
+oauthlib.oauth2.rfc6749
+~~~~~~~~~~~~~~~~~~~~~~~
+
+This module is an implementation of various logic needed
+for consuming OAuth 2.0 RFC6749.
+"""
+from .backend_application import BackendApplicationClient
+from .base import AUTH_HEADER, BODY, URI_QUERY, Client
+from .legacy_application import LegacyApplicationClient
+from .mobile_application import MobileApplicationClient
+from .service_application import ServiceApplicationClient
+from .web_application import WebApplicationClient
diff --git a/game/python-extra/oauthlib/oauth2/rfc6749/clients/backend_application.py b/game/python-extra/oauthlib/oauth2/rfc6749/clients/backend_application.py
new file mode 100644
index 0000000..e11e8fa
--- /dev/null
+++ b/game/python-extra/oauthlib/oauth2/rfc6749/clients/backend_application.py
@@ -0,0 +1,74 @@
+# -*- coding: utf-8 -*-
+"""
+oauthlib.oauth2.rfc6749
+~~~~~~~~~~~~~~~~~~~~~~~
+
+This module is an implementation of various logic needed
+for consuming and providing OAuth 2.0 RFC6749.
+"""
+from ..parameters import prepare_token_request
+from .base import Client
+
+
+class BackendApplicationClient(Client):
+
+ """A public client utilizing the client credentials grant workflow.
+
+ The client can request an access token using only its client
+ credentials (or other supported means of authentication) when the
+ client is requesting access to the protected resources under its
+ control, or those of another resource owner which has been previously
+ arranged with the authorization server (the method of which is beyond
+ the scope of this specification).
+
+ The client credentials grant type MUST only be used by confidential
+ clients.
+
+ Since the client authentication is used as the authorization grant,
+ no additional authorization request is needed.
+ """
+
+ grant_type = 'client_credentials'
+
+ def prepare_request_body(self, body='', scope=None,
+ include_client_id=False, **kwargs):
+ """Add the client credentials to the request body.
+
+ The client makes a request to the token endpoint by adding the
+ following parameters using the "application/x-www-form-urlencoded"
+ format per `Appendix B`_ in the HTTP request entity-body:
+
+ :param body: Existing request body (URL encoded string) to embed parameters
+ into. This may contain extra parameters. Default ''.
+ :param scope: The scope of the access request as described by
+ `Section 3.3`_.
+
+ :param include_client_id: `True` to send the `client_id` in the
+ body of the upstream request. This is required
+ if the client is not authenticating with the
+ authorization server as described in
+ `Section 3.2.1`_. False otherwise (default).
+ :type include_client_id: Boolean
+
+ :param kwargs: Extra credentials to include in the token request.
+
+ The client MUST authenticate with the authorization server as
+ described in `Section 3.2.1`_.
+
+ The prepared body will include all provided credentials as well as
+ the ``grant_type`` parameter set to ``client_credentials``::
+
+ >>> from oauthlib.oauth2 import BackendApplicationClient
+ >>> client = BackendApplicationClient('your_id')
+ >>> client.prepare_request_body(scope=['hello', 'world'])
+ 'grant_type=client_credentials&scope=hello+world'
+
+ .. _`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
+ """
+ kwargs['client_id'] = self.client_id
+ kwargs['include_client_id'] = include_client_id
+ scope = self.scope if scope is None else scope
+ return prepare_token_request(self.grant_type, body=body,
+ scope=scope, **kwargs)
diff --git a/game/python-extra/oauthlib/oauth2/rfc6749/clients/base.py b/game/python-extra/oauthlib/oauth2/rfc6749/clients/base.py
new file mode 100644
index 0000000..d5eb0cc
--- /dev/null
+++ b/game/python-extra/oauthlib/oauth2/rfc6749/clients/base.py
@@ -0,0 +1,604 @@
+# -*- coding: utf-8 -*-
+"""
+oauthlib.oauth2.rfc6749
+~~~~~~~~~~~~~~~~~~~~~~~
+
+This module is an implementation of various logic needed
+for consuming OAuth 2.0 RFC6749.
+"""
+import base64
+import hashlib
+import re
+import secrets
+import time
+import warnings
+
+from oauthlib.common import generate_token
+from oauthlib.oauth2.rfc6749 import tokens
+from oauthlib.oauth2.rfc6749.errors import (
+ InsecureTransportError, TokenExpiredError,
+)
+from oauthlib.oauth2.rfc6749.parameters import (
+ parse_token_response, prepare_token_request,
+ prepare_token_revocation_request,
+)
+from oauthlib.oauth2.rfc6749.utils import is_secure_transport
+
+AUTH_HEADER = 'auth_header'
+URI_QUERY = 'query'
+BODY = 'body'
+
+FORM_ENC_HEADERS = {
+ 'Content-Type': 'application/x-www-form-urlencoded'
+}
+
+
+class Client:
+ """Base OAuth2 client responsible for access token management.
+
+ This class also acts as a generic interface providing methods common to all
+ client types such as ``prepare_authorization_request`` and
+ ``prepare_token_revocation_request``. The ``prepare_x_request`` methods are
+ the recommended way of interacting with clients (as opposed to the abstract
+ prepare uri/body/etc methods). They are recommended over the older set
+ because they are easier to use (more consistent) and add a few additional
+ security checks, such as HTTPS and state checking.
+
+ Some of these methods require further implementation only provided by the
+ specific purpose clients such as
+ :py:class:`oauthlib.oauth2.MobileApplicationClient` and thus you should always
+ seek to use the client class matching the OAuth workflow you need. For
+ Python, this is usually :py:class:`oauthlib.oauth2.WebApplicationClient`.
+
+ """
+ refresh_token_key = 'refresh_token'
+
+ def __init__(self, client_id,
+ default_token_placement=AUTH_HEADER,
+ token_type='Bearer',
+ access_token=None,
+ refresh_token=None,
+ mac_key=None,
+ mac_algorithm=None,
+ token=None,
+ scope=None,
+ state=None,
+ redirect_url=None,
+ state_generator=generate_token,
+ code_verifier=None,
+ code_challenge=None,
+ code_challenge_method=None,
+ **kwargs):
+ """Initialize a client with commonly used attributes.
+
+ :param client_id: Client identifier given by the OAuth provider upon
+ registration.
+
+ :param default_token_placement: Tokens can be supplied in the Authorization
+ header (default), the URL query component (``query``) or the request
+ body (``body``).
+
+ :param token_type: OAuth 2 token type. Defaults to Bearer. Change this
+ if you specify the ``access_token`` parameter and know it is of a
+ different token type, such as a MAC, JWT or SAML token. Can
+ also be supplied as ``token_type`` inside the ``token`` dict parameter.
+
+ :param access_token: An access token (string) used to authenticate
+ requests to protected resources. Can also be supplied inside the
+ ``token`` dict parameter.
+
+ :param refresh_token: A refresh token (string) used to refresh expired
+ tokens. Can also be supplied inside the ``token`` dict parameter.
+
+ :param mac_key: Encryption key used with MAC tokens.
+
+ :param mac_algorithm: Hashing algorithm for MAC tokens.
+
+ :param token: A dict of token attributes such as ``access_token``,
+ ``token_type`` and ``expires_at``.
+
+ :param scope: A list of default scopes to request authorization for.
+
+ :param state: A CSRF protection string used during authorization.
+
+ :param redirect_url: The redirection endpoint on the client side to which
+ the user returns after authorization.
+
+ :param state_generator: A no argument state generation callable. Defaults
+ to :py:meth:`oauthlib.common.generate_token`.
+
+ :param code_verifier: PKCE parameter. A cryptographically random string that is used to correlate the
+ authorization request to the token request.
+
+ :param code_challenge: PKCE parameter. A challenge derived from the code verifier that is sent in the
+ authorization request, to be verified against later.
+
+ :param code_challenge_method: PKCE parameter. A method that was used to derive code challenge.
+ Defaults to "plain" if not present in the request.
+ """
+
+ self.client_id = client_id
+ self.default_token_placement = default_token_placement
+ self.token_type = token_type
+ self.access_token = access_token
+ self.refresh_token = refresh_token
+ self.mac_key = mac_key
+ self.mac_algorithm = mac_algorithm
+ self.token = token or {}
+ self.scope = scope
+ self.state_generator = state_generator
+ self.state = state
+ self.redirect_url = redirect_url
+ self.code_verifier = code_verifier
+ self.code_challenge = code_challenge
+ self.code_challenge_method = code_challenge_method
+ self.code = None
+ self.expires_in = None
+ self._expires_at = None
+ self.populate_token_attributes(self.token)
+
+ @property
+ def token_types(self):
+ """Supported token types and their respective methods
+
+ Additional tokens can be supported by extending this dictionary.
+
+ The Bearer token spec is stable and safe to use.
+
+ The MAC token spec is not yet stable and support for MAC tokens
+ is experimental and currently matching version 00 of the spec.
+ """
+ return {
+ 'Bearer': self._add_bearer_token,
+ 'MAC': self._add_mac_token
+ }
+
+ def prepare_request_uri(self, *args, **kwargs):
+ """Abstract method used to create request URIs."""
+ raise NotImplementedError("Must be implemented by inheriting classes.")
+
+ def prepare_request_body(self, *args, **kwargs):
+ """Abstract method used to create request bodies."""
+ raise NotImplementedError("Must be implemented by inheriting classes.")
+
+ 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):
+ """Add token to the request uri, body or authorization header.
+
+ The access token type provides the client with the information
+ required to successfully utilize the access token to make a protected
+ resource request (along with type-specific attributes). The client
+ MUST NOT use an access token if it does not understand the token
+ type.
+
+ For example, the "bearer" token type defined in
+ [`I-D.ietf-oauth-v2-bearer`_] is utilized by simply including the access
+ token string in the request:
+
+ .. code-block:: http
+
+ GET /resource/1 HTTP/1.1
+ Host: example.com
+ Authorization: Bearer mF_9.B5f-4.1JqM
+
+ while the "mac" token type defined in [`I-D.ietf-oauth-v2-http-mac`_] is
+ utilized by issuing a MAC key together with the access token which is
+ used to sign certain components of the HTTP requests:
+
+ .. code-block:: http
+
+ GET /resource/1 HTTP/1.1
+ Host: example.com
+ Authorization: MAC id="h480djs93hd8",
+ nonce="274312:dj83hs9s",
+ mac="kDZvddkndxvhGRXZhvuDjEWhGeE="
+
+ .. _`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()
+
+ token_placement = token_placement or self.default_token_placement
+
+ case_insensitive_token_types = {
+ k.lower(): v for k, v in self.token_types.items()}
+ 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 or self.token.get('access_token')):
+ raise ValueError("Missing access token.")
+
+ if self._expires_at and self._expires_at < time.time():
+ raise TokenExpiredError()
+
+ return case_insensitive_token_types[self.token_type.lower()](uri, http_method, body,
+ headers, token_placement, **kwargs)
+
+ def prepare_authorization_request(self, authorization_url, state=None,
+ redirect_url=None, scope=None, **kwargs):
+ """Prepare the authorization request.
+
+ This is the first step in many OAuth flows in which the user is
+ redirected to a certain authorization URL. This method adds
+ required parameters to the authorization URL.
+
+ :param authorization_url: Provider authorization endpoint URL.
+ :param state: CSRF protection string. Will be automatically created if
+ not provided. The generated state is available via the ``state``
+ attribute. Clients should verify that the state is unchanged and
+ present in the authorization response. This verification is done
+ automatically if using the ``authorization_response`` parameter
+ with ``prepare_token_request``.
+ :param redirect_url: Redirect URL to which the user will be returned
+ after authorization. Must be provided unless previously setup with
+ the provider. If provided then it must also be provided in the
+ token request.
+ :param scope: List of scopes to request. Must be equal to
+ or a subset of the scopes granted when obtaining the refresh
+ token. If none is provided, the ones provided in the constructor are
+ used.
+ :param kwargs: Additional parameters to included in the request.
+ :returns: The prepared request tuple with (url, headers, body).
+ """
+ if not is_secure_transport(authorization_url):
+ raise InsecureTransportError()
+
+ self.state = state or self.state_generator()
+ self.redirect_url = redirect_url or self.redirect_url
+ # do not assign scope to self automatically anymore
+ scope = self.scope if scope is None else scope
+ auth_url = self.prepare_request_uri(
+ authorization_url, redirect_uri=self.redirect_url,
+ scope=scope, state=self.state, **kwargs)
+ return auth_url, FORM_ENC_HEADERS, ''
+
+ def prepare_token_request(self, token_url, authorization_response=None,
+ redirect_url=None, state=None, body='', **kwargs):
+ """Prepare a token creation request.
+
+ Note that these requests usually require client authentication, either
+ by including client_id or a set of provider specific authentication
+ credentials.
+
+ :param token_url: Provider token creation endpoint URL.
+ :param authorization_response: The full redirection URL string, i.e.
+ the location to which the user was redirected after successful
+ authorization. Used to mine credentials needed to obtain a token
+ in this step, such as authorization code.
+ :param redirect_url: The redirect_url supplied with the authorization
+ request (if there was one).
+ :param state:
+ :param body: Existing request body (URL encoded string) to embed parameters
+ into. This may contain extra parameters. Default ''.
+ :param kwargs: Additional parameters to included in the request.
+ :returns: The prepared request tuple with (url, headers, body).
+ """
+ if not is_secure_transport(token_url):
+ raise InsecureTransportError()
+
+ state = state or self.state
+ if authorization_response:
+ self.parse_request_uri_response(
+ authorization_response, state=state)
+ self.redirect_url = redirect_url or self.redirect_url
+ body = self.prepare_request_body(body=body,
+ redirect_uri=self.redirect_url, **kwargs)
+
+ return token_url, FORM_ENC_HEADERS, body
+
+ def prepare_refresh_token_request(self, token_url, refresh_token=None,
+ body='', scope=None, **kwargs):
+ """Prepare an access token refresh request.
+
+ Expired access tokens can be replaced by new access tokens without
+ going through the OAuth dance if the client obtained a refresh token.
+ This refresh token and authentication credentials can be used to
+ obtain a new access token, and possibly a new refresh token.
+
+ :param token_url: Provider token refresh endpoint URL.
+ :param refresh_token: Refresh token string.
+ :param body: Existing request body (URL encoded string) to embed parameters
+ into. This may contain extra parameters. Default ''.
+ :param scope: List of scopes to request. Must be equal to
+ or a subset of the scopes granted when obtaining the refresh
+ token. If none is provided, the ones provided in the constructor are
+ used.
+ :param kwargs: Additional parameters to included in the request.
+ :returns: The prepared request tuple with (url, headers, body).
+ """
+ if not is_secure_transport(token_url):
+ raise InsecureTransportError()
+
+ # do not assign scope to self automatically anymore
+ scope = self.scope if scope is None else scope
+ body = self.prepare_refresh_body(body=body,
+ refresh_token=refresh_token, scope=scope, **kwargs)
+ return token_url, FORM_ENC_HEADERS, body
+
+ def prepare_token_revocation_request(self, revocation_url, token,
+ token_type_hint="access_token", body='', callback=None, **kwargs):
+ """Prepare a token revocation request.
+
+ :param revocation_url: Provider token revocation endpoint URL.
+ :param token: The access or refresh token to be revoked (string).
+ :param token_type_hint: ``"access_token"`` (default) or
+ ``"refresh_token"``. This is optional and if you wish to not pass it you
+ must provide ``token_type_hint=None``.
+ :param body:
+ :param callback: A jsonp callback such as ``package.callback`` to be invoked
+ upon receiving the response. Not that it should not include a () suffix.
+ :param kwargs: Additional parameters to included in the request.
+ :returns: The prepared request tuple with (url, headers, body).
+
+ Note that JSONP request may use GET requests as the parameters will
+ be added to the request URL query as opposed to the request body.
+
+ An example of a revocation request
+
+ .. code-block:: http
+
+ POST /revoke HTTP/1.1
+ Host: server.example.com
+ Content-Type: application/x-www-form-urlencoded
+ Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
+
+ token=45ghiukldjahdnhzdauz&token_type_hint=refresh_token
+
+ An example of a jsonp revocation request
+
+ .. code-block:: http
+
+ GET /revoke?token=agabcdefddddafdd&callback=package.myCallback HTTP/1.1
+ Host: server.example.com
+ Content-Type: application/x-www-form-urlencoded
+ Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
+
+ and an error response
+
+ .. code-block:: javascript
+
+ package.myCallback({"error":"unsupported_token_type"});
+
+ Note that these requests usually require client credentials, client_id in
+ the case for public clients and provider specific authentication
+ credentials for confidential clients.
+ """
+ if not is_secure_transport(revocation_url):
+ raise InsecureTransportError()
+
+ return prepare_token_revocation_request(revocation_url, token,
+ token_type_hint=token_type_hint, body=body, callback=callback,
+ **kwargs)
+
+ def parse_request_body_response(self, body, scope=None, **kwargs):
+ """Parse the JSON response body.
+
+ If the access token request is valid and authorized, the
+ authorization server issues an access token as described in
+ `Section 5.1`_. A refresh token SHOULD NOT be included. If the request
+ failed client authentication or is invalid, the authorization server
+ returns an error response as described in `Section 5.2`_.
+
+ :param body: The response body from the token request.
+ :param scope: Scopes originally requested. If none is provided, the ones
+ provided in the constructor are used.
+ :return: Dictionary of token parameters.
+ :raises: Warning if scope has changed. :py:class:`oauthlib.oauth2.errors.OAuth2Error`
+ if response is invalid.
+
+ These response are json encoded and could easily be parsed without
+ the assistance of OAuthLib. However, there are a few subtle issues
+ to be aware of regarding the response which are helpfully addressed
+ through the raising of various errors.
+
+ A successful response should always contain
+
+ **access_token**
+ The access token issued by the authorization server. Often
+ a random string.
+
+ **token_type**
+ The type of the token issued as described in `Section 7.1`_.
+ Commonly ``Bearer``.
+
+ While it is not mandated it is recommended that the provider include
+
+ **expires_in**
+ The lifetime in seconds of the access token. For
+ example, the value "3600" denotes that the access token will
+ expire in one hour from the time the response was generated.
+ If omitted, the authorization server SHOULD provide the
+ expiration time via other means or document the default value.
+
+ **scope**
+ Providers may supply this in all responses but are required to only
+ if it has changed since the authorization request.
+
+ .. _`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
+ """
+ scope = self.scope if scope is None else scope
+ self.token = parse_token_response(body, scope=scope)
+ self.populate_token_attributes(self.token)
+ return self.token
+
+ def prepare_refresh_body(self, body='', refresh_token=None, scope=None, **kwargs):
+ """Prepare an access token request, using a refresh token.
+
+ If the authorization server issued a refresh token to the client, the
+ client makes a refresh request to the token endpoint by adding the
+ following parameters using the `application/x-www-form-urlencoded`
+ format in the HTTP request entity-body:
+
+ :param refresh_token: REQUIRED. The refresh token issued to the client.
+ :param scope: OPTIONAL. The scope of the access request as described by
+ Section 3.3. The requested scope MUST NOT include any scope
+ not originally granted by the resource owner, and if omitted is
+ treated as equal to the scope originally granted by the
+ resource owner. Note that if none is provided, the ones provided
+ in the constructor are used if any.
+ """
+ refresh_token = refresh_token or self.refresh_token
+ scope = self.scope if scope is None else scope
+ return prepare_token_request(self.refresh_token_key, body=body, scope=scope,
+ refresh_token=refresh_token, **kwargs)
+
+ def _add_bearer_token(self, uri, http_method='GET', body=None,
+ headers=None, token_placement=None):
+ """Add a bearer token to the request uri, body or authorization header."""
+ if token_placement == AUTH_HEADER:
+ headers = tokens.prepare_bearer_headers(self.access_token, headers)
+
+ elif token_placement == URI_QUERY:
+ uri = tokens.prepare_bearer_uri(self.access_token, uri)
+
+ elif token_placement == BODY:
+ body = tokens.prepare_bearer_body(self.access_token, body)
+
+ else:
+ raise ValueError("Invalid token placement.")
+ return uri, headers, body
+
+ def create_code_verifier(self, length):
+ """Create PKCE **code_verifier** used in computing **code_challenge**.
+ See `RFC7636 Section 4.1`_
+
+ :param length: REQUIRED. The length of the code_verifier.
+
+ The client first creates a code verifier, "code_verifier", for each
+ OAuth 2.0 [RFC6749] Authorization Request, in the following manner:
+
+ .. code-block:: text
+
+ code_verifier = high-entropy cryptographic random STRING using the
+ unreserved characters [A-Z] / [a-z] / [0-9] / "-" / "." / "_" / "~"
+ from Section 2.3 of [RFC3986], with a minimum length of 43 characters
+ and a maximum length of 128 characters.
+
+ .. _`RFC7636 Section 4.1`: https://tools.ietf.org/html/rfc7636#section-4.1
+ """
+ code_verifier = None
+
+ if not length >= 43:
+ raise ValueError("Length must be greater than or equal to 43")
+
+ if not length <= 128:
+ raise ValueError("Length must be less than or equal to 128")
+
+ allowed_characters = re.compile('^[A-Zaa-z0-9-._~]')
+ code_verifier = secrets.token_urlsafe(length)
+
+ if not re.search(allowed_characters, code_verifier):
+ raise ValueError("code_verifier contains invalid characters")
+
+ self.code_verifier = code_verifier
+
+ return code_verifier
+
+ def create_code_challenge(self, code_verifier, code_challenge_method=None):
+ """Create PKCE **code_challenge** derived from the **code_verifier**.
+ See `RFC7636 Section 4.2`_
+
+ :param code_verifier: REQUIRED. The **code_verifier** generated from `create_code_verifier()`.
+ :param code_challenge_method: OPTIONAL. The method used to derive the **code_challenge**. Acceptable values include `S256`. DEFAULT is `plain`.
+
+ The client then creates a code challenge derived from the code
+ verifier by using one of the following transformations on the code
+ verifier::
+
+ plain
+ code_challenge = code_verifier
+ S256
+ code_challenge = BASE64URL-ENCODE(SHA256(ASCII(code_verifier)))
+
+ If the client is capable of using `S256`, it MUST use `S256`, as
+ `S256` is Mandatory To Implement (MTI) on the server. Clients are
+ permitted to use `plain` only if they cannot support `S256` for some
+ technical reason and know via out-of-band configuration that the
+ server supports `plain`.
+
+ The plain transformation is for compatibility with existing
+ deployments and for constrained environments that can't use the S256 transformation.
+
+ .. _`RFC7636 Section 4.2`: https://tools.ietf.org/html/rfc7636#section-4.2
+ """
+ code_challenge = None
+
+ if code_verifier == None:
+ raise ValueError("Invalid code_verifier")
+
+ if code_challenge_method == None:
+ code_challenge_method = "plain"
+ self.code_challenge_method = code_challenge_method
+ code_challenge = code_verifier
+ self.code_challenge = code_challenge
+
+ if code_challenge_method == "S256":
+ h = hashlib.sha256()
+ h.update(code_verifier.encode(encoding='ascii'))
+ sha256_val = h.digest()
+ code_challenge = bytes.decode(base64.urlsafe_b64encode(sha256_val))
+ # replace '+' with '-', '/' with '_', and remove trailing '='
+ code_challenge = code_challenge.replace("+", "-").replace("/", "_").replace("=", "")
+ self.code_challenge = code_challenge
+
+ return code_challenge
+
+ def _add_mac_token(self, uri, http_method='GET', body=None,
+ headers=None, token_placement=AUTH_HEADER, ext=None, **kwargs):
+ """Add a MAC token to the request authorization header.
+
+ Warning: MAC token support is experimental as the spec is not yet stable.
+ """
+ if token_placement != AUTH_HEADER:
+ raise ValueError("Invalid token placement.")
+
+ headers = tokens.prepare_mac_header(self.access_token, uri,
+ self.mac_key, http_method, headers=headers, body=body, ext=ext,
+ hash_algorithm=self.mac_algorithm, **kwargs)
+ return uri, headers, body
+
+ 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):
+ """Add attributes from a token exchange response to self."""
+
+ if 'access_token' in response:
+ self.access_token = response.get('access_token')
+
+ if 'refresh_token' in response:
+ self.refresh_token = response.get('refresh_token')
+
+ if 'token_type' in response:
+ self.token_type = response.get('token_type')
+
+ if 'expires_in' in response:
+ self.expires_in = response.get('expires_in')
+ self._expires_at = time.time() + int(self.expires_in)
+
+ if 'expires_at' in response:
+ try:
+ self._expires_at = int(response.get('expires_at'))
+ except:
+ self._expires_at = None
+
+ if 'mac_key' in response:
+ self.mac_key = response.get('mac_key')
+
+ if 'mac_algorithm' in response:
+ self.mac_algorithm = response.get('mac_algorithm')
diff --git a/game/python-extra/oauthlib/oauth2/rfc6749/clients/legacy_application.py b/game/python-extra/oauthlib/oauth2/rfc6749/clients/legacy_application.py
new file mode 100644
index 0000000..9920981
--- /dev/null
+++ b/game/python-extra/oauthlib/oauth2/rfc6749/clients/legacy_application.py
@@ -0,0 +1,84 @@
+# -*- coding: utf-8 -*-
+"""
+oauthlib.oauth2.rfc6749
+~~~~~~~~~~~~~~~~~~~~~~~
+
+This module is an implementation of various logic needed
+for consuming and providing OAuth 2.0 RFC6749.
+"""
+from ..parameters import prepare_token_request
+from .base import Client
+
+
+class LegacyApplicationClient(Client):
+
+ """A public client using the resource owner password and username directly.
+
+ The resource owner password credentials grant type is suitable in
+ cases where the resource owner has a trust relationship with the
+ client, such as the device operating system or a highly privileged
+ application. The authorization server should take special care when
+ enabling this grant type, and only allow it when other flows are not
+ viable.
+
+ The grant type is suitable for clients capable of obtaining the
+ resource owner's credentials (username and password, typically using
+ an interactive form). It is also used to migrate existing clients
+ using direct authentication schemes such as HTTP Basic or Digest
+ authentication to OAuth by converting the stored credentials to an
+ access token.
+
+ The method through which the client obtains the resource owner
+ credentials is beyond the scope of this specification. The client
+ MUST discard the credentials once an access token has been obtained.
+ """
+
+ grant_type = 'password'
+
+ def __init__(self, client_id, **kwargs):
+ super().__init__(client_id, **kwargs)
+
+ def prepare_request_body(self, username, password, body='', scope=None,
+ include_client_id=False, **kwargs):
+ """Add the resource owner password and username to the request body.
+
+ The client makes a request to the token endpoint by adding the
+ following parameters using the "application/x-www-form-urlencoded"
+ format per `Appendix B`_ in the HTTP request entity-body:
+
+ :param username: The resource owner username.
+ :param password: The resource owner password.
+ :param body: Existing request body (URL encoded string) to embed parameters
+ into. This may contain extra parameters. Default ''.
+ :param scope: The scope of the access request as described by
+ `Section 3.3`_.
+ :param include_client_id: `True` to send the `client_id` in the
+ body of the upstream request. This is required
+ if the client is not authenticating with the
+ authorization server as described in
+ `Section 3.2.1`_. False otherwise (default).
+ :type include_client_id: Boolean
+ :param kwargs: Extra credentials to include in the token request.
+
+ If the client type is confidential or 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`_.
+
+ The prepared body will include all provided credentials as well as
+ the ``grant_type`` parameter set to ``password``::
+
+ >>> from oauthlib.oauth2 import LegacyApplicationClient
+ >>> client = LegacyApplicationClient('your_id')
+ >>> client.prepare_request_body(username='foo', password='bar', scope=['hello', 'world'])
+ 'grant_type=password&username=foo&scope=hello+world&password=bar'
+
+ .. _`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
+ """
+ kwargs['client_id'] = self.client_id
+ kwargs['include_client_id'] = include_client_id
+ scope = self.scope if scope is None else scope
+ return prepare_token_request(self.grant_type, body=body, username=username,
+ password=password, scope=scope, **kwargs)
diff --git a/game/python-extra/oauthlib/oauth2/rfc6749/clients/mobile_application.py b/game/python-extra/oauthlib/oauth2/rfc6749/clients/mobile_application.py
new file mode 100644
index 0000000..b10b41c
--- /dev/null
+++ b/game/python-extra/oauthlib/oauth2/rfc6749/clients/mobile_application.py
@@ -0,0 +1,174 @@
+# -*- coding: utf-8 -*-
+"""
+oauthlib.oauth2.rfc6749
+~~~~~~~~~~~~~~~~~~~~~~~
+
+This module is an implementation of various logic needed
+for consuming and providing OAuth 2.0 RFC6749.
+"""
+from ..parameters import parse_implicit_response, prepare_grant_uri
+from .base import Client
+
+
+class MobileApplicationClient(Client):
+
+ """A public client utilizing the implicit code grant workflow.
+
+ A user-agent-based application is a public client in which the
+ client code is downloaded from a web server and executes within a
+ user-agent (e.g. web browser) on the device used by the resource
+ owner. Protocol data and credentials are easily accessible (and
+ often visible) to the resource owner. Since such applications
+ reside within the user-agent, they can make seamless use of the
+ user-agent capabilities when requesting authorization.
+
+ The implicit grant type is used to obtain access tokens (it does not
+ support the issuance of refresh tokens) and is optimized for public
+ clients known to operate a particular redirection URI. These clients
+ are typically implemented in a browser using a scripting language
+ such as JavaScript.
+
+ As a redirection-based flow, the client must be capable of
+ interacting with the resource owner's user-agent (typically a web
+ browser) and capable of receiving incoming requests (via redirection)
+ from the authorization server.
+
+ Unlike the authorization code grant type in which the client makes
+ separate requests for authorization and access token, the client
+ receives the access token as the result of the authorization request.
+
+ The implicit grant type does not include client authentication, and
+ relies on the presence of the resource owner and the registration of
+ the redirection URI. Because the access token is encoded into the
+ redirection URI, it may be exposed to the resource owner and other
+ applications residing on the same device.
+ """
+
+ response_type = 'token'
+
+ def prepare_request_uri(self, uri, redirect_uri=None, scope=None,
+ state=None, **kwargs):
+ """Prepare the implicit grant request URI.
+
+ The client constructs the request URI by adding the following
+ parameters to the query component of the authorization endpoint URI
+ using the "application/x-www-form-urlencoded" format, per `Appendix B`_:
+
+ :param redirect_uri: OPTIONAL. The redirect URI must be an absolute URI
+ and it should have been registered with the OAuth
+ provider prior to use. As described in `Section 3.1.2`_.
+
+ :param scope: OPTIONAL. The scope of the access request as described by
+ Section 3.3`_. These may be any string but are commonly
+ URIs or various categories such as ``videos`` or ``documents``.
+
+ :param state: RECOMMENDED. An opaque value used by the client to maintain
+ state between the request and callback. The authorization
+ server includes this value when redirecting the user-agent back
+ to the client. The parameter SHOULD be used for preventing
+ cross-site request forgery as described in `Section 10.12`_.
+
+ :param kwargs: Extra arguments to include in the request URI.
+
+ In addition to supplied parameters, OAuthLib will append the ``client_id``
+ that was provided in the constructor as well as the mandatory ``response_type``
+ argument, set to ``token``::
+
+ >>> from oauthlib.oauth2 import MobileApplicationClient
+ >>> client = MobileApplicationClient('your_id')
+ >>> client.prepare_request_uri('https://example.com')
+ 'https://example.com?client_id=your_id&response_type=token'
+ >>> client.prepare_request_uri('https://example.com', redirect_uri='https://a.b/callback')
+ 'https://example.com?client_id=your_id&response_type=token&redirect_uri=https%3A%2F%2Fa.b%2Fcallback'
+ >>> client.prepare_request_uri('https://example.com', scope=['profile', 'pictures'])
+ 'https://example.com?client_id=your_id&response_type=token&scope=profile+pictures'
+ >>> client.prepare_request_uri('https://example.com', foo='bar')
+ 'https://example.com?client_id=your_id&response_type=token&foo=bar'
+
+ .. _`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
+ """
+ scope = self.scope if scope is None else scope
+ return prepare_grant_uri(uri, self.client_id, self.response_type,
+ redirect_uri=redirect_uri, state=state, scope=scope, **kwargs)
+
+ def parse_request_uri_response(self, uri, state=None, scope=None):
+ """Parse the response URI fragment.
+
+ If the resource owner grants the access request, the authorization
+ server issues an access token and delivers it to the client by adding
+ the following parameters to the fragment component of the redirection
+ URI using the "application/x-www-form-urlencoded" format:
+
+ :param uri: The callback URI that resulted from the user being redirected
+ back from the provider to you, the client.
+ :param state: The state provided in the authorization request.
+ :param scope: The scopes provided in the authorization request.
+ :return: Dictionary of token parameters.
+ :raises: OAuth2Error if response is invalid.
+
+ A successful response should always contain
+
+ **access_token**
+ The access token issued by the authorization server. Often
+ a random string.
+
+ **token_type**
+ The type of the token issued as described in `Section 7.1`_.
+ Commonly ``Bearer``.
+
+ **state**
+ If you provided the state parameter in the authorization phase, then
+ the provider is required to include that exact state value in the
+ response.
+
+ While it is not mandated it is recommended that the provider include
+
+ **expires_in**
+ The lifetime in seconds of the access token. For
+ example, the value "3600" denotes that the access token will
+ expire in one hour from the time the response was generated.
+ If omitted, the authorization server SHOULD provide the
+ expiration time via other means or document the default value.
+
+ **scope**
+ Providers may supply this in all responses but are required to only
+ if it has changed since the authorization request.
+
+ A few example responses can be seen below::
+
+ >>> response_uri = 'https://example.com/callback#access_token=sdlfkj452&state=ss345asyht&token_type=Bearer&scope=hello+world'
+ >>> from oauthlib.oauth2 import MobileApplicationClient
+ >>> client = MobileApplicationClient('your_id')
+ >>> client.parse_request_uri_response(response_uri)
+ {
+ 'access_token': 'sdlfkj452',
+ 'token_type': 'Bearer',
+ 'state': 'ss345asyht',
+ 'scope': [u'hello', u'world']
+ }
+ >>> client.parse_request_uri_response(response_uri, state='other')
+ Traceback (most recent call last):
+ File "<stdin>", line 1, in <module>
+ File "oauthlib/oauth2/rfc6749/__init__.py", line 598, in parse_request_uri_response
+ **scope**
+ File "oauthlib/oauth2/rfc6749/parameters.py", line 197, in parse_implicit_response
+ raise ValueError("Mismatching or missing state in params.")
+ ValueError: Mismatching or missing state in params.
+ >>> def alert_scope_changed(message, old, new):
+ ... print(message, old, new)
+ ...
+ >>> oauthlib.signals.scope_changed.connect(alert_scope_changed)
+ >>> client.parse_request_body_response(response_body, scope=['other'])
+ ('Scope has changed from "other" to "hello world".', ['other'], ['hello', 'world'])
+
+ .. _`Section 7.1`: https://tools.ietf.org/html/rfc6749#section-7.1
+ .. _`Section 3.3`: https://tools.ietf.org/html/rfc6749#section-3.3
+ """
+ scope = self.scope if scope is None else scope
+ self.token = parse_implicit_response(uri, state=state, scope=scope)
+ self.populate_token_attributes(self.token)
+ return self.token
diff --git a/game/python-extra/oauthlib/oauth2/rfc6749/clients/service_application.py b/game/python-extra/oauthlib/oauth2/rfc6749/clients/service_application.py
new file mode 100644
index 0000000..8fb1737
--- /dev/null
+++ b/game/python-extra/oauthlib/oauth2/rfc6749/clients/service_application.py
@@ -0,0 +1,189 @@
+# -*- coding: utf-8 -*-
+"""
+oauthlib.oauth2.rfc6749
+~~~~~~~~~~~~~~~~~~~~~~~
+
+This module is an implementation of various logic needed
+for consuming and providing OAuth 2.0 RFC6749.
+"""
+import time
+
+from oauthlib.common import to_unicode
+
+from ..parameters import prepare_token_request
+from .base import Client
+
+
+class ServiceApplicationClient(Client):
+ """A public client utilizing the JWT bearer grant.
+
+ JWT bearer tokes can be used to request an access token when a client
+ wishes to utilize an existing trust relationship, expressed through the
+ semantics of (and digital signature or keyed message digest calculated
+ over) the JWT, without a direct user approval step at the authorization
+ server.
+
+ This grant type does not involve an authorization step. It may be
+ used by both public and confidential clients.
+ """
+
+ grant_type = 'urn:ietf:params:oauth:grant-type:jwt-bearer'
+
+ def __init__(self, client_id, private_key=None, subject=None, issuer=None,
+ audience=None, **kwargs):
+ """Initialize a JWT client with defaults for implicit use later.
+
+ :param client_id: Client identifier given by the OAuth provider upon
+ registration.
+
+ :param private_key: Private key used for signing and encrypting.
+ Must be given as a string.
+
+ :param subject: The principal that is the subject of the JWT, i.e.
+ which user is the token requested on behalf of.
+ For example, ``foo@example.com.
+
+ :param issuer: The JWT MUST contain an "iss" (issuer) claim that
+ contains a unique identifier for the entity that issued
+ the JWT. For example, ``your-client@provider.com``.
+
+ :param audience: A value identifying the authorization server as an
+ intended audience, e.g.
+ ``https://provider.com/oauth2/token``.
+
+ :param kwargs: Additional arguments to pass to base client, such as
+ state and token. See ``Client.__init__.__doc__`` for
+ details.
+ """
+ super().__init__(client_id, **kwargs)
+ self.private_key = private_key
+ self.subject = subject
+ self.issuer = issuer
+ self.audience = audience
+
+ def prepare_request_body(self,
+ private_key=None,
+ subject=None,
+ issuer=None,
+ audience=None,
+ expires_at=None,
+ issued_at=None,
+ extra_claims=None,
+ body='',
+ scope=None,
+ include_client_id=False,
+ **kwargs):
+ """Create and add a JWT assertion to the request body.
+
+ :param private_key: Private key used for signing and encrypting.
+ Must be given as a string.
+
+ :param subject: (sub) The principal that is the subject of the JWT,
+ i.e. which user is the token requested on behalf of.
+ For example, ``foo@example.com.
+
+ :param issuer: (iss) The JWT MUST contain an "iss" (issuer) claim that
+ contains a unique identifier for the entity that issued
+ the JWT. For example, ``your-client@provider.com``.
+
+ :param audience: (aud) A value identifying the authorization server as an
+ intended audience, e.g.
+ ``https://provider.com/oauth2/token``.
+
+ :param expires_at: A unix expiration timestamp for the JWT. Defaults
+ to an hour from now, i.e. ``time.time() + 3600``.
+
+ :param issued_at: A unix timestamp of when the JWT was created.
+ Defaults to now, i.e. ``time.time()``.
+
+ :param extra_claims: A dict of additional claims to include in the JWT.
+
+ :param body: Existing request body (URL encoded string) to embed parameters
+ into. This may contain extra parameters. Default ''.
+
+ :param scope: The scope of the access request.
+
+ :param include_client_id: `True` to send the `client_id` in the
+ body of the upstream request. This is required
+ if the client is not authenticating with the
+ authorization server as described in
+ `Section 3.2.1`_. False otherwise (default).
+ :type include_client_id: Boolean
+
+ :param not_before: A unix timestamp after which the JWT may be used.
+ Not included unless provided. *
+
+ :param jwt_id: A unique JWT token identifier. Not included unless
+ provided. *
+
+ :param kwargs: Extra credentials to include in the token request.
+
+ Parameters marked with a `*` above are not explicit arguments in the
+ function signature, but are specially documented arguments for items
+ appearing in the generic `**kwargs` keyworded input.
+
+ The "scope" parameter may be used, as defined in the Assertion
+ Framework for OAuth 2.0 Client Authentication and Authorization Grants
+ [I-D.ietf-oauth-assertions] specification, to indicate the requested
+ scope.
+
+ Authentication of the client is optional, as described in
+ `Section 3.2.1`_ of OAuth 2.0 [RFC6749] and consequently, the
+ "client_id" is only needed when a form of client authentication that
+ relies on the parameter is used.
+
+ The following non-normative example demonstrates an Access Token
+ Request with a JWT as an authorization grant (with extra line breaks
+ for display purposes only):
+
+ .. code-block: http
+
+ POST /token.oauth2 HTTP/1.1
+ Host: as.example.com
+ Content-Type: application/x-www-form-urlencoded
+
+ grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer
+ &assertion=eyJhbGciOiJFUzI1NiJ9.
+ eyJpc3Mi[...omitted for brevity...].
+ J9l-ZhwP[...omitted for brevity...]
+
+ .. _`Section 3.2.1`: https://tools.ietf.org/html/rfc6749#section-3.2.1
+ """
+ import jwt
+
+ key = private_key or self.private_key
+ if not key:
+ raise ValueError('An encryption key must be supplied to make JWT'
+ ' token requests.')
+ claim = {
+ 'iss': issuer 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()),
+ }
+
+ for attr in ('iss', 'aud', 'sub'):
+ if claim[attr] is None:
+ raise ValueError(
+ 'Claim must include %s but none was given.' % attr)
+
+ if 'not_before' in kwargs:
+ claim['nbf'] = kwargs.pop('not_before')
+
+ if 'jwt_id' in kwargs:
+ claim['jti'] = kwargs.pop('jwt_id')
+
+ claim.update(extra_claims or {})
+
+ assertion = jwt.encode(claim, key, 'RS256')
+ assertion = to_unicode(assertion)
+
+ kwargs['client_id'] = self.client_id
+ kwargs['include_client_id'] = include_client_id
+ scope = self.scope if scope is None else scope
+ return prepare_token_request(self.grant_type,
+ body=body,
+ assertion=assertion,
+ scope=scope,
+ **kwargs)
diff --git a/game/python-extra/oauthlib/oauth2/rfc6749/clients/web_application.py b/game/python-extra/oauthlib/oauth2/rfc6749/clients/web_application.py
new file mode 100644
index 0000000..50890fb
--- /dev/null
+++ b/game/python-extra/oauthlib/oauth2/rfc6749/clients/web_application.py
@@ -0,0 +1,222 @@
+# -*- coding: utf-8 -*-
+"""
+oauthlib.oauth2.rfc6749
+~~~~~~~~~~~~~~~~~~~~~~~
+
+This module is an implementation of various logic needed
+for consuming and providing OAuth 2.0 RFC6749.
+"""
+import warnings
+
+from ..parameters import (
+ parse_authorization_code_response, prepare_grant_uri,
+ prepare_token_request,
+)
+from .base import Client
+
+
+class WebApplicationClient(Client):
+
+ """A client utilizing the authorization code grant workflow.
+
+ A web application is a confidential client running on a web
+ server. Resource owners access the client via an HTML user
+ interface rendered in a user-agent on the device used by the
+ resource owner. The client credentials as well as any access
+ token issued to the client are stored on the web server and are
+ not exposed to or accessible by the resource owner.
+
+ The authorization code grant type is used to obtain both access
+ tokens and refresh tokens and is optimized for confidential clients.
+ As a redirection-based flow, the client must be capable of
+ interacting with the resource owner's user-agent (typically a web
+ browser) and capable of receiving incoming requests (via redirection)
+ from the authorization server.
+ """
+
+ grant_type = 'authorization_code'
+
+ def __init__(self, client_id, code=None, **kwargs):
+ super().__init__(client_id, **kwargs)
+ self.code = code
+
+ def prepare_request_uri(self, uri, redirect_uri=None, scope=None,
+ state=None, code_challenge=None, code_challenge_method='plain', **kwargs):
+ """Prepare the authorization code request URI
+
+ The client constructs the request URI by adding the following
+ parameters to the query component of the authorization endpoint URI
+ using the "application/x-www-form-urlencoded" format, per `Appendix B`_:
+
+ :param redirect_uri: OPTIONAL. The redirect URI must be an absolute URI
+ and it should have been registered with the OAuth
+ provider prior to use. As described in `Section 3.1.2`_.
+
+ :param scope: OPTIONAL. The scope of the access request as described by
+ Section 3.3`_. These may be any string but are commonly
+ URIs or various categories such as ``videos`` or ``documents``.
+
+ :param state: RECOMMENDED. An opaque value used by the client to maintain
+ state between the request and callback. The authorization
+ server includes this value when redirecting the user-agent back
+ to the client. The parameter SHOULD be used for preventing
+ cross-site request forgery as described in `Section 10.12`_.
+
+ :param code_challenge: OPTIONAL. PKCE parameter. REQUIRED if PKCE is enforced.
+ A challenge derived from the code_verifier that is sent in the
+ authorization request, to be verified against later.
+
+ :param code_challenge_method: OPTIONAL. PKCE parameter. A method that was used to derive code challenge.
+ Defaults to "plain" if not present in the request.
+
+ :param kwargs: Extra arguments to include in the request URI.
+
+ In addition to supplied parameters, OAuthLib will append the ``client_id``
+ that was provided in the constructor as well as the mandatory ``response_type``
+ argument, set to ``code``::
+
+ >>> from oauthlib.oauth2 import WebApplicationClient
+ >>> client = WebApplicationClient('your_id')
+ >>> client.prepare_request_uri('https://example.com')
+ 'https://example.com?client_id=your_id&response_type=code'
+ >>> client.prepare_request_uri('https://example.com', redirect_uri='https://a.b/callback')
+ 'https://example.com?client_id=your_id&response_type=code&redirect_uri=https%3A%2F%2Fa.b%2Fcallback'
+ >>> client.prepare_request_uri('https://example.com', scope=['profile', 'pictures'])
+ 'https://example.com?client_id=your_id&response_type=code&scope=profile+pictures'
+ >>> client.prepare_request_uri('https://example.com', code_challenge='kjasBS523KdkAILD2k78NdcJSk2k3KHG6')
+ 'https://example.com?client_id=your_id&response_type=code&code_challenge=kjasBS523KdkAILD2k78NdcJSk2k3KHG6'
+ >>> client.prepare_request_uri('https://example.com', code_challenge_method='S256')
+ 'https://example.com?client_id=your_id&response_type=code&code_challenge_method=S256'
+ >>> client.prepare_request_uri('https://example.com', foo='bar')
+ 'https://example.com?client_id=your_id&response_type=code&foo=bar'
+
+ .. _`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
+ """
+ scope = self.scope if scope is None else scope
+ return prepare_grant_uri(uri, self.client_id, 'code',
+ redirect_uri=redirect_uri, scope=scope, state=state, code_challenge=code_challenge,
+ code_challenge_method=code_challenge_method, **kwargs)
+
+ def prepare_request_body(self, code=None, redirect_uri=None, body='',
+ include_client_id=True, code_verifier=None, **kwargs):
+ """Prepare the access token request body.
+
+ The client makes a request to the token endpoint by adding the
+ following parameters using the "application/x-www-form-urlencoded"
+ format in the HTTP request entity-body:
+
+ :param code: REQUIRED. The authorization code received from the
+ authorization server.
+
+ :param redirect_uri: REQUIRED, if the "redirect_uri" parameter was included in the
+ authorization request as described in `Section 4.1.1`_, and their
+ values MUST be identical.
+
+ :param body: Existing request body (URL encoded string) to embed parameters
+ into. This may contain extra parameters. Default ''.
+
+ :param include_client_id: `True` (default) to send the `client_id` in the
+ body of the upstream request. This is required
+ if the client is not authenticating with the
+ authorization server as described in `Section 3.2.1`_.
+ :type include_client_id: Boolean
+
+ :param code_verifier: OPTIONAL. A cryptographically random string that is used to correlate the
+ authorization request to the token request.
+
+ :param kwargs: Extra parameters to include in the token request.
+
+ In addition OAuthLib will add the ``grant_type`` parameter set to
+ ``authorization_code``.
+
+ If the client type is confidential or 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`_::
+
+ >>> from oauthlib.oauth2 import WebApplicationClient
+ >>> client = WebApplicationClient('your_id')
+ >>> client.prepare_request_body(code='sh35ksdf09sf')
+ 'grant_type=authorization_code&code=sh35ksdf09sf'
+ >>> client.prepare_request_body(code_verifier='KB46DCKJ873NCGXK5GD682NHDKK34GR')
+ 'grant_type=authorization_code&code_verifier=KB46DCKJ873NCGXK5GD682NHDKK34GR'
+ >>> client.prepare_request_body(code='sh35ksdf09sf', foo='bar')
+ 'grant_type=authorization_code&code=sh35ksdf09sf&foo=bar'
+
+ `Section 3.2.1` also states:
+ In the "authorization_code" "grant_type" request to the token
+ endpoint, an unauthenticated client MUST send its "client_id" to
+ prevent itself from inadvertently accepting a code intended for a
+ client with a different "client_id". This protects the client from
+ substitution of the authentication code. (It provides no additional
+ security for the protected resource.)
+
+ .. _`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
+ if 'client_id' in kwargs:
+ warnings.warn("`client_id` has been deprecated in favor of "
+ "`include_client_id`, a boolean value which will "
+ "include the already configured `self.client_id`.",
+ DeprecationWarning)
+ if kwargs['client_id'] != self.client_id:
+ raise ValueError("`client_id` was supplied as an argument, but "
+ "it does not match `self.client_id`")
+
+ kwargs['client_id'] = self.client_id
+ kwargs['include_client_id'] = include_client_id
+ return prepare_token_request(self.grant_type, code=code, body=body,
+ redirect_uri=redirect_uri, code_verifier=code_verifier, **kwargs)
+
+ def parse_request_uri_response(self, uri, state=None):
+ """Parse the URI query for code and state.
+
+ If the resource owner grants the access request, the authorization
+ server issues an authorization code and delivers it to the client by
+ adding the following parameters to the query component of the
+ redirection URI using the "application/x-www-form-urlencoded" format:
+
+ :param uri: The callback URI that resulted from the user being redirected
+ back from the provider to you, the client.
+ :param state: The state provided in the authorization request.
+
+ **code**
+ The authorization code generated by the authorization server.
+ The authorization code MUST expire shortly after it is issued
+ to mitigate the risk of leaks. A maximum authorization code
+ lifetime of 10 minutes is RECOMMENDED. The client MUST NOT
+ use the authorization code more than once. If an authorization
+ code is used more than once, the authorization server MUST deny
+ the request and SHOULD revoke (when possible) all tokens
+ previously issued based on that authorization code.
+ The authorization code is bound to the client identifier and
+ redirection URI.
+
+ **state**
+ If the "state" parameter was present in the authorization request.
+
+ This method is mainly intended to enforce strict state checking with
+ the added benefit of easily extracting parameters from the URI::
+
+ >>> from oauthlib.oauth2 import WebApplicationClient
+ >>> client = WebApplicationClient('your_id')
+ >>> uri = 'https://example.com/callback?code=sdfkjh345&state=sfetw45'
+ >>> client.parse_request_uri_response(uri, state='sfetw45')
+ {'state': 'sfetw45', 'code': 'sdfkjh345'}
+ >>> client.parse_request_uri_response(uri, state='other')
+ Traceback (most recent call last):
+ File "<stdin>", line 1, in <module>
+ File "oauthlib/oauth2/rfc6749/__init__.py", line 357, in parse_request_uri_response
+ back from the provider to you, the client.
+ File "oauthlib/oauth2/rfc6749/parameters.py", line 153, in parse_authorization_code_response
+ raise MismatchingStateError()
+ oauthlib.oauth2.rfc6749.errors.MismatchingStateError
+ """
+ response = parse_authorization_code_response(uri, state=state)
+ self.populate_code_attributes(response)
+ return response
diff --git a/game/python-extra/oauthlib/oauth2/rfc6749/endpoints/__init__.py b/game/python-extra/oauthlib/oauth2/rfc6749/endpoints/__init__.py
new file mode 100644
index 0000000..1695b41
--- /dev/null
+++ b/game/python-extra/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/game/python-extra/oauthlib/oauth2/rfc6749/endpoints/authorization.py b/game/python-extra/oauthlib/oauth2/rfc6749/endpoints/authorization.py
new file mode 100644
index 0000000..7196786
--- /dev/null
+++ b/game/python-extra/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/game/python-extra/oauthlib/oauth2/rfc6749/endpoints/base.py b/game/python-extra/oauthlib/oauth2/rfc6749/endpoints/base.py
new file mode 100644
index 0000000..3f23991
--- /dev/null
+++ b/game/python-extra/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/game/python-extra/oauthlib/oauth2/rfc6749/endpoints/introspect.py b/game/python-extra/oauthlib/oauth2/rfc6749/endpoints/introspect.py
new file mode 100644
index 0000000..3cc61e6
--- /dev/null
+++ b/game/python-extra/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/game/python-extra/oauthlib/oauth2/rfc6749/endpoints/metadata.py b/game/python-extra/oauthlib/oauth2/rfc6749/endpoints/metadata.py
new file mode 100644
index 0000000..a2820f2
--- /dev/null
+++ b/game/python-extra/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/game/python-extra/oauthlib/oauth2/rfc6749/endpoints/pre_configured.py b/game/python-extra/oauthlib/oauth2/rfc6749/endpoints/pre_configured.py
new file mode 100644
index 0000000..d64a166
--- /dev/null
+++ b/game/python-extra/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/game/python-extra/oauthlib/oauth2/rfc6749/endpoints/resource.py b/game/python-extra/oauthlib/oauth2/rfc6749/endpoints/resource.py
new file mode 100644
index 0000000..f756225
--- /dev/null
+++ b/game/python-extra/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/game/python-extra/oauthlib/oauth2/rfc6749/endpoints/revocation.py b/game/python-extra/oauthlib/oauth2/rfc6749/endpoints/revocation.py
new file mode 100644
index 0000000..596d086
--- /dev/null
+++ b/game/python-extra/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/game/python-extra/oauthlib/oauth2/rfc6749/endpoints/token.py b/game/python-extra/oauthlib/oauth2/rfc6749/endpoints/token.py
new file mode 100644
index 0000000..ab9e091
--- /dev/null
+++ b/game/python-extra/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)
diff --git a/game/python-extra/oauthlib/oauth2/rfc6749/errors.py b/game/python-extra/oauthlib/oauth2/rfc6749/errors.py
new file mode 100644
index 0000000..da24fea
--- /dev/null
+++ b/game/python-extra/oauthlib/oauth2/rfc6749/errors.py
@@ -0,0 +1,400 @@
+"""
+oauthlib.oauth2.rfc6749.errors
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Error used both by OAuth 2 clients and providers to represent the spec
+defined error responses for all four core grant types.
+"""
+import json
+
+from oauthlib.common import add_params_to_uri, urlencode
+
+
+class OAuth2Error(Exception):
+ error = None
+ status_code = 400
+ description = ''
+
+ def __init__(self, description=None, uri=None, state=None,
+ status_code=None, request=None):
+ """
+ :param description: A human-readable ASCII [USASCII] text providing
+ additional information, used to assist the client
+ developer in understanding the error that occurred.
+ Values for the "error_description" parameter
+ MUST NOT include characters outside the set
+ x20-21 / x23-5B / x5D-7E.
+
+ :param uri: A URI identifying a human-readable web page with information
+ about the error, used to provide the client developer with
+ additional information about the error. Values for the
+ "error_uri" parameter MUST conform to the URI- Reference
+ syntax, and thus MUST NOT include characters outside the set
+ x21 / x23-5B / x5D-7E.
+
+ :param state: A CSRF protection value received from the client.
+
+ :param status_code:
+
+ :param request: OAuthlib request.
+ :type request: oauthlib.common.Request
+ """
+ if description is not None:
+ self.description = description
+
+ message = '({}) {}'.format(self.error, self.description)
+ if request:
+ message += ' ' + repr(request)
+ super().__init__(message)
+
+ self.uri = uri
+ self.state = state
+
+ if status_code:
+ self.status_code = status_code
+
+ if request:
+ self.redirect_uri = request.redirect_uri
+ self.client_id = request.client_id
+ self.scopes = request.scopes
+ self.response_type = request.response_type
+ self.response_mode = request.response_mode
+ 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):
+ fragment = self.response_mode == "fragment"
+ return add_params_to_uri(uri, self.twotuples, fragment)
+
+ @property
+ def twotuples(self):
+ error = [('error', self.error)]
+ if self.description:
+ error.append(('error_description', self.description))
+ if self.uri:
+ error.append(('error_uri', self.uri))
+ if self.state:
+ error.append(('state', self.state))
+ return error
+
+ @property
+ def urlencoded(self):
+ return urlencode(self.twotuples)
+
+ @property
+ def json(self):
+ return json.dumps(dict(self.twotuples))
+
+ @property
+ def headers(self):
+ if self.status_code == 401:
+ """
+ https://tools.ietf.org/html/rfc6750#section-3
+
+ All challenges defined by this specification MUST use the auth-scheme
+ value "Bearer". This scheme MUST be followed by one or more
+ auth-param values.
+ """
+ authvalues = ['error="{}"'.format(self.error)]
+ if self.description:
+ authvalues.append('error_description="{}"'.format(self.description))
+ if self.uri:
+ authvalues.append('error_uri="{}"'.format(self.uri))
+ return {"WWW-Authenticate": "Bearer " + ", ".join(authvalues)}
+ return {}
+
+
+class TokenExpiredError(OAuth2Error):
+ error = 'token_expired'
+
+
+class InsecureTransportError(OAuth2Error):
+ error = 'insecure_transport'
+ description = 'OAuth 2 MUST utilize https.'
+
+
+class MismatchingStateError(OAuth2Error):
+ error = 'mismatching_state'
+ description = 'CSRF Warning! State not equal in request and response.'
+
+
+class MissingCodeError(OAuth2Error):
+ error = 'missing_code'
+
+
+class MissingTokenError(OAuth2Error):
+ error = 'missing_token'
+
+
+class MissingTokenTypeError(OAuth2Error):
+ error = 'missing_token_type'
+
+
+class FatalClientError(OAuth2Error):
+ """
+ Errors during authorization where user should not be redirected back.
+
+ If the request fails due to a missing, invalid, or mismatching
+ redirection URI, or if the client identifier is missing or invalid,
+ the authorization server SHOULD inform the resource owner of the
+ error and MUST NOT automatically redirect the user-agent to the
+ invalid redirection URI.
+
+ Instead the user should be informed of the error by the provider itself.
+ """
+ pass
+
+
+class InvalidRequestFatalError(FatalClientError):
+ """
+ For fatal errors, the request is missing a required parameter, includes
+ an invalid parameter value, includes a parameter more than once, or is
+ otherwise malformed.
+ """
+ error = 'invalid_request'
+
+
+class InvalidRedirectURIError(InvalidRequestFatalError):
+ description = 'Invalid redirect URI.'
+
+
+class MissingRedirectURIError(InvalidRequestFatalError):
+ description = 'Missing redirect URI.'
+
+
+class MismatchingRedirectURIError(InvalidRequestFatalError):
+ description = 'Mismatching redirect URI.'
+
+
+class InvalidClientIdError(InvalidRequestFatalError):
+ description = 'Invalid client_id parameter value.'
+
+
+class MissingClientIdError(InvalidRequestFatalError):
+ description = 'Missing client_id parameter.'
+
+
+class InvalidRequestError(OAuth2Error):
+ """
+ The request is missing a required parameter, includes an invalid
+ parameter value, includes a parameter more than once, or is
+ otherwise malformed.
+ """
+ error = 'invalid_request'
+
+
+class MissingResponseTypeError(InvalidRequestError):
+ description = 'Missing response_type parameter.'
+
+
+class MissingCodeChallengeError(InvalidRequestError):
+ """
+ If the server requires Proof Key for Code Exchange (PKCE) by OAuth
+ public clients and the client does not send the "code_challenge" in
+ the request, the authorization endpoint MUST return the authorization
+ error response with the "error" value set to "invalid_request". The
+ "error_description" or the response of "error_uri" SHOULD explain the
+ nature of error, e.g., code challenge required.
+ """
+ description = 'Code challenge required.'
+
+
+class MissingCodeVerifierError(InvalidRequestError):
+ """
+ The request to the token endpoint, when PKCE is enabled, has
+ the parameter `code_verifier` REQUIRED.
+ """
+ description = 'Code verifier required.'
+
+
+class AccessDeniedError(OAuth2Error):
+ """
+ The resource owner or authorization server denied the request.
+ """
+ error = 'access_denied'
+
+
+class UnsupportedResponseTypeError(OAuth2Error):
+ """
+ The authorization server does not support obtaining an authorization
+ code using this method.
+ """
+ error = 'unsupported_response_type'
+
+
+class UnsupportedCodeChallengeMethodError(InvalidRequestError):
+ """
+ If the server supporting PKCE does not support the requested
+ transformation, the authorization endpoint MUST return the
+ authorization error response with "error" value set to
+ "invalid_request". The "error_description" or the response of
+ "error_uri" SHOULD explain the nature of error, e.g., transform
+ algorithm not supported.
+ """
+ description = 'Transform algorithm not supported.'
+
+
+class InvalidScopeError(OAuth2Error):
+ """
+ The requested scope is invalid, unknown, or malformed, or
+ exceeds the scope granted by the resource owner.
+
+ https://tools.ietf.org/html/rfc6749#section-5.2
+ """
+ error = 'invalid_scope'
+
+
+class ServerError(OAuth2Error):
+ """
+ The authorization server encountered an unexpected condition that
+ prevented it from fulfilling the request. (This error code is needed
+ because a 500 Internal Server Error HTTP status code cannot be returned
+ to the client via a HTTP redirect.)
+ """
+ error = 'server_error'
+
+
+class TemporarilyUnavailableError(OAuth2Error):
+ """
+ The authorization server is currently unable to handle the request
+ due to a temporary overloading or maintenance of the server.
+ (This error code is needed because a 503 Service Unavailable HTTP
+ status code cannot be returned to the client via a HTTP redirect.)
+ """
+ error = 'temporarily_unavailable'
+
+
+class InvalidClientError(FatalClientError):
+ """
+ Client authentication failed (e.g. unknown client, no client
+ authentication included, or unsupported authentication method).
+ The authorization server MAY return an HTTP 401 (Unauthorized) status
+ code to indicate which HTTP authentication schemes are supported.
+ If the client attempted to authenticate via the "Authorization" request
+ header field, the authorization server MUST respond with an
+ HTTP 401 (Unauthorized) status code, and include the "WWW-Authenticate"
+ response header field matching the authentication scheme used by the
+ client.
+ """
+ error = 'invalid_client'
+ status_code = 401
+
+
+class InvalidGrantError(OAuth2Error):
+ """
+ The provided authorization grant (e.g. authorization code, resource
+ owner credentials) or refresh token is invalid, expired, revoked, does
+ not match the redirection URI used in the authorization request, or was
+ issued to another client.
+
+ https://tools.ietf.org/html/rfc6749#section-5.2
+ """
+ error = 'invalid_grant'
+ status_code = 400
+
+
+class UnauthorizedClientError(OAuth2Error):
+ """
+ The authenticated client is not authorized to use this authorization
+ grant type.
+ """
+ error = 'unauthorized_client'
+
+
+class UnsupportedGrantTypeError(OAuth2Error):
+ """
+ The authorization grant type is not supported by the authorization
+ server.
+ """
+ error = 'unsupported_grant_type'
+
+
+class UnsupportedTokenTypeError(OAuth2Error):
+ """
+ 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.
+ """
+ error = 'unsupported_token_type'
+
+
+class InvalidTokenError(OAuth2Error):
+ """
+ The access token provided is expired, revoked, malformed, or
+ invalid for other reasons. The resource SHOULD respond with
+ the HTTP 401 (Unauthorized) status code. The client MAY
+ request a new access token and retry the protected resource
+ request.
+ """
+ error = 'invalid_token'
+ status_code = 401
+ description = ("The access token provided is expired, revoked, malformed, "
+ "or invalid for other reasons.")
+
+
+class InsufficientScopeError(OAuth2Error):
+ """
+ The request requires higher privileges than provided by the
+ access token. The resource server SHOULD respond with the HTTP
+ 403 (Forbidden) status code and MAY include the "scope"
+ attribute with the scope necessary to access the protected
+ resource.
+ """
+ error = 'insufficient_scope'
+ status_code = 403
+ description = ("The request requires higher privileges than provided by "
+ "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'
+
+
+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'
+
+
+class CustomOAuth2Error(OAuth2Error):
+ """
+ This error is a placeholder for all custom errors not described by the RFC.
+ Some of the popular OAuth2 providers are using custom errors.
+ """
+ def __init__(self, error, *args, **kwargs):
+ self.error = error
+ super().__init__(*args, **kwargs)
+
+
+def raise_from_error(error, params=None):
+ import inspect
+ import sys
+ kwargs = {
+ 'description': params.get('error_description'),
+ 'uri': params.get('error_uri'),
+ 'state': params.get('state')
+ }
+ for _, cls in inspect.getmembers(sys.modules[__name__], inspect.isclass):
+ if cls.error == error:
+ raise cls(**kwargs)
+ raise CustomOAuth2Error(error=error, **kwargs)
diff --git a/game/python-extra/oauthlib/oauth2/rfc6749/grant_types/__init__.py b/game/python-extra/oauthlib/oauth2/rfc6749/grant_types/__init__.py
new file mode 100644
index 0000000..eb88cfc
--- /dev/null
+++ b/game/python-extra/oauthlib/oauth2/rfc6749/grant_types/__init__.py
@@ -0,0 +1,11 @@
+"""
+oauthlib.oauth2.rfc6749.grant_types
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+"""
+from .authorization_code import AuthorizationCodeGrant
+from .client_credentials import ClientCredentialsGrant
+from .implicit import ImplicitGrant
+from .refresh_token import RefreshTokenGrant
+from .resource_owner_password_credentials import (
+ ResourceOwnerPasswordCredentialsGrant,
+)
diff --git a/game/python-extra/oauthlib/oauth2/rfc6749/grant_types/authorization_code.py b/game/python-extra/oauthlib/oauth2/rfc6749/grant_types/authorization_code.py
new file mode 100644
index 0000000..858855a
--- /dev/null
+++ b/game/python-extra/oauthlib/oauth2/rfc6749/grant_types/authorization_code.py
@@ -0,0 +1,548 @@
+"""
+oauthlib.oauth2.rfc6749.grant_types
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+"""
+import base64
+import hashlib
+import json
+import logging
+
+from oauthlib import common
+
+from .. import errors
+from .base import GrantTypeBase
+
+log = logging.getLogger(__name__)
+
+
+def code_challenge_method_s256(verifier, challenge):
+ """
+ If the "code_challenge_method" from `Section 4.3`_ was "S256", the
+ received "code_verifier" is hashed by SHA-256, base64url-encoded, and
+ then compared to the "code_challenge", i.e.:
+
+ BASE64URL-ENCODE(SHA256(ASCII(code_verifier))) == code_challenge
+
+ How to implement a base64url-encoding
+ function without padding, based upon the standard base64-encoding
+ function that uses padding.
+
+ To be concrete, example C# code implementing these functions is shown
+ below. Similar code could be used in other languages.
+
+ static string base64urlencode(byte [] arg)
+ {
+ string s = Convert.ToBase64String(arg); // Regular base64 encoder
+ s = s.Split('=')[0]; // Remove any trailing '='s
+ s = s.Replace('+', '-'); // 62nd char of encoding
+ s = s.Replace('/', '_'); // 63rd char of encoding
+ return s;
+ }
+
+ In python urlsafe_b64encode is already replacing '+' and '/', but preserve
+ the trailing '='. So we have to remove it.
+
+ .. _`Section 4.3`: https://tools.ietf.org/html/rfc7636#section-4.3
+ """
+ return base64.urlsafe_b64encode(
+ hashlib.sha256(verifier.encode()).digest()
+ ).decode().rstrip('=') == challenge
+
+
+def code_challenge_method_plain(verifier, challenge):
+ """
+ If the "code_challenge_method" from `Section 4.3`_ was "plain", they are
+ compared directly, i.e.:
+
+ code_verifier == code_challenge.
+
+ .. _`Section 4.3`: https://tools.ietf.org/html/rfc7636#section-4.3
+ """
+ return verifier == challenge
+
+
+class AuthorizationCodeGrant(GrantTypeBase):
+
+ """`Authorization Code Grant`_
+
+ The authorization code grant type is used to obtain both access
+ tokens and refresh tokens and is optimized for confidential clients.
+ Since this is a redirection-based flow, the client must be capable of
+ interacting with the resource owner's user-agent (typically a web
+ browser) and capable of receiving incoming requests (via redirection)
+ from the authorization server::
+
+ +----------+
+ | Resource |
+ | Owner |
+ | |
+ +----------+
+ ^
+ |
+ (B)
+ +----|-----+ Client Identifier +---------------+
+ | -+----(A)-- & Redirection URI ---->| |
+ | User- | | Authorization |
+ | Agent -+----(B)-- User authenticates --->| Server |
+ | | | |
+ | -+----(C)-- Authorization Code ---<| |
+ +-|----|---+ +---------------+
+ | | ^ v
+ (A) (C) | |
+ | | | |
+ ^ v | |
+ +---------+ | |
+ | |>---(D)-- Authorization Code ---------' |
+ | Client | & Redirection URI |
+ | | |
+ | |<---(E)----- Access Token -------------------'
+ +---------+ (w/ Optional Refresh Token)
+
+ Note: The lines illustrating steps (A), (B), and (C) are broken into
+ two parts as they pass through the user-agent.
+
+ Figure 3: Authorization Code Flow
+
+ The flow illustrated in Figure 3 includes the following steps:
+
+ (A) The client initiates the flow by directing the resource owner's
+ user-agent to the authorization endpoint. The client includes
+ its client identifier, requested scope, local state, and a
+ redirection URI to which the authorization server will send the
+ user-agent back once access is granted (or denied).
+
+ (B) The authorization server authenticates the resource owner (via
+ the user-agent) and establishes whether the resource owner
+ grants or denies the client's access request.
+
+ (C) Assuming the resource owner grants access, the authorization
+ server redirects the user-agent back to the client using the
+ redirection URI provided earlier (in the request or during
+ client registration). The redirection URI includes an
+ authorization code and any local state provided by the client
+ earlier.
+
+ (D) The client requests an access token from the authorization
+ server's token endpoint by including the authorization code
+ received in the previous step. When making the request, the
+ client authenticates with the authorization server. The client
+ includes the redirection URI used to obtain the authorization
+ code for verification.
+
+ (E) The authorization server authenticates the client, validates the
+ authorization code, and ensures that the redirection URI
+ received matches the URI used to redirect the client in
+ step (C). If valid, the authorization server responds back with
+ an access token and, optionally, a refresh token.
+
+ OAuth 2.0 public clients utilizing the Authorization Code Grant are
+ susceptible to the authorization code interception attack.
+
+ A technique to mitigate against the threat through the use of Proof Key for Code
+ Exchange (PKCE, pronounced "pixy") is implemented in the current oauthlib
+ implementation.
+
+ .. _`Authorization Code Grant`: https://tools.ietf.org/html/rfc6749#section-4.1
+ .. _`PKCE`: https://tools.ietf.org/html/rfc7636
+ """
+
+ default_response_mode = 'query'
+ response_types = ['code']
+
+ # This dict below is private because as RFC mention it:
+ # "S256" is Mandatory To Implement (MTI) on the server.
+ #
+ _code_challenge_methods = {
+ 'plain': code_challenge_method_plain,
+ 'S256': code_challenge_method_s256
+ }
+
+ def create_authorization_code(self, request):
+ """
+ Generates an authorization grant represented as a dictionary.
+
+ :param request: OAuthlib request.
+ :type request: oauthlib.common.Request
+ """
+ grant = {'code': common.generate_token()}
+ if hasattr(request, 'state') and request.state:
+ grant['state'] = request.state
+ log.debug('Created authorization code grant %r for request %r.',
+ grant, request)
+ return grant
+
+ def create_authorization_response(self, request, token_handler):
+ """
+ The client constructs the request URI by adding the following
+ parameters to the query component of the authorization endpoint URI
+ using the "application/x-www-form-urlencoded" format, per `Appendix B`_:
+
+ response_type
+ REQUIRED. Value MUST be set to "code" for standard OAuth2
+ authorization flow. For OpenID Connect it must be one of
+ "code token", "code id_token", or "code token id_token" - we
+ essentially test that "code" appears in the response_type.
+ client_id
+ REQUIRED. The client identifier as described in `Section 2.2`_.
+ redirect_uri
+ OPTIONAL. As described in `Section 3.1.2`_.
+ scope
+ OPTIONAL. The scope of the access request as described by
+ `Section 3.3`_.
+ state
+ RECOMMENDED. An opaque value used by the client to maintain
+ state between the request and callback. The authorization
+ server includes this value when redirecting the user-agent back
+ to the client. The parameter SHOULD be used for preventing
+ cross-site request forgery as described in `Section 10.12`_.
+
+ The client directs the resource owner to the constructed URI using an
+ HTTP redirection response, or by other means available to it via the
+ user-agent.
+
+ :param request: OAuthlib request.
+ :type request: oauthlib.common.Request
+ :param token_handler: A token handler instance, for example of type
+ oauthlib.oauth2.BearerToken.
+ :returns: headers, body, status
+ :raises: FatalClientError on invalid redirect URI or client id.
+
+ A few examples::
+
+ >>> from your_validator import your_validator
+ >>> request = Request('https://example.com/authorize?client_id=valid'
+ ... '&redirect_uri=http%3A%2F%2Fclient.com%2F')
+ >>> from oauthlib.common import Request
+ >>> from oauthlib.oauth2 import AuthorizationCodeGrant, BearerToken
+ >>> token = BearerToken(your_validator)
+ >>> grant = AuthorizationCodeGrant(your_validator)
+ >>> request.scopes = ['authorized', 'in', 'some', 'form']
+ >>> grant.create_authorization_response(request, token)
+ (u'http://client.com/?error=invalid_request&error_description=Missing+response_type+parameter.', None, None, 400)
+ >>> request = Request('https://example.com/authorize?client_id=valid'
+ ... '&redirect_uri=http%3A%2F%2Fclient.com%2F'
+ ... '&response_type=code')
+ >>> request.scopes = ['authorized', 'in', 'some', 'form']
+ >>> grant.create_authorization_response(request, token)
+ (u'http://client.com/?code=u3F05aEObJuP2k7DordviIgW5wl52N', None, None, 200)
+ >>> # If the client id or redirect uri fails validation
+ >>> grant.create_authorization_response(request, token)
+ Traceback (most recent call last):
+ File "<stdin>", line 1, in <module>
+ File "oauthlib/oauth2/rfc6749/grant_types.py", line 515, in create_authorization_response
+ >>> grant.create_authorization_response(request, token)
+ File "oauthlib/oauth2/rfc6749/grant_types.py", line 591, in validate_authorization_request
+ oauthlib.oauth2.rfc6749.errors.InvalidClientIdError
+
+ .. _`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:
+ self.validate_authorization_request(request)
+ log.debug('Pre resource owner authorization validation ok for %r.',
+ request)
+
+ # If the request fails due to a missing, invalid, or mismatching
+ # redirection URI, or if the client identifier is missing or invalid,
+ # the authorization server SHOULD inform the resource owner of the
+ # error and MUST NOT automatically redirect the user-agent to the
+ # invalid redirection URI.
+ except errors.FatalClientError as e:
+ log.debug('Fatal client error during validation of %r. %r.',
+ request, e)
+ raise
+
+ # If the resource owner denies the access request or if the request
+ # fails for reasons other than a missing or invalid redirection URI,
+ # the authorization server informs the client by adding the following
+ # parameters to the query component of the redirection URI using the
+ # "application/x-www-form-urlencoded" format, per 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
+ redirect_uri = common.add_params_to_uri(
+ request.redirect_uri, e.twotuples,
+ fragment=request.response_mode == "fragment")
+ return {'Location': redirect_uri}, None, 302
+
+ grant = self.create_authorization_code(request)
+ for modifier in self._code_modifiers:
+ grant = modifier(grant, token_handler, request)
+ if 'access_token' in grant:
+ self.request_validator.save_token(grant, request)
+ log.debug('Saving grant %r for %r.', grant, request)
+ self.request_validator.save_authorization_code(
+ request.client_id, grant, request)
+ return self.prepare_authorization_response(
+ request, grant, {}, None, 302)
+
+ def create_token_response(self, request, token_handler):
+ """Validate the authorization code.
+
+ The client MUST NOT use the authorization code more than once. If an
+ authorization code is used more than once, the authorization server
+ MUST deny the request and SHOULD revoke (when possible) all tokens
+ previously issued based on that authorization code. The authorization
+ code is bound to the client identifier and redirection URI.
+
+ :param request: OAuthlib request.
+ :type request: oauthlib.common.Request
+ :param token_handler: A token handler instance, for example of type
+ oauthlib.oauth2.BearerToken.
+
+ """
+ headers = self._get_default_headers()
+ try:
+ self.validate_token_request(request)
+ log.debug('Token request validation ok for %r.', request)
+ except errors.OAuth2Error as e:
+ log.debug('Client error during validation of %r. %r.', request, e)
+ headers.update(e.headers)
+ return headers, e.json, e.status_code
+
+ token = token_handler.create_token(request, refresh_token=self.refresh_token)
+
+ for modifier in self._token_modifiers:
+ token = modifier(token, token_handler, request)
+
+ self.request_validator.save_token(token, request)
+ self.request_validator.invalidate_authorization_code(
+ request.client_id, request.code, request)
+ headers.update(self._create_cors_headers(request))
+ return headers, json.dumps(token), 200
+
+ def validate_authorization_request(self, request):
+ """Check the authorization request for normal and fatal errors.
+
+ A normal error could be a missing response_type parameter or the client
+ attempting to access scope it is not allowed to ask authorization for.
+ Normal errors can safely be included in the redirection URI and
+ sent back to the client.
+
+ Fatal errors occur when the client_id or redirect_uri is invalid or
+ missing. These must be caught by the provider and handled, how this
+ is done is outside of the scope of OAuthLib but showing an error
+ page describing the issue is a good idea.
+
+ :param request: OAuthlib request.
+ :type request: oauthlib.common.Request
+ """
+
+ # First check for fatal errors
+
+ # If the request fails due to a missing, invalid, or mismatching
+ # redirection URI, or if the client identifier is missing or invalid,
+ # the authorization server SHOULD inform the resource owner of the
+ # error and MUST NOT automatically redirect the user-agent to the
+ # invalid redirection URI.
+
+ # First check duplicate parameters
+ for param in ('client_id', 'response_type', 'redirect_uri', 'scope', 'state'):
+ try:
+ duplicate_params = request.duplicate_params
+ except ValueError:
+ raise errors.InvalidRequestFatalError(description='Unable to parse query string', request=request)
+ if param in duplicate_params:
+ raise errors.InvalidRequestFatalError(description='Duplicate %s parameter.' % param, request=request)
+
+ # REQUIRED. The client identifier as described in Section 2.2.
+ # https://tools.ietf.org/html/rfc6749#section-2.2
+ if not request.client_id:
+ raise errors.MissingClientIdError(request=request)
+
+ if not self.request_validator.validate_client_id(request.client_id, request):
+ raise errors.InvalidClientIdError(request=request)
+
+ # OPTIONAL. As described in 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)
+
+ # OPTIONAL. As described in Section 3.1.2.
+ # https://tools.ietf.org/html/rfc6749#section-3.1.2
+ self._handle_redirects(request)
+
+ # Then check for normal errors.
+
+ # If the resource owner denies the access request or if the request
+ # fails for reasons other than a missing or invalid redirection URI,
+ # the authorization server informs the client by adding the following
+ # parameters to the query component of the redirection URI using the
+ # "application/x-www-form-urlencoded" format, per 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.
+
+ request_info = {}
+ for validator in self.custom_validators.pre_auth:
+ request_info.update(validator(request))
+
+ # REQUIRED.
+ if request.response_type is None:
+ raise errors.MissingResponseTypeError(request=request)
+ # Value MUST be set to "code" or one of the OpenID authorization code including
+ # response_types "code token", "code id_token", "code token id_token"
+ elif not 'code' in request.response_type and request.response_type != 'none':
+ raise errors.UnsupportedResponseTypeError(request=request)
+
+ if not self.request_validator.validate_response_type(request.client_id,
+ request.response_type,
+ request.client, request):
+
+ log.debug('Client %s is not authorized to use response_type %s.',
+ request.client_id, request.response_type)
+ raise errors.UnauthorizedClientError(request=request)
+
+ # OPTIONAL. Validate PKCE request or reply with "error"/"invalid_request"
+ # https://tools.ietf.org/html/rfc6749#section-4.4.1
+ if self.request_validator.is_pkce_required(request.client_id, request) is True:
+ if request.code_challenge is None:
+ raise errors.MissingCodeChallengeError(request=request)
+
+ if request.code_challenge is not None:
+ request_info["code_challenge"] = request.code_challenge
+
+ # OPTIONAL, defaults to "plain" if not present in the request.
+ if request.code_challenge_method is None:
+ request.code_challenge_method = "plain"
+
+ if request.code_challenge_method not in self._code_challenge_methods:
+ raise errors.UnsupportedCodeChallengeMethodError(request=request)
+ request_info["code_challenge_method"] = request.code_challenge_method
+
+ # OPTIONAL. The scope of the access request as described by Section 3.3
+ # https://tools.ietf.org/html/rfc6749#section-3.3
+ self.validate_scopes(request)
+
+ request_info.update({
+ 'client_id': request.client_id,
+ 'redirect_uri': request.redirect_uri,
+ 'response_type': request.response_type,
+ 'state': request.state,
+ 'request': request
+ })
+
+ for validator in self.custom_validators.post_auth:
+ request_info.update(validator(request))
+
+ return request.scopes, request_info
+
+ def validate_token_request(self, request):
+ """
+ :param request: OAuthlib request.
+ :type request: oauthlib.common.Request
+ """
+ # REQUIRED. Value MUST be set to "authorization_code".
+ if request.grant_type not in ('authorization_code', 'openid'):
+ raise errors.UnsupportedGrantTypeError(request=request)
+
+ for validator in self.custom_validators.pre_token:
+ validator(request)
+
+ if request.code is None:
+ raise errors.InvalidRequestError(
+ description='Missing code parameter.', request=request)
+
+ for param in ('client_id', 'grant_type', 'redirect_uri'):
+ if param in request.duplicate_params:
+ raise errors.InvalidRequestError(description='Duplicate %s parameter.' % param,
+ request=request)
+
+ if self.request_validator.client_authentication_required(request):
+ # If the client type is confidential or 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.
+ # 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.
+ # https://tools.ietf.org/html/rfc6749#section-3.2.1
+ log.debug('Client authentication failed, %r.', request)
+ raise errors.InvalidClientError(request=request)
+
+ if not hasattr(request.client, 'client_id'):
+ raise NotImplementedError('Authenticate client must set the '
+ 'request.client.client_id attribute '
+ 'in authenticate_client.')
+
+ request.client_id = request.client_id or request.client.client_id
+
+ # Ensure client is authorized use of this grant type
+ self.validate_grant_type(request)
+
+ # REQUIRED. The authorization code received from the
+ # authorization server.
+ if not self.request_validator.validate_code(request.client_id,
+ request.code, request.client, request):
+ log.debug('Client, %r (%r), is not allowed access to scopes %r.',
+ request.client_id, request.client, request.scopes)
+ raise errors.InvalidGrantError(request=request)
+
+ # OPTIONAL. Validate PKCE code_verifier
+ challenge = self.request_validator.get_code_challenge(request.code, request)
+
+ if challenge is not None:
+ if request.code_verifier is None:
+ raise errors.MissingCodeVerifierError(request=request)
+
+ challenge_method = self.request_validator.get_code_challenge_method(request.code, request)
+ if challenge_method is None:
+ raise errors.InvalidGrantError(request=request, description="Challenge method not found")
+
+ if challenge_method not in self._code_challenge_methods:
+ raise errors.ServerError(
+ description="code_challenge_method {} is not supported.".format(challenge_method),
+ request=request
+ )
+
+ if not self.validate_code_challenge(challenge,
+ challenge_method,
+ request.code_verifier):
+ log.debug('request provided a invalid code_verifier.')
+ raise errors.InvalidGrantError(request=request)
+ elif self.request_validator.is_pkce_required(request.client_id, request) is True:
+ if request.code_verifier is None:
+ raise errors.MissingCodeVerifierError(request=request)
+ raise errors.InvalidGrantError(request=request, description="Challenge not found")
+
+ for attr in ('user', 'scopes'):
+ if getattr(request, attr, None) is None:
+ log.debug('request.%s was not set on code validation.', attr)
+
+ # REQUIRED, if the "redirect_uri" parameter was included in the
+ # authorization request as described in Section 4.1.1, and their
+ # values MUST be identical.
+ if request.redirect_uri is None:
+ request.using_default_redirect_uri = True
+ request.redirect_uri = self.request_validator.get_default_redirect_uri(
+ request.client_id, request)
+ log.debug('Using default redirect_uri %s.', request.redirect_uri)
+ if not request.redirect_uri:
+ raise errors.MissingRedirectURIError(request=request)
+ else:
+ request.using_default_redirect_uri = False
+ log.debug('Using provided redirect_uri %s', request.redirect_uri)
+
+ if not self.request_validator.confirm_redirect_uri(request.client_id, request.code,
+ 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)
+
+ for validator in self.custom_validators.post_token:
+ validator(request)
+
+ def validate_code_challenge(self, challenge, challenge_method, verifier):
+ if challenge_method in self._code_challenge_methods:
+ return self._code_challenge_methods[challenge_method](verifier, challenge)
+ raise NotImplementedError('Unknown challenge_method %s' % challenge_method)
diff --git a/game/python-extra/oauthlib/oauth2/rfc6749/grant_types/base.py b/game/python-extra/oauthlib/oauth2/rfc6749/grant_types/base.py
new file mode 100644
index 0000000..ca343a1
--- /dev/null
+++ b/game/python-extra/oauthlib/oauth2/rfc6749/grant_types/base.py
@@ -0,0 +1,268 @@
+"""
+oauthlib.oauth2.rfc6749.grant_types
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+"""
+import logging
+from itertools import chain
+
+from oauthlib.common import add_params_to_uri
+from oauthlib.oauth2.rfc6749 import errors, utils
+from oauthlib.uri_validate import is_absolute_uri
+
+from ..request_validator import RequestValidator
+from ..utils import is_secure_transport
+
+log = logging.getLogger(__name__)
+
+
+class ValidatorsContainer:
+ """
+ Container object for holding custom validator callables to be invoked
+ as part of the grant type `validate_authorization_request()` or
+ `validate_authorization_request()` methods on the various grant types.
+
+ Authorization validators must be callables that take a request object and
+ return a dict, which may contain items to be added to the `request_info`
+ returned from the grant_type after validation.
+
+ Token validators must be callables that take a request object and
+ return None.
+
+ Both authorization validators and token validators may raise OAuth2
+ exceptions if validation conditions fail.
+
+ Authorization validators added to `pre_auth` will be run BEFORE
+ the standard validations (but after the critical ones that raise
+ fatal errors) as part of `validate_authorization_request()`
+
+ Authorization validators added to `post_auth` will be run AFTER
+ the standard validations as part of `validate_authorization_request()`
+
+ Token validators added to `pre_token` will be run BEFORE
+ the standard validations as part of `validate_token_request()`
+
+ Token validators added to `post_token` will be run AFTER
+ the standard validations as part of `validate_token_request()`
+
+ For example:
+
+ >>> def my_auth_validator(request):
+ ... return {'myval': True}
+ >>> auth_code_grant = AuthorizationCodeGrant(request_validator)
+ >>> auth_code_grant.custom_validators.pre_auth.append(my_auth_validator)
+ >>> def my_token_validator(request):
+ ... if not request.everything_okay:
+ ... raise errors.OAuth2Error("uh-oh")
+ >>> auth_code_grant.custom_validators.post_token.append(my_token_validator)
+ """
+
+ def __init__(self, post_auth, post_token,
+ pre_auth, pre_token):
+ self.pre_auth = pre_auth
+ self.post_auth = post_auth
+ self.pre_token = pre_token
+ self.post_token = post_token
+
+ @property
+ def all_pre(self):
+ return chain(self.pre_auth, self.pre_token)
+
+ @property
+ def all_post(self):
+ return chain(self.post_auth, self.post_token)
+
+
+class GrantTypeBase:
+ error_uri = None
+ request_validator = None
+ default_response_mode = 'fragment'
+ refresh_token = True
+ response_types = ['code']
+
+ def __init__(self, request_validator=None, **kwargs):
+ self.request_validator = request_validator or RequestValidator()
+
+ # Transforms class variables into instance variables:
+ self.response_types = self.response_types
+ self.refresh_token = self.refresh_token
+ self._setup_custom_validators(kwargs)
+ self._code_modifiers = []
+ self._token_modifiers = []
+
+ for kw, val in kwargs.items():
+ setattr(self, kw, val)
+
+ def _setup_custom_validators(self, kwargs):
+ post_auth = kwargs.get('post_auth', [])
+ post_token = kwargs.get('post_token', [])
+ pre_auth = kwargs.get('pre_auth', [])
+ pre_token = kwargs.get('pre_token', [])
+ if not hasattr(self, 'validate_authorization_request'):
+ if post_auth or pre_auth:
+ msg = ("{} does not support authorization validators. Use "
+ "token validators instead.").format(self.__class__.__name__)
+ raise ValueError(msg)
+ # Using tuples here because they can't be appended to:
+ post_auth, pre_auth = (), ()
+ self.custom_validators = ValidatorsContainer(post_auth, post_token,
+ pre_auth, pre_token)
+
+ def register_response_type(self, response_type):
+ self.response_types.append(response_type)
+
+ def register_code_modifier(self, modifier):
+ self._code_modifiers.append(modifier)
+
+ def register_token_modifier(self, modifier):
+ self._token_modifiers.append(modifier)
+
+ def create_authorization_response(self, request, token_handler):
+ """
+ :param request: OAuthlib request.
+ :type request: oauthlib.common.Request
+ :param token_handler: A token handler instance, for example of type
+ oauthlib.oauth2.BearerToken.
+ """
+ raise NotImplementedError('Subclasses must implement this method.')
+
+ def create_token_response(self, request, token_handler):
+ """
+ :param request: OAuthlib request.
+ :type request: oauthlib.common.Request
+ :param token_handler: A token handler instance, for example of type
+ oauthlib.oauth2.BearerToken.
+ """
+ raise NotImplementedError('Subclasses must implement this method.')
+
+ def add_token(self, token, token_handler, request):
+ """
+ :param token:
+ :param token_handler: A token handler instance, for example of type
+ oauthlib.oauth2.BearerToken.
+ :param request: OAuthlib request.
+ :type request: oauthlib.common.Request
+ """
+ # Only add a hybrid access token on auth step if asked for
+ if not request.response_type in ["token", "code token", "id_token token", "code id_token token"]:
+ return token
+
+ token.update(token_handler.create_token(request, refresh_token=False))
+ return token
+
+ def validate_grant_type(self, request):
+ """
+ :param request: OAuthlib request.
+ :type request: oauthlib.common.Request
+ """
+ client_id = getattr(request, 'client_id', None)
+ if not self.request_validator.validate_grant_type(client_id,
+ request.grant_type, request.client, request):
+ log.debug('Unauthorized from %r (%r) access to grant type %s.',
+ request.client_id, request.client, request.grant_type)
+ raise errors.UnauthorizedClientError(request=request)
+
+ def validate_scopes(self, request):
+ """
+ :param request: OAuthlib request.
+ :type request: oauthlib.common.Request
+ """
+ if not request.scopes:
+ request.scopes = utils.scope_to_list(request.scope) or utils.scope_to_list(
+ self.request_validator.get_default_scopes(request.client_id, request))
+ log.debug('Validating access to scopes %r for client %r (%r).',
+ request.scopes, request.client_id, request.client)
+ if not self.request_validator.validate_scopes(request.client_id,
+ request.scopes, request.client, request):
+ raise errors.InvalidScopeError(request=request)
+
+ def prepare_authorization_response(self, request, token, headers, body, status):
+ """Place token according to response mode.
+
+ Base classes can define a default response mode for their authorization
+ response by overriding the static `default_response_mode` member.
+
+ :param request: OAuthlib request.
+ :type request: oauthlib.common.Request
+ :param token:
+ :param headers:
+ :param body:
+ :param status:
+ """
+ request.response_mode = request.response_mode or self.default_response_mode
+
+ if request.response_mode not in ('query', 'fragment'):
+ log.debug('Overriding invalid response mode %s with %s',
+ request.response_mode, self.default_response_mode)
+ request.response_mode = self.default_response_mode
+
+ token_items = token.items()
+
+ if request.response_type == 'none':
+ state = token.get('state', None)
+ if state:
+ token_items = [('state', state)]
+ else:
+ token_items = []
+
+ if request.response_mode == 'query':
+ headers['Location'] = add_params_to_uri(
+ request.redirect_uri, token_items, fragment=False)
+ return headers, body, status
+
+ if request.response_mode == 'fragment':
+ headers['Location'] = add_params_to_uri(
+ request.redirect_uri, token_items, fragment=True)
+ return headers, body, status
+
+ raise NotImplementedError(
+ 'Subclasses must set a valid default_response_mode')
+
+ def _get_default_headers(self):
+ """Create default headers for grant responses."""
+ return {
+ 'Content-Type': 'application/json',
+ 'Cache-Control': 'no-store',
+ 'Pragma': 'no-cache',
+ }
+
+ def _handle_redirects(self, request):
+ if request.redirect_uri is not None:
+ request.using_default_redirect_uri = False
+ log.debug('Using provided redirect_uri %s', request.redirect_uri)
+ if not is_absolute_uri(request.redirect_uri):
+ raise errors.InvalidRedirectURIError(request=request)
+
+ # The authorization server MUST verify that the redirection URI
+ # to which it will redirect the access token matches a
+ # redirection URI registered by the client as described in
+ # 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)
+ else:
+ request.redirect_uri = self.request_validator.get_default_redirect_uri(
+ request.client_id, request)
+ request.using_default_redirect_uri = True
+ log.debug('Using default redirect_uri %s.', request.redirect_uri)
+ if not request.redirect_uri:
+ raise errors.MissingRedirectURIError(request=request)
+ if not is_absolute_uri(request.redirect_uri):
+ raise errors.InvalidRedirectURIError(request=request)
+
+ def _create_cors_headers(self, request):
+ """If CORS is allowed, create the appropriate headers."""
+ if 'origin' not in request.headers:
+ return {}
+
+ origin = request.headers['origin']
+ if not is_secure_transport(origin):
+ log.debug('Origin "%s" is not HTTPS, CORS not allowed.', origin)
+ return {}
+ elif not self.request_validator.is_origin_allowed(
+ request.client_id, origin, request):
+ log.debug('Invalid origin "%s", CORS not allowed.', origin)
+ return {}
+ else:
+ log.debug('Valid origin "%s", injecting CORS headers.', origin)
+ return {'Access-Control-Allow-Origin': origin}
diff --git a/game/python-extra/oauthlib/oauth2/rfc6749/grant_types/client_credentials.py b/game/python-extra/oauthlib/oauth2/rfc6749/grant_types/client_credentials.py
new file mode 100644
index 0000000..e7b4618
--- /dev/null
+++ b/game/python-extra/oauthlib/oauth2/rfc6749/grant_types/client_credentials.py
@@ -0,0 +1,123 @@
+"""
+oauthlib.oauth2.rfc6749.grant_types
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+"""
+import json
+import logging
+
+from .. import errors
+from .base import GrantTypeBase
+
+log = logging.getLogger(__name__)
+
+
+class ClientCredentialsGrant(GrantTypeBase):
+
+ """`Client Credentials Grant`_
+
+ The client can request an access token using only its client
+ credentials (or other supported means of authentication) when the
+ client is requesting access to the protected resources under its
+ control, or those of another resource owner that have been previously
+ arranged with the authorization server (the method of which is beyond
+ the scope of this specification).
+
+ The client credentials grant type MUST only be used by confidential
+ clients::
+
+ +---------+ +---------------+
+ : : : :
+ : :>-- A - Client Authentication --->: Authorization :
+ : Client : : Server :
+ : :<-- B ---- Access Token ---------<: :
+ : : : :
+ +---------+ +---------------+
+
+ Figure 6: Client Credentials Flow
+
+ The flow illustrated in Figure 6 includes the following steps:
+
+ (A) The client authenticates with the authorization server and
+ requests an access token from the token endpoint.
+
+ (B) The authorization server authenticates the client, and if valid,
+ issues an access token.
+
+ .. _`Client Credentials Grant`: https://tools.ietf.org/html/rfc6749#section-4.4
+ """
+
+ def create_token_response(self, request, token_handler):
+ """Return token or error in JSON format.
+
+ :param request: OAuthlib request.
+ :type request: oauthlib.common.Request
+ :param token_handler: A token handler instance, for example of type
+ oauthlib.oauth2.BearerToken.
+
+ If the access token request is valid and authorized, the
+ authorization server issues an access token as described in
+ `Section 5.1`_. A refresh token SHOULD NOT be included. If the request
+ failed client authentication or is invalid, the authorization server
+ returns an error response as described in `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 = self._get_default_headers()
+ try:
+ log.debug('Validating access token request, %r.', request)
+ self.validate_token_request(request)
+ except errors.OAuth2Error as e:
+ log.debug('Client error in token request. %s.', e)
+ headers.update(e.headers)
+ return headers, e.json, e.status_code
+
+ token = token_handler.create_token(request, refresh_token=False)
+
+ for modifier in self._token_modifiers:
+ token = modifier(token)
+
+ self.request_validator.save_token(token, request)
+
+ log.debug('Issuing token to client id %r (%r), %r.',
+ request.client_id, request.client, token)
+ return headers, json.dumps(token), 200
+
+ def validate_token_request(self, request):
+ """
+ :param request: OAuthlib request.
+ :type request: oauthlib.common.Request
+ """
+ for validator in self.custom_validators.pre_token:
+ validator(request)
+
+ if not getattr(request, 'grant_type', None):
+ raise errors.InvalidRequestError('Request is missing grant type.',
+ request=request)
+
+ if not request.grant_type == 'client_credentials':
+ raise errors.UnsupportedGrantTypeError(request=request)
+
+ for param in ('grant_type', 'scope'):
+ if param in request.duplicate_params:
+ raise errors.InvalidRequestError(description='Duplicate %s parameter.' % param,
+ request=request)
+
+ log.debug('Authenticating client, %r.', request)
+ if not self.request_validator.authenticate_client(request):
+ log.debug('Client authentication failed, %r.', request)
+ raise errors.InvalidClientError(request=request)
+ else:
+ if not hasattr(request.client, 'client_id'):
+ raise NotImplementedError('Authenticate client must set the '
+ 'request.client.client_id attribute '
+ 'in authenticate_client.')
+ # Ensure client is authorized use of this grant type
+ self.validate_grant_type(request)
+
+ request.client_id = request.client_id or request.client.client_id
+ log.debug('Authorizing access to client %r.', request.client_id)
+ self.validate_scopes(request)
+
+ for validator in self.custom_validators.post_token:
+ validator(request)
diff --git a/game/python-extra/oauthlib/oauth2/rfc6749/grant_types/implicit.py b/game/python-extra/oauthlib/oauth2/rfc6749/grant_types/implicit.py
new file mode 100644
index 0000000..6110b6f
--- /dev/null
+++ b/game/python-extra/oauthlib/oauth2/rfc6749/grant_types/implicit.py
@@ -0,0 +1,376 @@
+"""
+oauthlib.oauth2.rfc6749.grant_types
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+"""
+import logging
+
+from oauthlib import common
+
+from .. import errors
+from .base import GrantTypeBase
+
+log = logging.getLogger(__name__)
+
+
+class ImplicitGrant(GrantTypeBase):
+
+ """`Implicit Grant`_
+
+ The implicit grant type is used to obtain access tokens (it does not
+ support the issuance of refresh tokens) and is optimized for public
+ clients known to operate a particular redirection URI. These clients
+ are typically implemented in a browser using a scripting language
+ such as JavaScript.
+
+ Unlike the authorization code grant type, in which the client makes
+ separate requests for authorization and for an access token, the
+ client receives the access token as the result of the authorization
+ request.
+
+ The implicit grant type does not include client authentication, and
+ relies on the presence of the resource owner and the registration of
+ the redirection URI. Because the access token is encoded into the
+ redirection URI, it may be exposed to the resource owner and other
+ applications residing on the same device::
+
+ +----------+
+ | Resource |
+ | Owner |
+ | |
+ +----------+
+ ^
+ |
+ (B)
+ +----|-----+ Client Identifier +---------------+
+ | -+----(A)-- & Redirection URI --->| |
+ | User- | | Authorization |
+ | Agent -|----(B)-- User authenticates -->| Server |
+ | | | |
+ | |<---(C)--- Redirection URI ----<| |
+ | | with Access Token +---------------+
+ | | in Fragment
+ | | +---------------+
+ | |----(D)--- Redirection URI ---->| Web-Hosted |
+ | | without Fragment | Client |
+ | | | Resource |
+ | (F) |<---(E)------- Script ---------<| |
+ | | +---------------+
+ +-|--------+
+ | |
+ (A) (G) Access Token
+ | |
+ ^ v
+ +---------+
+ | |
+ | Client |
+ | |
+ +---------+
+
+ Note: The lines illustrating steps (A) and (B) are broken into two
+ parts as they pass through the user-agent.
+
+ Figure 4: Implicit Grant Flow
+
+ The flow illustrated in Figure 4 includes the following steps:
+
+ (A) The client initiates the flow by directing the resource owner's
+ user-agent to the authorization endpoint. The client includes
+ its client identifier, requested scope, local state, and a
+ redirection URI to which the authorization server will send the
+ user-agent back once access is granted (or denied).
+
+ (B) The authorization server authenticates the resource owner (via
+ the user-agent) and establishes whether the resource owner
+ grants or denies the client's access request.
+
+ (C) Assuming the resource owner grants access, the authorization
+ server redirects the user-agent back to the client using the
+ redirection URI provided earlier. The redirection URI includes
+ the access token in the URI fragment.
+
+ (D) The user-agent follows the redirection instructions by making a
+ request to the web-hosted client resource (which does not
+ include the fragment per [RFC2616]). The user-agent retains the
+ fragment information locally.
+
+ (E) The web-hosted client resource returns a web page (typically an
+ HTML document with an embedded script) capable of accessing the
+ full redirection URI including the fragment retained by the
+ user-agent, and extracting the access token (and other
+ parameters) contained in the fragment.
+
+ (F) The user-agent executes the script provided by the web-hosted
+ client resource locally, which extracts the access token.
+
+ (G) The user-agent passes the access token to the client.
+
+ See `Section 10.3`_ and `Section 10.16`_ for important security considerations
+ when using the implicit grant.
+
+ .. _`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']
+ grant_allows_refresh_token = False
+
+ def create_authorization_response(self, request, token_handler):
+ """Create an authorization response.
+
+ :param request: OAuthlib request.
+ :type request: oauthlib.common.Request
+ :param token_handler: A token handler instance, for example of type
+ oauthlib.oauth2.BearerToken.
+
+ The client constructs the request URI by adding the following
+ parameters to the query component of the authorization endpoint URI
+ using the "application/x-www-form-urlencoded" format, per `Appendix B`_:
+
+ response_type
+ REQUIRED. Value MUST be set to "token" for standard OAuth2 implicit flow
+ or "id_token token" or just "id_token" for OIDC implicit flow
+
+ client_id
+ REQUIRED. The client identifier as described in `Section 2.2`_.
+
+ redirect_uri
+ OPTIONAL. As described in `Section 3.1.2`_.
+
+ scope
+ OPTIONAL. The scope of the access request as described by
+ `Section 3.3`_.
+
+ state
+ RECOMMENDED. An opaque value used by the client to maintain
+ state between the request and callback. The authorization
+ server includes this value when redirecting the user-agent back
+ to the client. The parameter SHOULD be used for preventing
+ cross-site request forgery as described in `Section 10.12`_.
+
+ The authorization server validates the request to ensure that all
+ required parameters are present and valid. The authorization server
+ MUST verify that the redirection URI to which it will redirect the
+ access token matches a redirection URI registered by the client as
+ described in `Section 3.1.2`_.
+
+ .. _`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)
+
+ def create_token_response(self, request, token_handler):
+ """Return token or error embedded in the URI fragment.
+
+ :param request: OAuthlib request.
+ :type request: oauthlib.common.Request
+ :param token_handler: A token handler instance, for example of type
+ oauthlib.oauth2.BearerToken.
+
+ If the resource owner grants the access request, the authorization
+ server issues an access token and delivers it to 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`_:
+
+ access_token
+ REQUIRED. The access token issued by the authorization server.
+
+ token_type
+ REQUIRED. The type of the token issued as described in
+ `Section 7.1`_. Value is case insensitive.
+
+ expires_in
+ RECOMMENDED. The lifetime in seconds of the access token. For
+ example, the value "3600" denotes that the access token will
+ expire in one hour from the time the response was generated.
+ If omitted, the authorization server SHOULD provide the
+ expiration time via other means or document the default value.
+
+ scope
+ OPTIONAL, if identical to the scope requested by the client;
+ otherwise, REQUIRED. The scope of the access token as
+ described by `Section 3.3`_.
+
+ state
+ REQUIRED if the "state" parameter was present in the client
+ authorization request. The exact value received from the
+ client.
+
+ The authorization server MUST NOT issue a refresh token.
+
+ .. _`Appendix B`: https://tools.ietf.org/html/rfc6749#appendix-B
+ .. _`Section 3.3`: https://tools.ietf.org/html/rfc6749#section-3.3
+ .. _`Section 7.1`: https://tools.ietf.org/html/rfc6749#section-7.1
+ """
+ try:
+ self.validate_token_request(request)
+
+ # If the request fails due to a missing, invalid, or mismatching
+ # redirection URI, or if the client identifier is missing or invalid,
+ # the authorization server SHOULD inform the resource owner of the
+ # error and MUST NOT automatically redirect the user-agent to the
+ # invalid redirection URI.
+ except errors.FatalClientError as e:
+ log.debug('Fatal client error during validation of %r. %r.',
+ request, e)
+ raise
+
+ # If the resource owner denies the access request or if the request
+ # fails for reasons other than a missing or invalid redirection URI,
+ # the authorization server informs the client by adding the following
+ # parameters to the fragment component of the redirection URI using the
+ # "application/x-www-form-urlencoded" format, per Appendix B:
+ # https://tools.ietf.org/html/rfc6749#appendix-B
+ except errors.OAuth2Error as e:
+ log.debug('Client error during validation of %r. %r.', request, e)
+ return {'Location': common.add_params_to_uri(request.redirect_uri, e.twotuples,
+ fragment=True)}, None, 302
+
+ # In OIDC implicit flow it is possible to have a request_type that does not include the access_token!
+ # "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():
+ token = token_handler.create_token(request, refresh_token=False)
+ else:
+ token = {}
+
+ if request.state is not None:
+ token['state'] = request.state
+
+ for modifier in self._token_modifiers:
+ token = modifier(token, token_handler, 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)
+
+ def validate_authorization_request(self, request):
+ """
+ :param request: OAuthlib request.
+ :type request: oauthlib.common.Request
+ """
+ return self.validate_token_request(request)
+
+ def validate_token_request(self, request):
+ """Check the token request for normal and fatal errors.
+
+ :param request: OAuthlib request.
+ :type request: oauthlib.common.Request
+
+ This method is very similar to validate_authorization_request in
+ the AuthorizationCodeGrant but differ in a few subtle areas.
+
+ A normal error could be a missing response_type parameter or the client
+ attempting to access scope it is not allowed to ask authorization for.
+ Normal errors can safely be included in the redirection URI and
+ sent back to the client.
+
+ Fatal errors occur when the client_id or redirect_uri is invalid or
+ missing. These must be caught by the provider and handled, how this
+ is done is outside of the scope of OAuthLib but showing an error
+ page describing the issue is a good idea.
+ """
+
+ # First check for fatal errors
+
+ # If the request fails due to a missing, invalid, or mismatching
+ # redirection URI, or if the client identifier is missing or invalid,
+ # the authorization server SHOULD inform the resource owner of the
+ # error and MUST NOT automatically redirect the user-agent to the
+ # invalid redirection URI.
+
+ # First check duplicate parameters
+ for param in ('client_id', 'response_type', 'redirect_uri', 'scope', 'state'):
+ try:
+ duplicate_params = request.duplicate_params
+ except ValueError:
+ raise errors.InvalidRequestFatalError(description='Unable to parse query string', request=request)
+ if param in duplicate_params:
+ raise errors.InvalidRequestFatalError(description='Duplicate %s parameter.' % param, request=request)
+
+ # REQUIRED. The client identifier as described in Section 2.2.
+ # https://tools.ietf.org/html/rfc6749#section-2.2
+ if not request.client_id:
+ raise errors.MissingClientIdError(request=request)
+
+ if not self.request_validator.validate_client_id(request.client_id, request):
+ raise errors.InvalidClientIdError(request=request)
+
+ # OPTIONAL. As described in Section 3.1.2.
+ # https://tools.ietf.org/html/rfc6749#section-3.1.2
+ self._handle_redirects(request)
+
+ # Then check for normal errors.
+
+ request_info = self._run_custom_validators(request,
+ self.custom_validators.all_pre)
+
+ # If the resource owner denies the access request or if the request
+ # fails for reasons other than a missing or invalid redirection URI,
+ # the authorization server informs the client by adding the following
+ # parameters to the fragment component of the redirection URI using the
+ # "application/x-www-form-urlencoded" format, per Appendix B.
+ # 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
+
+ # REQUIRED.
+ if request.response_type is None:
+ raise errors.MissingResponseTypeError(request=request)
+ # Value MUST be one of our registered types: "token" by default or if using OIDC "id_token" or "id_token token"
+ elif not set(request.response_type.split()).issubset(self.response_types):
+ raise errors.UnsupportedResponseTypeError(request=request)
+
+ log.debug('Validating use of response_type token for client %r (%r).',
+ request.client_id, request.client)
+ if not self.request_validator.validate_response_type(request.client_id,
+ request.response_type,
+ request.client, request):
+
+ log.debug('Client %s is not authorized to use response_type %s.',
+ request.client_id, request.response_type)
+ raise errors.UnauthorizedClientError(request=request)
+
+ # OPTIONAL. The scope of the access request as described by Section 3.3
+ # https://tools.ietf.org/html/rfc6749#section-3.3
+ self.validate_scopes(request)
+
+ request_info.update({
+ 'client_id': request.client_id,
+ 'redirect_uri': request.redirect_uri,
+ 'response_type': request.response_type,
+ 'state': request.state,
+ 'request': request,
+ })
+
+ 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,
+ request_info=None):
+ # Make a copy so we don't modify the existing request_info dict
+ request_info = {} if request_info is None else request_info.copy()
+ # For implicit grant, auth_validators and token_validators are
+ # basically equivalent since the token is returned from the
+ # authorization endpoint.
+ for validator in validations:
+ result = validator(request)
+ if result is not None:
+ request_info.update(result)
+ return request_info
diff --git a/game/python-extra/oauthlib/oauth2/rfc6749/grant_types/refresh_token.py b/game/python-extra/oauthlib/oauth2/rfc6749/grant_types/refresh_token.py
new file mode 100644
index 0000000..ce33df0
--- /dev/null
+++ b/game/python-extra/oauthlib/oauth2/rfc6749/grant_types/refresh_token.py
@@ -0,0 +1,136 @@
+"""
+oauthlib.oauth2.rfc6749.grant_types
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+"""
+import json
+import logging
+
+from .. import errors, utils
+from .base import GrantTypeBase
+
+log = logging.getLogger(__name__)
+
+
+class RefreshTokenGrant(GrantTypeBase):
+
+ """`Refresh token grant`_
+
+ .. _`Refresh token grant`: https://tools.ietf.org/html/rfc6749#section-6
+ """
+
+ def __init__(self, request_validator=None,
+ issue_new_refresh_tokens=True,
+ **kwargs):
+ super().__init__(
+ request_validator,
+ issue_new_refresh_tokens=issue_new_refresh_tokens,
+ **kwargs)
+
+ def create_token_response(self, request, token_handler):
+ """Create a new access token from a refresh_token.
+
+ :param request: OAuthlib request.
+ :type request: oauthlib.common.Request
+ :param token_handler: A token handler instance, for example of type
+ oauthlib.oauth2.BearerToken.
+
+ If valid and authorized, the authorization server issues an access
+ token as described in `Section 5.1`_. If the request failed
+ verification or is invalid, the authorization server returns an error
+ response as described in `Section 5.2`_.
+
+ The authorization server MAY issue a new refresh token, in which case
+ the client MUST discard the old refresh token and replace it with the
+ new refresh token. The authorization server MAY revoke the old
+ refresh token after issuing a new refresh token to the client. If a
+ new refresh token is issued, the refresh token scope MUST be
+ identical to that of the refresh token included by the client in the
+ request.
+
+ .. _`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 = self._get_default_headers()
+ try:
+ log.debug('Validating refresh token request, %r.', request)
+ self.validate_token_request(request)
+ except errors.OAuth2Error as e:
+ log.debug('Client error in token request, %s.', e)
+ headers.update(e.headers)
+ return headers, e.json, e.status_code
+
+ token = token_handler.create_token(request,
+ refresh_token=self.issue_new_refresh_tokens)
+
+ for modifier in self._token_modifiers:
+ token = modifier(token, token_handler, request)
+
+ self.request_validator.save_token(token, request)
+
+ log.debug('Issuing new token to client id %r (%r), %r.',
+ request.client_id, request.client, token)
+ headers.update(self._create_cors_headers(request))
+ return headers, json.dumps(token), 200
+
+ def validate_token_request(self, request):
+ """
+ :param request: OAuthlib request.
+ :type request: oauthlib.common.Request
+ """
+ # REQUIRED. Value MUST be set to "refresh_token".
+ if request.grant_type != 'refresh_token':
+ raise errors.UnsupportedGrantTypeError(request=request)
+
+ for validator in self.custom_validators.pre_token:
+ validator(request)
+
+ if request.refresh_token is None:
+ raise errors.InvalidRequestError(
+ description='Missing refresh token parameter.',
+ request=request)
+
+ # Because refresh tokens are typically long-lasting credentials used to
+ # request additional access tokens, the refresh token is bound to the
+ # client to which it was issued. If the client type is confidential or
+ # 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.
+ # 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):
+ log.debug('Invalid client (%r), denying access.', request)
+ raise errors.InvalidClientError(request=request)
+ elif not self.request_validator.authenticate_client_id(request.client_id, request):
+ log.debug('Client authentication failed, %r.', request)
+ raise errors.InvalidClientError(request=request)
+
+ # Ensure client is authorized use of this grant type
+ self.validate_grant_type(request)
+
+ # REQUIRED. The refresh token issued to the client.
+ log.debug('Validating refresh token %s for client %r.',
+ request.refresh_token, request.client)
+ if not self.request_validator.validate_refresh_token(
+ request.refresh_token, request.client, request):
+ log.debug('Invalid refresh token, %s, for client %r.',
+ request.refresh_token, request.client)
+ raise errors.InvalidGrantError(request=request)
+
+ original_scopes = utils.scope_to_list(
+ self.request_validator.get_original_scopes(
+ request.refresh_token, request))
+
+ if request.scope:
+ request.scopes = utils.scope_to_list(request.scope)
+ if (not all(s in original_scopes for s in request.scopes)
+ and not self.request_validator.is_within_original_scope(
+ request.scopes, request.refresh_token, request)):
+ log.debug('Refresh token %s lack requested scopes, %r.',
+ request.refresh_token, request.scopes)
+ raise errors.InvalidScopeError(request=request)
+ else:
+ request.scopes = original_scopes
+
+ for validator in self.custom_validators.post_token:
+ validator(request)
diff --git a/game/python-extra/oauthlib/oauth2/rfc6749/grant_types/resource_owner_password_credentials.py b/game/python-extra/oauthlib/oauth2/rfc6749/grant_types/resource_owner_password_credentials.py
new file mode 100644
index 0000000..4b0de5b
--- /dev/null
+++ b/game/python-extra/oauthlib/oauth2/rfc6749/grant_types/resource_owner_password_credentials.py
@@ -0,0 +1,199 @@
+"""
+oauthlib.oauth2.rfc6749.grant_types
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+"""
+import json
+import logging
+
+from .. import errors
+from .base import GrantTypeBase
+
+log = logging.getLogger(__name__)
+
+
+class ResourceOwnerPasswordCredentialsGrant(GrantTypeBase):
+
+ """`Resource Owner Password Credentials Grant`_
+
+ The resource owner password credentials grant type is suitable in
+ cases where the resource owner has a trust relationship with the
+ client, such as the device operating system or a highly privileged
+ application. The authorization server should take special care when
+ enabling this grant type and only allow it when other flows are not
+ viable.
+
+ This grant type is suitable for clients capable of obtaining the
+ resource owner's credentials (username and password, typically using
+ an interactive form). It is also used to migrate existing clients
+ using direct authentication schemes such as HTTP Basic or Digest
+ authentication to OAuth by converting the stored credentials to an
+ access token::
+
+ +----------+
+ | Resource |
+ | Owner |
+ | |
+ +----------+
+ v
+ | Resource Owner
+ (A) Password Credentials
+ |
+ v
+ +---------+ +---------------+
+ | |>--(B)---- Resource Owner ------->| |
+ | | Password Credentials | Authorization |
+ | Client | | Server |
+ | |<--(C)---- Access Token ---------<| |
+ | | (w/ Optional Refresh Token) | |
+ +---------+ +---------------+
+
+ Figure 5: Resource Owner Password Credentials Flow
+
+ The flow illustrated in Figure 5 includes the following steps:
+
+ (A) The resource owner provides the client with its username and
+ password.
+
+ (B) The client requests an access token from the authorization
+ server's token endpoint by including the credentials received
+ from the resource owner. When making the request, the client
+ authenticates with the authorization server.
+
+ (C) The authorization server authenticates the client and validates
+ the resource owner credentials, and if valid, issues an access
+ token.
+
+ .. _`Resource Owner Password Credentials Grant`: https://tools.ietf.org/html/rfc6749#section-4.3
+ """
+
+ def create_token_response(self, request, token_handler):
+ """Return token or error in json format.
+
+ :param request: OAuthlib request.
+ :type request: oauthlib.common.Request
+ :param token_handler: A token handler instance, for example of type
+ oauthlib.oauth2.BearerToken.
+
+ If the access token request is valid and authorized, the
+ authorization server issues an access token and optional refresh
+ token as described in `Section 5.1`_. If the request failed client
+ authentication or is invalid, the authorization server returns an
+ error response as described in `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 = self._get_default_headers()
+ try:
+ if self.request_validator.client_authentication_required(request):
+ log.debug('Authenticating client, %r.', request)
+ 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):
+ log.debug('Client authentication failed, %r.', request)
+ raise errors.InvalidClientError(request=request)
+ log.debug('Validating access token request, %r.', request)
+ self.validate_token_request(request)
+ except errors.OAuth2Error as e:
+ log.debug('Client error in token request, %s.', e)
+ headers.update(e.headers)
+ return headers, e.json, e.status_code
+
+ token = token_handler.create_token(request, self.refresh_token)
+
+ for modifier in self._token_modifiers:
+ token = modifier(token)
+
+ self.request_validator.save_token(token, request)
+
+ log.debug('Issuing token %r to client id %r (%r) and username %s.',
+ token, request.client_id, request.client, request.username)
+ return headers, json.dumps(token), 200
+
+ def validate_token_request(self, request):
+ """
+ :param request: OAuthlib request.
+ :type request: oauthlib.common.Request
+
+ The client makes a request to the token endpoint by adding the
+ following parameters using the "application/x-www-form-urlencoded"
+ format per Appendix B with a character encoding of UTF-8 in the HTTP
+ request entity-body:
+
+ grant_type
+ REQUIRED. Value MUST be set to "password".
+
+ username
+ REQUIRED. The resource owner username.
+
+ password
+ REQUIRED. The resource owner password.
+
+ scope
+ OPTIONAL. The scope of the access request as described by
+ `Section 3.3`_.
+
+ If the client type is confidential or 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`_.
+
+ The authorization server MUST:
+
+ o require client authentication for confidential clients or for any
+ client that was issued client credentials (or with other
+ authentication requirements),
+
+ o authenticate the client if client authentication is included, and
+
+ o validate the resource owner password credentials using its
+ existing password validation algorithm.
+
+ Since this access token request utilizes the resource owner's
+ password, the authorization server MUST protect the endpoint against
+ brute force attacks (e.g., using rate-limitation or generating
+ alerts).
+
+ .. _`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)
+
+ for param in ('grant_type', 'username', 'password'):
+ if not getattr(request, param, None):
+ raise errors.InvalidRequestError(
+ 'Request is missing %s parameter.' % param, request=request)
+
+ for param in ('grant_type', 'username', 'password', 'scope'):
+ if param in request.duplicate_params:
+ raise errors.InvalidRequestError(description='Duplicate %s parameter.' % param, request=request)
+
+ # This error should rarely (if ever) occur if requests are routed to
+ # grant type handlers based on the grant_type parameter.
+ if not request.grant_type == 'password':
+ raise errors.UnsupportedGrantTypeError(request=request)
+
+ log.debug('Validating username %s.', request.username)
+ if not self.request_validator.validate_user(request.username,
+ request.password, request.client, request):
+ raise errors.InvalidGrantError(
+ 'Invalid credentials given.', request=request)
+ else:
+ if not hasattr(request.client, 'client_id'):
+ raise NotImplementedError(
+ 'Validate user must set the '
+ 'request.client.client_id attribute '
+ 'in authenticate_client.')
+ log.debug('Authorizing access to user %r.', request.user)
+
+ # Ensure client is authorized use of this grant type
+ self.validate_grant_type(request)
+
+ if request.client:
+ request.client_id = request.client_id or request.client.client_id
+ self.validate_scopes(request)
+
+ for validator in self.custom_validators.post_token:
+ validator(request)
diff --git a/game/python-extra/oauthlib/oauth2/rfc6749/parameters.py b/game/python-extra/oauthlib/oauth2/rfc6749/parameters.py
new file mode 100644
index 0000000..8f6ce2c
--- /dev/null
+++ b/game/python-extra/oauthlib/oauth2/rfc6749/parameters.py
@@ -0,0 +1,471 @@
+"""
+oauthlib.oauth2.rfc6749.parameters
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This module contains methods related to `Section 4`_ of the OAuth 2 RFC.
+
+.. _`Section 4`: https://tools.ietf.org/html/rfc6749#section-4
+"""
+import json
+import os
+import time
+import urllib.parse as urlparse
+
+from oauthlib.common import add_params_to_qs, add_params_to_uri
+from oauthlib.signals import scope_changed
+
+from .errors import (
+ InsecureTransportError, MismatchingStateError, MissingCodeError,
+ MissingTokenError, MissingTokenTypeError, raise_from_error,
+)
+from .tokens import OAuth2Token
+from .utils import is_secure_transport, list_to_scope, scope_to_list
+
+
+def prepare_grant_uri(uri, client_id, response_type, redirect_uri=None,
+ scope=None, state=None, code_challenge=None, code_challenge_method='plain', **kwargs):
+ """Prepare the authorization grant request URI.
+
+ The client constructs the request URI by adding the following
+ parameters to the query component of the authorization endpoint URI
+ using the ``application/x-www-form-urlencoded`` format as defined by
+ [`W3C.REC-html401-19991224`_]:
+
+ :param uri:
+ :param client_id: The client identifier as described in `Section 2.2`_.
+ :param response_type: To indicate which OAuth 2 grant/flow is required,
+ "code" and "token".
+ :param redirect_uri: The client provided URI to redirect back to after
+ authorization as described in `Section 3.1.2`_.
+ :param scope: The scope of the access request as described by
+ `Section 3.3`_.
+ :param state: An opaque value used by the client to maintain
+ state between the request and callback. The authorization
+ server includes this value when redirecting the user-agent
+ back to the client. The parameter SHOULD be used for
+ preventing cross-site request forgery as described in
+ `Section 10.12`_.
+ :param code_challenge: PKCE parameter. A challenge derived from the
+ code_verifier that is sent in the authorization
+ request, to be verified against later.
+ :param code_challenge_method: PKCE parameter. A method that was used to derive the
+ code_challenge. Defaults to "plain" if not present in the request.
+ :param kwargs: Extra arguments to embed in the grant/authorization URL.
+
+ An example of an authorization code grant authorization URL:
+
+ .. code-block:: http
+
+ GET /authorize?response_type=code&client_id=s6BhdRkqt3&state=xyz
+ &code_challenge=kjasBS523KdkAILD2k78NdcJSk2k3KHG6&code_challenge_method=S256
+ &redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb HTTP/1.1
+ Host: server.example.com
+
+ .. _`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()
+
+ params = [(('response_type', response_type)),
+ (('client_id', client_id))]
+
+ if redirect_uri:
+ params.append(('redirect_uri', redirect_uri))
+ if scope:
+ params.append(('scope', list_to_scope(scope)))
+ if state:
+ params.append(('state', state))
+ if code_challenge is not None:
+ params.append(('code_challenge', code_challenge))
+ params.append(('code_challenge_method', code_challenge_method))
+
+ for k in kwargs:
+ if kwargs[k]:
+ params.append((str(k), kwargs[k]))
+
+ return add_params_to_uri(uri, params)
+
+
+def prepare_token_request(grant_type, body='', include_client_id=True, code_verifier=None, **kwargs):
+ """Prepare the access token request.
+
+ The client makes a request to the token endpoint by adding the
+ following parameters using the ``application/x-www-form-urlencoded``
+ format in the HTTP request entity-body:
+
+ :param grant_type: To indicate grant type being used, i.e. "password",
+ "authorization_code" or "client_credentials".
+
+ :param body: Existing request body (URL encoded string) to embed parameters
+ into. This may contain extra parameters. Default ''.
+
+ :param include_client_id: `True` (default) to send the `client_id` in the
+ body of the upstream request. This is required
+ if the client is not authenticating with the
+ authorization server as described in
+ `Section 3.2.1`_.
+ :type include_client_id: Boolean
+
+ :param client_id: Unicode client identifier. Will only appear if
+ `include_client_id` is True. *
+
+ :param client_secret: Unicode client secret. Will only appear if set to a
+ value that is not `None`. Invoking this function with
+ an empty string will send an empty `client_secret`
+ value to the server. *
+
+ :param code: If using authorization_code grant, pass the previously
+ obtained authorization code as the ``code`` argument. *
+
+ :param redirect_uri: If the "redirect_uri" parameter was included in the
+ authorization request as described in
+ `Section 4.1.1`_, and their values MUST be identical. *
+
+ :param code_verifier: PKCE parameter. A cryptographically random string that is used to correlate the
+ authorization request to the token request.
+
+ :param kwargs: Extra arguments to embed in the request body.
+
+ Parameters marked with a `*` above are not explicit arguments in the
+ function signature, but are specially documented arguments for items
+ appearing in the generic `**kwargs` keyworded input.
+
+ An example of an authorization code token request body:
+
+ .. code-block:: http
+
+ grant_type=authorization_code&code=SplxlOBeZQQYbYS6WxSbIA
+ &redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb
+
+ .. _`Section 4.1.1`: https://tools.ietf.org/html/rfc6749#section-4.1.1
+ """
+ params = [('grant_type', grant_type)]
+
+ if 'scope' in kwargs:
+ kwargs['scope'] = list_to_scope(kwargs['scope'])
+
+ # pull the `client_id` out of the kwargs.
+ client_id = kwargs.pop('client_id', None)
+ if include_client_id:
+ if client_id is not None:
+ params.append(('client_id', client_id))
+
+ # use code_verifier if code_challenge was passed in the authorization request
+ if code_verifier is not None:
+ params.append(('code_verifier', code_verifier))
+
+ # the kwargs iteration below only supports including boolean truth (truthy)
+ # values, but some servers may require an empty string for `client_secret`
+ client_secret = kwargs.pop('client_secret', None)
+ if client_secret is not None:
+ params.append(('client_secret', client_secret))
+
+ # this handles: `code`, `redirect_uri`, and other undocumented params
+ for k in kwargs:
+ if kwargs[k]:
+ params.append((str(k), kwargs[k]))
+
+ return add_params_to_qs(body, params)
+
+
+def prepare_token_revocation_request(url, token, token_type_hint="access_token",
+ callback=None, body='', **kwargs):
+ """Prepare a token revocation request.
+
+ The client constructs the request by including the following parameters
+ using the ``application/x-www-form-urlencoded`` format in the HTTP request
+ entity-body:
+
+ :param token: REQUIRED. The token that the client wants to get revoked.
+
+ :param 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 values for `token_type_hint`:
+
+ * 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`_.
+
+ .. _`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):
+ raise InsecureTransportError()
+
+ params = [('token', token)]
+
+ if token_type_hint:
+ params.append(('token_type_hint', token_type_hint))
+
+ for k in kwargs:
+ if kwargs[k]:
+ params.append((str(k), kwargs[k]))
+
+ headers = {'Content-Type': 'application/x-www-form-urlencoded'}
+
+ if callback:
+ params.append(('callback', callback))
+ return add_params_to_uri(url, params), headers, body
+ else:
+ return url, headers, add_params_to_qs(body, params)
+
+
+def parse_authorization_code_response(uri, state=None):
+ """Parse authorization grant response URI into a dict.
+
+ If the resource owner grants the access request, the authorization
+ server issues an authorization code and delivers it to the client by
+ adding the following parameters to the query component of the
+ redirection URI using the ``application/x-www-form-urlencoded`` format:
+
+ **code**
+ REQUIRED. The authorization code generated by the
+ authorization server. The authorization code MUST expire
+ shortly after it is issued to mitigate the risk of leaks. A
+ maximum authorization code lifetime of 10 minutes is
+ RECOMMENDED. The client MUST NOT use the authorization code
+ more than once. If an authorization code is used more than
+ once, the authorization server MUST deny the request and SHOULD
+ revoke (when possible) all tokens previously issued based on
+ that authorization code. The authorization code is bound to
+ the client identifier and redirection URI.
+
+ **state**
+ REQUIRED if the "state" parameter was present in the client
+ authorization request. The exact value received from the
+ client.
+
+ :param uri: The full redirect URL back to the client.
+ :param state: The state parameter from the authorization request.
+
+ For example, the authorization server redirects the user-agent by
+ sending the following HTTP response:
+
+ .. code-block:: http
+
+ HTTP/1.1 302 Found
+ Location: https://client.example.com/cb?code=SplxlOBeZQQYbYS6WxSbIA
+ &state=xyz
+
+ """
+ if not is_secure_transport(uri):
+ raise InsecureTransportError()
+
+ query = urlparse.urlparse(uri).query
+ params = dict(urlparse.parse_qsl(query))
+
+ if state and params.get('state', None) != state:
+ raise MismatchingStateError()
+
+ if 'error' in params:
+ raise_from_error(params.get('error'), params)
+
+ if not 'code' in params:
+ raise MissingCodeError("Missing code parameter in response.")
+
+ return params
+
+
+def parse_implicit_response(uri, state=None, scope=None):
+ """Parse the implicit token response URI into a dict.
+
+ If the resource owner grants the access request, the authorization
+ server issues an access token and delivers it to the client by adding
+ the following parameters to the fragment component of the redirection
+ URI using the ``application/x-www-form-urlencoded`` format:
+
+ **access_token**
+ REQUIRED. The access token issued by the authorization server.
+
+ **token_type**
+ REQUIRED. The type of the token issued as described in
+ Section 7.1. Value is case insensitive.
+
+ **expires_in**
+ RECOMMENDED. The lifetime in seconds of the access token. For
+ example, the value "3600" denotes that the access token will
+ expire in one hour from the time the response was generated.
+ If omitted, the authorization server SHOULD provide the
+ expiration time via other means or document the default value.
+
+ **scope**
+ OPTIONAL, if identical to the scope requested by the client,
+ otherwise REQUIRED. The scope of the access token as described
+ by Section 3.3.
+
+ **state**
+ REQUIRED if the "state" parameter was present in the client
+ authorization request. The exact value received from the
+ client.
+
+ :param uri:
+ :param state:
+ :param scope:
+
+ Similar to the authorization code response, but with a full token provided
+ in the URL fragment:
+
+ .. code-block:: http
+
+ HTTP/1.1 302 Found
+ Location: http://example.com/cb#access_token=2YotnFZFEjr1zCsicMWpAA
+ &state=xyz&token_type=example&expires_in=3600
+ """
+ if not is_secure_transport(uri):
+ raise InsecureTransportError()
+
+ fragment = urlparse.urlparse(uri).fragment
+ params = dict(urlparse.parse_qsl(fragment, keep_blank_values=True))
+
+ 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' in params:
+ params['expires_at'] = time.time() + int(params['expires_in'])
+
+ if state and params.get('state', None) != state:
+ raise ValueError("Mismatching or missing state in params.")
+
+ params = OAuth2Token(params, old_scope=scope)
+ validate_token_parameters(params)
+ return params
+
+
+def parse_token_response(body, scope=None):
+ """Parse the JSON token response body into a dict.
+
+ The authorization server issues an access token and optional refresh
+ token, and constructs the response by adding the following parameters
+ to the entity body of the HTTP response with a 200 (OK) status code:
+
+ access_token
+ REQUIRED. The access token issued by the authorization server.
+ token_type
+ REQUIRED. The type of the token issued as described in
+ `Section 7.1`_. Value is case insensitive.
+ expires_in
+ RECOMMENDED. The lifetime in seconds of the access token. For
+ example, the value "3600" denotes that the access token will
+ expire in one hour from the time the response was generated.
+ If omitted, the authorization server SHOULD provide the
+ expiration time via other means or document the default value.
+ refresh_token
+ OPTIONAL. The refresh token which can be used to obtain new
+ access tokens using the same authorization grant as described
+ in `Section 6`_.
+ scope
+ OPTIONAL, if identical to the scope requested by the client,
+ otherwise REQUIRED. The scope of the access token as described
+ by `Section 3.3`_.
+
+ The parameters are included in the entity body of the HTTP response
+ using the "application/json" media type as defined by [`RFC4627`_]. The
+ parameters are serialized into a JSON structure by adding each
+ parameter at the highest structure level. Parameter names and string
+ values are included as JSON strings. Numerical values are included
+ as JSON numbers. The order of parameters does not matter and can
+ vary.
+
+ :param body: The full json encoded response body.
+ :param scope: The scope requested during authorization.
+
+ For example:
+
+ .. code-block:: http
+
+ HTTP/1.1 200 OK
+ Content-Type: application/json
+ Cache-Control: no-store
+ Pragma: no-cache
+
+ {
+ "access_token":"2YotnFZFEjr1zCsicMWpAA",
+ "token_type":"example",
+ "expires_in":3600,
+ "refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA",
+ "example_parameter":"example_value"
+ }
+
+ .. _`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)
+ except ValueError:
+
+ # Fall back to URL-encoded string, to support old implementations,
+ # including (at time of writing) Facebook. See:
+ # https://github.com/oauthlib/oauthlib/issues/267
+
+ params = dict(urlparse.parse_qsl(body))
+ 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' in params:
+ if params['expires_in'] is None:
+ params.pop('expires_in')
+ else:
+ params['expires_at'] = time.time() + int(params['expires_in'])
+
+ params = OAuth2Token(params, old_scope=scope)
+ validate_token_parameters(params)
+ return params
+
+
+def validate_token_parameters(params):
+ """Ensures token presence, token type, expiration and scope in params."""
+ if 'error' in params:
+ raise_from_error(params.get('error'), params)
+
+ if not 'access_token' in params:
+ raise MissingTokenError(description="Missing access token parameter.")
+
+ if not 'token_type' in params:
+ if os.environ.get('OAUTHLIB_STRICT_TOKEN_TYPE'):
+ raise MissingTokenTypeError()
+
+ # 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.
+ # 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,
+ )
+ scope_changed.send(message=message, old=params.old_scopes, new=params.scopes)
+ if not os.environ.get('OAUTHLIB_RELAX_TOKEN_SCOPE', None):
+ w = Warning(message)
+ w.token = params
+ w.old_scope = params.old_scopes
+ w.new_scope = params.scopes
+ raise w
diff --git a/game/python-extra/oauthlib/oauth2/rfc6749/request_validator.py b/game/python-extra/oauthlib/oauth2/rfc6749/request_validator.py
new file mode 100644
index 0000000..3910c0b
--- /dev/null
+++ b/game/python-extra/oauthlib/oauth2/rfc6749/request_validator.py
@@ -0,0 +1,680 @@
+"""
+oauthlib.oauth2.rfc6749.request_validator
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+"""
+import logging
+
+log = logging.getLogger(__name__)
+
+
+class RequestValidator:
+
+ def client_authentication_required(self, request, *args, **kwargs):
+ """Determine if client authentication is required for current request.
+
+ According to the rfc6749, client authentication is required in the following cases:
+ - Resource Owner Password Credentials Grant, when Client type is Confidential or when
+ Client was issued client credentials or whenever Client provided client
+ authentication, see `Section 4.3.2`_.
+ - Authorization Code Grant, when Client type is Confidential or when Client was issued
+ client credentials or whenever Client provided client authentication,
+ see `Section 4.1.3`_.
+ - Refresh Token Grant, when Client type is Confidential or when Client was issued
+ client credentials or whenever Client provided client authentication, see
+ `Section 6`_
+
+ :param request: OAuthlib request.
+ :type request: oauthlib.common.Request
+ :rtype: True or False
+
+ Method is used by:
+ - Authorization Code Grant
+ - Resource Owner Password Credentials Grant
+ - Refresh Token Grant
+
+ .. _`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
+
+ def authenticate_client(self, request, *args, **kwargs):
+ """Authenticate client through means outside the OAuth 2 spec.
+
+ Means of authentication is negotiated beforehand and may for example
+ be `HTTP Basic Authentication Scheme`_ which utilizes the Authorization
+ header.
+
+ Headers may be accesses through request.headers and parameters found in
+ both body and query can be obtained by direct attribute access, i.e.
+ request.client_id for client_id in the URL query.
+
+ The authentication process is required to contain the identification of
+ the client (i.e. search the database based on the client_id). In case the
+ client doesn't exist based on the received client_id, this method has to
+ return False and the HTTP response created by the library will contain
+ 'invalid_client' message.
+
+ After the client identification succeeds, this method needs to set the
+ client on the request, i.e. request.client = client. A client object's
+ class must contain the 'client_id' attribute and the 'client_id' must have
+ a value.
+
+ :param request: OAuthlib request.
+ :type request: oauthlib.common.Request
+ :rtype: True or False
+
+ Method is used by:
+ - Authorization Code Grant
+ - Resource Owner Password Credentials Grant (may be disabled)
+ - Client Credentials Grant
+ - Refresh Token Grant
+
+ .. _`HTTP Basic Authentication Scheme`: https://tools.ietf.org/html/rfc1945#section-11.1
+ """
+ raise NotImplementedError('Subclasses must implement this method.')
+
+ def authenticate_client_id(self, client_id, request, *args, **kwargs):
+ """Ensure client_id belong to a non-confidential client.
+
+ A non-confidential client is one that is not required to authenticate
+ through other means, such as using HTTP Basic.
+
+ Note, while not strictly necessary it can often be very convenient
+ to set request.client to the client object associated with the
+ given client_id.
+
+ :param client_id: Unicode client identifier.
+ :param request: OAuthlib request.
+ :type request: oauthlib.common.Request
+ :rtype: True or False
+
+ Method is used by:
+ - Authorization Code Grant
+ """
+ raise NotImplementedError('Subclasses must implement this method.')
+
+ 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'.
+
+ If the client specifies a redirect_uri when obtaining code then that
+ redirect URI must be bound to the code and verified equal in this
+ method, according to RFC 6749 section 4.1.3. Do not compare against
+ the client's allowed redirect URIs, but against the URI used when the
+ code was saved.
+
+ :param client_id: Unicode client identifier.
+ :param code: Unicode authorization_code.
+ :param redirect_uri: Unicode absolute URI.
+ :param client: Client object set by you, see ``.authenticate_client``.
+ :param request: OAuthlib request.
+ :type request: oauthlib.common.Request
+ :rtype: True or False
+
+ Method is used by:
+ - Authorization Code Grant (during token request)
+ """
+ raise NotImplementedError('Subclasses must implement this method.')
+
+ def get_default_redirect_uri(self, client_id, request, *args, **kwargs):
+ """Get the default redirect URI for the client.
+
+ :param client_id: Unicode client identifier.
+ :param request: OAuthlib request.
+ :type request: oauthlib.common.Request
+ :rtype: The default redirect URI for the client
+
+ Method is used by:
+ - Authorization Code Grant
+ - Implicit Grant
+ """
+ raise NotImplementedError('Subclasses must implement this method.')
+
+ def get_default_scopes(self, client_id, request, *args, **kwargs):
+ """Get the default scopes for the client.
+
+ :param client_id: Unicode client identifier.
+ :param request: OAuthlib request.
+ :type request: oauthlib.common.Request
+ :rtype: List of default scopes
+
+ Method is used by all core grant types:
+ - Authorization Code Grant
+ - Implicit Grant
+ - Resource Owner Password Credentials Grant
+ - Client Credentials grant
+ """
+ raise NotImplementedError('Subclasses must implement this method.')
+
+ def get_original_scopes(self, refresh_token, request, *args, **kwargs):
+ """Get the list of scopes associated with the refresh token.
+
+ :param refresh_token: Unicode refresh token.
+ :param request: OAuthlib request.
+ :type request: oauthlib.common.Request
+ :rtype: List of scopes.
+
+ Method is used by:
+ - Refresh token grant
+ """
+ raise NotImplementedError('Subclasses must implement this method.')
+
+ def is_within_original_scope(self, request_scopes, refresh_token, request, *args, **kwargs):
+ """Check if requested scopes are within a scope of the refresh token.
+
+ When access tokens are refreshed the scope of the new token
+ needs to be within the scope of the original token. This is
+ ensured by checking that all requested scopes strings are on
+ the list returned by the get_original_scopes. If this check
+ fails, is_within_original_scope is called. The method can be
+ used in situations where returning all valid scopes from the
+ get_original_scopes is not practical.
+
+ :param request_scopes: A list of scopes that were requested by client.
+ :param refresh_token: Unicode refresh_token.
+ :param request: OAuthlib request.
+ :type request: oauthlib.common.Request
+ :rtype: True or False
+
+ Method is used by:
+ - Refresh token grant
+ """
+ return False
+
+ def introspect_token(self, token, token_type_hint, request, *args, **kwargs):
+ """Introspect an access or refresh token.
+
+ 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
+ efficiency, but must fallback to other types 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: OAuthlib request.
+ :type request: oauthlib.common.Request
+
+ 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.')
+
+ def invalidate_authorization_code(self, client_id, code, request, *args, **kwargs):
+ """Invalidate an authorization code after use.
+
+ :param client_id: Unicode client identifier.
+ :param code: The authorization code grant (request.code).
+ :param request: OAuthlib request.
+ :type request: oauthlib.common.Request
+
+ Method is used by:
+ - Authorization Code Grant
+ """
+ raise NotImplementedError('Subclasses must implement this method.')
+
+ def revoke_token(self, token, token_type_hint, request, *args, **kwargs):
+ """Revoke an access or refresh token.
+
+ :param token: The token string.
+ :param token_type_hint: access_token or refresh_token.
+ :param request: OAuthlib request.
+ :type request: oauthlib.common.Request
+
+ Method is used by:
+ - Revocation Endpoint
+ """
+ raise NotImplementedError('Subclasses must implement this method.')
+
+ def rotate_refresh_token(self, request):
+ """Determine whether to rotate the refresh token. Default, yes.
+
+ When access tokens are refreshed the old refresh token can be kept
+ or replaced with a new one (rotated). Return True to rotate and
+ and False for keeping original.
+
+ :param request: OAuthlib request.
+ :type request: oauthlib.common.Request
+ :rtype: True or False
+
+ Method is used by:
+ - Refresh Token Grant
+ """
+ return True
+
+ def save_authorization_code(self, client_id, code, request, *args, **kwargs):
+ """Persist the authorization_code.
+
+ The code should at minimum be stored with:
+ - the client_id (``client_id``)
+ - the redirect URI used (``request.redirect_uri``)
+ - a resource owner / user (``request.user``)
+ - the authorized scopes (``request.scopes``)
+
+ To support PKCE, you MUST associate the code with:
+ - Code Challenge (``request.code_challenge``) and
+ - Code Challenge Method (``request.code_challenge_method``)
+
+ To support OIDC, you MUST associate the code with:
+ - nonce, if present (``code["nonce"]``)
+
+ The ``code`` argument is actually a dictionary, containing at least a
+ ``code`` key with the actual authorization code:
+
+ ``{'code': 'sdf345jsdf0934f'}``
+
+ It may also have a ``claims`` parameter which, when present, will be a dict
+ deserialized from JSON as described at
+ http://openid.net/specs/openid-connect-core-1_0.html#ClaimsParameter
+ This value should be saved in this method and used again in ``.validate_code``.
+
+ :param client_id: Unicode client identifier.
+ :param code: A dict of the authorization code grant and, optionally, state.
+ :param request: OAuthlib request.
+ :type request: oauthlib.common.Request
+
+ Method is used by:
+ - Authorization Code Grant
+ """
+ raise NotImplementedError('Subclasses must implement this method.')
+
+ def save_token(self, token, request, *args, **kwargs):
+ """Persist the token with a token type specific method.
+
+ Currently, only save_bearer_token is supported.
+
+ :param token: A (Bearer) token dict.
+ :param request: OAuthlib request.
+ :type request: oauthlib.common.Request
+ """
+ return self.save_bearer_token(token, request, *args, **kwargs)
+
+ def save_bearer_token(self, token, request, *args, **kwargs):
+ """Persist the Bearer token.
+
+ The Bearer token should at minimum be associated with:
+ - a client and it's client_id, if available
+ - a resource owner / user (request.user)
+ - authorized scopes (request.scopes)
+ - an expiration time
+ - a refresh token, if issued
+ - a claims document, if present in request.claims
+
+ The Bearer token dict may hold a number of items::
+
+ {
+ 'token_type': 'Bearer',
+ 'access_token': 'askfjh234as9sd8',
+ 'expires_in': 3600,
+ 'scope': 'string of space separated authorized scopes',
+ 'refresh_token': '23sdf876234', # if issued
+ 'state': 'given_by_client', # if supplied by client (implicit ONLY)
+ }
+
+ Note that while "scope" is a string-separated list of authorized 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
+ the claims dict, which should be saved for later use when generating the
+ id_token and/or UserInfo response content.
+
+ :param token: A Bearer token dict.
+ :param request: OAuthlib request.
+ :type request: oauthlib.common.Request
+ :rtype: The default redirect URI for the client
+
+ Method is used by all core grant types issuing Bearer tokens:
+ - Authorization Code Grant
+ - Implicit Grant
+ - Resource Owner Password Credentials Grant (might not associate a client)
+ - Client Credentials 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.
+
+ :param token: A string of random characters.
+ :param scopes: A list of scopes associated with the protected resource.
+ :param request: OAuthlib request.
+ :type request: oauthlib.common.Request
+
+ A key to OAuth 2 security and restricting impact of leaked tokens is
+ the short expiration time of tokens, *always ensure the token has not
+ expired!*.
+
+ Two different approaches to scope validation:
+
+ 1) all(scopes). The token must be authorized access to all scopes
+ associated with the resource. For example, the
+ token has access to ``read-only`` and ``images``,
+ thus the client can view images but not upload new.
+ Allows for fine grained access control through
+ combining various scopes.
+
+ 2) any(scopes). The token must be authorized access to one of the
+ scopes associated with the resource. For example,
+ token has access to ``read-only-images``.
+ Allows for fine grained, although arguably less
+ convenient, access control.
+
+ A powerful way to use scopes would mimic UNIX ACLs and see a scope
+ as a group with certain privileges. For a restful API these might
+ map to HTTP verbs instead of read, write and execute.
+
+ Note, the request.user attribute can be set to the resource owner
+ associated with this token. Similarly the request.client and
+ request.scopes attribute can be set to associated client object
+ and authorized scopes. If you then use a decorator such as the
+ one provided for django these attributes will be made available
+ in all protected views as keyword arguments.
+
+ :param token: Unicode Bearer token
+ :param scopes: List of scopes (defined by you)
+ :param request: OAuthlib request.
+ :type request: oauthlib.common.Request
+ :rtype: True or False
+
+ Method is indirectly used by all core Bearer token issuing grant types:
+ - Authorization Code Grant
+ - Implicit Grant
+ - Resource Owner Password Credentials Grant
+ - Client Credentials Grant
+ """
+ raise NotImplementedError('Subclasses must implement this method.')
+
+ def validate_client_id(self, client_id, request, *args, **kwargs):
+ """Ensure client_id belong to a valid and active client.
+
+ Note, while not strictly necessary it can often be very convenient
+ to set request.client to the client object associated with the
+ given client_id.
+
+ :param client_id: Unicode client identifier.
+ :param request: OAuthlib request.
+ :type request: oauthlib.common.Request
+ :rtype: True or False
+
+ Method is used by:
+ - Authorization Code Grant
+ - Implicit Grant
+ """
+ raise NotImplementedError('Subclasses must implement this method.')
+
+ def validate_code(self, client_id, code, client, request, *args, **kwargs):
+ """Verify that the authorization_code is valid and assigned to the given
+ client.
+
+ Before returning true, set the following based on the information stored
+ with the code in 'save_authorization_code':
+
+ - request.user
+ - request.scopes
+ - request.claims (if given)
+
+ OBS! The request.user attribute should be set to the resource owner
+ associated with this authorization code. Similarly request.scopes
+ must also be set.
+
+ The request.claims property, if it was given, should assigned a dict.
+
+ If PKCE is enabled (see 'is_pkce_required' and 'save_authorization_code')
+ you MUST set the following based on the information stored:
+
+ - request.code_challenge
+ - request.code_challenge_method
+
+ :param client_id: Unicode client identifier.
+ :param code: Unicode authorization code.
+ :param client: Client object set by you, see ``.authenticate_client``.
+ :param request: OAuthlib request.
+ :type request: oauthlib.common.Request
+ :rtype: True or False
+
+ Method is used by:
+ - Authorization Code Grant
+ """
+ raise NotImplementedError('Subclasses must implement this method.')
+
+ def validate_grant_type(self, client_id, grant_type, client, request, *args, **kwargs):
+ """Ensure client is authorized to use the grant_type requested.
+
+ :param client_id: Unicode client identifier.
+ :param grant_type: Unicode grant type, i.e. authorization_code, password.
+ :param client: Client object set by you, see ``.authenticate_client``.
+ :param request: OAuthlib request.
+ :type request: oauthlib.common.Request
+ :rtype: True or False
+
+ Method is used by:
+ - Authorization Code Grant
+ - Resource Owner Password Credentials Grant
+ - Client Credentials Grant
+ - Refresh Token Grant
+ """
+ raise NotImplementedError('Subclasses must implement this method.')
+
+ def validate_redirect_uri(self, client_id, redirect_uri, request, *args, **kwargs):
+ """Ensure client is authorized to redirect to the redirect_uri requested.
+
+ All clients should register the absolute URIs of all URIs they intend
+ to redirect to. The registration is outside of the scope of oauthlib.
+
+ :param client_id: Unicode client identifier.
+ :param redirect_uri: Unicode absolute URI.
+ :param request: OAuthlib request.
+ :type request: oauthlib.common.Request
+ :rtype: True or False
+
+ Method is used by:
+ - Authorization Code Grant
+ - Implicit Grant
+ """
+ raise NotImplementedError('Subclasses must implement this method.')
+
+ def validate_refresh_token(self, refresh_token, client, request, *args, **kwargs):
+ """Ensure the Bearer token is valid and authorized access to scopes.
+
+ OBS! The request.user attribute should be set to the resource owner
+ associated with this refresh token.
+
+ :param refresh_token: Unicode refresh token.
+ :param client: Client object set by you, see ``.authenticate_client``.
+ :param request: OAuthlib request.
+ :type request: oauthlib.common.Request
+ :rtype: True or False
+
+ Method is used by:
+ - Authorization Code Grant (indirectly by issuing refresh tokens)
+ - Resource Owner Password Credentials Grant (also indirectly)
+ - Refresh Token Grant
+ """
+ raise NotImplementedError('Subclasses must implement this method.')
+
+ def validate_response_type(self, client_id, response_type, client, request, *args, **kwargs):
+ """Ensure client is authorized to use the response_type requested.
+
+ :param client_id: Unicode client identifier.
+ :param response_type: Unicode response type, i.e. code, token.
+ :param client: Client object set by you, see ``.authenticate_client``.
+ :param request: OAuthlib request.
+ :type request: oauthlib.common.Request
+ :rtype: True or False
+
+ Method is used by:
+ - Authorization Code Grant
+ - Implicit Grant
+ """
+ raise NotImplementedError('Subclasses must implement this method.')
+
+ def validate_scopes(self, client_id, scopes, client, request, *args, **kwargs):
+ """Ensure the client is authorized access to requested scopes.
+
+ :param client_id: Unicode client identifier.
+ :param scopes: List of scopes (defined by you).
+ :param client: Client object set by you, see ``.authenticate_client``.
+ :param request: OAuthlib request.
+ :type request: oauthlib.common.Request
+ :rtype: True or False
+
+ Method is used by all core grant types:
+ - Authorization Code Grant
+ - Implicit Grant
+ - Resource Owner Password Credentials Grant
+ - Client Credentials Grant
+ """
+ raise NotImplementedError('Subclasses must implement this method.')
+
+ def validate_user(self, username, password, client, request, *args, **kwargs):
+ """Ensure the username and password is valid.
+
+ OBS! The validation should also set the user attribute of the request
+ to a valid resource owner, i.e. request.user = username or similar. If
+ not set you will be unable to associate a token with a user in the
+ persistence method used (commonly, save_bearer_token).
+
+ :param username: Unicode username.
+ :param password: Unicode password.
+ :param client: Client object set by you, see ``.authenticate_client``.
+ :param request: OAuthlib request.
+ :type request: oauthlib.common.Request
+ :rtype: True or False
+
+ Method is used by:
+ - Resource Owner Password Credentials Grant
+ """
+ raise NotImplementedError('Subclasses must implement this method.')
+
+ def is_pkce_required(self, client_id, request):
+ """Determine if current request requires PKCE. Default, False.
+ This is called for both "authorization" and "token" requests.
+
+ Override this method by ``return True`` to enable PKCE for everyone.
+ You might want to enable it only for public clients.
+ Note that PKCE can also be used in addition of a client authentication.
+
+ OAuth 2.0 public clients utilizing the Authorization Code Grant are
+ susceptible to the authorization code interception attack. This
+ specification describes the attack as well as a technique to mitigate
+ against the threat through the use of Proof Key for Code Exchange
+ (PKCE, pronounced "pixy"). See `RFC7636`_.
+
+ :param client_id: Client identifier.
+ :param request: OAuthlib request.
+ :type request: oauthlib.common.Request
+ :rtype: True or False
+
+ Method is used by:
+ - Authorization Code Grant
+
+ .. _`RFC7636`: https://tools.ietf.org/html/rfc7636
+ """
+ return False
+
+ def get_code_challenge(self, code, request):
+ """Is called for every "token" requests.
+
+ When the server issues the authorization code in the authorization
+ response, it MUST associate the ``code_challenge`` and
+ ``code_challenge_method`` values with the authorization code so it can
+ be verified later.
+
+ Typically, the ``code_challenge`` and ``code_challenge_method`` values
+ are stored in encrypted form in the ``code`` itself but could
+ alternatively be stored on the server associated with the code. The
+ server MUST NOT include the ``code_challenge`` value in client requests
+ in a form that other entities can extract.
+
+ Return the ``code_challenge`` associated to the code.
+ If ``None`` is returned, code is considered to not be associated to any
+ challenges.
+
+ :param code: Authorization code.
+ :param request: OAuthlib request.
+ :type request: oauthlib.common.Request
+ :rtype: code_challenge string
+
+ Method is used by:
+ - Authorization Code Grant - when PKCE is active
+
+ """
+ return None
+
+ def get_code_challenge_method(self, code, request):
+ """Is called during the "token" request processing, when a
+ ``code_verifier`` and a ``code_challenge`` has been provided.
+
+ See ``.get_code_challenge``.
+
+ Must return ``plain`` or ``S256``. You can return a custom value if you have
+ implemented your own ``AuthorizationCodeGrant`` class.
+
+ :param code: Authorization code.
+ :param request: OAuthlib request.
+ :type request: oauthlib.common.Request
+ :rtype: code_challenge_method string
+
+ Method is used by:
+ - Authorization Code Grant - when PKCE is active
+
+ """
+ raise NotImplementedError('Subclasses must implement this method.')
+
+ def is_origin_allowed(self, client_id, origin, request, *args, **kwargs):
+ """Indicate if the given origin is allowed to access the token endpoint
+ via Cross-Origin Resource Sharing (CORS). CORS is used by browser-based
+ clients, such as Single-Page Applications, to perform the Authorization
+ Code Grant.
+
+ (Note: If performing Authorization Code Grant via a public client such
+ as a browser, you should use PKCE as well.)
+
+ If this method returns true, the appropriate CORS headers will be added
+ to the response. By default this method always returns False, meaning
+ CORS is disabled.
+
+ :param client_id: Unicode client identifier.
+ :param redirect_uri: Unicode origin.
+ :param request: OAuthlib request.
+ :type request: oauthlib.common.Request
+ :rtype: bool
+
+ Method is used by:
+ - Authorization Code Grant
+ - Refresh Token Grant
+
+ """
+ return False
diff --git a/game/python-extra/oauthlib/oauth2/rfc6749/tokens.py b/game/python-extra/oauthlib/oauth2/rfc6749/tokens.py
new file mode 100644
index 0000000..0757d07
--- /dev/null
+++ b/game/python-extra/oauthlib/oauth2/rfc6749/tokens.py
@@ -0,0 +1,356 @@
+"""
+oauthlib.oauth2.rfc6749.tokens
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This module contains methods for adding two types of access tokens to requests.
+
+- Bearer https://tools.ietf.org/html/rfc6750
+- MAC https://tools.ietf.org/html/draft-ietf-oauth-v2-http-mac-01
+"""
+import hashlib
+import hmac
+import warnings
+from binascii import b2a_base64
+from urllib.parse import urlparse
+
+from oauthlib import common
+from oauthlib.common import add_params_to_qs, add_params_to_uri
+
+from . import utils
+
+
+class OAuth2Token(dict):
+
+ def __init__(self, params, old_scope=None):
+ super().__init__(params)
+ self._new_scope = None
+ if 'scope' in params and params['scope']:
+ self._new_scope = set(utils.scope_to_list(params['scope']))
+ if old_scope is not None:
+ self._old_scope = set(utils.scope_to_list(old_scope))
+ if self._new_scope is None:
+ # the rfc says that if the scope hasn't changed, it's optional
+ # in params so set the new scope to the old scope
+ self._new_scope = self._old_scope
+ else:
+ self._old_scope = self._new_scope
+
+ @property
+ def scope_changed(self):
+ return self._new_scope != self._old_scope
+
+ @property
+ def old_scope(self):
+ return utils.list_to_scope(self._old_scope)
+
+ @property
+ def old_scopes(self):
+ return list(self._old_scope)
+
+ @property
+ def scope(self):
+ return utils.list_to_scope(self._new_scope)
+
+ @property
+ def scopes(self):
+ return list(self._new_scope)
+
+ @property
+ def missing_scopes(self):
+ return list(self._old_scope - self._new_scope)
+
+ @property
+ def additional_scopes(self):
+ return list(self._new_scope - self._old_scope)
+
+
+def prepare_mac_header(token, uri, key, http_method,
+ nonce=None,
+ headers=None,
+ body=None,
+ ext='',
+ hash_algorithm='hmac-sha-1',
+ issue_time=None,
+ draft=0):
+ """Add an `MAC Access Authentication`_ signature to headers.
+
+ Unlike OAuth 1, this HMAC signature does not require inclusion of the
+ request payload/body, neither does it use a combination of client_secret
+ and token_secret but rather a mac_key provided together with the access
+ token.
+
+ Currently two algorithms are supported, "hmac-sha-1" and "hmac-sha-256",
+ `extension algorithms`_ are not supported.
+
+ Example MAC Authorization header, linebreaks added for clarity
+
+ Authorization: MAC id="h480djs93hd8",
+ nonce="1336363200:dj83hs9s",
+ mac="bhCQXTVyfj5cmA9uKkPFx1zeOXM="
+
+ .. _`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 token:
+ :param uri: Request URI.
+ :param key: MAC given provided by token endpoint.
+ :param http_method: HTTP Request method.
+ :param nonce:
+ :param headers: Request headers as a dictionary.
+ :param body:
+ :param ext:
+ :param hash_algorithm: HMAC algorithm provided by token endpoint.
+ :param issue_time: Time when the MAC credentials were issued (datetime).
+ :param draft: MAC authentication specification version.
+ :return: headers dictionary with the authorization field added.
+ """
+ http_method = http_method.upper()
+ host, port = utils.host_from_uri(uri)
+
+ if hash_algorithm.lower() == 'hmac-sha-1':
+ h = hashlib.sha1
+ elif hash_algorithm.lower() == 'hmac-sha-256':
+ h = hashlib.sha256
+ else:
+ raise ValueError('unknown hash algorithm')
+
+ if draft == 0:
+ nonce = nonce or '{}:{}'.format(utils.generate_age(issue_time),
+ common.generate_nonce())
+ else:
+ ts = common.generate_timestamp()
+ nonce = common.generate_nonce()
+
+ sch, net, path, par, query, fra = urlparse(uri)
+
+ if query:
+ request_uri = path + '?' + query
+ else:
+ request_uri = path
+
+ # Hash the body/payload
+ if body is not None and draft == 0:
+ body = body.encode('utf-8')
+ bodyhash = b2a_base64(h(body).digest())[:-1].decode('utf-8')
+ else:
+ bodyhash = ''
+
+ # Create the normalized base string
+ base = []
+ if draft == 0:
+ base.append(nonce)
+ else:
+ base.append(ts)
+ base.append(nonce)
+ base.append(http_method.upper())
+ base.append(request_uri)
+ base.append(host)
+ base.append(port)
+ if draft == 0:
+ base.append(bodyhash)
+ base.append(ext or '')
+ base_string = '\n'.join(base) + '\n'
+
+ # hmac struggles with unicode strings - http://bugs.python.org/issue5285
+ if isinstance(key, str):
+ key = key.encode('utf-8')
+ sign = hmac.new(key, base_string.encode('utf-8'), h)
+ sign = b2a_base64(sign.digest())[:-1].decode('utf-8')
+
+ header = []
+ header.append('MAC id="%s"' % token)
+ if draft != 0:
+ header.append('ts="%s"' % ts)
+ header.append('nonce="%s"' % nonce)
+ if bodyhash:
+ header.append('bodyhash="%s"' % bodyhash)
+ if ext:
+ header.append('ext="%s"' % ext)
+ header.append('mac="%s"' % sign)
+
+ headers = headers or {}
+ headers['Authorization'] = ', '.join(header)
+ return headers
+
+
+def prepare_bearer_uri(token, uri):
+ """Add a `Bearer Token`_ to the request URI.
+ Not recommended, use only if client can't use authorization header or body.
+
+ http://www.example.com/path?access_token=h480djs93hd8
+
+ .. _`Bearer Token`: https://tools.ietf.org/html/rfc6750
+
+ :param token:
+ :param uri:
+ """
+ return add_params_to_uri(uri, [(('access_token', token))])
+
+
+def prepare_bearer_headers(token, headers=None):
+ """Add a `Bearer Token`_ to the request URI.
+ Recommended method of passing bearer tokens.
+
+ Authorization: Bearer h480djs93hd8
+
+ .. _`Bearer Token`: https://tools.ietf.org/html/rfc6750
+
+ :param token:
+ :param headers:
+ """
+ headers = headers or {}
+ headers['Authorization'] = 'Bearer %s' % token
+ return headers
+
+
+def prepare_bearer_body(token, body=''):
+ """Add a `Bearer Token`_ to the request body.
+
+ access_token=h480djs93hd8
+
+ .. _`Bearer Token`: https://tools.ietf.org/html/rfc6750
+
+ :param token:
+ :param body:
+ """
+ return add_params_to_qs(body, [(('access_token', token))])
+
+
+def random_token_generator(request, refresh_token=False):
+ """
+ :param request: OAuthlib request.
+ :type request: oauthlib.common.Request
+ :param refresh_token:
+ """
+ return common.generate_token()
+
+
+def signed_token_generator(private_pem, **kwargs):
+ """
+ :param private_pem:
+ """
+ def signed_token_generator(request):
+ request.claims = kwargs
+ return common.generate_signed_token(private_pem, request)
+
+ return signed_token_generator
+
+
+def get_token_from_header(request):
+ """
+ Helper function to extract a token from the request header.
+
+ :param request: OAuthlib request.
+ :type request: oauthlib.common.Request
+ :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].lower() == 'bearer':
+ token = split_header[1]
+ else:
+ token = request.access_token
+
+ return token
+
+
+class TokenBase:
+ __slots__ = ()
+
+ def __call__(self, request, refresh_token=False):
+ raise NotImplementedError('Subclasses must implement this method.')
+
+ def validate_request(self, request):
+ """
+ :param request: OAuthlib request.
+ :type request: oauthlib.common.Request
+ """
+ raise NotImplementedError('Subclasses must implement this method.')
+
+ def estimate_type(self, request):
+ """
+ :param request: OAuthlib request.
+ :type request: oauthlib.common.Request
+ """
+ raise NotImplementedError('Subclasses must implement this method.')
+
+
+class BearerToken(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, **kwargs):
+ """
+ Create a BearerToken, by default without refresh token.
+
+ :param request: OAuthlib request.
+ :type request: oauthlib.common.Request
+ :param refresh_token:
+ """
+ if "save_token" in kwargs:
+ warnings.warn("`save_token` has been deprecated, it was not called internally."
+ "If you do, call `request_validator.save_token()` instead.",
+ DeprecationWarning)
+
+ if callable(self.expires_in):
+ expires_in = self.expires_in(request)
+ else:
+ expires_in = self.expires_in
+
+ request.expires_in = expires_in
+
+ token = {
+ 'access_token': self.token_generator(request),
+ 'expires_in': expires_in,
+ 'token_type': 'Bearer',
+ }
+
+ # If provided, include - this is optional in some cases https://tools.ietf.org/html/rfc6749#section-3.3 but
+ # there is currently no mechanism to coordinate issuing a token for only a subset of the requested scopes so
+ # all tokens issued are for the entire set of requested scopes.
+ if request.scopes is not None:
+ token['scope'] = ' '.join(request.scopes)
+
+ if refresh_token:
+ if (request.refresh_token and
+ not self.request_validator.rotate_refresh_token(request)):
+ token['refresh_token'] = request.refresh_token
+ else:
+ token['refresh_token'] = self.refresh_token_generator(request)
+
+ token.update(request.extra_credentials or {})
+ return OAuth2Token(token)
+
+ def validate_request(self, request):
+ """
+ :param request: OAuthlib request.
+ :type request: oauthlib.common.Request
+ """
+ token = get_token_from_header(request)
+ return self.request_validator.validate_bearer_token(
+ token, request.scopes, request)
+
+ def estimate_type(self, request):
+ """
+ :param request: OAuthlib request.
+ :type request: oauthlib.common.Request
+ """
+ if request.headers.get('Authorization', '').split(' ')[0].lower() == 'bearer':
+ return 9
+ elif request.access_token is not None:
+ return 5
+ else:
+ return 0
diff --git a/game/python-extra/oauthlib/oauth2/rfc6749/utils.py b/game/python-extra/oauthlib/oauth2/rfc6749/utils.py
new file mode 100644
index 0000000..7dc27b3
--- /dev/null
+++ b/game/python-extra/oauthlib/oauth2/rfc6749/utils.py
@@ -0,0 +1,83 @@
+"""
+oauthlib.utils
+~~~~~~~~~~~~~~
+
+This module contains utility methods used by various parts of the OAuth 2 spec.
+"""
+import datetime
+import os
+from urllib.parse import quote, urlparse
+
+from oauthlib.common import urldecode
+
+
+def list_to_scope(scope):
+ """Convert a list of scopes to a space separated string."""
+ if isinstance(scope, str) or scope is None:
+ return scope
+ elif isinstance(scope, (set, tuple, list)):
+ return " ".join([str(s) for s in scope])
+ else:
+ raise ValueError("Invalid scope (%s), must be string, tuple, set, or list." % scope)
+
+
+def scope_to_list(scope):
+ """Convert a space separated string to a list of scopes."""
+ if isinstance(scope, (tuple, list, set)):
+ return [str(s) for s in scope]
+ elif scope is None:
+ return None
+ else:
+ return scope.strip().split(" ")
+
+
+def params_from_uri(uri):
+ params = dict(urldecode(urlparse(uri).query))
+ if 'scope' in params:
+ params['scope'] = scope_to_list(params['scope'])
+ return params
+
+
+def host_from_uri(uri):
+ """Extract hostname and port from URI.
+
+ Will use default port for HTTP and HTTPS if none is present in the URI.
+ """
+ default_ports = {
+ 'HTTP': '80',
+ 'HTTPS': '443',
+ }
+
+ sch, netloc, path, par, query, fra = urlparse(uri)
+ if ':' in netloc:
+ netloc, port = netloc.split(':', 1)
+ else:
+ port = default_ports.get(sch.upper())
+
+ return netloc, port
+
+
+def escape(u):
+ """Escape a string in an OAuth-compatible fashion.
+
+ TODO: verify whether this can in fact be used for OAuth 2
+
+ """
+ if not isinstance(u, str):
+ raise ValueError('Only unicode objects are escapable.')
+ return quote(u.encode('utf-8'), safe=b'~')
+
+
+def generate_age(issue_time):
+ """Generate a age parameter for MAC authentication draft 00."""
+ td = datetime.datetime.now() - issue_time
+ age = (td.microseconds + (td.seconds + td.days * 24 * 3600)
+ * 10 ** 6) / 10 ** 6
+ return str(age)
+
+
+def is_secure_transport(uri):
+ """Check if the uri is over ssl."""
+ if os.environ.get('OAUTHLIB_INSECURE_TRANSPORT'):
+ return True
+ return uri.lower().startswith('https://')