Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package python-oauthlib for openSUSE:Factory 
checked in at 2022-02-06 23:53:37
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-oauthlib (Old)
 and      /work/SRC/openSUSE:Factory/.python-oauthlib.new.1898 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-oauthlib"

Sun Feb  6 23:53:37 2022 rev:31 rq:951386 version:3.2.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-oauthlib/python-oauthlib.changes  
2021-06-11 22:30:43.370135280 +0200
+++ 
/work/SRC/openSUSE:Factory/.python-oauthlib.new.1898/python-oauthlib.changes    
    2022-02-06 23:54:00.694928550 +0100
@@ -1,0 +2,24 @@
+Thu Feb  3 20:02:09 UTC 2022 - Arun Persaud <a...@gmx.de>
+
+- specfile:
+  * update copyright year
+
+- update to version 3.2.0:
+  * OAuth2.0 Client: * #795: Add Device Authorization Flow for Web
+    Application * #786: Add PKCE support for Client * #783: Fallback
+    to none in case of wrong expires_at format.
+  * OAuth2.0 Provider: * #790: Add support for CORS to metadata
+    endpoint. * #791: Add support for CORS to token endpoint. * #787:
+    Remove comma after Bearer in WWW-Authenticate
+  * OAuth2.0 Provider - OIDC:
+    + #755: Call save_token in Hybrid code flow
+    + #751: OIDC add support of refreshing ID Tokens with
+       refresh_id_token
+    + #751: The RefreshTokenGrant modifiers now take the same
+       arguments as the AuthorizationCodeGrant modifiers (token,
+       token_handler, request).
+  * General:
+    + Added Python 3.9, 3.10, 3.11
+    + Improve Travis & Coverage
+
+-------------------------------------------------------------------

Old:
----
  oauthlib-3.1.1.tar.gz

New:
----
  oauthlib-3.2.0.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-oauthlib.spec ++++++
--- /var/tmp/diff_new_pack.lz5yZc/_old  2022-02-06 23:54:01.182925296 +0100
+++ /var/tmp/diff_new_pack.lz5yZc/_new  2022-02-06 23:54:01.186925269 +0100
@@ -1,7 +1,7 @@
 #
 # spec file for package python-oauthlib
 #
-# Copyright (c) 2021 SUSE LLC
+# Copyright (c) 2022 SUSE LLC
 #
 # All modifications and additions to the file contributed by third parties
 # remain the property of their copyright owners, unless otherwise agreed
@@ -19,7 +19,7 @@
 %{?!python_module:%define python_module() python-%{**} python3-%{**}}
 %define skip_python2 1
 Name:           python-oauthlib
-Version:        3.1.1
+Version:        3.2.0
 Release:        0
 Summary:        A Generic Implementation of the OAuth Request-Signing Logic
 License:        BSD-3-Clause

++++++ oauthlib-3.1.1.tar.gz -> oauthlib-3.2.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/oauthlib-3.1.1/CHANGELOG.rst 
new/oauthlib-3.2.0/CHANGELOG.rst
--- old/oauthlib-3.1.1/CHANGELOG.rst    2021-06-01 14:52:20.000000000 +0200
+++ new/oauthlib-3.2.0/CHANGELOG.rst    2022-01-29 22:58:41.000000000 +0100
@@ -1,6 +1,28 @@
 Changelog
 =========
 
+3.2.0 (2022-01-29)
+------------------
+OAuth2.0 Client:
+* #795: Add Device Authorization Flow for Web Application
+* #786: Add PKCE support for Client
+* #783: Fallback to none in case of wrong expires_at format.
+
+OAuth2.0 Provider:
+* #790: Add support for CORS to metadata endpoint.
+* #791: Add support for CORS to token endpoint.
+* #787: Remove comma after Bearer in WWW-Authenticate
+
+OAuth2.0 Provider - OIDC:
+  * #755: Call save_token in Hybrid code flow
+  * #751: OIDC add support of refreshing ID Tokens with `refresh_id_token`
+  * #751: The RefreshTokenGrant modifiers now take the same arguments as the
+    AuthorizationCodeGrant modifiers (`token`, `token_handler`, `request`).
+
+General:
+  * Added Python 3.9, 3.10, 3.11
+  * Improve Travis & Coverage
+
 3.1.1 (2021-05-31)
 ------------------
 OAuth2.0 Provider - Bugfixes
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/oauthlib-3.1.1/PKG-INFO new/oauthlib-3.2.0/PKG-INFO
--- old/oauthlib-3.1.1/PKG-INFO 2021-06-01 14:53:11.000000000 +0200
+++ new/oauthlib-3.2.0/PKG-INFO 2022-01-29 22:59:32.000000000 +0100
@@ -1,6 +1,6 @@
 Metadata-Version: 2.1
 Name: oauthlib
-Version: 3.1.1
+Version: 3.2.0
 Summary: A generic, spec-compliant, thorough implementation of the OAuth 
request-signing logic
 Home-page: https://github.com/oauthlib/oauthlib
 Author: The OAuthlib Community
@@ -22,6 +22,8 @@
 Classifier: Programming Language :: Python :: 3.6
 Classifier: Programming Language :: Python :: 3.7
 Classifier: Programming Language :: Python :: 3.8
+Classifier: Programming Language :: Python :: 3.9
+Classifier: Programming Language :: Python :: 3.10
 Classifier: Programming Language :: Python :: 3 :: Only
 Classifier: Programming Language :: Python :: Implementation
 Classifier: Programming Language :: Python :: Implementation :: CPython
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/oauthlib-3.1.1/oauthlib/__init__.py 
new/oauthlib-3.2.0/oauthlib/__init__.py
--- old/oauthlib-3.1.1/oauthlib/__init__.py     2021-06-01 14:52:20.000000000 
+0200
+++ new/oauthlib-3.2.0/oauthlib/__init__.py     2022-01-29 22:58:41.000000000 
+0100
@@ -12,7 +12,7 @@
 from logging import NullHandler
 
 __author__ = 'The OAuthlib Community'
-__version__ = '3.1.1'
+__version__ = '3.2.0'
 
 logging.getLogger('oauthlib').addHandler(NullHandler())
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/oauthlib-3.1.1/oauthlib/oauth2/__init__.py 
new/oauthlib-3.2.0/oauthlib/oauth2/__init__.py
--- old/oauthlib-3.1.1/oauthlib/oauth2/__init__.py      2021-06-01 
14:52:20.000000000 +0200
+++ new/oauthlib-3.2.0/oauthlib/oauth2/__init__.py      2022-01-29 
22:58:41.000000000 +0100
@@ -33,3 +33,4 @@
 from .rfc6749.request_validator import RequestValidator
 from .rfc6749.tokens import BearerToken, OAuth2Token
 from .rfc6749.utils import is_secure_transport
