mirror of
https://github.com/Tautulli/Tautulli.git
synced 2025-07-07 13:41:15 -07:00
Bump requests-oauthlib from 1.3.0 to 1.3.1 (#1636)
* Bump requests-oauthlib from 1.3.0 to 1.3.1 Bumps [requests-oauthlib](https://github.com/requests/requests-oauthlib) from 1.3.0 to 1.3.1. - [Release notes](https://github.com/requests/requests-oauthlib/releases) - [Changelog](https://github.com/requests/requests-oauthlib/blob/master/HISTORY.rst) - [Commits](https://github.com/requests/requests-oauthlib/compare/v1.3.0...v1.3.1) --- updated-dependencies: - dependency-name: requests-oauthlib dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> * Update requests-oauthlib==1.3.1 Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> [skip ci]
This commit is contained in:
parent
5523d4ba88
commit
61960aa744
26 changed files with 464 additions and 77 deletions
|
@ -33,3 +33,4 @@ from .rfc6749.grant_types import (
|
|||
from .rfc6749.request_validator import RequestValidator
|
||||
from .rfc6749.tokens import BearerToken, OAuth2Token
|
||||
from .rfc6749.utils import is_secure_transport
|
||||
from .rfc8628.clients import DeviceClient
|
||||
|
|
|
@ -8,6 +8,10 @@ for consuming OAuth 2.0 RFC6749.
|
|||
"""
|
||||
import time
|
||||
import warnings
|
||||
import secrets
|
||||
import re
|
||||
import hashlib
|
||||
import base64
|
||||
|
||||
from oauthlib.common import generate_token
|
||||
from oauthlib.oauth2.rfc6749 import tokens
|
||||
|
@ -61,6 +65,9 @@ class Client:
|
|||
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.
|
||||
|
||||
|
@ -99,6 +106,15 @@ class Client:
|
|||
|
||||
: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
|
||||
|
@ -113,6 +129,9 @@ class Client:
|
|||
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
|
||||
|
@ -471,6 +490,91 @@ class Client:
|
|||
raise ValueError("Invalid token placement.")
|
||||
return uri, headers, body
|
||||
|
||||
def create_code_verifier(self, length):
|
||||
"""Create PKCE **code_verifier** used in computing **code_challenge**.
|
||||
|
||||
: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_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.
|
||||
|
||||
.. _`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**.
|
||||
|
||||
: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.
|
||||
|
||||
.. _`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.
|
||||
|
@ -513,7 +617,10 @@ class Client:
|
|||
self._expires_at = time.time() + int(self.expires_in)
|
||||
|
||||
if 'expires_at' in response:
|
||||
self._expires_at = int(response.get('expires_at'))
|
||||
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')
|
||||
|
|
|
@ -41,7 +41,7 @@ class WebApplicationClient(Client):
|
|||
self.code = code
|
||||
|
||||
def prepare_request_uri(self, uri, redirect_uri=None, scope=None,
|
||||
state=None, **kwargs):
|
||||
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
|
||||
|
@ -62,6 +62,13 @@ class WebApplicationClient(Client):
|
|||
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``
|
||||
|
@ -76,6 +83,10 @@ class WebApplicationClient(Client):
|
|||
'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'
|
||||
|
||||
|
@ -87,10 +98,11 @@ class WebApplicationClient(Client):
|
|||
"""
|
||||
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, **kwargs)
|
||||
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, **kwargs):
|
||||
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
|
||||
|
@ -113,6 +125,9 @@ class WebApplicationClient(Client):
|
|||
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
|
||||
|
@ -127,6 +142,8 @@ class WebApplicationClient(Client):
|
|||
>>> 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'
|
||||
|
||||
|
@ -154,7 +171,7 @@ class WebApplicationClient(Client):
|
|||
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, **kwargs)
|
||||
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.
|
||||
|
|
|
@ -54,7 +54,8 @@ class MetadataEndpoint(BaseEndpoint):
|
|||
"""Create metadata response
|
||||
"""
|
||||
headers = {
|
||||
'Content-Type': 'application/json'
|
||||
'Content-Type': 'application/json',
|
||||
'Access-Control-Allow-Origin': '*',
|
||||
}
|
||||
return headers, json.dumps(self.claims), 200
|
||||
|
||||
|
|
|
@ -103,15 +103,12 @@ class OAuth2Error(Exception):
|
|||
value "Bearer". This scheme MUST be followed by one or more
|
||||
auth-param values.
|
||||
"""
|
||||
authvalues = [
|
||||
"Bearer",
|
||||
'error="{}"'.format(self.error)
|
||||
]
|
||||
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": ", ".join(authvalues)}
|
||||
return {"WWW-Authenticate": "Bearer " + ", ".join(authvalues)}
|
||||
return {}
|
||||
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ import logging
|
|||
from oauthlib import common
|
||||
|
||||
from .. import errors
|
||||
from ..utils import is_secure_transport
|
||||
from .base import GrantTypeBase
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
@ -272,6 +273,8 @@ class AuthorizationCodeGrant(GrantTypeBase):
|
|||
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)
|
||||
|
@ -310,6 +313,7 @@ class AuthorizationCodeGrant(GrantTypeBase):
|
|||
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):
|
||||
|
@ -543,3 +547,20 @@ class AuthorizationCodeGrant(GrantTypeBase):
|
|||
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)
|
||||
|
||||
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}
|
||||
|
|
|
@ -63,7 +63,7 @@ class RefreshTokenGrant(GrantTypeBase):
|
|||
refresh_token=self.issue_new_refresh_tokens)
|
||||
|
||||
for modifier in self._token_modifiers:
|
||||
token = modifier(token)
|
||||
token = modifier(token, token_handler, request)
|
||||
|
||||
self.request_validator.save_token(token, request)
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@ 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, **kwargs):
|
||||
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
|
||||
|
@ -45,6 +45,11 @@ def prepare_grant_uri(uri, client_id, response_type, redirect_uri=None,
|
|||
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 paramater. 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:
|
||||
|
@ -52,6 +57,7 @@ def prepare_grant_uri(uri, client_id, response_type, redirect_uri=None,
|
|||
.. 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
|
||||
|
||||
|
@ -73,6 +79,9 @@ def prepare_grant_uri(uri, client_id, response_type, redirect_uri=None,
|
|||
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]:
|
||||
|
@ -81,7 +90,7 @@ def prepare_grant_uri(uri, client_id, response_type, redirect_uri=None,
|
|||
return add_params_to_uri(uri, params)
|
||||
|
||||
|
||||
def prepare_token_request(grant_type, body='', include_client_id=True, **kwargs):
|
||||
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
|
||||
|
@ -116,6 +125,9 @@ def prepare_token_request(grant_type, body='', include_client_id=True, **kwargs)
|
|||
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
|
||||
|
@ -142,6 +154,10 @@ def prepare_token_request(grant_type, body='', include_client_id=True, **kwargs)
|
|||
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)
|
||||
|
|
|
@ -649,3 +649,28 @@ class RequestValidator:
|
|||
|
||||
"""
|
||||
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
|
||||
|
||||
"""
|
||||
return False
|
||||
|
|
10
lib/oauthlib/oauth2/rfc8628/__init__.py
Normal file
10
lib/oauthlib/oauth2/rfc8628/__init__.py
Normal file
|
@ -0,0 +1,10 @@
|
|||
"""
|
||||
oauthlib.oauth2.rfc8628
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
This module is an implementation of various logic needed
|
||||
for consuming and providing OAuth 2.0 Device Authorization RFC8628.
|
||||
"""
|
||||
import logging
|
||||
|
||||
log = logging.getLogger(__name__)
|
8
lib/oauthlib/oauth2/rfc8628/clients/__init__.py
Normal file
8
lib/oauthlib/oauth2/rfc8628/clients/__init__.py
Normal file
|
@ -0,0 +1,8 @@
|
|||
"""
|
||||
oauthlib.oauth2.rfc8628
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
This module is an implementation of various logic needed
|
||||
for consuming OAuth 2.0 Device Authorization RFC8628.
|
||||
"""
|
||||
from .device import DeviceClient
|
94
lib/oauthlib/oauth2/rfc8628/clients/device.py
Normal file
94
lib/oauthlib/oauth2/rfc8628/clients/device.py
Normal file
|
@ -0,0 +1,94 @@
|
|||
"""
|
||||
oauthlib.oauth2.rfc8628
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
This module is an implementation of various logic needed
|
||||
for consuming and providing OAuth 2.0 Device Authorization RFC8628.
|
||||
"""
|
||||
|
||||
from oauthlib.oauth2 import BackendApplicationClient, Client
|
||||
from oauthlib.oauth2.rfc6749.errors import InsecureTransportError
|
||||
from oauthlib.oauth2.rfc6749.parameters import prepare_token_request
|
||||
from oauthlib.oauth2.rfc6749.utils import is_secure_transport, list_to_scope
|
||||
from oauthlib.common import add_params_to_uri
|
||||
|
||||
|
||||
class DeviceClient(Client):
|
||||
|
||||
"""A public client utilizing the device authorization workflow.
|
||||
|
||||
The client can request an access token using a device code and
|
||||
a public client id associated with the device code as defined
|
||||
in RFC8628.
|
||||
|
||||
The device authorization grant type can be used to obtain both
|
||||
access tokens and refresh tokens and is intended to be used in
|
||||
a scenario where the device being authorized does not have a
|
||||
user interface that is suitable for performing authentication.
|
||||
"""
|
||||
|
||||
grant_type = 'urn:ietf:params:oauth:grant-type:device_code'
|
||||
|
||||
def __init__(self, client_id, **kwargs):
|
||||
super().__init__(client_id, **kwargs)
|
||||
self.client_secret = kwargs.get('client_secret')
|
||||
|
||||
def prepare_request_uri(self, uri, scope=None, **kwargs):
|
||||
if not is_secure_transport(uri):
|
||||
raise InsecureTransportError()
|
||||
|
||||
scope = self.scope if scope is None else scope
|
||||
params = [(('client_id', self.client_id)), (('grant_type', self.grant_type))]
|
||||
|
||||
if self.client_secret is not None:
|
||||
params.append(('client_secret', self.client_secret))
|
||||
|
||||
if scope:
|
||||
params.append(('scope', list_to_scope(scope)))
|
||||
|
||||
for k in kwargs:
|
||||
if kwargs[k]:
|
||||
params.append((str(k), kwargs[k]))
|
||||
|
||||
return add_params_to_uri(uri, params)
|
||||
|
||||
def prepare_request_body(self, device_code, body='', scope=None,
|
||||
include_client_id=False, **kwargs):
|
||||
"""Add device_code to request body
|
||||
|
||||
The client makes a request to the token endpoint by adding the
|
||||
device_code as a parameter using the
|
||||
"application/x-www-form-urlencoded" format to the HTTP request
|
||||
body.
|
||||
|
||||
:param body: Existing request body (URL encoded string) to embed parameters
|
||||
into. This may contain extra paramters. 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 prepared body will include all provided device_code as well as
|
||||
the ``grant_type`` parameter set to
|
||||
``urn:ietf:params:oauth:grant-type:device_code``::
|
||||
|
||||
>>> from oauthlib.oauth2 import DeviceClient
|
||||
>>> client = DeviceClient('your_id', 'your_code')
|
||||
>>> client.prepare_request_body(scope=['hello', 'world'])
|
||||
'grant_type=urn:ietf:params:oauth:grant-type:device_code&scope=hello+world'
|
||||
|
||||
.. _`Section 3.4`: https://datatracker.ietf.org/doc/html/rfc8628#section-3.4
|
||||
"""
|
||||
|
||||
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, device_code=device_code,
|
||||
scope=scope, **kwargs)
|
Loading…
Add table
Add a link
Reference in a new issue