aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/python/requests-oauthlib
diff options
context:
space:
mode:
authorrobot-piglet <robot-piglet@yandex-team.com>2024-03-25 09:11:17 +0300
committerrobot-piglet <robot-piglet@yandex-team.com>2024-03-25 09:17:48 +0300
commit4624e4cfd95649270db02616edde8d0ca249b63d (patch)
tree1c8a43f50533ca759d137f258e42862e8cf5e80f /contrib/python/requests-oauthlib
parentd2d971701bd8377ead5f973c96be81042774bd2a (diff)
downloadydb-4624e4cfd95649270db02616edde8d0ca249b63d.tar.gz
Intermediate changes
Diffstat (limited to 'contrib/python/requests-oauthlib')
-rw-r--r--contrib/python/requests-oauthlib/.dist-info/METADATA40
-rw-r--r--contrib/python/requests-oauthlib/AUTHORS.rst12
-rw-r--r--contrib/python/requests-oauthlib/README.rst4
-rw-r--r--contrib/python/requests-oauthlib/requests_oauthlib/__init__.py3
-rw-r--r--contrib/python/requests-oauthlib/requests_oauthlib/compliance_fixes/__init__.py3
-rw-r--r--contrib/python/requests-oauthlib/requests_oauthlib/compliance_fixes/douban.py4
-rw-r--r--contrib/python/requests-oauthlib/requests_oauthlib/compliance_fixes/ebay.py3
-rw-r--r--contrib/python/requests-oauthlib/requests_oauthlib/compliance_fixes/facebook.py10
-rw-r--r--contrib/python/requests-oauthlib/requests_oauthlib/compliance_fixes/fitbit.py4
-rw-r--r--contrib/python/requests-oauthlib/requests_oauthlib/compliance_fixes/instagram.py5
-rw-r--r--contrib/python/requests-oauthlib/requests_oauthlib/compliance_fixes/mailchimp.py6
-rw-r--r--contrib/python/requests-oauthlib/requests_oauthlib/compliance_fixes/plentymarkets.py4
-rw-r--r--contrib/python/requests-oauthlib/requests_oauthlib/compliance_fixes/slack.py5
-rw-r--r--contrib/python/requests-oauthlib/requests_oauthlib/compliance_fixes/weibo.py4
-rw-r--r--contrib/python/requests-oauthlib/requests_oauthlib/oauth1_auth.py13
-rw-r--r--contrib/python/requests-oauthlib/requests_oauthlib/oauth1_session.py13
-rw-r--r--contrib/python/requests-oauthlib/requests_oauthlib/oauth2_auth.py1
-rw-r--r--contrib/python/requests-oauthlib/requests_oauthlib/oauth2_session.py65
-rw-r--r--contrib/python/requests-oauthlib/tests/examples/base.py106
-rw-r--r--contrib/python/requests-oauthlib/tests/examples/test_native_spa_pkce_auth0.py39
-rw-r--r--contrib/python/requests-oauthlib/tests/test_compliance_fixes.py63
-rw-r--r--contrib/python/requests-oauthlib/tests/test_core.py6
-rw-r--r--contrib/python/requests-oauthlib/tests/test_oauth1_session.py39
-rw-r--r--contrib/python/requests-oauthlib/tests/test_oauth2_auth.py1
-rw-r--r--contrib/python/requests-oauthlib/tests/test_oauth2_session.py37
-rw-r--r--contrib/python/requests-oauthlib/ya.make2
26 files changed, 366 insertions, 126 deletions
diff --git a/contrib/python/requests-oauthlib/.dist-info/METADATA b/contrib/python/requests-oauthlib/.dist-info/METADATA
index 975ce567fc..1029e2ff65 100644
--- a/contrib/python/requests-oauthlib/.dist-info/METADATA
+++ b/contrib/python/requests-oauthlib/.dist-info/METADATA
@@ -1,12 +1,11 @@
Metadata-Version: 2.1
Name: requests-oauthlib
-Version: 1.3.1
+Version: 1.4.0
Summary: OAuthlib authentication support for Requests.
Home-page: https://github.com/requests/requests-oauthlib
Author: Kenneth Reitz
Author-email: me@kennethreitz.com
License: ISC
-Platform: UNKNOWN
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: Natural Language :: English
@@ -21,20 +20,23 @@ 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.11
+Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Programming Language :: Python :: Implementation :: PyPy
Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*
Description-Content-Type: text/x-rst
License-File: LICENSE
-Requires-Dist: oauthlib (>=3.0.0)
-Requires-Dist: requests (>=2.0.0)
+Requires-Dist: oauthlib >=3.0.0
+Requires-Dist: requests >=2.0.0
Provides-Extra: rsa
-Requires-Dist: oauthlib[signedtoken] (>=3.0.0) ; extra == 'rsa'
+Requires-Dist: oauthlib[signedtoken] >=3.0.0 ; extra == 'rsa'
Requests-OAuthlib |build-status| |coverage-status| |docs|
=========================================================
-This project provides first-class OAuth library support for `Requests <http://python-requests.org>`_.
+This project provides first-class OAuth library support for `Requests <https://requests.readthedocs.io>`_.
The OAuth 1 workflow
--------------------
@@ -79,7 +81,7 @@ To install requests and requests_oauthlib you can use pip:
.. code-block:: bash
- $ pip install requests requests_oauthlib
+ pip install requests requests-oauthlib
.. |build-status| image:: https://github.com/requests/requests-oauthlib/actions/workflows/run-tests.yml/badge.svg
:target: https://github.com/requests/requests-oauthlib/actions
@@ -94,10 +96,32 @@ To install requests and requests_oauthlib you can use pip:
History
-------
+v1.4.0 (27 Feb 2024)
+++++++++++++++++++++++++
+
+Full set of changes are in [github](https://github.com/requests/requests-oauthlib/milestone/4?closed=1).
+
+Additions & changes:
+
+- ``OAuth2Session`` now correctly uses the ``self.verify`` value if ``verify``
+ is not overridden in ``fetch_token`` and ``refresh_token``. Fixes `#404
+ <https://github.com/requests/requests-oauthlib/issues/404>`_.
+- ``OAuth2Session`` constructor now uses its ``client.scope`` when a ``client``
+ is provided and ``scope`` is not overridden. Fixes `#408
+ <https://github.com/requests/requests-oauthlib/issues/408>`_
+- Add ``refresh_token_request`` and ``access_token_request`` compliance hooks
+- Add PKCE support and Auth0 example
+- Add support for Python 3.8-3.12
+- Remove support of Python 2.x, <3.7
+- Migrated to Github Action
+- Updated dependencies
+- Cleanup some docs and examples
+
v1.3.1 (21 January 2022)
++++++++++++++++++++++++
- Add initial support for OAuth Mutual TLS (draft-ietf-oauth-mtls)
+- Removed outdated LinkedIn Compliance Fixes
- Add eBay compliance fix
- Add Spotify OAuth 2 Tutorial
- Add support for python 3.8, 3.9
@@ -241,5 +265,3 @@ v0.4.0 (29 September 2013)
- OAuth1 now updates r.headers instead of replacing it with non case insensitive dict
- Remove last use of Response.content (in OAuth1Session). #44.
- State param can now be supplied in OAuth2Session.authorize_url
-
-
diff --git a/contrib/python/requests-oauthlib/AUTHORS.rst b/contrib/python/requests-oauthlib/AUTHORS.rst
index c8fba5e997..0b2fb31615 100644
--- a/contrib/python/requests-oauthlib/AUTHORS.rst
+++ b/contrib/python/requests-oauthlib/AUTHORS.rst
@@ -1,13 +1,12 @@
-Requests-oauthlib is written and maintained by Kenneth Reitz and various
-contributors:
+Requests-oauthlib has been written by Kenneth Reitz, various contributors:
-Development Lead
+Initial Write-up
----------------
- Kenneth Reitz <me@kennethreitz.com>
-Patches and Suggestions
------------------------
+Maintainers & Contributors
+--------------------------
- Cory Benfield <cory@lukasa.co.uk>
- Ib Lundgren <ib.lundgren@gmail.com>
@@ -15,6 +14,7 @@ Patches and Suggestions
- Imad Mouhtassem <mouhtasi@gmail.com>
- Johan Euphrosine <proppy@google.com>
- Johannes Spielmann <js@shezi.de>
+- Jonathan Huot <jonathan.huot@gmail.com>
- Martin Trigaux <me@mart-e.be>
- Matt McClure <matt.mcclure@mapmyfitness.com>
- Mikhail Sobolev <mss@mawhrin.net>
@@ -22,4 +22,6 @@ Patches and Suggestions
- Vinay Raikar <rockraikar@gmail.com>
- kracekumar <me@kracekumar.com>
- David Baumgold <david@davidbaumgold.com>
+- Sylvain Marie <sylvain.marie@se.com>
- Craig Anderson <craiga@craiga.id.au>
+- Hugo van Kemenade <https://github.com/hugovk>
diff --git a/contrib/python/requests-oauthlib/README.rst b/contrib/python/requests-oauthlib/README.rst
index 9fd1bb9767..c1e99dd0e9 100644
--- a/contrib/python/requests-oauthlib/README.rst
+++ b/contrib/python/requests-oauthlib/README.rst
@@ -1,7 +1,7 @@
Requests-OAuthlib |build-status| |coverage-status| |docs|
=========================================================
-This project provides first-class OAuth library support for `Requests <http://python-requests.org>`_.
+This project provides first-class OAuth library support for `Requests <https://requests.readthedocs.io>`_.
The OAuth 1 workflow
--------------------
@@ -46,7 +46,7 @@ To install requests and requests_oauthlib you can use pip:
.. code-block:: bash
- $ pip install requests requests_oauthlib
+ pip install requests requests-oauthlib
.. |build-status| image:: https://github.com/requests/requests-oauthlib/actions/workflows/run-tests.yml/badge.svg
:target: https://github.com/requests/requests-oauthlib/actions
diff --git a/contrib/python/requests-oauthlib/requests_oauthlib/__init__.py b/contrib/python/requests-oauthlib/requests_oauthlib/__init__.py
index 0d3e49f991..a71064cca0 100644
--- a/contrib/python/requests-oauthlib/requests_oauthlib/__init__.py
+++ b/contrib/python/requests-oauthlib/requests_oauthlib/__init__.py
@@ -1,3 +1,4 @@
+# ruff: noqa: F401
import logging
from .oauth1_auth import OAuth1
@@ -5,7 +6,7 @@ from .oauth1_session import OAuth1Session
from .oauth2_auth import OAuth2
from .oauth2_session import OAuth2Session, TokenUpdated
-__version__ = "1.3.1"
+__version__ = "1.4.0"
import requests
diff --git a/contrib/python/requests-oauthlib/requests_oauthlib/compliance_fixes/__init__.py b/contrib/python/requests-oauthlib/requests_oauthlib/compliance_fixes/__init__.py
index 0e8e3ac84f..8815ea0b8e 100644
--- a/contrib/python/requests-oauthlib/requests_oauthlib/compliance_fixes/__init__.py
+++ b/contrib/python/requests-oauthlib/requests_oauthlib/compliance_fixes/__init__.py
@@ -1,5 +1,4 @@
-from __future__ import absolute_import
-
+# ruff: noqa: F401
from .facebook import facebook_compliance_fix
from .fitbit import fitbit_compliance_fix
from .slack import slack_compliance_fix
diff --git a/contrib/python/requests-oauthlib/requests_oauthlib/compliance_fixes/douban.py b/contrib/python/requests-oauthlib/requests_oauthlib/compliance_fixes/douban.py
index ecc57b0818..c8b99c721e 100644
--- a/contrib/python/requests-oauthlib/requests_oauthlib/compliance_fixes/douban.py
+++ b/contrib/python/requests-oauthlib/requests_oauthlib/compliance_fixes/douban.py
@@ -1,14 +1,12 @@
import json
-from oauthlib.common import to_unicode
-
def douban_compliance_fix(session):
def fix_token_type(r):
token = json.loads(r.text)
token.setdefault("token_type", "Bearer")
fixed_token = json.dumps(token)
- r._content = to_unicode(fixed_token).encode("utf-8")
+ r._content = fixed_token.encode()
return r
session._client_default_token_placement = "query"
diff --git a/contrib/python/requests-oauthlib/requests_oauthlib/compliance_fixes/ebay.py b/contrib/python/requests-oauthlib/requests_oauthlib/compliance_fixes/ebay.py
index 4aa423b3fe..ef33f39101 100644
--- a/contrib/python/requests-oauthlib/requests_oauthlib/compliance_fixes/ebay.py
+++ b/contrib/python/requests-oauthlib/requests_oauthlib/compliance_fixes/ebay.py
@@ -1,5 +1,4 @@
import json
-from oauthlib.common import to_unicode
def ebay_compliance_fix(session):
@@ -13,7 +12,7 @@ def ebay_compliance_fix(session):
if token.get("token_type") in ["Application Access Token", "User Access Token"]:
token["token_type"] = "Bearer"
fixed_token = json.dumps(token)
- response._content = to_unicode(fixed_token).encode("utf-8")
+ response._content = fixed_token.encode()
return response
diff --git a/contrib/python/requests-oauthlib/requests_oauthlib/compliance_fixes/facebook.py b/contrib/python/requests-oauthlib/requests_oauthlib/compliance_fixes/facebook.py
index 90e7921272..f44558a839 100644
--- a/contrib/python/requests-oauthlib/requests_oauthlib/compliance_fixes/facebook.py
+++ b/contrib/python/requests-oauthlib/requests_oauthlib/compliance_fixes/facebook.py
@@ -1,11 +1,5 @@
from json import dumps
-
-try:
- from urlparse import parse_qsl
-except ImportError:
- from urllib.parse import parse_qsl
-
-from oauthlib.common import to_unicode
+from urllib.parse import parse_qsl
def facebook_compliance_fix(session):
@@ -26,7 +20,7 @@ def facebook_compliance_fix(session):
if expires is not None:
token["expires_in"] = expires
token["token_type"] = "Bearer"
- r._content = to_unicode(dumps(token)).encode("UTF-8")
+ r._content = dumps(token).encode()
return r
session.register_compliance_hook("access_token_response", _compliance_fix)
diff --git a/contrib/python/requests-oauthlib/requests_oauthlib/compliance_fixes/fitbit.py b/contrib/python/requests-oauthlib/requests_oauthlib/compliance_fixes/fitbit.py
index 7e62702401..aacc68bfbb 100644
--- a/contrib/python/requests-oauthlib/requests_oauthlib/compliance_fixes/fitbit.py
+++ b/contrib/python/requests-oauthlib/requests_oauthlib/compliance_fixes/fitbit.py
@@ -8,8 +8,6 @@ MissingTokenError.
from json import loads, dumps
-from oauthlib.common import to_unicode
-
def fitbit_compliance_fix(session):
def _missing_error(r):
@@ -17,7 +15,7 @@ def fitbit_compliance_fix(session):
if "errors" in token:
# Set the error to the first one we have
token["error"] = token["errors"][0]["errorType"]
- r._content = to_unicode(dumps(token)).encode("UTF-8")
+ r._content = dumps(token).encode()
return r
session.register_compliance_hook("access_token_response", _missing_error)
diff --git a/contrib/python/requests-oauthlib/requests_oauthlib/compliance_fixes/instagram.py b/contrib/python/requests-oauthlib/requests_oauthlib/compliance_fixes/instagram.py
index 4e07fe08b5..7d5a2ad447 100644
--- a/contrib/python/requests-oauthlib/requests_oauthlib/compliance_fixes/instagram.py
+++ b/contrib/python/requests-oauthlib/requests_oauthlib/compliance_fixes/instagram.py
@@ -1,7 +1,4 @@
-try:
- from urlparse import urlparse, parse_qs
-except ImportError:
- from urllib.parse import urlparse, parse_qs
+from urllib.parse import urlparse, parse_qs
from oauthlib.common import add_params_to_uri
diff --git a/contrib/python/requests-oauthlib/requests_oauthlib/compliance_fixes/mailchimp.py b/contrib/python/requests-oauthlib/requests_oauthlib/compliance_fixes/mailchimp.py
index c69ce9fdae..0d602659c6 100644
--- a/contrib/python/requests-oauthlib/requests_oauthlib/compliance_fixes/mailchimp.py
+++ b/contrib/python/requests-oauthlib/requests_oauthlib/compliance_fixes/mailchimp.py
@@ -1,21 +1,19 @@
import json
-from oauthlib.common import to_unicode
-
def mailchimp_compliance_fix(session):
def _null_scope(r):
token = json.loads(r.text)
if "scope" in token and token["scope"] is None:
token.pop("scope")
- r._content = to_unicode(json.dumps(token)).encode("utf-8")
+ r._content = json.dumps(token).encode()
return r
def _non_zero_expiration(r):
token = json.loads(r.text)
if "expires_in" in token and token["expires_in"] == 0:
token["expires_in"] = 3600
- r._content = to_unicode(json.dumps(token)).encode("utf-8")
+ r._content = json.dumps(token).encode()
return r
session.register_compliance_hook("access_token_response", _null_scope)
diff --git a/contrib/python/requests-oauthlib/requests_oauthlib/compliance_fixes/plentymarkets.py b/contrib/python/requests-oauthlib/requests_oauthlib/compliance_fixes/plentymarkets.py
index 9f605f058c..859f0566a5 100644
--- a/contrib/python/requests-oauthlib/requests_oauthlib/compliance_fixes/plentymarkets.py
+++ b/contrib/python/requests-oauthlib/requests_oauthlib/compliance_fixes/plentymarkets.py
@@ -1,8 +1,6 @@
from json import dumps, loads
import re
-from oauthlib.common import to_unicode
-
def plentymarkets_compliance_fix(session):
def _to_snake_case(n):
@@ -22,7 +20,7 @@ def plentymarkets_compliance_fix(session):
for k, v in token.items():
fixed_token[_to_snake_case(k)] = v
- r._content = to_unicode(dumps(fixed_token)).encode("UTF-8")
+ r._content = dumps(fixed_token).encode()
return r
session.register_compliance_hook("access_token_response", _compliance_fix)
diff --git a/contrib/python/requests-oauthlib/requests_oauthlib/compliance_fixes/slack.py b/contrib/python/requests-oauthlib/requests_oauthlib/compliance_fixes/slack.py
index 3f574b03ad..9095a470cd 100644
--- a/contrib/python/requests-oauthlib/requests_oauthlib/compliance_fixes/slack.py
+++ b/contrib/python/requests-oauthlib/requests_oauthlib/compliance_fixes/slack.py
@@ -1,7 +1,4 @@
-try:
- from urlparse import urlparse, parse_qs
-except ImportError:
- from urllib.parse import urlparse, parse_qs
+from urllib.parse import urlparse, parse_qs
from oauthlib.common import add_params_to_uri
diff --git a/contrib/python/requests-oauthlib/requests_oauthlib/compliance_fixes/weibo.py b/contrib/python/requests-oauthlib/requests_oauthlib/compliance_fixes/weibo.py
index 6733abeb15..f1623fd6d7 100644
--- a/contrib/python/requests-oauthlib/requests_oauthlib/compliance_fixes/weibo.py
+++ b/contrib/python/requests-oauthlib/requests_oauthlib/compliance_fixes/weibo.py
@@ -1,13 +1,11 @@
from json import loads, dumps
-from oauthlib.common import to_unicode
-
def weibo_compliance_fix(session):
def _missing_token_type(r):
token = loads(r.text)
token["token_type"] = "Bearer"
- r._content = to_unicode(dumps(token)).encode("UTF-8")
+ r._content = dumps(token).encode()
return r
session._client.default_token_placement = "query"
diff --git a/contrib/python/requests-oauthlib/requests_oauthlib/oauth1_auth.py b/contrib/python/requests-oauthlib/requests_oauthlib/oauth1_auth.py
index cfbbd5902c..f8c0bd6e74 100644
--- a/contrib/python/requests-oauthlib/requests_oauthlib/oauth1_auth.py
+++ b/contrib/python/requests-oauthlib/requests_oauthlib/oauth1_auth.py
@@ -1,20 +1,15 @@
# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
import logging
from oauthlib.common import extract_params
from oauthlib.oauth1 import Client, SIGNATURE_HMAC, SIGNATURE_TYPE_AUTH_HEADER
from oauthlib.oauth1 import SIGNATURE_TYPE_BODY
-from requests.compat import is_py3
from requests.utils import to_native_string
from requests.auth import AuthBase
CONTENT_TYPE_FORM_URLENCODED = "application/x-www-form-urlencoded"
CONTENT_TYPE_MULTI_PART = "multipart/form-data"
-if is_py3:
- unicode = str
log = logging.getLogger(__name__)
@@ -83,7 +78,7 @@ class OAuth1(AuthBase):
or self.client.signature_type == SIGNATURE_TYPE_BODY
):
content_type = CONTENT_TYPE_FORM_URLENCODED
- if not isinstance(content_type, unicode):
+ if not isinstance(content_type, str):
content_type = content_type.decode("utf-8")
is_form_encoded = CONTENT_TYPE_FORM_URLENCODED in content_type
@@ -96,17 +91,17 @@ class OAuth1(AuthBase):
if is_form_encoded:
r.headers["Content-Type"] = CONTENT_TYPE_FORM_URLENCODED
r.url, headers, r.body = self.client.sign(
- unicode(r.url), unicode(r.method), r.body or "", r.headers
+ str(r.url), str(r.method), r.body or "", r.headers
)
elif self.force_include_body:
# To allow custom clients to work on non form encoded bodies.
r.url, headers, r.body = self.client.sign(
- unicode(r.url), unicode(r.method), r.body or "", r.headers
+ str(r.url), str(r.method), r.body or "", r.headers
)
else:
# Omit body data in the signing of non form-encoded requests
r.url, headers, _ = self.client.sign(
- unicode(r.url), unicode(r.method), None, r.headers
+ str(r.url), str(r.method), None, r.headers
)
r.prepare_headers(headers)
diff --git a/contrib/python/requests-oauthlib/requests_oauthlib/oauth1_session.py b/contrib/python/requests-oauthlib/requests_oauthlib/oauth1_session.py
index 88f2853ca0..7625c8084e 100644
--- a/contrib/python/requests-oauthlib/requests_oauthlib/oauth1_session.py
+++ b/contrib/python/requests-oauthlib/requests_oauthlib/oauth1_session.py
@@ -1,9 +1,4 @@
-from __future__ import unicode_literals
-
-try:
- from urlparse import urlparse
-except ImportError:
- from urllib.parse import urlparse
+from urllib.parse import urlparse
import logging
@@ -85,7 +80,7 @@ class OAuth1Session(requests.Session):
'https://api.twitter.com/oauth/authorize?oauth_token=sdf0o9823sjdfsdf&oauth_callback=https%3A%2F%2F127.0.0.1%2Fcallback'
>>>
>>> # Third step. Fetch the access token
- >>> redirect_response = raw_input('Paste the full redirect URL here.')
+ >>> redirect_response = input('Paste the full redirect URL here.')
>>> oauth_session.parse_authorization_response(redirect_response)
{
'oauth_token: 'kjerht2309u',
@@ -258,7 +253,7 @@ class OAuth1Session(requests.Session):
return add_params_to_uri(url, kwargs.items())
def fetch_request_token(self, url, realm=None, **request_kwargs):
- r"""Fetch a request token.
+ """Fetch a request token.
This is the first step in the OAuth 1 workflow. A request token is
obtained by making a signed post request to url. The token is then
@@ -267,7 +262,7 @@ class OAuth1Session(requests.Session):
:param url: The request token endpoint URL.
:param realm: A list of realms to request access to.
- :param \*\*request_kwargs: Optional arguments passed to ''post''
+ :param request_kwargs: Optional arguments passed to ''post''
function in ''requests.Session''
:returns: The response in dict format.
diff --git a/contrib/python/requests-oauthlib/requests_oauthlib/oauth2_auth.py b/contrib/python/requests-oauthlib/requests_oauthlib/oauth2_auth.py
index b880f72f58..f19f52ac90 100644
--- a/contrib/python/requests-oauthlib/requests_oauthlib/oauth2_auth.py
+++ b/contrib/python/requests-oauthlib/requests_oauthlib/oauth2_auth.py
@@ -1,4 +1,3 @@
-from __future__ import unicode_literals
from oauthlib.oauth2 import WebApplicationClient, InsecureTransportError
from oauthlib.oauth2 import is_secure_transport
from requests.auth import AuthBase
diff --git a/contrib/python/requests-oauthlib/requests_oauthlib/oauth2_session.py b/contrib/python/requests-oauthlib/requests_oauthlib/oauth2_session.py
index db4468089b..93cc4d7bbd 100644
--- a/contrib/python/requests-oauthlib/requests_oauthlib/oauth2_session.py
+++ b/contrib/python/requests-oauthlib/requests_oauthlib/oauth2_session.py
@@ -1,5 +1,3 @@
-from __future__ import unicode_literals
-
import logging
from oauthlib.common import generate_token, urldecode
@@ -46,6 +44,7 @@ class OAuth2Session(requests.Session):
token=None,
state=None,
token_updater=None,
+ pkce=None,
**kwargs
):
"""Construct a new OAuth 2 client session.
@@ -72,18 +71,23 @@ class OAuth2Session(requests.Session):
set a TokenUpdated warning will be raised when a token
has been refreshed. This warning will carry the token
in its token argument.
+ :param pkce: Set "S256" or "plain" to enable PKCE. Default is disabled.
:param kwargs: Arguments to pass to the Session constructor.
"""
super(OAuth2Session, self).__init__(**kwargs)
self._client = client or WebApplicationClient(client_id, token=token)
self.token = token or {}
- self.scope = scope
+ self._scope = scope
self.redirect_uri = redirect_uri
self.state = state or generate_token
self._state = state
self.auto_refresh_url = auto_refresh_url
self.auto_refresh_kwargs = auto_refresh_kwargs or {}
self.token_updater = token_updater
+ self._pkce = pkce
+
+ if self._pkce not in ["S256", "plain", None]:
+ raise AttributeError("Wrong value for {}(.., pkce={})".format(self.__class__, self._pkce))
# Ensure that requests doesn't do any automatic auth. See #278.
# The default behavior can be re-enabled by setting auth to None.
@@ -95,8 +99,24 @@ class OAuth2Session(requests.Session):
"access_token_response": set(),
"refresh_token_response": set(),
"protected_request": set(),
+ "refresh_token_request": set(),
+ "access_token_request": set(),
}
+ @property
+ def scope(self):
+ """By default the scope from the client is used, except if overridden"""
+ if self._scope is not None:
+ return self._scope
+ elif self._client is not None:
+ return self._client.scope
+ else:
+ return None
+
+ @scope.setter
+ def scope(self, scope):
+ self._scope = scope
+
def new_state(self):
"""Generates a state string to be used in authorizations."""
try:
@@ -161,6 +181,13 @@ class OAuth2Session(requests.Session):
:return: authorization_url, state
"""
state = state or self.new_state()
+ if self._pkce:
+ self._code_verifier = self._client.create_code_verifier(43)
+ kwargs["code_challenge_method"] = self._pkce
+ kwargs["code_challenge"] = self._client.create_code_challenge(
+ code_verifier=self._code_verifier,
+ code_challenge_method=self._pkce
+ )
return (
self._client.prepare_request_uri(
url,
@@ -185,7 +212,7 @@ class OAuth2Session(requests.Session):
force_querystring=False,
timeout=None,
headers=None,
- verify=True,
+ verify=None,
proxies=None,
include_client_id=None,
client_secret=None,
@@ -252,6 +279,13 @@ class OAuth2Session(requests.Session):
"Please supply either code or " "authorization_response parameters."
)
+ if self._pkce:
+ if self._code_verifier is None:
+ raise ValueError(
+ "Code verifier is not found, authorization URL must be generated before"
+ )
+ kwargs["code_verifier"] = self._code_verifier
+
# Earlier versions of this library build an HTTPBasicAuth header out of
# `username` and `password`. The RFC states, however these attributes
# must be in the request body and not the header.
@@ -325,7 +359,7 @@ class OAuth2Session(requests.Session):
headers = headers or {
"Accept": "application/json",
- "Content-Type": "application/x-www-form-urlencoded;charset=UTF-8",
+ "Content-Type": "application/x-www-form-urlencoded",
}
self.token = {}
request_kwargs = {}
@@ -338,6 +372,12 @@ class OAuth2Session(requests.Session):
else:
raise ValueError("The method kwarg must be POST or GET.")
+ for hook in self.compliance_hook["access_token_request"]:
+ log.debug("Invoking access_token_request hook %s.", hook)
+ token_url, headers, request_kwargs = hook(
+ token_url, headers, request_kwargs
+ )
+
r = self.request(
method=method,
url=token_url,
@@ -388,7 +428,7 @@ class OAuth2Session(requests.Session):
auth=None,
timeout=None,
headers=None,
- verify=True,
+ verify=None,
proxies=None,
**kwargs
):
@@ -426,9 +466,13 @@ class OAuth2Session(requests.Session):
if headers is None:
headers = {
"Accept": "application/json",
- "Content-Type": ("application/x-www-form-urlencoded;charset=UTF-8"),
+ "Content-Type": ("application/x-www-form-urlencoded"),
}
+ for hook in self.compliance_hook["refresh_token_request"]:
+ log.debug("Invoking refresh_token_request hook %s.", hook)
+ token_url, headers, body = hook(token_url, headers, body)
+
r = self.post(
token_url,
data=dict(urldecode(body)),
@@ -450,7 +494,7 @@ class OAuth2Session(requests.Session):
r = hook(r)
self.token = self._client.parse_request_body_response(r.text, scope=self.scope)
- if not "refresh_token" in self.token:
+ if "refresh_token" not in self.token:
log.debug("No new refresh token given. Re-using old.")
self.token["refresh_token"] = refresh_token
return self.token
@@ -464,6 +508,7 @@ class OAuth2Session(requests.Session):
withhold_token=False,
client_id=None,
client_secret=None,
+ files=None,
**kwargs
):
"""Intercept all requests and add the OAuth 2 token if present."""
@@ -519,7 +564,7 @@ class OAuth2Session(requests.Session):
log.debug("Supplying headers %s and data %s", headers, data)
log.debug("Passing through key word arguments %s.", kwargs)
return super(OAuth2Session, self).request(
- method, url, headers=headers, data=data, **kwargs
+ method, url, headers=headers, data=data, files=files, **kwargs
)
def register_compliance_hook(self, hook_type, hook):
@@ -529,6 +574,8 @@ class OAuth2Session(requests.Session):
access_token_response invoked before token parsing.
refresh_token_response invoked before refresh token parsing.
protected_request invoked before making a request.
+ access_token_request invoked before making a token fetch request.
+ refresh_token_request invoked before making a refresh request.
If you find a new hook is needed please send a GitHub PR request
or open an issue.
diff --git a/contrib/python/requests-oauthlib/tests/examples/base.py b/contrib/python/requests-oauthlib/tests/examples/base.py
new file mode 100644
index 0000000000..2efa5dd746
--- /dev/null
+++ b/contrib/python/requests-oauthlib/tests/examples/base.py
@@ -0,0 +1,106 @@
+import os.path
+import os
+import subprocess
+import shlex
+import shutil
+from selenium import webdriver
+from selenium.webdriver.common.by import By
+from selenium.webdriver.common.keys import Keys
+from selenium.webdriver.support import expected_conditions as EC
+from selenium.webdriver.support.wait import WebDriverWait
+
+
+cwd = os.path.dirname(os.path.realpath(__file__))
+
+
+class Sample():
+ def setUp(self):
+ super().setUp()
+ self.proc = None
+ self.outputs = []
+
+ def tearDown(self):
+ super().tearDown()
+ if self.proc is not None:
+ self.proc.stdin.close()
+ self.proc.stdout.close()
+ self.proc.kill()
+
+ def replaceVariables(self, filein ,fileout, vars):
+ with open(filein, "rt") as fin:
+ with open(fileout, "wt") as fout:
+ for line in fin:
+ for k, v in vars.items():
+ line = line.replace(k, v)
+ fout.write(line)
+
+ def run_sample(self, filepath, variables):
+ inpath = os.path.join(cwd, "..", "..", "docs", "examples", filepath)
+ outpath = os.path.join(cwd, "tmp_{}".format(filepath))
+ self.replaceVariables(inpath, outpath, variables)
+
+ self.proc = subprocess.Popen(
+ [shutil.which("python"),
+ outpath],
+ text=True, bufsize=1,
+ stdin=subprocess.PIPE,
+ stdout=subprocess.PIPE
+ )
+
+ def write(self, string):
+ self.proc.stdin.write(string)
+ self.proc.stdin.flush()
+
+ def wait_for_pattern(self, pattern):
+ try:
+ while True:
+ line = self.proc.stdout.readline()
+ self.outputs.append(line)
+ if pattern in line:
+ return line
+ except subprocess.TimeoutExpired:
+ self.assertTrue(False, "timeout when looking for output")
+
+ def wait_for_end(self):
+ try:
+ outs, err = self.proc.communicate(timeout=10)
+ self.outputs += filter(lambda x: x != '', outs.split('\n'))
+ except subprocess.TimeoutExpired:
+ self.assertTrue(False, "timeout when looking for output")
+ return self.outputs[-1]
+
+
+
+class Browser():
+ def setUp(self):
+ super().setUp()
+ options = webdriver.ChromeOptions()
+ options.add_argument("--headless=new")
+ self.driver = webdriver.Chrome(options=options)
+ self.user_username = os.environ.get("AUTH0_USERNAME")
+ self.user_password = os.environ.get("AUTH0_PASSWORD")
+
+ if not self.user_username or not self.user_password:
+ self.skipTest("auth0 is not configured properly")
+
+ def tearDown(self):
+ super().tearDown()
+ self.driver.quit()
+
+ def authorize_auth0(self, authorize_url, expected_redirect_uri):
+ self.driver.get(authorize_url)
+ username = self.driver.find_element(By.ID, "username")
+ password = self.driver.find_element(By.ID, "password")
+
+ wait = WebDriverWait(self.driver, timeout=2)
+ wait.until(lambda d : username.is_displayed())
+ wait.until(lambda d : password.is_displayed())
+
+ username.clear()
+ username.send_keys(self.user_username)
+ password.send_keys(self.user_password)
+ username.send_keys(Keys.RETURN)
+
+ wait.until(EC.url_contains(expected_redirect_uri))
+ return self.driver.current_url
+
diff --git a/contrib/python/requests-oauthlib/tests/examples/test_native_spa_pkce_auth0.py b/contrib/python/requests-oauthlib/tests/examples/test_native_spa_pkce_auth0.py
new file mode 100644
index 0000000000..6ff41e251c
--- /dev/null
+++ b/contrib/python/requests-oauthlib/tests/examples/test_native_spa_pkce_auth0.py
@@ -0,0 +1,39 @@
+import os
+import unittest
+
+from . import base
+
+class TestNativeAuth0Test(base.Sample, base.Browser, unittest.TestCase):
+ def setUp(self):
+ super().setUp()
+ self.client_id = os.environ.get("AUTH0_PKCE_CLIENT_ID")
+ self.idp_domain = os.environ.get("AUTH0_DOMAIN")
+
+ if not self.client_id or not self.idp_domain:
+ self.skipTest("native auth0 is not configured properly")
+
+ def test_login(self):
+ # redirect_uri is http://
+ os.environ['OAUTHLIB_INSECURE_TRANSPORT'] = "1"
+
+ self.run_sample(
+ "native_spa_pkce_auth0.py", {
+ "OAUTH_CLIENT_ID": self.client_id,
+ "OAUTH_IDP_DOMAIN": self.idp_domain,
+ }
+ )
+ authorize_url = self.wait_for_pattern("https://")
+ redirect_uri = self.authorize_auth0(authorize_url, "http://")
+ self.write(redirect_uri)
+ last_line = self.wait_for_end()
+
+ import ast
+ response = ast.literal_eval(last_line)
+ self.assertIn("access_token", response)
+ self.assertIn("id_token", response)
+ self.assertIn("scope", response)
+ self.assertIn("openid", response["scope"])
+ self.assertIn("expires_in", response)
+ self.assertIn("expires_at", response)
+ self.assertIn("token_type", response)
+ self.assertEqual("Bearer", response["token_type"])
diff --git a/contrib/python/requests-oauthlib/tests/test_compliance_fixes.py b/contrib/python/requests-oauthlib/tests/test_compliance_fixes.py
index 5c90d52660..c5166bdb2f 100644
--- a/contrib/python/requests-oauthlib/tests/test_compliance_fixes.py
+++ b/contrib/python/requests-oauthlib/tests/test_compliance_fixes.py
@@ -1,14 +1,10 @@
-from __future__ import unicode_literals
from unittest import TestCase
import requests
import requests_mock
import time
-try:
- from urlparse import urlparse, parse_qs
-except ImportError:
- from urllib.parse import urlparse, parse_qs
+from urllib.parse import urlparse, parse_qs
from oauthlib.oauth2.rfc6749.errors import InvalidGrantError
from requests_oauthlib import OAuth2Session
@@ -332,3 +328,60 @@ class EbayComplianceFixTest(TestCase):
authorization_response="https://i.b/?code=hello",
)
assert token["token_type"] == "Bearer"
+
+
+def access_and_refresh_token_request_compliance_fix_test(session, client_secret):
+ def _non_compliant_header(url, headers, body):
+ headers["X-Client-Secret"] = client_secret
+ return url, headers, body
+
+ session.register_compliance_hook("access_token_request", _non_compliant_header)
+ session.register_compliance_hook("refresh_token_request", _non_compliant_header)
+ return session
+
+
+class RefreshTokenRequestComplianceFixTest(TestCase):
+ value_to_test_for = "value_to_test_for"
+
+ def setUp(self):
+ mocker = requests_mock.Mocker()
+ mocker.post(
+ "https://example.com/token",
+ request_headers={"X-Client-Secret": self.value_to_test_for},
+ json={
+ "access_token": "this is the access token",
+ "expires_in": 7200,
+ "token_type": "Bearer",
+ },
+ headers={"Content-Type": "application/json"},
+ )
+ mocker.post(
+ "https://example.com/refresh",
+ request_headers={"X-Client-Secret": self.value_to_test_for},
+ json={
+ "access_token": "this is the access token",
+ "expires_in": 7200,
+ "token_type": "Bearer",
+ },
+ headers={"Content-Type": "application/json"},
+ )
+ mocker.start()
+ self.addCleanup(mocker.stop)
+
+ session = OAuth2Session()
+ self.fixed_session = access_and_refresh_token_request_compliance_fix_test(
+ session, self.value_to_test_for
+ )
+
+ def test_access_token(self):
+ token = self.fixed_session.fetch_token(
+ "https://example.com/token",
+ authorization_response="https://i.b/?code=hello",
+ )
+ assert token["token_type"] == "Bearer"
+
+ def test_refresh_token(self):
+ token = self.fixed_session.refresh_token(
+ "https://example.com/refresh",
+ )
+ assert token["token_type"] == "Bearer"
diff --git a/contrib/python/requests-oauthlib/tests/test_core.py b/contrib/python/requests-oauthlib/tests/test_core.py
index 6892e9f1ce..09cd0f0212 100644
--- a/contrib/python/requests-oauthlib/tests/test_core.py
+++ b/contrib/python/requests-oauthlib/tests/test_core.py
@@ -1,5 +1,4 @@
# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
import requests
import requests_oauthlib
import oauthlib
@@ -7,10 +6,7 @@ import os.path
from io import StringIO
import unittest
-try:
- import mock
-except ImportError:
- from unittest import mock
+from unittest import mock
@mock.patch("oauthlib.oauth1.rfc5849.generate_timestamp")
diff --git a/contrib/python/requests-oauthlib/tests/test_oauth1_session.py b/contrib/python/requests-oauthlib/tests/test_oauth1_session.py
index 1dd2b2f158..b3c8c70483 100644
--- a/contrib/python/requests-oauthlib/tests/test_oauth1_session.py
+++ b/contrib/python/requests-oauthlib/tests/test_oauth1_session.py
@@ -1,19 +1,13 @@
-from __future__ import unicode_literals, print_function
import unittest
-import sys
import requests
from io import StringIO
+from unittest import mock
from oauthlib.oauth1 import SIGNATURE_TYPE_QUERY, SIGNATURE_TYPE_BODY
from oauthlib.oauth1 import SIGNATURE_RSA, SIGNATURE_PLAINTEXT
from requests_oauthlib import OAuth1Session
try:
- import mock
-except ImportError:
- from unittest import mock
-
-try:
import cryptography
except ImportError:
cryptography = None
@@ -23,11 +17,6 @@ try:
except ImportError:
jwt = None
-if sys.version[0] == "3":
- unicode_type = str
-else:
- unicode_type = unicode
-
TEST_RSA_KEY = (
"-----BEGIN RSA PRIVATE KEY-----\n"
@@ -165,8 +154,8 @@ class OAuth1SessionTest(unittest.TestCase):
self.assertEqual(resp["oauth_token"], "foo")
self.assertEqual(resp["oauth_verifier"], "bar")
for k, v in resp.items():
- self.assertIsInstance(k, unicode_type)
- self.assertIsInstance(v, unicode_type)
+ self.assertIsInstance(k, str)
+ self.assertIsInstance(v, str)
def test_fetch_request_token(self):
auth = OAuth1Session("foo")
@@ -174,8 +163,8 @@ class OAuth1SessionTest(unittest.TestCase):
resp = auth.fetch_request_token("https://example.com/token")
self.assertEqual(resp["oauth_token"], "foo")
for k, v in resp.items():
- self.assertIsInstance(k, unicode_type)
- self.assertIsInstance(v, unicode_type)
+ self.assertIsInstance(k, str)
+ self.assertIsInstance(v, str)
def test_fetch_request_token_with_optional_arguments(self):
auth = OAuth1Session("foo")
@@ -185,8 +174,8 @@ class OAuth1SessionTest(unittest.TestCase):
)
self.assertEqual(resp["oauth_token"], "foo")
for k, v in resp.items():
- self.assertIsInstance(k, unicode_type)
- self.assertIsInstance(v, unicode_type)
+ self.assertIsInstance(k, str)
+ self.assertIsInstance(v, str)
def test_fetch_access_token(self):
auth = OAuth1Session("foo", verifier="bar")
@@ -194,8 +183,8 @@ class OAuth1SessionTest(unittest.TestCase):
resp = auth.fetch_access_token("https://example.com/token")
self.assertEqual(resp["oauth_token"], "foo")
for k, v in resp.items():
- self.assertIsInstance(k, unicode_type)
- self.assertIsInstance(v, unicode_type)
+ self.assertIsInstance(k, str)
+ self.assertIsInstance(v, str)
def test_fetch_access_token_with_optional_arguments(self):
auth = OAuth1Session("foo", verifier="bar")
@@ -205,8 +194,8 @@ class OAuth1SessionTest(unittest.TestCase):
)
self.assertEqual(resp["oauth_token"], "foo")
for k, v in resp.items():
- self.assertIsInstance(k, unicode_type)
- self.assertIsInstance(v, unicode_type)
+ self.assertIsInstance(k, str)
+ self.assertIsInstance(v, str)
def _test_fetch_access_token_raises_error(self, auth):
"""Assert that an error is being raised whenever there's no verifier
@@ -308,12 +297,6 @@ class OAuth1SessionTest(unittest.TestCase):
generate_nonce.return_value = "abc"
generate_timestamp.return_value = "123"
- signature = (
- "OAuth "
- 'oauth_nonce="abc", oauth_timestamp="123", oauth_version="1.0", '
- 'oauth_signature_method="RSA-SHA1", oauth_consumer_key="foo", '
- 'oauth_verifier="bar", oauth_signature="{sig}"'
- ).format(sig=TEST_RSA_OAUTH_SIGNATURE)
sess = OAuth1Session(
"key",
"secret",
diff --git a/contrib/python/requests-oauthlib/tests/test_oauth2_auth.py b/contrib/python/requests-oauthlib/tests/test_oauth2_auth.py
index accb561ef6..69ed6f6647 100644
--- a/contrib/python/requests-oauthlib/tests/test_oauth2_auth.py
+++ b/contrib/python/requests-oauthlib/tests/test_oauth2_auth.py
@@ -1,4 +1,3 @@
-from __future__ import unicode_literals
import unittest
from oauthlib.oauth2 import WebApplicationClient, MobileApplicationClient
diff --git a/contrib/python/requests-oauthlib/tests/test_oauth2_session.py b/contrib/python/requests-oauthlib/tests/test_oauth2_session.py
index cfc6236855..7e3e63c57a 100644
--- a/contrib/python/requests-oauthlib/tests/test_oauth2_session.py
+++ b/contrib/python/requests-oauthlib/tests/test_oauth2_session.py
@@ -1,4 +1,3 @@
-from __future__ import unicode_literals
import json
import time
import tempfile
@@ -8,10 +7,7 @@ from base64 import b64encode
from copy import deepcopy
from unittest import TestCase
-try:
- import mock
-except ImportError:
- from unittest import mock
+from unittest import mock
from oauthlib.common import urlencode
from oauthlib.oauth2 import TokenExpiredError, OAuth2Error
@@ -124,6 +120,27 @@ class OAuth2SessionTest(TestCase):
self.assertIn(self.client_id, auth_url)
self.assertIn("response_type=token", auth_url)
+ def test_pkce_authorization_url(self):
+ url = "https://example.com/authorize?foo=bar"
+
+ web = WebApplicationClient(self.client_id)
+ s = OAuth2Session(client=web, pkce="S256")
+ auth_url, state = s.authorization_url(url)
+ self.assertIn(state, auth_url)
+ self.assertIn(self.client_id, auth_url)
+ self.assertIn("response_type=code", auth_url)
+ self.assertIn("code_challenge=", auth_url)
+ self.assertIn("code_challenge_method=S256", auth_url)
+
+ mobile = MobileApplicationClient(self.client_id)
+ s = OAuth2Session(client=mobile, pkce="S256")
+ auth_url, state = s.authorization_url(url)
+ self.assertIn(state, auth_url)
+ self.assertIn(self.client_id, auth_url)
+ self.assertIn("response_type=token", auth_url)
+ self.assertIn("code_challenge=", auth_url)
+ self.assertIn("code_challenge_method=S256", auth_url)
+
@mock.patch("time.time", new=lambda: fake_time)
def test_refresh_token_request(self):
self.expired_token = dict(self.token)
@@ -424,6 +441,16 @@ class OAuth2SessionTest(TestCase):
authorization_response="https://i.b/no-state?code=abc",
)
+ @mock.patch("time.time", new=lambda: fake_time)
+ def test_pkce_web_app_fetch_token(self):
+ url = "https://example.com/token"
+
+ web = WebApplicationClient(self.client_id, code=CODE)
+ sess = OAuth2Session(client=web, token=self.token, pkce="S256")
+ sess.send = fake_token(self.token)
+ sess._code_verifier = "foobar"
+ self.assertEqual(sess.fetch_token(url), self.token)
+
def test_client_id_proxy(self):
sess = OAuth2Session("test-id")
self.assertEqual(sess.client_id, "test-id")
diff --git a/contrib/python/requests-oauthlib/ya.make b/contrib/python/requests-oauthlib/ya.make
index 2145e60cc4..d1a60dc100 100644
--- a/contrib/python/requests-oauthlib/ya.make
+++ b/contrib/python/requests-oauthlib/ya.make
@@ -2,7 +2,7 @@
PY3_LIBRARY()
-VERSION(1.3.1)
+VERSION(1.4.0)
LICENSE(ISC)