+from .rfc8628.clients import DeviceClient
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/oauthlib-3.1.1/oauthlib/oauth2/rfc6749/clients/base.py 
new/oauthlib-3.2.0/oauthlib/oauth2/rfc6749/clients/base.py
--- old/oauthlib-3.1.1/oauthlib/oauth2/rfc6749/clients/base.py  2021-06-01 
14:52:20.000000000 +0200
+++ new/oauthlib-3.2.0/oauthlib/oauth2/rfc6749/clients/base.py  2022-01-29 
22:58:41.000000000 +0100
@@ -8,6 +8,10 @@
 """
 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 @@
                  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 @@
 
         :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 @@
         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 @@
             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 @@
             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')
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/oauthlib-3.1.1/oauthlib/oauth2/rfc6749/clients/web_application.py 
new/oauthlib-3.2.0/oauthlib/oauth2/rfc6749/clients/web_application.py
--- old/oauthlib-3.1.1/oauthlib/oauth2/rfc6749/clients/web_application.py       
2021-06-01 14:52:20.000000000 +0200
+++ new/oauthlib-3.2.0/oauthlib/oauth2/rfc6749/clients/web_application.py       
2022-01-29 22:58:41.000000000 +0100
@@ -41,7 +41,7 @@
         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 @@
                         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 @@
             
'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 @@
         """
         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 @@
                                   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 @@
             >>> 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 @@
         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.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/oauthlib-3.1.1/oauthlib/oauth2/rfc6749/endpoints/metadata.py 
new/oauthlib-3.2.0/oauthlib/oauth2/rfc6749/endpoints/metadata.py
--- old/oauthlib-3.1.1/oauthlib/oauth2/rfc6749/endpoints/metadata.py    
2021-06-01 14:52:20.000000000 +0200
+++ new/oauthlib-3.2.0/oauthlib/oauth2/rfc6749/endpoints/metadata.py    
2022-01-29 22:58:41.000000000 +0100
@@ -54,7 +54,8 @@
         """Create metadata response
         """
         headers = {
-            'Content-Type': 'application/json'
+            'Content-Type': 'application/json',
+            'Access-Control-Allow-Origin': '*',
         }
         return headers, json.dumps(self.claims), 200
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/oauthlib-3.1.1/oauthlib/oauth2/rfc6749/errors.py 
new/oauthlib-3.2.0/oauthlib/oauth2/rfc6749/errors.py
--- old/oauthlib-3.1.1/oauthlib/oauth2/rfc6749/errors.py        2021-06-01 
14:52:20.000000000 +0200
+++ new/oauthlib-3.2.0/oauthlib/oauth2/rfc6749/errors.py        2022-01-29 
22:58:41.000000000 +0100
@@ -103,15 +103,12 @@
             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 {}
 
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/oauthlib-3.1.1/oauthlib/oauth2/rfc6749/grant_types/authorization_code.py 
new/oauthlib-3.2.0/oauthlib/oauth2/rfc6749/grant_types/authorization_code.py
--- 
old/oauthlib-3.1.1/oauthlib/oauth2/rfc6749/grant_types/authorization_code.py    
    2021-06-01 14:52:20.000000000 +0200
+++ 
new/oauthlib-3.2.0/oauthlib/oauth2/rfc6749/grant_types/authorization_code.py    
    2022-01-29 22:58:41.000000000 +0100
@@ -10,6 +10,7 @@
 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 @@
         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 @@
         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 @@
         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}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/oauthlib-3.1.1/oauthlib/oauth2/rfc6749/grant_types/refresh_token.py 
new/oauthlib-3.2.0/oauthlib/oauth2/rfc6749/grant_types/refresh_token.py
--- old/oauthlib-3.1.1/oauthlib/oauth2/rfc6749/grant_types/refresh_token.py     
2021-06-01 14:52:20.000000000 +0200
+++ new/oauthlib-3.2.0/oauthlib/oauth2/rfc6749/grant_types/refresh_token.py     
2022-01-29 22:58:41.000000000 +0100
@@ -63,7 +63,7 @@
                                            
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)
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/oauthlib-3.1.1/oauthlib/oauth2/rfc6749/parameters.py 
new/oauthlib-3.2.0/oauthlib/oauth2/rfc6749/parameters.py
--- old/oauthlib-3.1.1/oauthlib/oauth2/rfc6749/parameters.py    2021-06-01 
14:52:20.000000000 +0200
+++ new/oauthlib-3.2.0/oauthlib/oauth2/rfc6749/parameters.py    2022-01-29 
22:58:41.000000000 +0100
@@ -23,7 +23,7 @@
 
 
 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 @@
                   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 @@
     .. 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 @@
         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 @@
     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 @@
                          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 @@
         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)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/oauthlib-3.1.1/oauthlib/oauth2/rfc6749/request_validator.py 
