summaryrefslogtreecommitdiff
path: root/game/python-extra/oauthlib/oauth2/rfc6749/clients/base.py
diff options
context:
space:
mode:
authorJesusaves <cpntb1@ymail.com>2023-03-05 22:42:39 -0300
committerJesusaves <cpntb1@ymail.com>2023-03-05 22:42:39 -0300
commit972344a32ba39e8f2733b5937935145a4611a27d (patch)
tree389399e1bb93aea9a96c3d764d849a180394a28a /game/python-extra/oauthlib/oauth2/rfc6749/clients/base.py
parentdadf8ed045e5761317416dcc7d4805e788945fb7 (diff)
downloadrenpy-972344a32ba39e8f2733b5937935145a4611a27d.tar.gz
renpy-972344a32ba39e8f2733b5937935145a4611a27d.tar.bz2
renpy-972344a32ba39e8f2733b5937935145a4611a27d.tar.xz
renpy-972344a32ba39e8f2733b5937935145a4611a27d.zip
Import mwlib... On python3 builds only
Diffstat (limited to 'game/python-extra/oauthlib/oauth2/rfc6749/clients/base.py')
-rw-r--r--game/python-extra/oauthlib/oauth2/rfc6749/clients/base.py604
1 files changed, 604 insertions, 0 deletions
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')