new/oauthlib-3.2.0/oauthlib/oauth2/rfc6749/request_validator.py
--- old/oauthlib-3.1.1/oauthlib/oauth2/rfc6749/request_validator.py     
2021-06-01 14:52:20.000000000 +0200
+++ new/oauthlib-3.2.0/oauthlib/oauth2/rfc6749/request_validator.py     
2022-01-29 22:58:41.000000000 +0100
@@ -649,3 +649,28 @@
 
         """
         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
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/oauthlib-3.1.1/oauthlib/oauth2/rfc8628/__init__.py 
new/oauthlib-3.2.0/oauthlib/oauth2/rfc8628/__init__.py
--- old/oauthlib-3.1.1/oauthlib/oauth2/rfc8628/__init__.py      1970-01-01 
01:00:00.000000000 +0100
+++ new/oauthlib-3.2.0/oauthlib/oauth2/rfc8628/__init__.py      2022-01-29 
22:58:41.000000000 +0100
@@ -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__)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/oauthlib-3.1.1/oauthlib/oauth2/rfc8628/clients/__init__.py 
new/oauthlib-3.2.0/oauthlib/oauth2/rfc8628/clients/__init__.py
--- old/oauthlib-3.1.1/oauthlib/oauth2/rfc8628/clients/__init__.py      
1970-01-01 01:00:00.000000000 +0100
+++ new/oauthlib-3.2.0/oauthlib/oauth2/rfc8628/clients/__init__.py      
2022-01-29 22:58:41.000000000 +0100
@@ -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
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/oauthlib-3.1.1/oauthlib/oauth2/rfc8628/clients/device.py 
new/oauthlib-3.2.0/oauthlib/oauth2/rfc8628/clients/device.py
--- old/oauthlib-3.1.1/oauthlib/oauth2/rfc8628/clients/device.py        
1970-01-01 01:00:00.000000000 +0100
+++ new/oauthlib-3.2.0/oauthlib/oauth2/rfc8628/clients/device.py        
2022-01-29 22:58:41.000000000 +0100
@@ -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)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/oauthlib-3.1.1/oauthlib/openid/connect/core/grant_types/__init__.py 
new/oauthlib-3.2.0/oauthlib/openid/connect/core/grant_types/__init__.py
--- old/oauthlib-3.1.1/oauthlib/openid/connect/core/grant_types/__init__.py     
2021-06-01 14:52:20.000000000 +0200
+++ new/oauthlib-3.2.0/oauthlib/openid/connect/core/grant_types/__init__.py     
2022-01-29 22:58:41.000000000 +0100
@@ -10,3 +10,4 @@
 )
 from .hybrid import HybridGrant
 from .implicit import ImplicitGrant
+from .refresh_token import RefreshTokenGrant
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/oauthlib-3.1.1/oauthlib/openid/connect/core/grant_types/refresh_token.py 
new/oauthlib-3.2.0/oauthlib/openid/connect/core/grant_types/refresh_token.py
--- 
old/oauthlib-3.1.1/oauthlib/openid/connect/core/grant_types/refresh_token.py    
    1970-01-01 01:00:00.000000000 +0100
+++ 
new/oauthlib-3.2.0/oauthlib/openid/connect/core/grant_types/refresh_token.py    
    2022-01-29 22:58:41.000000000 +0100
@@ -0,0 +1,34 @@
+"""
+oauthlib.openid.connect.core.grant_types
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+"""
+import logging
+
+from oauthlib.oauth2.rfc6749.grant_types.refresh_token import (
+    RefreshTokenGrant as OAuth2RefreshTokenGrant,
+)
+
+from .base import GrantTypeBase
+
+log = logging.getLogger(__name__)
+
+
+class RefreshTokenGrant(GrantTypeBase):
+
+    def __init__(self, request_validator=None, **kwargs):
+        self.proxy_target = OAuth2RefreshTokenGrant(
+            request_validator=request_validator, **kwargs)
+        self.register_token_modifier(self.add_id_token)
+
+    def add_id_token(self, token, token_handler, request):
+        """
+        Construct an initial version of id_token, and let the
+        request_validator sign or encrypt it.
+
+        The authorization_code version of this method is used to
+        retrieve the nonce accordingly to the code storage.
+        """
+        if not self.request_validator.refresh_id_token(request):
+            return token
+
+        return super().add_id_token(token, token_handler, request)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/oauthlib-3.1.1/oauthlib/openid/connect/core/request_validator.py 
new/oauthlib-3.2.0/oauthlib/openid/connect/core/request_validator.py
--- old/oauthlib-3.1.1/oauthlib/openid/connect/core/request_validator.py        
2021-06-01 14:52:20.000000000 +0200
+++ new/oauthlib-3.2.0/oauthlib/openid/connect/core/request_validator.py        
2022-01-29 22:58:41.000000000 +0100
@@ -306,3 +306,15 @@
         Method is used by:
             UserInfoEndpoint
         """
+
+    def refresh_id_token(self, request):
+        """Whether the id token should be refreshed. Default, True
+
+        :param request: OAuthlib request.
+        :type request: oauthlib.common.Request
+        :rtype: True or False
+
+        Method is used by:
+            RefreshTokenGrant
+        """
+        return True
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/oauthlib-3.1.1/oauthlib.egg-info/PKG-INFO 
new/oauthlib-3.2.0/oauthlib.egg-info/PKG-INFO
--- old/oauthlib-3.1.1/oauthlib.egg-info/PKG-INFO       2021-06-01 
14:53:11.000000000 +0200
+++ new/oauthlib-3.2.0/oauthlib.egg-info/PKG-INFO       2022-01-29 
22:59:32.000000000 +0100
@@ -1,6 +1,6 @@
 Metadata-Version: 2.1
 Name: oauthlib
-Version: 3.1.1
+Version: 3.2.0
 Summary: A generic, spec-compliant, thorough implementation of the OAuth 
request-signing logic
 Home-page: https://github.com/oauthlib/oauthlib
 Author: The OAuthlib Community
@@ -22,6 +22,8 @@
 Classifier: Programming Language :: Python :: 3.6
 Classifier: Programming Language :: Python :: 3.7
 Classifier: Programming Language :: Python :: 3.8
+Classifier: Programming Language :: Python :: 3.9
+Classifier: Programming Language :: Python :: 3.10
 Classifier: Programming Language :: Python :: 3 :: Only
 Classifier: Programming Language :: Python :: Implementation
 Classifier: Programming Language :: Python :: Implementation :: CPython
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/oauthlib-3.1.1/oauthlib.egg-info/SOURCES.txt 
new/oauthlib-3.2.0/oauthlib.egg-info/SOURCES.txt
--- old/oauthlib-3.1.1/oauthlib.egg-info/SOURCES.txt    2021-06-01 
14:53:11.000000000 +0200
+++ new/oauthlib-3.2.0/oauthlib.egg-info/SOURCES.txt    2022-01-29 
22:59:32.000000000 +0100
@@ -58,6 +58,9 @@
 oauthlib/oauth2/rfc6749/grant_types/implicit.py
 oauthlib/oauth2/rfc6749/grant_types/refresh_token.py
 oauthlib/oauth2/rfc6749/grant_types/resource_owner_password_credentials.py
+oauthlib/oauth2/rfc8628/__init__.py
+oauthlib/oauth2/rfc8628/clients/__init__.py
+oauthlib/oauth2/rfc8628/clients/device.py
 oauthlib/openid/__init__.py
 oauthlib/openid/connect/__init__.py
 oauthlib/openid/connect/core/__init__.py
@@ -73,6 +76,7 @@
 oauthlib/openid/connect/core/grant_types/dispatchers.py
 oauthlib/openid/connect/core/grant_types/hybrid.py
 oauthlib/openid/connect/core/grant_types/implicit.py
+oauthlib/openid/connect/core/grant_types/refresh_token.py
 tests/__init__.py
 tests/test_common.py
 tests/test_uri_validate.py
@@ -122,6 +126,9 @@
 tests/oauth2/rfc6749/grant_types/test_implicit.py
 tests/oauth2/rfc6749/grant_types/test_refresh_token.py
 tests/oauth2/rfc6749/grant_types/test_resource_owner_password.py
+tests/oauth2/rfc8628/__init__.py
+tests/oauth2/rfc8628/clients/__init__.py
+tests/oauth2/rfc8628/clients/test_device.py
 tests/openid/__init__.py
 tests/openid/connect/__init__.py
 tests/openid/connect/core/__init__.py
@@ -138,4 +145,5 @@
 tests/openid/connect/core/grant_types/test_dispatchers.py
 tests/openid/connect/core/grant_types/test_hybrid.py
 tests/openid/connect/core/grant_types/test_implicit.py
+tests/openid/connect/core/grant_types/test_refresh_token.py
 tests/unittest/__init__.py
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/oauthlib-3.1.1/oauthlib.egg-info/requires.txt 
new/oauthlib-3.2.0/oauthlib.egg-info/requires.txt
--- old/oauthlib-3.1.1/oauthlib.egg-info/requires.txt   2021-06-01 
14:53:11.000000000 +0200
+++ new/oauthlib-3.2.0/oauthlib.egg-info/requires.txt   2022-01-29 
22:59:32.000000000 +0100
@@ -1,10 +1,10 @@
 
 [rsa]
-cryptography<4,>=3.0.0
+cryptography>=3.0.0
 
 [signals]
 blinker>=1.4.0
 
 [signedtoken]
-cryptography<4,>=3.0.0
+cryptography>=3.0.0
 pyjwt<3,>=2.0.0
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/oauthlib-3.1.1/setup.cfg new/oauthlib-3.2.0/setup.cfg
--- old/oauthlib-3.1.1/setup.cfg        2021-06-01 14:53:11.000000000 +0200
+++ new/oauthlib-3.2.0/setup.cfg        2022-01-29 22:59:32.000000000 +0100
@@ -1,6 +1,3 @@
-[bdist_wheel]
-universal = 1
-
 [metadata]
 license_file = LICENSE
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/oauthlib-3.1.1/setup.py new/oauthlib-3.2.0/setup.py
--- old/oauthlib-3.1.1/setup.py 2021-06-01 14:52:20.000000000 +0200
+++ new/oauthlib-3.2.0/setup.py 2022-01-29 22:58:41.000000000 +0100
@@ -16,8 +16,8 @@
         return f.read()
 
 
-rsa_require = ['cryptography>=3.0.0,<4']
-signedtoken_require = ['cryptography>=3.0.0,<4', 'pyjwt>=2.0.0,<3']
+rsa_require = ['cryptography>=3.0.0']
+signedtoken_require = ['cryptography>=3.0.0', 'pyjwt>=2.0.0,<3']
 signals_require = ['blinker>=1.4.0']
 
 setup(
@@ -54,6 +54,8 @@
         'Programming Language :: Python :: 3.6',
         'Programming Language :: Python :: 3.7',
         'Programming Language :: Python :: 3.8',
+        'Programming Language :: Python :: 3.9',
+        'Programming Language :: Python :: 3.10',
         'Programming Language :: Python :: 3 :: Only',
         'Programming Language :: Python :: Implementation',
         'Programming Language :: Python :: Implementation :: CPython',
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/oauthlib-3.1.1/tests/oauth2/rfc6749/clients/test_base.py 
new/oauthlib-3.2.0/tests/oauth2/rfc6749/clients/test_base.py
--- old/oauthlib-3.1.1/tests/oauth2/rfc6749/clients/test_base.py        
2021-06-01 14:52:20.000000000 +0200
+++ new/oauthlib-3.2.0/tests/oauth2/rfc6749/clients/test_base.py        
2022-01-29 22:58:41.000000000 +0100
@@ -301,3 +301,55 @@
         self.assertEqual(u, url)
         self.assertEqual(h, {'Content-Type': 
'application/x-www-form-urlencoded'})
         self.assertFormBodyEqual(b, 
'grant_type=refresh_token&scope={}&refresh_token={}'.format(scope, token))
+
+    def test_parse_token_response_invalid_expires_at(self):
+        token_json = ('{   "access_token":"2YotnFZFEjr1zCsicMWpAA",'
+                      '    "token_type":"example",'
+                      '    "expires_at":"2006-01-02T15:04:05Z",'
+                      '    "scope":"/profile",'
+                      '    "example_parameter":"example_value"}')
+        token = {
+            "access_token": "2YotnFZFEjr1zCsicMWpAA",
+            "token_type": "example",
+            "expires_at": "2006-01-02T15:04:05Z",
+            "scope": ["/profile"],
+            "example_parameter": "example_value"
+        }
+
+        client = Client(self.client_id)
+
+        # Parse code and state
+        response = client.parse_request_body_response(token_json, 
scope=["/profile"])
+        self.assertEqual(response, token)
+        self.assertEqual(None, client._expires_at)
+        self.assertEqual(client.access_token, response.get("access_token"))
+        self.assertEqual(client.refresh_token, response.get("refresh_token"))
+        self.assertEqual(client.token_type, response.get("token_type"))
+
+
+    def test_create_code_verifier_min_length(self):
+        client = Client(self.client_id)
+        length = 43
+        code_verifier = client.create_code_verifier(length=length)
+        self.assertEqual(client.code_verifier, code_verifier)
+
+    def test_create_code_verifier_max_length(self):
+        client = Client(self.client_id)
+        length = 128
+        code_verifier = client.create_code_verifier(length=length)
+        self.assertEqual(client.code_verifier, code_verifier)
+
+    def test_create_code_challenge_plain(self):
+        client = Client(self.client_id)
+        code_verifier = client.create_code_verifier(length=128)
+        code_challenge_plain = 
client.create_code_challenge(code_verifier=code_verifier)
+
+        # if no code_challenge_method specified, code_challenge = code_verifier
+        self.assertEqual(code_challenge_plain, client.code_verifier)
+        self.assertEqual(client.code_challenge_method, "plain")
+
+    def test_create_code_challenge_s256(self):
+        client = Client(self.client_id)
+        code_verifier = client.create_code_verifier(length=128)
+        code_challenge_s256 = 
client.create_code_challenge(code_verifier=code_verifier, 
code_challenge_method='S256')
+        self.assertEqual(code_challenge_s256, client.code_challenge)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/oauthlib-3.1.1/tests/oauth2/rfc6749/clients/test_web_application.py 
new/oauthlib-3.2.0/tests/oauth2/rfc6749/clients/test_web_application.py
--- old/oauthlib-3.1.1/tests/oauth2/rfc6749/clients/test_web_application.py     
2021-06-01 14:52:20.000000000 +0200
+++ new/oauthlib-3.2.0/tests/oauth2/rfc6749/clients/test_web_application.py     
2022-01-29 22:58:41.000000000 +0100
@@ -24,10 +24,15 @@
     uri_id = uri + "&response_type=code&client_id=" + client_id
     uri_redirect = uri_id + "&redirect_uri=http%3A%2F%2Fmy.page.com%2Fcallback"
     redirect_uri = "http://my.page.com/callback";
+    code_verifier = "code_verifier"
     scope = ["/profile"]
     state = "xyz"
+    code_challenge = "code_challenge"
+    code_challenge_method = "S256"
     uri_scope = uri_id + "&scope=%2Fprofile"
     uri_state = uri_id + "&state=" + state
+    uri_code_challenge = uri_id + "&code_challenge=" + code_challenge + 
"&code_challenge_method=" + code_challenge_method
+    uri_code_challenge_method = uri_id + "&code_challenge=" + code_challenge + 
"&code_challenge_method=plain"
     kwargs = {
         "some": "providers",
         "require": "extra arguments"
@@ -40,6 +45,7 @@
 
     body_code = 
"not=empty&grant_type=authorization_code&code={}&client_id={}".format(code, 
client_id)
     body_redirect = body_code + 
"&redirect_uri=http%3A%2F%2Fmy.page.com%2Fcallback"
+    bode_code_verifier = body_code + "&code_verifier=code_verifier"
     body_kwargs = body_code + "&some=providers&require=extra+arguments"
 
     response_uri = "https://client.example.com/cb?code=zzzzaaaa&state=xyz";
@@ -80,6 +86,14 @@
         uri = client.prepare_request_uri(self.uri, state=self.state)
         self.assertURLEqual(uri, self.uri_state)
 
+        # with code_challenge and code_challenge_method
+        uri = client.prepare_request_uri(self.uri, 
code_challenge=self.code_challenge, 
code_challenge_method=self.code_challenge_method)
+        self.assertURLEqual(uri, self.uri_code_challenge)
+
+        # with no code_challenge_method
+        uri = client.prepare_request_uri(self.uri, 
code_challenge=self.code_challenge)
+        self.assertURLEqual(uri, self.uri_code_challenge_method)
+
         # With extra parameters through kwargs
         uri = client.prepare_request_uri(self.uri, **self.kwargs)
         self.assertURLEqual(uri, self.uri_kwargs)
@@ -99,6 +113,10 @@
         body = client.prepare_request_body(body=self.body, 
redirect_uri=self.redirect_uri)
         self.assertFormBodyEqual(body, self.body_redirect)
 
+        # With code verifier
+        body = client.prepare_request_body(body=self.body, 
code_verifier=self.code_verifier)
+        self.assertFormBodyEqual(body, self.bode_code_verifier)
+
         # With extra parameters
         body = client.prepare_request_body(body=self.body, **self.kwargs)
         self.assertFormBodyEqual(body, self.body_kwargs)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/oauthlib-3.1.1/tests/oauth2/rfc6749/endpoints/test_error_responses.py 
new/oauthlib-3.2.0/tests/oauth2/rfc6749/endpoints/test_error_responses.py
--- old/oauthlib-3.1.1/tests/oauth2/rfc6749/endpoints/test_error_responses.py   
2021-06-01 14:52:20.000000000 +0200
+++ new/oauthlib-3.2.0/tests/oauth2/rfc6749/endpoints/test_error_responses.py   
2022-01-29 22:58:41.000000000 +0100
@@ -178,21 +178,21 @@
         description = 'Duplicate client_id parameter.'
 
         # Authorization code
-        self.assertRaisesRegexp(errors.InvalidRequestFatalError,
+        self.assertRaisesRegex(errors.InvalidRequestFatalError,
                               description,
                               self.web.validate_authorization_request,
                               uri.format('code'))
-        self.assertRaisesRegexp(errors.InvalidRequestFatalError,
+        self.assertRaisesRegex(errors.InvalidRequestFatalError,
                               description,
                               self.web.create_authorization_response,
                               uri.format('code'), scopes=['foo'])
 
         # Implicit grant
-        self.assertRaisesRegexp(errors.InvalidRequestFatalError,
+        self.assertRaisesRegex(errors.InvalidRequestFatalError,
                               description,
                               self.mobile.validate_authorization_request,
                               uri.format('token'))
-        self.assertRaisesRegexp(errors.InvalidRequestFatalError,
+        self.assertRaisesRegex(errors.InvalidRequestFatalError,
                               description,
                               self.mobile.create_authorization_response,
                               uri.format('token'), scopes=['foo'])
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/oauthlib-3.1.1/tests/oauth2/rfc6749/endpoints/test_introspect_endpoint.py 
new/oauthlib-3.2.0/tests/oauth2/rfc6749/endpoints/test_introspect_endpoint.py
--- 
old/oauthlib-3.1.1/tests/oauth2/rfc6749/endpoints/test_introspect_endpoint.py   
    2021-06-01 14:52:20.000000000 +0200
+++ 
new/oauthlib-3.2.0/tests/oauth2/rfc6749/endpoints/test_introspect_endpoint.py   
    2022-01-29 22:58:41.000000000 +0100
@@ -87,7 +87,7 @@
             'Content-Type': 'application/json',
             'Cache-Control': 'no-store',
             'Pragma': 'no-cache',
-            "WWW-Authenticate": 'Bearer, error="invalid_client"'
+            "WWW-Authenticate": 'Bearer error="invalid_client"'
         })
         self.assertEqual(loads(b)['error'], 'invalid_client')
         self.assertEqual(s, 401)
@@ -115,7 +115,7 @@
             'Content-Type': 'application/json',
             'Cache-Control': 'no-store',
             'Pragma': 'no-cache',
-            "WWW-Authenticate": 'Bearer, error="invalid_client"'
+            "WWW-Authenticate": 'Bearer error="invalid_client"'
         })
         self.assertEqual(loads(b)['error'], 'invalid_client')
         self.assertEqual(s, 401)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/oauthlib-3.1.1/tests/oauth2/rfc6749/endpoints/test_metadata.py 
new/oauthlib-3.2.0/tests/oauth2/rfc6749/endpoints/test_metadata.py
--- old/oauthlib-3.1.1/tests/oauth2/rfc6749/endpoints/test_metadata.py  
2021-06-01 14:52:20.000000000 +0200
+++ new/oauthlib-3.2.0/tests/oauth2/rfc6749/endpoints/test_metadata.py  
2022-01-29 22:58:41.000000000 +0100
@@ -1,6 +1,7 @@
 # -*- coding: utf-8 -*-
 from oauthlib.oauth2 import MetadataEndpoint, Server, TokenEndpoint
 
+import json
 from tests.unittest import TestCase
 
 
@@ -37,6 +38,20 @@
         self.maxDiff = None
         self.assertEqual(openid_claims, oauth2_claims)
 
+    def test_create_metadata_response(self):
+        endpoint = TokenEndpoint(None, None, grant_types={"password": None})
+        metadata = MetadataEndpoint([endpoint], {
+            "issuer": 'https://foo.bar',
+            "token_endpoint": "https://foo.bar/token";
+        })
+        headers, body, status = metadata.create_metadata_response('/', 'GET')
+        assert headers == {
+            'Content-Type': 'application/json',
+            'Access-Control-Allow-Origin': '*',
+        }
+        claims = json.loads(body)
+        assert claims['issuer'] == 'https://foo.bar'
+
     def test_token_endpoint(self):
         endpoint = TokenEndpoint(None, None, grant_types={"password": None})
         metadata = MetadataEndpoint([endpoint], {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/oauthlib-3.1.1/tests/oauth2/rfc6749/endpoints/test_revocation_endpoint.py 
new/oauthlib-3.2.0/tests/oauth2/rfc6749/endpoints/test_revocation_endpoint.py
--- 
old/oauthlib-3.1.1/tests/oauth2/rfc6749/endpoints/test_revocation_endpoint.py   
    2021-06-01 14:52:20.000000000 +0200
+++ 
new/oauthlib-3.2.0/tests/oauth2/rfc6749/endpoints/test_revocation_endpoint.py   
    2022-01-29 22:58:41.000000000 +0100
@@ -55,7 +55,7 @@
             'Content-Type': 'application/json',
             'Cache-Control': 'no-store',
             'Pragma': 'no-cache',
-            "WWW-Authenticate": 'Bearer, error="invalid_client"'
+            "WWW-Authenticate": 'Bearer error="invalid_client"'
         })
         self.assertEqual(loads(b)['error'], 'invalid_client')
         self.assertEqual(s, 401)
@@ -83,7 +83,7 @@
             'Content-Type': 'application/json',
             'Cache-Control': 'no-store',
             'Pragma': 'no-cache',
-            "WWW-Authenticate": 'Bearer, error="invalid_client"'
+            "WWW-Authenticate": 'Bearer error="invalid_client"'
         })
         self.assertEqual(loads(b)['error'], 'invalid_client')
         self.assertEqual(s, 401)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/oauthlib-3.1.1/tests/oauth2/rfc6749/grant_types/test_authorization_code.py 
new/oauthlib-3.2.0/tests/oauth2/rfc6749/grant_types/test_authorization_code.py
--- 
old/oauthlib-3.1.1/tests/oauth2/rfc6749/grant_types/test_authorization_code.py  
    2021-06-01 14:52:20.000000000 +0200
+++ 
new/oauthlib-3.2.0/tests/oauth2/rfc6749/grant_types/test_authorization_code.py  
    2022-01-29 22:58:41.000000000 +0100
@@ -28,6 +28,7 @@
         self.mock_validator = mock.MagicMock()
         self.mock_validator.is_pkce_required.return_value = False
         self.mock_validator.get_code_challenge.return_value = None
+        self.mock_validator.is_origin_allowed.return_value = False
         self.mock_validator.authenticate_client.side_effect = self.set_client
         self.auth = 
AuthorizationCodeGrant(request_validator=self.mock_validator)
 
@@ -324,3 +325,58 @@
             
authorization_code.code_challenge_method_s256("dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk",
                                                           
"E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM")
         )
+
+    def test_code_modifier_called(self):
+        bearer = BearerToken(self.mock_validator)
+        code_modifier = mock.MagicMock(wraps=lambda grant, *a: grant)
+        self.auth.register_code_modifier(code_modifier)
+        self.auth.create_authorization_response(self.request, bearer)
+        code_modifier.assert_called_once()
+
+    def test_hybrid_token_save(self):
+        bearer = BearerToken(self.mock_validator)
+        self.auth.register_code_modifier(
+            lambda grant, *a: dict(list(grant.items()) + [('access_token', 1)])
+        )
+        self.auth.create_authorization_response(self.request, bearer)
+        self.mock_validator.save_token.assert_called_once()
+
+    # CORS
+
+    def test_create_cors_headers(self):
+        bearer = BearerToken(self.mock_validator)
+        self.request.headers['origin'] = 'https://foo.bar'
+        self.mock_validator.is_origin_allowed.return_value = True
+
+        headers = self.auth.create_token_response(self.request, bearer)[0]
+        self.assertEqual(
+            headers['Access-Control-Allow-Origin'], 'https://foo.bar'
+        )
+        self.mock_validator.is_origin_allowed.assert_called_once_with(
+            'abcdef', 'https://foo.bar', self.request
+        )
+
+    def test_create_cors_headers_no_origin(self):
+        bearer = BearerToken(self.mock_validator)
+        headers = self.auth.create_token_response(self.request, bearer)[0]
+        self.assertNotIn('Access-Control-Allow-Origin', headers)
+        self.mock_validator.is_origin_allowed.assert_not_called()
+
+    def test_create_cors_headers_insecure_origin(self):
+        bearer = BearerToken(self.mock_validator)
+        self.request.headers['origin'] = 'http://foo.bar'
+
+        headers = self.auth.create_token_response(self.request, bearer)[0]
+        self.assertNotIn('Access-Control-Allow-Origin', headers)
+        self.mock_validator.is_origin_allowed.assert_not_called()
+
+    def test_create_cors_headers_invalid_origin(self):
+        bearer = BearerToken(self.mock_validator)
+        self.request.headers['origin'] = 'https://foo.bar'
+        self.mock_validator.is_origin_allowed.return_value = False
+
+        headers = self.auth.create_token_response(self.request, bearer)[0]
+        self.assertNotIn('Access-Control-Allow-Origin', headers)
+        self.mock_validator.is_origin_allowed.assert_called_once_with(
+            'abcdef', 'https://foo.bar', self.request
+        )
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/oauthlib-3.1.1/tests/oauth2/rfc6749/test_parameters.py 
new/oauthlib-3.2.0/tests/oauth2/rfc6749/test_parameters.py
--- old/oauthlib-3.1.1/tests/oauth2/rfc6749/test_parameters.py  2021-06-01 
14:52:20.000000000 +0200
+++ new/oauthlib-3.2.0/tests/oauth2/rfc6749/test_parameters.py  2022-01-29 
22:58:41.000000000 +0100
@@ -21,12 +21,15 @@
     list_scope = ['list', 'of', 'scopes']
 
     auth_grant = {'response_type': 'code'}
+    auth_grant_pkce = {'response_type': 'code', 'code_challenge': 
"code_challenge",
+                       'code_challenge_method': 'code_challenge_method'}
     auth_grant_list_scope = {}
     auth_implicit = {'response_type': 'token', 'extra': 'extra'}
     auth_implicit_list_scope = {}
 
     def setUp(self):
         self.auth_grant.update(self.auth_base)
+        self.auth_grant_pkce.update(self.auth_base)
         self.auth_implicit.update(self.auth_base)
         self.auth_grant_list_scope.update(self.auth_grant)
         self.auth_grant_list_scope['scope'] = self.list_scope
@@ -37,7 +40,14 @@
                      '&client_id=s6BhdRkqt3&redirect_uri=https%3A%2F%2F'
                      'client.example.com%2Fcb&scope={1}&state={2}{3}')
 
+    auth_base_uri_pkce = 
('https://server.example.com/authorize?response_type={0}'
+                     '&client_id=s6BhdRkqt3&redirect_uri=https%3A%2F%2F'
+                     
'client.example.com%2Fcb&scope={1}&state={2}{3}&code_challenge={4}'
+                     '&code_challenge_method={5}')
+
     auth_grant_uri = auth_base_uri.format('code', 'photos', state, '')
+    auth_grant_uri_pkce = auth_base_uri_pkce.format('code', 'photos', state, 
'', 'code_challenge',
+                                                    'code_challenge_method')
     auth_grant_uri_list_scope = auth_base_uri.format('code', 'list+of+scopes', 
state, '')
     auth_implicit_uri = auth_base_uri.format('token', 'photos', state, 
'&extra=extra')
     auth_implicit_uri_list_scope = auth_base_uri.format('token', 
'list+of+scopes', state, '&extra=extra')
@@ -47,11 +57,21 @@
         'code': 'SplxlOBeZQQYbYS6WxSbIA',
         'redirect_uri': 'https://client.example.com/cb'
     }
+    grant_body_pkce = {
+        'grant_type': 'authorization_code',
+        'code': 'SplxlOBeZQQYbYS6WxSbIA',
+        'redirect_uri': 'https://client.example.com/cb',
+        'code_verifier': 'code_verifier'
+    }
     grant_body_scope = {'scope': 'photos'}
     grant_body_list_scope = {'scope': list_scope}
     auth_grant_body = ('grant_type=authorization_code&'
                        'code=SplxlOBeZQQYbYS6WxSbIA&'
                        'redirect_uri=https%3A%2F%2Fclient.example.com%2Fcb')
+    auth_grant_body_pkce = ('grant_type=authorization_code&'
+                       'code=SplxlOBeZQQYbYS6WxSbIA&'
+                       'redirect_uri=https%3A%2F%2Fclient.example.com%2Fcb'
+                       '&code_verifier=code_verifier')
     auth_grant_body_scope = auth_grant_body + '&scope=photos'
     auth_grant_body_list_scope = auth_grant_body + '&scope=list+of+scopes'
 
@@ -179,12 +199,14 @@
         self.assertURLEqual(prepare_grant_uri(**self.auth_grant_list_scope), 
self.auth_grant_uri_list_scope)
         self.assertURLEqual(prepare_grant_uri(**self.auth_implicit), 
self.auth_implicit_uri)
         
self.assertURLEqual(prepare_grant_uri(**self.auth_implicit_list_scope), 
self.auth_implicit_uri_list_scope)
+        self.assertURLEqual(prepare_grant_uri(**self.auth_grant_pkce), 
self.auth_grant_uri_pkce)
 
     def test_prepare_token_request(self):
         """Verify correct access token request body construction."""
         self.assertFormBodyEqual(prepare_token_request(**self.grant_body), 
self.auth_grant_body)
         self.assertFormBodyEqual(prepare_token_request(**self.pwd_body), 
self.password_body)
         self.assertFormBodyEqual(prepare_token_request(**self.cred_grant), 
self.cred_body)
+        
self.assertFormBodyEqual(prepare_token_request(**self.grant_body_pkce), 
self.auth_grant_body_pkce)
 
     def test_grant_response(self):
         """Verify correct parameter parsing and validation for auth code 
responses."""
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/oauthlib-3.1.1/tests/oauth2/rfc6749/test_request_validator.py 
new/oauthlib-3.2.0/tests/oauth2/rfc6749/test_request_validator.py
--- old/oauthlib-3.1.1/tests/oauth2/rfc6749/test_request_validator.py   
2021-06-01 14:52:20.000000000 +0200
+++ new/oauthlib-3.2.0/tests/oauth2/rfc6749/test_request_validator.py   
2022-01-29 22:58:41.000000000 +0100
@@ -46,3 +46,6 @@
         self.assertRaises(NotImplementedError, v.validate_user,
                 'username', 'password', 'client', 'request')
         self.assertTrue(v.client_authentication_required('r'))
+        self.assertFalse(
+            v.is_origin_allowed('client_id', 'https://foo.bar', 'r')
+        )
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/oauthlib-3.1.1/tests/oauth2/rfc8628/clients/test_device.py 
new/oauthlib-3.2.0/tests/oauth2/rfc8628/clients/test_device.py
--- old/oauthlib-3.1.1/tests/oauth2/rfc8628/clients/test_device.py      
1970-01-01 01:00:00.000000000 +0100
+++ new/oauthlib-3.2.0/tests/oauth2/rfc8628/clients/test_device.py      
2022-01-29 22:58:41.000000000 +0100
@@ -0,0 +1,63 @@
+import os
+from unittest.mock import patch
+
+from oauthlib import signals
+from oauthlib.oauth2 import DeviceClient
+
+from tests.unittest import TestCase
+
+
+class DeviceClientTest(TestCase):
+
+    client_id = "someclientid"
+    kwargs = {
+        "some": "providers",
+        "require": "extra arguments"
+    }
+
+    client_secret = "asecret"
+
+    device_code = "somedevicecode"
+
+    scope = ["profile", "email"]
+
+    body = "not=empty"
+
+    body_up = 
"not=empty&grant_type=urn:ietf:params:oauth:grant-type:device_code"
+    body_code = body_up + "&device_code=somedevicecode"
+    body_kwargs = body_code + "&some=providers&require=extra+arguments"
+
+    uri = "https://example.com/path?query=world";
+    uri_id = uri + "&client_id=" + client_id
+    uri_grant = uri_id + 
"&grant_type=urn:ietf:params:oauth:grant-type:device_code"
+    uri_secret = uri_grant + "&client_secret=asecret"
+    uri_scope = uri_secret + "&scope=profile+email"
+
+    def test_request_body(self):
+        client = DeviceClient(self.client_id)
+
+        # Basic, no extra arguments
+        body = client.prepare_request_body(self.device_code, body=self.body)
+        self.assertFormBodyEqual(body, self.body_code)
+
+        rclient = DeviceClient(self.client_id)
+        body = rclient.prepare_request_body(self.device_code, body=self.body)
+        self.assertFormBodyEqual(body, self.body_code)
+
+        # With extra parameters
+        body = client.prepare_request_body(
+            self.device_code, body=self.body, **self.kwargs)
+        self.assertFormBodyEqual(body, self.body_kwargs)
+
+    def test_request_uri(self):
+        client = DeviceClient(self.client_id)
+
+        uri = client.prepare_request_uri(self.uri)
+        self.assertURLEqual(uri, self.uri_grant)
+
+        client = DeviceClient(self.client_id, client_secret=self.client_secret)
+        uri = client.prepare_request_uri(self.uri)
+        self.assertURLEqual(uri, self.uri_secret)
+
+        uri = client.prepare_request_uri(self.uri, scope=self.scope)
+        self.assertURLEqual(uri, self.uri_scope)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/oauthlib-3.1.1/tests/openid/connect/core/grant_types/test_refresh_token.py 
new/oauthlib-3.2.0/tests/openid/connect/core/grant_types/test_refresh_token.py
--- 
old/oauthlib-3.1.1/tests/openid/connect/core/grant_types/test_refresh_token.py  
    1970-01-01 01:00:00.000000000 +0100
+++ 
new/oauthlib-3.2.0/tests/openid/connect/core/grant_types/test_refresh_token.py  
    2022-01-29 22:58:41.000000000 +0100
@@ -0,0 +1,105 @@
+import json
+from unittest import mock
+
+from oauthlib.common import Request
+from oauthlib.oauth2.rfc6749.tokens import BearerToken
+from oauthlib.openid.connect.core.grant_types import RefreshTokenGrant
+
+from tests.oauth2.rfc6749.grant_types.test_refresh_token import (
+    RefreshTokenGrantTest,
+)
+from tests.unittest import TestCase
+
+
+def get_id_token_mock(token, token_handler, request):
+    return "MOCKED_TOKEN"
+
+
+class OpenIDRefreshTokenInterferenceTest(RefreshTokenGrantTest):
+    """Test that OpenID don't interfere with normal OAuth 2 flows."""
+
+    def setUp(self):
+        super().setUp()
+        self.auth = RefreshTokenGrant(request_validator=self.mock_validator)
+
+
+class OpenIDRefreshTokenTest(TestCase):
+
+    def setUp(self):
+        self.request = Request('http://a.b/path')
+        self.request.grant_type = 'refresh_token'
+        self.request.refresh_token = 'lsdkfhj230'
+        self.request.scope = ('hello', 'openid')
+        self.mock_validator = mock.MagicMock()
+
+        self.mock_validator = mock.MagicMock()
+        self.mock_validator.authenticate_client.side_effect = self.set_client
+        self.mock_validator.get_id_token.side_effect = get_id_token_mock
+        self.auth = RefreshTokenGrant(request_validator=self.mock_validator)
+
+    def set_client(self, request):
+        request.client = mock.MagicMock()
+        request.client.client_id = 'mocked'
+        return True
+
+    def test_refresh_id_token(self):
+        self.mock_validator.get_original_scopes.return_value = [
+            'hello', 'openid'
+        ]
+        bearer = BearerToken(self.mock_validator)
+
+        headers, body, status_code = self.auth.create_token_response(
+            self.request, bearer
+        )
+
+        token = json.loads(body)
+        self.assertEqual(self.mock_validator.save_token.call_count, 1)
+        self.assertIn('access_token', token)
+        self.assertIn('refresh_token', token)
+        self.assertIn('id_token', token)
+        self.assertIn('token_type', token)
+        self.assertIn('expires_in', token)
+        self.assertEqual(token['scope'], 'hello openid')
+        self.mock_validator.refresh_id_token.assert_called_once_with(
+            self.request
+        )
+
+    def test_refresh_id_token_false(self):
+        self.mock_validator.refresh_id_token.return_value = False
+        self.mock_validator.get_original_scopes.return_value = [
+            'hello', 'openid'
+        ]
+        bearer = BearerToken(self.mock_validator)
+
+        headers, body, status_code = self.auth.create_token_response(
+            self.request, bearer
+        )
+
+        token = json.loads(body)
+        self.assertEqual(self.mock_validator.save_token.call_count, 1)
+        self.assertIn('access_token', token)
+        self.assertIn('refresh_token', token)
+        self.assertIn('token_type', token)
+        self.assertIn('expires_in', token)
+        self.assertEqual(token['scope'], 'hello openid')
+        self.assertNotIn('id_token', token)
+        self.mock_validator.refresh_id_token.assert_called_once_with(
+            self.request
+        )
+
+    def test_refresh_token_without_openid_scope(self):
+        self.request.scope = "hello"
+        bearer = BearerToken(self.mock_validator)
+
+        headers, body, status_code = self.auth.create_token_response(
+            self.request, bearer
+        )
+
+        token = json.loads(body)
+        self.assertEqual(self.mock_validator.save_token.call_count, 1)
+        self.assertIn('access_token', token)
+        self.assertIn('refresh_token', token)
+        self.assertIn('token_type', token)
+        self.assertIn('expires_in', token)
+        self.assertNotIn('id_token', token)
+        self.assertEqual(token['scope'], 'hello')

Reply via email to