././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1643482341.6034906 requests-oauthlib-1.3.1/0000755000175100001710000000000000000000000014534 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1643482333.0 requests-oauthlib-1.3.1/AUTHORS.rst0000644000175100001710000000134400000000000016415 0ustar00runnerdockerRequests-oauthlib is written and maintained by Kenneth Reitz and various contributors: Development Lead ---------------- - Kenneth Reitz Patches and Suggestions ----------------------- - Cory Benfield - Ib Lundgren - Devin Sevilla - Imad Mouhtassem - Johan Euphrosine - Johannes Spielmann - Martin Trigaux - Matt McClure - Mikhail Sobolev - Paul Bonser - Vinay Raikar - kracekumar - David Baumgold - Craig Anderson ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1643482333.0 requests-oauthlib-1.3.1/HISTORY.rst0000644000175100001710000001410000000000000016423 0ustar00runnerdockerHistory ------- v1.3.1 (21 January 2022) ++++++++++++++++++++++++ - Add initial support for OAuth Mutual TLS (draft-ietf-oauth-mtls) - Add eBay compliance fix - Add Spotify OAuth 2 Tutorial - Add support for python 3.8, 3.9 - Fixed LinkedIn Compliance Fixes - Fixed ReadTheDocs Documentation and sphinx errors - Moved pipeline to GitHub Actions v1.3.0 (6 November 2019) ++++++++++++++++++++++++ - Instagram compliance fix - Added ``force_querystring`` argument to fetch_token() method on OAuth2Session v1.2.0 (14 January 2019) ++++++++++++++++++++++++ - This project now depends on OAuthlib 3.0.0 and above. It does **not** support versions of OAuthlib before 3.0.0. - Updated oauth2 tests to use 'sess' for an OAuth2Session instance instead of `auth` because OAuth2Session objects and methods acceept an `auth` paramether which is typically an instance of `requests.auth.HTTPBasicAuth` - `OAuth2Session.fetch_token` previously tried to guess how and where to provide "client" and "user" credentials incorrectly. This was incompatible with some OAuth servers and incompatible with breaking changes in oauthlib that seek to correctly provide the `client_id`. The older implementation also did not raise the correct exceptions when username and password are not present on Legacy clients. - Avoid automatic netrc authentication for OAuth2Session. v1.1.0 (9 January 2019) +++++++++++++++++++++++ - Adjusted version specifier for ``oauthlib`` dependency: this project is not yet compatible with ``oauthlib`` 3.0.0. - Dropped dependency on ``nose``. - Minor changes to clean up the code and make it more readable/maintainable. v1.0.0 (4 June 2018) ++++++++++++++++++++ - **Removed support for Python 2.6 and Python 3.3.** This project now supports Python 2.7, and Python 3.4 and above. - Added several examples to the documentation. - Added plentymarkets compliance fix. - Added a ``token`` property to OAuth1Session, to match the corresponding ``token`` property on OAuth2Session. v0.8.0 (14 February 2017) +++++++++++++++++++++++++ - Added Fitbit compliance fix. - Fixed an issue where newlines in the response body for the access token request would cause errors when trying to extract the token. - Fixed an issue introduced in v0.7.0 where users passing ``auth`` to several methods would encounter conflicts with the ``client_id`` and ``client_secret``-derived auth. The user-supplied ``auth`` argument is now used in preference to those options. v0.7.0 (22 September 2016) ++++++++++++++++++++++++++ - Allowed ``OAuth2Session.request`` to take the ``client_id`` and ``client_secret`` parameters for the purposes of automatic token refresh, which may need them. v0.6.2 (12 July 2016) +++++++++++++++++++++ - Use ``client_id`` and ``client_secret`` for the Authorization header if provided. - Allow explicit bypass of the Authorization header by setting ``auth=False``. - Pass through the ``proxies`` kwarg when refreshing tokens. - Miscellaneous cleanups. v0.6.1 (19 February 2016) +++++++++++++++++++++++++ - Fixed a bug when sending authorization in headers with no username and password present. - Make sure we clear the session token before obtaining a new one. - Some improvements to the Slack compliance fix. - Avoid timing problems around token refresh. - Allow passing arbitrary arguments to requests when calling ``fetch_request_token`` and ``fetch_access_token``. v0.6.0 (14 December 2015) +++++++++++++++++++++++++ - Add compliance fix for Slack. - Add compliance fix for Mailchimp. - ``TokenRequestDenied`` exceptions now carry the entire response, not just the status code. - Pass through keyword arguments when refreshing tokens automatically. - Send authorization in headers, not just body, to maximize compatibility. - More getters/setters available for OAuth2 session client values. - Allow sending custom headers when refreshing tokens, and set some defaults. v0.5.0 (4 May 2015) +++++++++++++++++++ - Fix ``TypeError`` being raised instead of ``TokenMissing`` error. - Raise requests exceptions on 4XX and 5XX responses in the OAuth2 flow. - Avoid ``AttributeError`` when initializing the ``OAuth2Session`` class without complete client information. v0.4.2 (16 October 2014) ++++++++++++++++++++++++ - New ``authorized`` property on OAuth1Session and OAuth2Session, which allows you to easily determine if the session is already authorized with OAuth tokens or not. - New ``TokenMissing`` and ``VerifierMissing`` exception classes for OAuth1Session: this will make it easier to catch and identify these exceptions. v0.4.1 (6 June 2014) ++++++++++++++++++++ - New install target ``[rsa]`` for people using OAuth1 RSA-SHA1 signature method. - Fixed bug in OAuth2 where supplied state param was not used in auth url. - OAuth2 HTTPS checking can be disabled by setting environment variable ``OAUTHLIB_INSECURE_TRANSPORT``. - OAuth1 now re-authorize upon redirects. - OAuth1 token fetching now raise a detailed error message when the response body is incorrectly encoded or the request was denied. - Added support for custom OAuth1 clients. - OAuth2 compliance fix for Sina Weibo. - Multiple fixes to facebook compliance fix. - Compliance fixes now re-encode body properly as bytes in Python 3. - Logging now properly done under ``requests_oauthlib`` namespace instead of piggybacking on oauthlib namespace. - Logging introduced for OAuth1 auth and session. v0.4.0 (29 September 2013) ++++++++++++++++++++++++++ - OAuth1Session methods only return unicode strings. #55. - Renamed requests_oauthlib.core to requests_oauthlib.oauth1_auth for consistency. #79. - Added Facebook compliance fix and access_token_response hook to OAuth2Session. #63. - Added LinkedIn compliance fix. - Added refresh_token_response compliance hook, invoked before parsing the refresh token. - Correctly limit compliance hooks to running only once! - Content type guessing should only be done when no content type is given - 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 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1643482333.0 requests-oauthlib-1.3.1/LICENSE0000644000175100001710000000135100000000000015541 0ustar00runnerdockerISC License Copyright (c) 2014 Kenneth Reitz. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1643482333.0 requests-oauthlib-1.3.1/MANIFEST.in0000644000175100001710000000014700000000000016274 0ustar00runnerdockerinclude README.rst LICENSE HISTORY.rst requirements.txt AUTHORS.rst graft tests graft docs prune *.pyc ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1643482341.6034906 requests-oauthlib-1.3.1/PKG-INFO0000644000175100001710000002336500000000000015642 0ustar00runnerdockerMetadata-Version: 2.1 Name: requests-oauthlib Version: 1.3.1 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 Classifier: License :: OSI Approved :: BSD License Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.4 Classifier: Programming Language :: Python :: 3.5 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 :: 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 Provides-Extra: rsa License-File: LICENSE Requests-OAuthlib |build-status| |coverage-status| |docs| ========================================================= This project provides first-class OAuth library support for `Requests `_. The OAuth 1 workflow -------------------- OAuth 1 can seem overly complicated and it sure has its quirks. Luckily, requests_oauthlib hides most of these and let you focus at the task at hand. Accessing protected resources using requests_oauthlib is as simple as: .. code-block:: pycon >>> from requests_oauthlib import OAuth1Session >>> twitter = OAuth1Session('client_key', client_secret='client_secret', resource_owner_key='resource_owner_key', resource_owner_secret='resource_owner_secret') >>> url = 'https://api.twitter.com/1/account/settings.json' >>> r = twitter.get(url) Before accessing resources you will need to obtain a few credentials from your provider (e.g. Twitter) and authorization from the user for whom you wish to retrieve resources for. You can read all about this in the full `OAuth 1 workflow guide on RTD `_. The OAuth 2 workflow -------------------- OAuth 2 is generally simpler than OAuth 1 but comes in more flavours. The most common being the Authorization Code Grant, also known as the WebApplication flow. Fetching a protected resource after obtaining an access token can be extremely simple. However, before accessing resources you will need to obtain a few credentials from your provider (e.g. Google) and authorization from the user for whom you wish to retrieve resources for. You can read all about this in the full `OAuth 2 workflow guide on RTD `_. Installation ------------- To install requests and requests_oauthlib you can use pip: .. code-block:: bash $ 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 .. |coverage-status| image:: https://img.shields.io/coveralls/requests/requests-oauthlib.svg :target: https://coveralls.io/r/requests/requests-oauthlib .. |docs| image:: https://readthedocs.org/projects/requests-oauthlib/badge/ :alt: Documentation Status :scale: 100% :target: https://requests-oauthlib.readthedocs.io/ History ------- v1.3.1 (21 January 2022) ++++++++++++++++++++++++ - Add initial support for OAuth Mutual TLS (draft-ietf-oauth-mtls) - Add eBay compliance fix - Add Spotify OAuth 2 Tutorial - Add support for python 3.8, 3.9 - Fixed LinkedIn Compliance Fixes - Fixed ReadTheDocs Documentation and sphinx errors - Moved pipeline to GitHub Actions v1.3.0 (6 November 2019) ++++++++++++++++++++++++ - Instagram compliance fix - Added ``force_querystring`` argument to fetch_token() method on OAuth2Session v1.2.0 (14 January 2019) ++++++++++++++++++++++++ - This project now depends on OAuthlib 3.0.0 and above. It does **not** support versions of OAuthlib before 3.0.0. - Updated oauth2 tests to use 'sess' for an OAuth2Session instance instead of `auth` because OAuth2Session objects and methods acceept an `auth` paramether which is typically an instance of `requests.auth.HTTPBasicAuth` - `OAuth2Session.fetch_token` previously tried to guess how and where to provide "client" and "user" credentials incorrectly. This was incompatible with some OAuth servers and incompatible with breaking changes in oauthlib that seek to correctly provide the `client_id`. The older implementation also did not raise the correct exceptions when username and password are not present on Legacy clients. - Avoid automatic netrc authentication for OAuth2Session. v1.1.0 (9 January 2019) +++++++++++++++++++++++ - Adjusted version specifier for ``oauthlib`` dependency: this project is not yet compatible with ``oauthlib`` 3.0.0. - Dropped dependency on ``nose``. - Minor changes to clean up the code and make it more readable/maintainable. v1.0.0 (4 June 2018) ++++++++++++++++++++ - **Removed support for Python 2.6 and Python 3.3.** This project now supports Python 2.7, and Python 3.4 and above. - Added several examples to the documentation. - Added plentymarkets compliance fix. - Added a ``token`` property to OAuth1Session, to match the corresponding ``token`` property on OAuth2Session. v0.8.0 (14 February 2017) +++++++++++++++++++++++++ - Added Fitbit compliance fix. - Fixed an issue where newlines in the response body for the access token request would cause errors when trying to extract the token. - Fixed an issue introduced in v0.7.0 where users passing ``auth`` to several methods would encounter conflicts with the ``client_id`` and ``client_secret``-derived auth. The user-supplied ``auth`` argument is now used in preference to those options. v0.7.0 (22 September 2016) ++++++++++++++++++++++++++ - Allowed ``OAuth2Session.request`` to take the ``client_id`` and ``client_secret`` parameters for the purposes of automatic token refresh, which may need them. v0.6.2 (12 July 2016) +++++++++++++++++++++ - Use ``client_id`` and ``client_secret`` for the Authorization header if provided. - Allow explicit bypass of the Authorization header by setting ``auth=False``. - Pass through the ``proxies`` kwarg when refreshing tokens. - Miscellaneous cleanups. v0.6.1 (19 February 2016) +++++++++++++++++++++++++ - Fixed a bug when sending authorization in headers with no username and password present. - Make sure we clear the session token before obtaining a new one. - Some improvements to the Slack compliance fix. - Avoid timing problems around token refresh. - Allow passing arbitrary arguments to requests when calling ``fetch_request_token`` and ``fetch_access_token``. v0.6.0 (14 December 2015) +++++++++++++++++++++++++ - Add compliance fix for Slack. - Add compliance fix for Mailchimp. - ``TokenRequestDenied`` exceptions now carry the entire response, not just the status code. - Pass through keyword arguments when refreshing tokens automatically. - Send authorization in headers, not just body, to maximize compatibility. - More getters/setters available for OAuth2 session client values. - Allow sending custom headers when refreshing tokens, and set some defaults. v0.5.0 (4 May 2015) +++++++++++++++++++ - Fix ``TypeError`` being raised instead of ``TokenMissing`` error. - Raise requests exceptions on 4XX and 5XX responses in the OAuth2 flow. - Avoid ``AttributeError`` when initializing the ``OAuth2Session`` class without complete client information. v0.4.2 (16 October 2014) ++++++++++++++++++++++++ - New ``authorized`` property on OAuth1Session and OAuth2Session, which allows you to easily determine if the session is already authorized with OAuth tokens or not. - New ``TokenMissing`` and ``VerifierMissing`` exception classes for OAuth1Session: this will make it easier to catch and identify these exceptions. v0.4.1 (6 June 2014) ++++++++++++++++++++ - New install target ``[rsa]`` for people using OAuth1 RSA-SHA1 signature method. - Fixed bug in OAuth2 where supplied state param was not used in auth url. - OAuth2 HTTPS checking can be disabled by setting environment variable ``OAUTHLIB_INSECURE_TRANSPORT``. - OAuth1 now re-authorize upon redirects. - OAuth1 token fetching now raise a detailed error message when the response body is incorrectly encoded or the request was denied. - Added support for custom OAuth1 clients. - OAuth2 compliance fix for Sina Weibo. - Multiple fixes to facebook compliance fix. - Compliance fixes now re-encode body properly as bytes in Python 3. - Logging now properly done under ``requests_oauthlib`` namespace instead of piggybacking on oauthlib namespace. - Logging introduced for OAuth1 auth and session. v0.4.0 (29 September 2013) ++++++++++++++++++++++++++ - OAuth1Session methods only return unicode strings. #55. - Renamed requests_oauthlib.core to requests_oauthlib.oauth1_auth for consistency. #79. - Added Facebook compliance fix and access_token_response hook to OAuth2Session. #63. - Added LinkedIn compliance fix. - Added refresh_token_response compliance hook, invoked before parsing the refresh token. - Correctly limit compliance hooks to running only once! - Content type guessing should only be done when no content type is given - 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 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1643482333.0 requests-oauthlib-1.3.1/README.rst0000644000175100001710000000475500000000000016236 0ustar00runnerdockerRequests-OAuthlib |build-status| |coverage-status| |docs| ========================================================= This project provides first-class OAuth library support for `Requests `_. The OAuth 1 workflow -------------------- OAuth 1 can seem overly complicated and it sure has its quirks. Luckily, requests_oauthlib hides most of these and let you focus at the task at hand. Accessing protected resources using requests_oauthlib is as simple as: .. code-block:: pycon >>> from requests_oauthlib import OAuth1Session >>> twitter = OAuth1Session('client_key', client_secret='client_secret', resource_owner_key='resource_owner_key', resource_owner_secret='resource_owner_secret') >>> url = 'https://api.twitter.com/1/account/settings.json' >>> r = twitter.get(url) Before accessing resources you will need to obtain a few credentials from your provider (e.g. Twitter) and authorization from the user for whom you wish to retrieve resources for. You can read all about this in the full `OAuth 1 workflow guide on RTD `_. The OAuth 2 workflow -------------------- OAuth 2 is generally simpler than OAuth 1 but comes in more flavours. The most common being the Authorization Code Grant, also known as the WebApplication flow. Fetching a protected resource after obtaining an access token can be extremely simple. However, before accessing resources you will need to obtain a few credentials from your provider (e.g. Google) and authorization from the user for whom you wish to retrieve resources for. You can read all about this in the full `OAuth 2 workflow guide on RTD `_. Installation ------------- To install requests and requests_oauthlib you can use pip: .. code-block:: bash $ 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 .. |coverage-status| image:: https://img.shields.io/coveralls/requests/requests-oauthlib.svg :target: https://coveralls.io/r/requests/requests-oauthlib .. |docs| image:: https://readthedocs.org/projects/requests-oauthlib/badge/ :alt: Documentation Status :scale: 100% :target: https://requests-oauthlib.readthedocs.io/ ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1643482341.5954921 requests-oauthlib-1.3.1/docs/0000755000175100001710000000000000000000000015464 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1643482333.0 requests-oauthlib-1.3.1/docs/Makefile0000644000175100001710000001522600000000000017132 0ustar00runnerdocker# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = BUILDDIR = _build # User-friendly check for sphinx-build ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) endif # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . # the i18n builder cannot share the environment and doctrees with the others I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " singlehtml to make a single large HTML file" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " devhelp to make HTML files and a Devhelp project" @echo " epub to make an epub" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " latexpdf to make LaTeX files and run them through pdflatex" @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" @echo " text to make text files" @echo " man to make manual pages" @echo " texinfo to make Texinfo files" @echo " info to make Texinfo files and run them through makeinfo" @echo " gettext to make PO message catalogs" @echo " changes to make an overview of all changed/added/deprecated items" @echo " xml to make Docutils-native XML files" @echo " pseudoxml to make pseudoxml-XML files for display purposes" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" clean: rm -rf $(BUILDDIR)/* html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." singlehtml: $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Requests-OAuthlib.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Requests-OAuthlib.qhc" devhelp: $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @echo "To view the help file:" @echo "# mkdir -p $$HOME/.local/share/devhelp/Requests-OAuthlib" @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/Requests-OAuthlib" @echo "# devhelp" epub: $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make' in that directory to run these through (pdf)latex" \ "(use \`make latexpdf' here to do that automatically)." latexpdf: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." $(MAKE) -C $(BUILDDIR)/latex all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." latexpdfja: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through platex and dvipdfmx..." $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." text: $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo @echo "Build finished. The text files are in $(BUILDDIR)/text." man: $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." texinfo: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." @echo "Run \`make' in that directory to run these through makeinfo" \ "(use \`make info' here to do that automatically)." info: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo "Running Texinfo files through makeinfo..." make -C $(BUILDDIR)/texinfo info @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." gettext: $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale @echo @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." xml: $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml @echo @echo "Build finished. The XML files are in $(BUILDDIR)/xml." pseudoxml: $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml @echo @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1643482333.0 requests-oauthlib-1.3.1/docs/api.rst0000644000175100001710000000062600000000000016773 0ustar00runnerdocker.. _api: Developer Interface =================== .. module:: requests_oauthlib OAuth 1.0 --------- .. autoclass:: OAuth1 :members: OAuth 1.0 Session ----------------- .. autoclass:: OAuth1Session :members: OAuth 2.0 --------- .. autoclass:: OAuth2 :members: .. autoclass:: TokenUpdated :members: OAuth 2.0 Session ----------------- .. autoclass:: OAuth2Session :members: ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1643482333.0 requests-oauthlib-1.3.1/docs/conf.py0000644000175100001710000002100300000000000016757 0ustar00runnerdocker# -*- coding: utf-8 -*- # # Requests-OAuthlib documentation build configuration file, created by # sphinx-quickstart on Fri May 10 11:49:01 2013. # # This file is execfile()d with the current directory set to its containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. import sys, os # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. sys.path.insert(0, os.path.abspath("..")) from requests_oauthlib import __version__ # -- General configuration ----------------------------------------------------- # If your documentation needs a minimal Sphinx version, state it here. # needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = ["sphinx.ext.autodoc", "sphinx.ext.intersphinx"] # Add any paths that contain templates here, relative to this directory. templates_path = ["_templates"] # The suffix of source filenames. source_suffix = ".rst" # The encoding of source files. # source_encoding = 'utf-8-sig' # The master toctree document. master_doc = "index" # General information about the project. project = u"Requests-OAuthlib" copyright = u"2014, Kenneth Reitz" # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. version = __version__ # The full version, including alpha/beta/rc tags. release = version # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. # language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: # today = '' # Else, today_fmt is used as the format for a strftime call. # today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = ["_build"] # The reST default role (used for this markup: `text`) to use for all documents. # default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. # add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). # add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. # show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = "sphinx" # A list of ignored prefixes for module index sorting. # modindex_common_prefix = [] # If true, keep warnings as "system message" paragraphs in the built documents. # keep_warnings = False # -- Options for HTML output --------------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. html_theme = "sphinx_rtd_theme" # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. # html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. # html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". # html_title = None # A shorter title for the navigation bar. Default is the same as html_title. # html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. # html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. # html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". # html_static_path = ["_static"] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. # html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. # html_use_smartypants = True # Custom sidebar templates, maps document names to template names. # html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. # html_additional_pages = {} # If false, no module index is generated. # html_domain_indices = True # If false, no index is generated. # html_use_index = True # If true, the index is split into individual pages for each letter. # html_split_index = False # If true, links to the reST sources are added to the pages. # html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. # html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. # html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. # html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). # html_file_suffix = None # Output file base name for HTML help builder. htmlhelp_basename = "Requests-OAuthlibdoc" # -- Options for LaTeX output -------------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). #'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). #'pointsize': '10pt', # Additional stuff for the LaTeX preamble. #'preamble': '', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ ( "index", "Requests-OAuthlib.tex", u"Requests-OAuthlib Documentation", u"Requests-OAuthlib Contributors", "manual", ) ] # The name of an image file (relative to this directory) to place at the top of # the title page. # latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. # latex_use_parts = False # If true, show page references after internal links. # latex_show_pagerefs = False # If true, show URL addresses after external links. # latex_show_urls = False # Documents to append as an appendix to all manuals. # latex_appendices = [] # If false, no module index is generated. # latex_domain_indices = True # -- Options for manual page output -------------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ ( "index", "requests-oauthlib", u"Requests-OAuthlib Documentation", [u"Requests-OAuthlib Contributors"], 1, ) ] # If true, show URL addresses after external links. # man_show_urls = False # -- Options for Texinfo output ------------------------------------------------ # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ ( "index", "Requests-OAuthlib", u"Requests-OAuthlib Documentation", u"Requests-OAuthlib Contributors", "Requests-OAuthlib", "One line description of project.", "Miscellaneous", ) ] # Documents to append as an appendix to all manuals. # texinfo_appendices = [] # If false, no module index is generated. # texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. # texinfo_show_urls = 'footnote' # If true, do not generate a @detailmenu in the "Top" node's menu. # texinfo_no_detailmenu = False # Example configuration for intersphinx: refer to the Python standard library. intersphinx_mapping = { "python": ("https://python.readthedocs.io/en/latest/", None), "requests": ("https://requests.readthedocs.io/en/latest/", None), "oauthlib": ("https://oauthlib.readthedocs.io/en/latest/", None), } ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1643482333.0 requests-oauthlib-1.3.1/docs/contributing.rst0000644000175100001710000000236200000000000020730 0ustar00runnerdocker============ Contributing ============ Test simple changes =================== Requests-OAuthlib is using `tox`_ as main test tool. It helps creating the required virtualenv for your python version. For example, if you have installed Python3.7: .. sourcecode:: bash $ tox -e py37 Validate documentation changes ============================== Tox contains also a build method to generate documentation locally. .. sourcecode:: bash $ tox -e docs,readme Then open the HTML page in `_build/html/index.html` Verify all pythons versions =========================== Requests-OAuthlib supports multiple versions of Python. You can test all Python versions conveniently using `tox`_. .. sourcecode:: bash $ tox In order to run successfully, you will need all versions of Python installed. We recommend using `pyenv`_ to install those Python versions. .. sourcecode:: bash $ pyenv install 2.7.18 $ pyenv install 3.4.10 $ pyenv install 3.5.10 $ pyenv install 3.6.14 $ pyenv install 3.7.11 $ pyenv install pypy2.7-7.1.1 $ pyenv install pypy3.6-7.1.1 .. _`tox`: https://tox.readthedocs.io/en/latest/install.html .. _`virtualenv`: https://virtualenv.pypa.io/en/latest/installation/ .. _`pyenv`: https://github.com/pyenv/pyenv ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1643482341.5994914 requests-oauthlib-1.3.1/docs/examples/0000755000175100001710000000000000000000000017302 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1643482333.0 requests-oauthlib-1.3.1/docs/examples/bitbucket.rst0000644000175100001710000000342300000000000022012 0ustar00runnerdockerBitbucket OAuth 1 Tutorial ========================== Start with setting up a new consumer by following the instructions on `Bitbucket`_. When you have obtained a ``key`` and a ``secret`` you can try out the command line interactive example below. .. _`Bitbucket`: https://confluence.atlassian.com/display/BITBUCKET/OAuth+on+Bitbucket .. code-block:: pycon # Credentials you get from adding a new consumer in bitbucket -> manage account # -> integrated applications. >>> key = '' >>> secret = '' >>> # OAuth endpoints given in the Bitbucket API documentation >>> request_token_url = 'https://bitbucket.org/!api/1.0/oauth/request_token' >>> authorization_base_url = 'https://bitbucket.org/!api/1.0/oauth/authenticate' >>> access_token_url = 'https://bitbucket.org/!api/1.0/oauth/access_token' >>> # 2. Fetch a request token >>> from requests_oauthlib import OAuth1Session >>> bitbucket = OAuth1Session(key, client_secret=secret, >>> callback_uri='http://127.0.0.1/cb') >>> bitbucket.fetch_request_token(request_token_url) >>> # 3. Redirect user to Bitbucket for authorization >>> authorization_url = bitbucket.authorization_url(authorization_base_url) >>> print 'Please go here and authorize,', authorization_url >>> # 4. Get the authorization verifier code from the callback url >>> redirect_response = raw_input('Paste the full redirect URL here:') >>> bitbucket.parse_authorization_response(redirect_response) >>> # 5. Fetch the access token >>> bitbucket.fetch_access_token(access_token_url) >>> # 6. Fetch a protected resource, i.e. user profile >>> r = bitbucket.get('https://bitbucket.org/api/1.0/user') >>> print r.content ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1643482333.0 requests-oauthlib-1.3.1/docs/examples/examples.rst0000644000175100001710000000031600000000000021652 0ustar00runnerdockerExamples ========= .. toctree:: :maxdepth: 2 bitbucket facebook fitbit github google linkedin outlook spotify tumblr real_world_example real_world_example_with_refresh ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1643482333.0 requests-oauthlib-1.3.1/docs/examples/facebook.rst0000644000175100001710000000331500000000000021607 0ustar00runnerdockerFacebook OAuth 2 Tutorial ========================= Setup a new web application client in the `Facebook APP console`_ When you have obtained a ``client_id``, ``client_secret`` and registered a callback URL then you can try out the command line interactive example below. .. _`Facebook APP console`: https://developers.facebook.com/apps .. code-block:: pycon >>> # Credentials you get from registering a new application >>> client_id = '' >>> client_secret = '' >>> # OAuth endpoints given in the Facebook API documentation >>> authorization_base_url = 'https://www.facebook.com/dialog/oauth' >>> token_url = 'https://graph.facebook.com/oauth/access_token' >>> redirect_uri = 'https://localhost/' # Should match Site URL >>> from requests_oauthlib import OAuth2Session >>> from requests_oauthlib.compliance_fixes import facebook_compliance_fix >>> facebook = OAuth2Session(client_id, redirect_uri=redirect_uri) >>> facebook = facebook_compliance_fix(facebook) >>> # Redirect user to Facebook for authorization >>> authorization_url, state = facebook.authorization_url(authorization_base_url) >>> print 'Please go here and authorize,', authorization_url >>> # Get the authorization verifier code from the callback url >>> redirect_response = raw_input('Paste the full redirect URL here:') >>> # Fetch the access token >>> facebook.fetch_token(token_url, client_secret=client_secret, ..> authorization_response=redirect_response) >>> # Fetch a protected resource, i.e. user profile >>> r = facebook.get('https://graph.facebook.com/me?') >>> print r.content ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1643482333.0 requests-oauthlib-1.3.1/docs/examples/fitbit.rst0000644000175100001710000000356300000000000021324 0ustar00runnerdocker.. _fitbit: Fitbit OAuth 2 (Mobile Application Flow) Tutorial ================================================= This makes use of the Implicit Grant Flow to obtain an access token for the `Fitbit API`_. Register a new client application there with a callback URL, and have your client ID handy. Based on an `another example`_ of the Mobile Application Flow. .. _`Fitbit API`: https://dev.fitbit.com/ .. _`another example`: https://github.com/requests/requests-oauthlib/issues/104 .. code-block:: pycon >>> import requests >>> from requests_oauthlib import OAuth2Session >>> from oauthlib.oauth2 import MobileApplicationClient # Set up your client ID and scope: the scope must match that which you requested when you set up your application. >>> client_id = "" >>> scope = ["activity", "heartrate", "location", "nutrition", "profile", "settings", "sleep", "social", "weight"] # Initialize client >>> client = MobileApplicationClient(client_id) >>> fitbit = OAuth2Session(client_id, client=client, scope=scope) >>> authorization_url = "https://www.fitbit.com/oauth2/authorize" # Grab the URL for Fitbit's authorization page. >>> auth_url, state = fitbit.authorization_url(authorization_url) >>> print("Visit this page in your browser: {}".format(auth_url)) # After authenticating, Fitbit will redirect you to the URL you specified in your application settings. It contains the access token. >>> callback_url = input("Paste URL you get back here: ") # Now we extract the token from the URL to make use of it. >>> fitbit.token_from_fragment(callback_url) # We can also store the token for use later. >>> token = fitbit['token'] # At this point, assuming nothing blew up, we can make calls to the API as normal, for example: >>> r = fitbit.get('https://api.fitbit.com/1/user/-/sleep/goal.json') ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1643482333.0 requests-oauthlib-1.3.1/docs/examples/github.rst0000644000175100001710000000263400000000000021323 0ustar00runnerdockerGitHub OAuth 2 Tutorial ========================== Setup credentials following the instructions on `GitHub`_. When you have obtained a ``client_id`` and a ``client_secret`` you can try out the command line interactive example below. .. _`GitHub`: https://github.com/settings/applications/new .. code-block:: pycon >>> # Credentials you get from registering a new application >>> client_id = '' >>> client_secret = '' >>> # OAuth endpoints given in the GitHub API documentation >>> authorization_base_url = 'https://github.com/login/oauth/authorize' >>> token_url = 'https://github.com/login/oauth/access_token' >>> from requests_oauthlib import OAuth2Session >>> github = OAuth2Session(client_id) >>> # Redirect user to GitHub for authorization >>> authorization_url, state = github.authorization_url(authorization_base_url) >>> print 'Please go here and authorize,', authorization_url >>> # Get the authorization verifier code from the callback url >>> redirect_response = raw_input('Paste the full redirect URL here:') >>> # Fetch the access token >>> github.fetch_token(token_url, client_secret=client_secret, >>> authorization_response=redirect_response) >>> # Fetch a protected resource, i.e. user profile >>> r = github.get('https://api.github.com/user') >>> print r.content ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1643482333.0 requests-oauthlib-1.3.1/docs/examples/google.rst0000644000175100001710000000364600000000000021321 0ustar00runnerdockerGoogle OAuth 2 Tutorial ========================== Setup a new web project in the `Google Cloud Console`, (application type: web application)_ When you have obtained a ``client_id``, ``client_secret``, and registered a callback URL then you can try out the command line interactive example below. .. _`Google Cloud Console`: https://cloud.google.com/console/project .. code-block:: pycon >>> # Credentials you get from registering a new application >>> client_id = '' >>> client_secret = '' >>> redirect_uri = 'https://your.registered/callback' >>> # OAuth endpoints given in the Google API documentation >>> authorization_base_url = "https://accounts.google.com/o/oauth2/v2/auth" >>> token_url = "https://www.googleapis.com/oauth2/v4/token" >>> scope = [ ... "openid", ... "https://www.googleapis.com/auth/userinfo.email", ... "https://www.googleapis.com/auth/userinfo.profile" ... ] >>> from requests_oauthlib import OAuth2Session >>> google = OAuth2Session(client_id, scope=scope, redirect_uri=redirect_uri) >>> # Redirect user to Google for authorization >>> authorization_url, state = google.authorization_url(authorization_base_url, ... # offline for refresh token ... # force to always make user click authorize ... access_type="offline", prompt="select_account") >>> print('Please go here and authorize:', authorization_url) >>> # Get the authorization verifier code from the callback url >>> redirect_response = input('Paste the full redirect URL here: ') >>> # Fetch the access token >>> google.fetch_token(token_url, client_secret=client_secret, ... authorization_response=redirect_response) >>> # Fetch a protected resource, i.e. user profile >>> r = google.get('https://www.googleapis.com/oauth2/v1/userinfo') >>> print(r.content) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1643482333.0 requests-oauthlib-1.3.1/docs/examples/linkedin.rst0000644000175100001710000000370500000000000021636 0ustar00runnerdockerLinkedIn OAuth 2 Tutorial ========================= Setup credentials following the instructions on `LinkedIn`_. When you have obtained a ``client_id`` and a ``client_secret`` you can try out the command line interactive example below. .. _`LinkedIn`: https://www.linkedin.com/secure/developer .. code-block:: pycon >>> # Imports >>> import os >>> from requests_oauthlib import OAuth2Session >>> # Set environment variables >>> os.environ['OAUTHLIB_INSECURE_TRANSPORT'] = '1' >>> # Credentials you get from registering a new application >>> client_id = '' >>> client_secret = '' >>> # LinkedIn OAuth2 requests require scope and redirect_url parameters. >>> # Ensure these values match the auth values in your LinkedIn App >>> # (see auth tab on LinkedIn Developer page) >>> scope = ['r_liteprofile'] >>> redirect_url = 'http://127.0.0.1' >>> # OAuth endpoints given in the LinkedIn API documentation >>> authorization_base_url = 'https://www.linkedin.com/oauth/v2/authorization' >>> token_url = 'https://www.linkedin.com/oauth/v2/accessToken' >>> linkedin = OAuth2Session(client_id, redirect_uri='http://127.0.0.1', scope=scope) >>> # Redirect user to LinkedIn for authorization >>> authorization_url, state = linkedin.authorization_url(authorization_base_url) >>> print(f"Please go here and authorize: {authorization_url}") >>> # Get the authorization verifier code from the callback url >>> redirect_response = input('Paste the full redirect URL here:') >>> # Fetch the access token >>> linkedin.fetch_token(token_url, client_secret=client_secret, ... include_client_id=True, ... authorization_response=redirect_response) >>> # Fetch a protected resource, i.e. user profile >>> r = linkedin.get('https://api.linkedin.com/v2/me') >>> print(r.content) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1643482333.0 requests-oauthlib-1.3.1/docs/examples/outlook.rst0000644000175100001710000000370300000000000021533 0ustar00runnerdockerOutlook Calendar OAuth 2 Tutorial ================================= Create a new web application client in the `Microsoft Application Registration Portal`_ (a login is required) When you have obtained a ``client_id``, ``client_secret`` and registered a callback URL then you can try out the command line interactive example below. .. _`Outlook App console`: https://apps.dev.microsoft.com .. _`Microsoft Application Registration Portal`: https://portal.azure.com/#blade/Microsoft_AAD_RegisteredApps/ApplicationsListBlade .. code-block:: pycon >>> # This information is obtained upon registration of a new Outlook Application >>> client_id = '' >>> client_secret = '' >>> # OAuth endpoints given in Outlook API documentation >>> authorization_base_url = 'https://login.microsoftonline.com/common/oauth2/v2.0/authorize' >>> token_url = 'https://login.microsoftonline.com/common/oauth2/v2.0/token' >>> scope = ['https://outlook.office.com/calendars.readwrite'] >>> redirect_uri = 'https://localhost/' # Should match Site URL >>> from requests_oauthlib import OAuth2Session >>> outlook = OAuth2Session(client_id,scope=scope,redirect_uri=redirect_uri) >>> # Redirect the user owner to the OAuth provider (i.e. Outlook) using an URL with a few key OAuth parameters. >>> authorization_url, state = outlook.authorization_url(authorization_base_url) >>> print('Please go here and authorize,', authorization_url) >>> # Get the authorization verifier code from the callback url >>> redirect_response = input('Paste the full redirect URL here:') >>> # Fetch the access token >>> token = outlook.fetch_token(token_url, client_secret=client_secret, authorization_response=redirect_response) >>> # Fetch a protected resource, i.e. calendar information >>> o = outlook.get('https://outlook.office.com/api/v1.0/me/calendars') >>> print(o.content) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1643482333.0 requests-oauthlib-1.3.1/docs/examples/real_world_example.rst0000644000175100001710000000760200000000000023706 0ustar00runnerdocker.. _real_example: Web App Example of OAuth 2 web application flow =============================================== OAuth is commonly used by web applications. The example below shows what such a web application might look like using the `Flask web framework `_ and GitHub as a provider. It should be easily transferrable to any web framework. .. note:: While the flow remains the same across most providers, Github is special in that the ``redirect_uri`` parameter is optional. This means that it may be necessary to explicitly pass a redirect_uri to the ``OAuth2Session`` object (e.g. when creating a custom OAuthProvider with ``flask-oauthlib``). .. code-block:: python from requests_oauthlib import OAuth2Session from flask import Flask, request, redirect, session, url_for from flask.json import jsonify import os app = Flask(__name__) # This information is obtained upon registration of a new GitHub OAuth # application here: https://github.com/settings/applications/new client_id = "" client_secret = "" authorization_base_url = 'https://github.com/login/oauth/authorize' token_url = 'https://github.com/login/oauth/access_token' @app.route("/") def demo(): """Step 1: User Authorization. Redirect the user/resource owner to the OAuth provider (i.e. Github) using an URL with a few key OAuth parameters. """ github = OAuth2Session(client_id) authorization_url, state = github.authorization_url(authorization_base_url) # State is used to prevent CSRF, keep this for later. session['oauth_state'] = state return redirect(authorization_url) # Step 2: User authorization, this happens on the provider. @app.route("/callback", methods=["GET"]) def callback(): """ Step 3: Retrieving an access token. The user has been redirected back from the provider to your registered callback URL. With this redirection comes an authorization code included in the redirect URL. We will use that to obtain an access token. """ github = OAuth2Session(client_id, state=session['oauth_state']) token = github.fetch_token(token_url, client_secret=client_secret, authorization_response=request.url) # At this point you can fetch protected resources but lets save # the token and show how this is done from a persisted token # in /profile. session['oauth_token'] = token return redirect(url_for('.profile')) @app.route("/profile", methods=["GET"]) def profile(): """Fetching a protected resource using an OAuth 2 token. """ github = OAuth2Session(client_id, token=session['oauth_token']) return jsonify(github.get('https://api.github.com/user').json()) if __name__ == "__main__": # This allows us to use a plain HTTP callback os.environ['OAUTHLIB_INSECURE_TRANSPORT'] = "1" app.secret_key = os.urandom(24) app.run(debug=True) This example is lovingly borrowed from `this gist `_. **N.B:** You should note that Oauth2 works through SSL layer. If your server is not parametrized to allow HTTPS, the *fetch_token* method will raise an **oauthlib.oauth2.rfc6749.errors.InsecureTransportError**. Most people don't set SSL on their server while testing and that is fine. You can disable this check in two ways: 1. By setting an environment variable. .. code-block:: bash export OAUTHLIB_INSECURE_TRANSPORT=1 2. Equivalent to above you can set this in Python (if you have problems setting environment variables) .. code-block:: python # Somewhere in webapp_example.py, before the app.run for example import os os.environ['OAUTHLIB_INSECURE_TRANSPORT'] = '1' ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1643482333.0 requests-oauthlib-1.3.1/docs/examples/real_world_example_with_refresh.rst0000644000175100001710000001463300000000000026461 0ustar00runnerdocker.. _token_refresh: Refreshing tokens in OAuth 2 ============================ OAuth 2 providers may allow you to refresh access tokens using refresh tokens. Commonly, only clients that authenticate may refresh tokens, e.g. web applications but not javascript clients. The provider will mention whether they allow token refresh in their API documentation and if you see a "refresh_token" in your token response you are good to go. This example shows how a simple web application (using the `Flask web framework `_) can refresh Google OAuth 2 tokens. It should be trivial to transfer to any other web framework and provider. .. code-block:: python from pprint import pformat from time import time from flask import Flask, request, redirect, session, url_for from flask.json import jsonify import requests from requests_oauthlib import OAuth2Session app = Flask(__name__) # This information is obtained upon registration of a new Google OAuth # application at https://code.google.com/apis/console client_id = "" client_secret = "" redirect_uri = 'https://your.registered/callback' # Uncomment for detailed oauthlib logs #import logging #import sys #log = logging.getLogger('oauthlib') #log.addHandler(logging.StreamHandler(sys.stdout)) #log.setLevel(logging.DEBUG) # OAuth endpoints given in the Google API documentation authorization_base_url = "https://accounts.google.com/o/oauth2/auth" token_url = "https://accounts.google.com/o/oauth2/token" refresh_url = token_url # True for Google but not all providers. scope = [ "https://www.googleapis.com/auth/userinfo.email", "https://www.googleapis.com/auth/userinfo.profile", ] @app.route("/") def demo(): """Step 1: User Authorization. Redirect the user/resource owner to the OAuth provider (i.e. Google) using an URL with a few key OAuth parameters. """ google = OAuth2Session(client_id, scope=scope, redirect_uri=redirect_uri) authorization_url, state = google.authorization_url(authorization_base_url, # offline for refresh token # force to always make user click authorize access_type="offline", prompt="select_account") # State is used to prevent CSRF, keep this for later. session['oauth_state'] = state return redirect(authorization_url) # Step 2: User authorization, this happens on the provider. @app.route("/callback", methods=["GET"]) def callback(): """ Step 3: Retrieving an access token. The user has been redirected back from the provider to your registered callback URL. With this redirection comes an authorization code included in the redirect URL. We will use that to obtain an access token. """ google = OAuth2Session(client_id, redirect_uri=redirect_uri, state=session['oauth_state']) token = google.fetch_token(token_url, client_secret=client_secret, authorization_response=request.url) # We use the session as a simple DB for this example. session['oauth_token'] = token return redirect(url_for('.menu')) @app.route("/menu", methods=["GET"]) def menu(): """""" return """

Congratulations, you have obtained an OAuth 2 token!

What would you like to do next?

        %s
        
""" % pformat(session['oauth_token'], indent=4) @app.route("/profile", methods=["GET"]) def profile(): """Fetching a protected resource using an OAuth 2 token. """ google = OAuth2Session(client_id, token=session['oauth_token']) return jsonify(google.get('https://www.googleapis.com/oauth2/v1/userinfo').json()) @app.route("/automatic_refresh", methods=["GET"]) def automatic_refresh(): """Refreshing an OAuth 2 token using a refresh token. """ token = session['oauth_token'] # We force an expiration by setting expired at in the past. # This will trigger an automatic refresh next time we interact with # Googles API. token['expires_at'] = time() - 10 extra = { 'client_id': client_id, 'client_secret': client_secret, } def token_updater(token): session['oauth_token'] = token google = OAuth2Session(client_id, token=token, auto_refresh_kwargs=extra, auto_refresh_url=refresh_url, token_updater=token_updater) # Trigger the automatic refresh jsonify(google.get('https://www.googleapis.com/oauth2/v1/userinfo').json()) return jsonify(session['oauth_token']) @app.route("/manual_refresh", methods=["GET"]) def manual_refresh(): """Refreshing an OAuth 2 token using a refresh token. """ token = session['oauth_token'] extra = { 'client_id': client_id, 'client_secret': client_secret, } google = OAuth2Session(client_id, token=token) session['oauth_token'] = google.refresh_token(refresh_url, **extra) return jsonify(session['oauth_token']) @app.route("/validate", methods=["GET"]) def validate(): """Validate a token with the OAuth provider Google. """ token = session['oauth_token'] # Defined at https://developers.google.com/accounts/docs/OAuth2LoginV1#validatingtoken validate_url = ('https://www.googleapis.com/oauth2/v1/tokeninfo?' 'access_token=%s' % token['access_token']) # No OAuth2Session is needed, just a plain GET request return jsonify(requests.get(validate_url).json()) if __name__ == "__main__": # This allows us to use a plain HTTP callback import os os.environ['OAUTHLIB_INSECURE_TRANSPORT'] = "1" app.secret_key = os.urandom(24) app.run(debug=True) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1643482333.0 requests-oauthlib-1.3.1/docs/examples/spotify.rst0000644000175100001710000000377000000000000021540 0ustar00runnerdockerSpotify OAuth 2 Tutorial ========================== Setup a new app in the `Spotify Developer Console`_. When you have obtained a ``client_id``, ``client_secret`` and registered a Redirect URI, then you can try out the command line interactive example below. .. _`Spotify Developer Console`: https://developer.spotify.com/dashboard/applications .. code-block:: pycon >>> # Credentials you get from registering a new application >>> client_id = '' >>> client_secret = '' >>> redirect_uri = 'https://your.registered/callback' >>> # OAuth endpoints given in the Spotify API documentation >>> # https://developer.spotify.com/documentation/general/guides/authorization/code-flow/ >>> authorization_base_url = "https://accounts.spotify.com/authorize" >>> token_url = "https://accounts.spotify.com/api/token" >>> # https://developer.spotify.com/documentation/general/guides/authorization/scopes/ >>> scope = [ ... "user-read-email", ... "playlist-read-collaborative" ... ] >>> from requests_oauthlib import OAuth2Session >>> spotify = OAuth2Session(client_id, scope=scope, redirect_uri=redirect_uri) >>> # Redirect user to Spotify for authorization >>> authorization_url, state = spotify.authorization_url(authorization_base_url) >>> print('Please go here and authorize: ', authorization_url) >>> # Get the authorization verifier code from the callback url >>> redirect_response = input('\n\nPaste the full redirect URL here: ') >>> from requests.auth import HTTPBasicAuth >>> auth = HTTPBasicAuth(client_id, client_secret) >>> # Fetch the access token >>> token = spotify.fetch_token(token_url, auth=auth, ... authorization_response=redirect_response) >>> print(token) >>> # Fetch a protected resource, i.e. user profile >>> r = spotify.get('https://api.spotify.com/v1/me') >>> print(r.content) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1643482333.0 requests-oauthlib-1.3.1/docs/examples/tumblr.rst0000644000175100001710000000274500000000000021351 0ustar00runnerdockerTumblr OAuth1 Tutorial ====================== Register a new application on the `tumblr application page`_. Enter a call back url (can just be http://www.tumblr.com/dashboard) and get the ``OAuth Consumer Key`` and ``Secret Key``. .. _`tumblr application page`: http://www.tumblr.com/oauth/apps .. code-block:: pycon >>> # Credentials from the application page >>> key = '' >>> secret = '' >>> # OAuth URLs given on the application page >>> request_token_url = 'http://www.tumblr.com/oauth/request_token' >>> authorization_base_url = 'http://www.tumblr.com/oauth/authorize' >>> access_token_url = 'http://www.tumblr.com/oauth/access_token' >>> # Fetch a request token >>> from requests_oauthlib import OAuth1Session >>> tumblr = OAuth1Session(key, client_secret=secret, callback_uri='http://www.tumblr.com/dashboard') >>> tumblr.fetch_request_token(request_token_url) >>> # Link user to authorization page >>> authorization_url = tumblr.authorization_url(authorization_base_url) >>> print 'Please go here and authorize,', authorization_url >>> # Get the verifier code from the URL >>> redirect_response = raw_input('Paste the full redirect URL here: ') >>> tumblr.parse_authorization_response(redirect_response) >>> # Fetch the access token >>> tumblr.fetch_access_token(access_token_url) >>> # Fetch a protected resource >>> print tumblr.get('http://api.tumblr.com/v2/user/dashboard') ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1643482333.0 requests-oauthlib-1.3.1/docs/index.rst0000644000175100001710000000431100000000000017324 0ustar00runnerdocker.. Requests-OAuthlib documentation master file, created by sphinx-quickstart on Fri May 10 11:49:01 2013. You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. Requests-OAuthlib: OAuth for Humans =================================== Requests-OAuthlib uses the Python `Requests `_ and `OAuthlib `_ libraries to provide an easy-to-use Python interface for building OAuth1 and OAuth2 clients. Overview -------- A simple Flask application which connects to the Github OAuth2 API looks approximately like this: .. code-block:: python from requests_oauthlib import OAuth2Session from flask import Flask, request, redirect, session, url_for from flask.json import jsonify # This information is obtained upon registration of a new GitHub client_id = "" client_secret = "" authorization_base_url = 'https://github.com/login/oauth/authorize' token_url = 'https://github.com/login/oauth/access_token' @app.route("/login") def login(): github = OAuth2Session(client_id) authorization_url, state = github.authorization_url(authorization_base_url) # State is used to prevent CSRF, keep this for later. session['oauth_state'] = state return redirect(authorization_url) @app.route("/callback") def callback(): github = OAuth2Session(client_id, state=session['oauth_state']) token = github.fetch_token(token_url, client_secret=client_secret, authorization_response=request.url) return jsonify(github.get('https://api.github.com/user').json()) The above is a truncated example. A full working example is available here: :ref:`real_example` Installation ============ Requests-OAuthlib can be installed with `pip `_: :: $ pip install requests_oauthlib Getting Started: ================ .. toctree:: :maxdepth: 2 oauth1_workflow oauth2_workflow examples/examples api contributing Indices and tables ================== * :ref:`genindex` * :ref:`modindex` * :ref:`search` ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1643482333.0 requests-oauthlib-1.3.1/docs/make.bat0000644000175100001710000001510300000000000017071 0ustar00runnerdocker@ECHO OFF REM Command file for Sphinx documentation if "%SPHINXBUILD%" == "" ( set SPHINXBUILD=sphinx-build ) set BUILDDIR=_build set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . set I18NSPHINXOPTS=%SPHINXOPTS% . if NOT "%PAPER%" == "" ( set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% ) if "%1" == "" goto help if "%1" == "help" ( :help echo.Please use `make ^` where ^ is one of echo. html to make standalone HTML files echo. dirhtml to make HTML files named index.html in directories echo. singlehtml to make a single large HTML file echo. pickle to make pickle files echo. json to make JSON files echo. htmlhelp to make HTML files and a HTML help project echo. qthelp to make HTML files and a qthelp project echo. devhelp to make HTML files and a Devhelp project echo. epub to make an epub echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter echo. text to make text files echo. man to make manual pages echo. texinfo to make Texinfo files echo. gettext to make PO message catalogs echo. changes to make an overview over all changed/added/deprecated items echo. xml to make Docutils-native XML files echo. pseudoxml to make pseudoxml-XML files for display purposes echo. linkcheck to check all external links for integrity echo. doctest to run all doctests embedded in the documentation if enabled goto end ) if "%1" == "clean" ( for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i del /q /s %BUILDDIR%\* goto end ) %SPHINXBUILD% 2> nul if errorlevel 9009 ( echo. echo.The 'sphinx-build' command was not found. Make sure you have Sphinx echo.installed, then set the SPHINXBUILD environment variable to point echo.to the full path of the 'sphinx-build' executable. Alternatively you echo.may add the Sphinx directory to PATH. echo. echo.If you don't have Sphinx installed, grab it from echo.http://sphinx-doc.org/ exit /b 1 ) if "%1" == "html" ( %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/html. goto end ) if "%1" == "dirhtml" ( %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. goto end ) if "%1" == "singlehtml" ( %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. goto end ) if "%1" == "pickle" ( %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the pickle files. goto end ) if "%1" == "json" ( %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the JSON files. goto end ) if "%1" == "htmlhelp" ( %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run HTML Help Workshop with the ^ .hhp project file in %BUILDDIR%/htmlhelp. goto end ) if "%1" == "qthelp" ( %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run "qcollectiongenerator" with the ^ .qhcp project file in %BUILDDIR%/qthelp, like this: echo.^> qcollectiongenerator %BUILDDIR%\qthelp\Requests-OAuthlib.qhcp echo.To view the help file: echo.^> assistant -collectionFile %BUILDDIR%\qthelp\Requests-OAuthlib.ghc goto end ) if "%1" == "devhelp" ( %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp if errorlevel 1 exit /b 1 echo. echo.Build finished. goto end ) if "%1" == "epub" ( %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub if errorlevel 1 exit /b 1 echo. echo.Build finished. The epub file is in %BUILDDIR%/epub. goto end ) if "%1" == "latex" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex if errorlevel 1 exit /b 1 echo. echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. goto end ) if "%1" == "latexpdf" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex cd %BUILDDIR%/latex make all-pdf cd %BUILDDIR%/.. echo. echo.Build finished; the PDF files are in %BUILDDIR%/latex. goto end ) if "%1" == "latexpdfja" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex cd %BUILDDIR%/latex make all-pdf-ja cd %BUILDDIR%/.. echo. echo.Build finished; the PDF files are in %BUILDDIR%/latex. goto end ) if "%1" == "text" ( %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text if errorlevel 1 exit /b 1 echo. echo.Build finished. The text files are in %BUILDDIR%/text. goto end ) if "%1" == "man" ( %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man if errorlevel 1 exit /b 1 echo. echo.Build finished. The manual pages are in %BUILDDIR%/man. goto end ) if "%1" == "texinfo" ( %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo if errorlevel 1 exit /b 1 echo. echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. goto end ) if "%1" == "gettext" ( %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale if errorlevel 1 exit /b 1 echo. echo.Build finished. The message catalogs are in %BUILDDIR%/locale. goto end ) if "%1" == "changes" ( %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes if errorlevel 1 exit /b 1 echo. echo.The overview file is in %BUILDDIR%/changes. goto end ) if "%1" == "linkcheck" ( %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck if errorlevel 1 exit /b 1 echo. echo.Link check complete; look for any errors in the above output ^ or in %BUILDDIR%/linkcheck/output.txt. goto end ) if "%1" == "doctest" ( %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest if errorlevel 1 exit /b 1 echo. echo.Testing of doctests in the sources finished, look at the ^ results in %BUILDDIR%/doctest/output.txt. goto end ) if "%1" == "xml" ( %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml if errorlevel 1 exit /b 1 echo. echo.Build finished. The XML files are in %BUILDDIR%/xml. goto end ) if "%1" == "pseudoxml" ( %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml if errorlevel 1 exit /b 1 echo. echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. goto end ) :end ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1643482333.0 requests-oauthlib-1.3.1/docs/oauth1_workflow.rst0000644000175100001710000002115100000000000021351 0ustar00runnerdockerOAuth 1 Workflow ================ You will be forced to go through a few steps when you are using OAuth. Below is an example of the most common OAuth workflow using HMAC-SHA1 signed requests where the signature is supplied in the Authorization header. The example assumes an interactive prompt which is good for demonstration but in practice you will likely be using a web application (which makes authorizing much less awkward since you can simply redirect). The guide will show two ways of carrying out the OAuth1 workflow. One using the authentication helper OAuth1 and the alternative using OAuth1Session. The latter is usually more convenient and requires less code. Workflow example showing use of both OAuth1 and OAuth1Session ------------------------------------------------------------- 0. Manual client signup with the OAuth provider (i.e. Google, Twitter) to get a set of client credentials. Usually a client key and secret. Client might sometimes be referred to as consumer. For example: .. code-block:: pycon >>> # Using OAuth1Session >>> from requests_oauthlib import OAuth1Session >>> # Using OAuth1 auth helper >>> import requests >>> from requests_oauthlib import OAuth1 >>> client_key = '...' >>> client_secret = '...' 1. Obtain a request token which will identify you (the client) in the next step. At this stage you will only need your client key and secret. .. code-block:: pycon >>> request_token_url = 'https://api.twitter.com/oauth/request_token' >>> # Using OAuth1Session >>> oauth = OAuth1Session(client_key, client_secret=client_secret) >>> fetch_response = oauth.fetch_request_token(request_token_url) { "oauth_token": "Z6eEdO8MOmk394WozF5oKyuAv855l4Mlqo7hhlSLik", "oauth_token_secret": "Kd75W4OQfb2oJTV0vzGzeXftVAwgMnEK9MumzYcM" } >>> resource_owner_key = fetch_response.get('oauth_token') >>> resource_owner_secret = fetch_response.get('oauth_token_secret') >>> # Using OAuth1 auth helper >>> oauth = OAuth1(client_key, client_secret=client_secret) >>> r = requests.post(url=request_token_url, auth=oauth) >>> r.content "oauth_token=Z6eEdO8MOmk394WozF5oKyuAv855l4Mlqo7hhlSLik&oauth_token_secret=Kd75W4OQfb2oJTV0vzGzeXftVAwgMnEK9MumzYcM" >>> from urlparse import parse_qs >>> credentials = parse_qs(r.content) >>> resource_owner_key = credentials.get('oauth_token')[0] >>> resource_owner_secret = credentials.get('oauth_token_secret')[0] 2. Obtain authorization from the user (resource owner) to access their protected resources (images, tweets, etc.). This is commonly done by redirecting the user to a specific url to which you add the request token as a query parameter. Note that not all services will give you a verifier even if they should. Also the oauth_token given here will be the same as the one in the previous step. .. code-block:: pycon >>> base_authorization_url = 'https://api.twitter.com/oauth/authorize' >>> # Using OAuth1Session >>> authorization_url = oauth.authorization_url(base_authorization_url) >>> print 'Please go here and authorize,', authorization_url >>> redirect_response = raw_input('Paste the full redirect URL here: ') >>> oauth_response = oauth.parse_authorization_response(redirect_response) { "oauth_token": "Z6eEdO8MOmk394WozF5oKyuAv855l4Mlqo7hhlSLik", "oauth_verifier": "sdflk3450FASDLJasd2349dfs" } >>> verifier = oauth_response.get('oauth_verifier') >>> # Using OAuth1 auth helper >>> authorize_url = base_authorization_url + '?oauth_token=' >>> authorize_url = authorize_url + resource_owner_key >>> print 'Please go here and authorize,', authorize_url >>> verifier = raw_input('Please input the verifier') 3. Obtain an access token from the OAuth provider. Save this token as it can be re-used later. In this step we will re-use most of the credentials obtained uptil this point. .. code-block:: pycon >>> access_token_url = 'https://api.twitter.com/oauth/access_token' >>> # Using OAuth1Session >>> oauth = OAuth1Session(client_key, client_secret=client_secret, resource_owner_key=resource_owner_key, resource_owner_secret=resource_owner_secret, verifier=verifier) >>> oauth_tokens = oauth.fetch_access_token(access_token_url) { "oauth_token": "6253282-eWudHldSbIaelX7swmsiHImEL4KinwaGloHANdrY", "oauth_token_secret": "2EEfA6BG3ly3sR3RjE0IBSnlQu4ZrUzPiYKmrkVU" } >>> resource_owner_key = oauth_tokens.get('oauth_token') >>> resource_owner_secret = oauth_tokens.get('oauth_token_secret') >>> # Using OAuth1 auth helper >>> oauth = OAuth1(client_key, client_secret=client_secret, resource_owner_key=resource_owner_key, resource_owner_secret=resource_owner_secret, verifier=verifier) >>> r = requests.post(url=access_token_url, auth=oauth) >>> r.content "oauth_token=6253282-eWudHldSbIaelX7swmsiHImEL4KinwaGloHANdrY&oauth_token_secret=2EEfA6BG3ly3sR3RjE0IBSnlQu4ZrUzPiYKmrkVU" >>> credentials = parse_qs(r.content) >>> resource_owner_key = credentials.get('oauth_token')[0] >>> resource_owner_secret = credentials.get('oauth_token_secret')[0] 4. Access protected resources. OAuth1 access tokens typically do not expire and may be re-used until revoked by the user or yourself. .. code-block:: pycon >>> protected_url = 'https://api.twitter.com/1/account/settings.json' >>> # Using OAuth1Session >>> oauth = OAuth1Session(client_key, client_secret=client_secret, resource_owner_key=resource_owner_key, resource_owner_secret=resource_owner_secret) >>> r = oauth.get(protected_url) >>> # Using OAuth1 auth helper >>> oauth = OAuth1(client_key, client_secret=client_secret, resource_owner_key=resource_owner_key, resource_owner_secret=resource_owner_secret) >>> r = requests.get(url=protected_url, auth=oauth) Signature placement - header, query or body? -------------------------------------------- OAuth takes many forms, so let's take a look at a few different forms: .. code-block:: python import requests from requests_oauthlib import OAuth1 url = u'https://api.twitter.com/1/account/settings.json' client_key = u'...' client_secret = u'...' resource_owner_key = u'...' resource_owner_secret = u'...' Header signing (recommended): .. code-block:: python headeroauth = OAuth1(client_key, client_secret, resource_owner_key, resource_owner_secret, signature_type='auth_header') r = requests.get(url, auth=headeroauth) Query signing: .. code-block:: python queryoauth = OAuth1(client_key, client_secret, resource_owner_key, resource_owner_secret, signature_type='query') r = requests.get(url, auth=queryoauth) Body signing: .. code-block:: python bodyoauth = OAuth1(client_key, client_secret, resource_owner_key, resource_owner_secret, signature_type='body') r = requests.post(url, auth=bodyoauth) Signature types - HMAC (most common), RSA, Plaintext ---------------------------------------------------- OAuth1 defaults to using HMAC and examples can be found in the previous sections. Plaintext work on the same credentials as HMAC and the only change you will need to make when using it is to add signature_type='PLAINTEXT' to the OAuth1 constructor: .. code-block:: python headeroauth = OAuth1(client_key, client_secret, resource_owner_key, resource_owner_secret, signature_method='PLAINTEXT') RSA is different in that it does not use client_secret nor resource_owner_secret. Instead it uses public and private keys. The public key is provided to the OAuth provider during client registration. The private key is used to sign requests. The previous section can be summarized as: .. code-block:: python key = open("your_rsa_key.pem").read() queryoauth = OAuth1(client_key, signature_method=SIGNATURE_RSA, rsa_key=key, signature_type='query') headeroauth = OAuth1(client_key, signature_method=SIGNATURE_RSA, rsa_key=key, signature_type='auth_header') bodyoauth = OAuth1(client_key, signature_method=SIGNATURE_RSA, rsa_key=key, signature_type='body') ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1643482333.0 requests-oauthlib-1.3.1/docs/oauth2_workflow.rst0000644000175100001710000002566600000000000021371 0ustar00runnerdockerOAuth 2 Workflow ================ .. contents:: :depth: 3 :local: Introduction ------------ The following sections provide some example code that demonstrates some of the possible OAuth2 flows you can use with requests-oauthlib. We provide four examples: one for each of the grant types defined by the OAuth2 RFC. These grant types (or workflows) are the Authorization Code Grant (or Web Application Flow), the Implicit Grant (or Mobile Application Flow), the Resource Owner Password Credentials Grant (or, more succinctly, the Legacy Application Flow), and the Client Credentials Grant (or Backend Application Flow). Available Workflows ~~~~~~~~~~~~~~~~~~~ There are four core work flows: 1. :ref:`Authorization Code Grant ` (Web Application Flow). 2. :ref:`Implicit Grant ` (Mobile Application flow). 3. :ref:`Resource Owner Password Credentials Grant ` (Legacy Application flow). 4. :ref:`Client Credentials Grant ` (Backend Application flow). .. _web-application-flow: Web Application Flow -------------------- The steps below outline how to use the default Authorization Grant Type flow to obtain an access token and fetch a protected resource. In this example the provider is Google and the protected resource is the user's profile. 0. Obtain credentials from your OAuth provider manually. At minimum you will need a ``client_id`` but likely also a ``client_secret``. During this process you might also be required to register a default redirect URI to be used by your application. Save these things in your Python script: .. code-block:: pycon >>> client_id = r'your_client_id' >>> client_secret = r'your_client_secret' >>> redirect_uri = 'https://your.callback/uri' 1. User authorization through redirection. First we will create an authorization url from the base URL given by the provider and the credentials previously obtained. In addition most providers will request that you ask for access to a certain scope. In this example we will ask Google for access to the email address of the user and the users profile. .. code-block:: pycon # Note that these are Google specific scopes >>> scope = ['https://www.googleapis.com/auth/userinfo.email', 'https://www.googleapis.com/auth/userinfo.profile'] >>> oauth = OAuth2Session(client_id, redirect_uri=redirect_uri, scope=scope) >>> authorization_url, state = oauth.authorization_url( 'https://accounts.google.com/o/oauth2/auth', # access_type and prompt are Google specific extra # parameters. access_type="offline", prompt="select_account") >>> print 'Please go to %s and authorize access.' % authorization_url >>> authorization_response = raw_input('Enter the full callback URL') 2. Fetch an access token from the provider using the authorization code obtained during user authorization. .. code-block:: pycon >>> token = oauth.fetch_token( 'https://accounts.google.com/o/oauth2/token', authorization_response=authorization_response, # Google specific extra parameter used for client # authentication client_secret=client_secret) 3. Access protected resources using the access token you just obtained. For example, get the users profile info. .. code-block:: pycon >>> r = oauth.get('https://www.googleapis.com/oauth2/v1/userinfo') >>> # Enjoy =) .. _mobile-application-flow: Mobile Application Flow ----------------------- The steps below outline how to use the Implicit Code Grant Type flow to obtain an access token. 0. You will need the following settings. .. code-block:: pycon >>> client_id = 'your_client_id' >>> scopes = ['scope_1', 'scope_2'] >>> auth_url = 'https://your.oauth2/auth' 1. Get the authorization_url .. code-block:: pycon >>> from oauthlib.oauth2 import MobileApplicationClient >>> from requests_oauthlib import OAuth2Session >>> oauth = OAuth2Session(client=MobileApplicationClient(client_id=client_id), scope=scopes) >>> authorization_url, state = oauth.authorization_url(auth_url) 2. Fetch an access token from the provider. .. code-block:: pycon >>> response = oauth.get(authorization_url) >>> oauth.token_from_fragment(response.url) .. _legacy-application-flow: Legacy Application Flow ----------------------- The steps below outline how to use the Resource Owner Password Credentials Grant Type flow to obtain an access token. 0. You will need the following settings. ``client_secret`` is optional depending on the provider. .. code-block:: pycon >>> client_id = 'your_client_id' >>> client_secret = 'your_client_secret' >>> username = 'your_username' >>> password = 'your_password' 1. Fetch an access token from the provider. .. code-block:: pycon >>> from oauthlib.oauth2 import LegacyApplicationClient >>> from requests_oauthlib import OAuth2Session >>> oauth = OAuth2Session(client=LegacyApplicationClient(client_id=client_id)) >>> token = oauth.fetch_token(token_url='https://somesite.com/oauth2/token', username=username, password=password, client_id=client_id, client_secret=client_secret) .. _backend-application-flow: Backend Application Flow ------------------------ The steps below outline how to use the Resource Owner Client Credentials Grant Type flow to obtain an access token. 0. Obtain credentials from your OAuth provider. At minimum you will need a ``client_id`` and ``client_secret``. .. code-block:: pycon >>> client_id = 'your_client_id' >>> client_secret = 'your_client_secret' 1. Fetch an access token from the provider. .. code-block:: pycon >>> from oauthlib.oauth2 import BackendApplicationClient >>> from requests_oauthlib import OAuth2Session >>> client = BackendApplicationClient(client_id=client_id) >>> oauth = OAuth2Session(client=client) >>> token = oauth.fetch_token(token_url='https://provider.com/oauth2/token', client_id=client_id, client_secret=client_secret) If your provider requires that you pass auth credentials in a Basic Auth header, you can do this instead: .. code-block:: pycon >>> from oauthlib.oauth2 import BackendApplicationClient >>> from requests_oauthlib import OAuth2Session >>> from requests.auth import HTTPBasicAuth >>> auth = HTTPBasicAuth(client_id, client_secret) >>> client = BackendApplicationClient(client_id=client_id) >>> oauth = OAuth2Session(client=client) >>> token = oauth.fetch_token(token_url='https://provider.com/oauth2/token', auth=auth) Refreshing tokens ----------------- Certain providers will give you a ``refresh_token`` along with the ``access_token``. These can be used to directly fetch new access tokens without going through the normal OAuth workflow. ``requests-oauthlib`` provides three methods of obtaining refresh tokens. All of these are dependant on you specifying an accurate ``expires_in`` in the token. ``expires_in`` is a credential given with the access and refresh token indicating in how many seconds from now the access token expires. Commonly, access tokens expire after an hour and the ``expires_in`` would be ``3600``. Without this it is impossible for ``requests-oauthlib`` to know when a token is expired as the status code of a request failing due to token expiration is not defined. If you are not interested in token refreshing, always pass in a positive value for ``expires_in`` or omit it entirely. (ALL) Define the token, token saver and needed credentials ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. code-block:: pycon >>> token = { ... 'access_token': 'eswfld123kjhn1v5423', ... 'refresh_token': 'asdfkljh23490sdf', ... 'token_type': 'Bearer', ... 'expires_in': '-30', # initially 3600, need to be updated by you ... } >>> client_id = r'foo' >>> refresh_url = 'https://provider.com/token' >>> protected_url = 'https://provider.com/secret' >>> # most providers will ask you for extra credentials to be passed along >>> # when refreshing tokens, usually for authentication purposes. >>> extra = { ... 'client_id': client_id, ... 'client_secret': r'potato', ... } >>> # After updating the token you will most likely want to save it. >>> def token_saver(token): ... # save token in database / session (First) Define Try-Catch TokenExpiredError on each request ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This is the most basic version in which an error is raised when refresh is necessary but refreshing is done manually. .. code-block:: pycon >>> from requests_oauthlib import OAuth2Session >>> from oauthlib.oauth2 import TokenExpiredError >>> try: ... client = OAuth2Session(client_id, token=token) ... r = client.get(protected_url) >>> except TokenExpiredError as e: ... token = client.refresh_token(refresh_url, **extra) ... token_saver(token) >>> client = OAuth2Session(client_id, token=token) >>> r = client.get(protected_url) (Second) Define automatic token refresh automatic but update manually ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This is the, arguably awkward, middle between the basic and convenient refresh methods in which a token is automatically refreshed, but saving the new token is done manually. .. code-block:: pycon >>> from requests_oauthlib import OAuth2Session, TokenUpdated >>> try: ... client = OAuth2Session(client_id, token=token, ... auto_refresh_kwargs=extra, auto_refresh_url=refresh_url) ... r = client.get(protected_url) >>> except TokenUpdated as e: ... token_saver(e.token) (Third, Recommended) Define automatic token refresh and update ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The third and recommended method will automatically fetch refresh tokens and save them. It requires no exception catching and results in clean code. Remember however that you still need to update ``expires_in`` to trigger the refresh. .. code-block:: pycon >>> from requests_oauthlib import OAuth2Session >>> client = OAuth2Session(client_id, token=token, auto_refresh_url=refresh_url, ... auto_refresh_kwargs=extra, token_updater=token_saver) >>> r = client.get(protected_url) TLS Client Authentication ------------------------- To use TLS Client Authentication (draft-ietf-oauth-mtls) via a self-signed or CA-issued certificate, pass the certificate in the token request and ensure that the client id is sent in the request: .. code-block:: pycon >>> oauth.fetch_token(token_url='https://somesite.com/oauth2/token', ... include_client_id=True, cert=('test-client.pem', 'test-client-key.pem')) .. _write this section: https://github.com/requests/requests-oauthlib/issues/48 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1643482333.0 requests-oauthlib-1.3.1/docs/requirements.txt0000644000175100001710000000017500000000000020753 0ustar00runnerdockersphinx<2 docutils<=0.15 # docutils 0.16 has an error when generating in api.rst sphinx_rtd_theme<0.5 readthedocs-sphinx-ext ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1643482341.5994914 requests-oauthlib-1.3.1/requests_oauthlib/0000755000175100001710000000000000000000000020276 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1643482333.0 requests-oauthlib-1.3.1/requests_oauthlib/__init__.py0000644000175100001710000000102100000000000022401 0ustar00runnerdockerimport logging from .oauth1_auth import OAuth1 from .oauth1_session import OAuth1Session from .oauth2_auth import OAuth2 from .oauth2_session import OAuth2Session, TokenUpdated __version__ = "1.3.1" import requests if requests.__version__ < "2.0.0": msg = ( "You are using requests version %s, which is older than " "requests-oauthlib expects, please upgrade to 2.0.0 or later." ) raise Warning(msg % requests.__version__) logging.getLogger("requests_oauthlib").addHandler(logging.NullHandler()) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1643482341.6034906 requests-oauthlib-1.3.1/requests_oauthlib/compliance_fixes/0000755000175100001710000000000000000000000023606 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1643482333.0 requests-oauthlib-1.3.1/requests_oauthlib/compliance_fixes/__init__.py0000644000175100001710000000061600000000000025722 0ustar00runnerdockerfrom __future__ import absolute_import from .facebook import facebook_compliance_fix from .fitbit import fitbit_compliance_fix from .slack import slack_compliance_fix from .instagram import instagram_compliance_fix from .mailchimp import mailchimp_compliance_fix from .weibo import weibo_compliance_fix from .plentymarkets import plentymarkets_compliance_fix from .ebay import ebay_compliance_fix ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1643482333.0 requests-oauthlib-1.3.1/requests_oauthlib/compliance_fixes/douban.py0000644000175100001710000000073000000000000025430 0ustar00runnerdockerimport 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") return r session._client_default_token_placement = "query" session.register_compliance_hook("access_token_response", fix_token_type) return session ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1643482333.0 requests-oauthlib-1.3.1/requests_oauthlib/compliance_fixes/ebay.py0000644000175100001710000000157200000000000025105 0ustar00runnerdockerimport json from oauthlib.common import to_unicode def ebay_compliance_fix(session): def _compliance_fix(response): token = json.loads(response.text) # eBay responds with non-compliant token types. # https://developer.ebay.com/api-docs/static/oauth-client-credentials-grant.html # https://developer.ebay.com/api-docs/static/oauth-auth-code-grant-request.html # Modify these to be "Bearer". 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") return response session.register_compliance_hook("access_token_response", _compliance_fix) session.register_compliance_hook("refresh_token_response", _compliance_fix) return session ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1643482333.0 requests-oauthlib-1.3.1/requests_oauthlib/compliance_fixes/facebook.py0000644000175100001710000000213700000000000025734 0ustar00runnerdockerfrom json import dumps try: from urlparse import parse_qsl except ImportError: from urllib.parse import parse_qsl from oauthlib.common import to_unicode def facebook_compliance_fix(session): def _compliance_fix(r): # if Facebook claims to be sending us json, let's trust them. if "application/json" in r.headers.get("content-type", {}): return r # Facebook returns a content-type of text/plain when sending their # x-www-form-urlencoded responses, along with a 200. If not, let's # assume we're getting JSON and bail on the fix. if "text/plain" in r.headers.get("content-type", {}) and r.status_code == 200: token = dict(parse_qsl(r.text, keep_blank_values=True)) else: return r expires = token.get("expires") if expires is not None: token["expires_in"] = expires token["token_type"] = "Bearer" r._content = to_unicode(dumps(token)).encode("UTF-8") return r session.register_compliance_hook("access_token_response", _compliance_fix) return session ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1643482333.0 requests-oauthlib-1.3.1/requests_oauthlib/compliance_fixes/fitbit.py0000644000175100001710000000161100000000000025440 0ustar00runnerdocker""" The Fitbit API breaks from the OAuth2 RFC standard by returning an "errors" object list, rather than a single "error" string. This puts hooks in place so that oauthlib can process an error in the results from access token and refresh token responses. This is necessary to prevent getting the generic red herring MissingTokenError. """ from json import loads, dumps from oauthlib.common import to_unicode def fitbit_compliance_fix(session): def _missing_error(r): token = loads(r.text) 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") return r session.register_compliance_hook("access_token_response", _missing_error) session.register_compliance_hook("refresh_token_response", _missing_error) return session ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1643482333.0 requests-oauthlib-1.3.1/requests_oauthlib/compliance_fixes/instagram.py0000644000175100001710000000166400000000000026154 0ustar00runnerdockertry: from urlparse import urlparse, parse_qs except ImportError: from urllib.parse import urlparse, parse_qs from oauthlib.common import add_params_to_uri def instagram_compliance_fix(session): def _non_compliant_param_name(url, headers, data): # If the user has already specified the token in the URL # then there's nothing to do. # If the specified token is different from ``session.access_token``, # we assume the user intends to override the access token. url_query = dict(parse_qs(urlparse(url).query)) token = url_query.get("access_token") if token: # Nothing to do, just return. return url, headers, data token = [("access_token", session.access_token)] url = add_params_to_uri(url, token) return url, headers, data session.register_compliance_hook("protected_request", _non_compliant_param_name) return session ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1643482333.0 requests-oauthlib-1.3.1/requests_oauthlib/compliance_fixes/mailchimp.py0000644000175100001710000000136500000000000026130 0ustar00runnerdockerimport 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") 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") return r session.register_compliance_hook("access_token_response", _null_scope) session.register_compliance_hook("access_token_response", _non_zero_expiration) return session ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1643482333.0 requests-oauthlib-1.3.1/requests_oauthlib/compliance_fixes/plentymarkets.py0000644000175100001710000000143400000000000027064 0ustar00runnerdockerfrom json import dumps, loads import re from oauthlib.common import to_unicode def plentymarkets_compliance_fix(session): def _to_snake_case(n): return re.sub("(.)([A-Z][a-z]+)", r"\1_\2", n).lower() def _compliance_fix(r): # Plenty returns the Token in CamelCase instead of _ if ( "application/json" in r.headers.get("content-type", {}) and r.status_code == 200 ): token = loads(r.text) else: return r fixed_token = {} for k, v in token.items(): fixed_token[_to_snake_case(k)] = v r._content = to_unicode(dumps(fixed_token)).encode("UTF-8") return r session.register_compliance_hook("access_token_response", _compliance_fix) return session ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1643482333.0 requests-oauthlib-1.3.1/requests_oauthlib/compliance_fixes/slack.py0000644000175100001710000000265500000000000025265 0ustar00runnerdockertry: from urlparse import urlparse, parse_qs except ImportError: from urllib.parse import urlparse, parse_qs from oauthlib.common import add_params_to_uri def slack_compliance_fix(session): def _non_compliant_param_name(url, headers, data): # If the user has already specified the token, either in the URL # or in a data dictionary, then there's nothing to do. # If the specified token is different from ``session.access_token``, # we assume the user intends to override the access token. url_query = dict(parse_qs(urlparse(url).query)) token = url_query.get("token") if not token and isinstance(data, dict): token = data.get("token") if token: # Nothing to do, just return. return url, headers, data if not data: data = {"token": session.access_token} elif isinstance(data, dict): data["token"] = session.access_token else: # ``data`` is something other than a dict: maybe a stream, # maybe a file object, maybe something else. We can't easily # modify it, so we'll set the token by modifying the URL instead. token = [("token", session.access_token)] url = add_params_to_uri(url, token) return url, headers, data session.register_compliance_hook("protected_request", _non_compliant_param_name) return session ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1643482333.0 requests-oauthlib-1.3.1/requests_oauthlib/compliance_fixes/weibo.py0000644000175100001710000000067400000000000025274 0ustar00runnerdockerfrom 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") return r session._client.default_token_placement = "query" session.register_compliance_hook("access_token_response", _missing_token_type) return session ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1643482333.0 requests-oauthlib-1.3.1/requests_oauthlib/oauth1_auth.py0000644000175100001710000000723100000000000023075 0ustar00runnerdocker# -*- 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__) # OBS!: Correct signing of requests are conditional on invoking OAuth1 # as the last step of preparing a request, or at least having the # content-type set properly. class OAuth1(AuthBase): """Signs the request using OAuth 1 (RFC5849)""" client_class = Client def __init__( self, client_key, client_secret=None, resource_owner_key=None, resource_owner_secret=None, callback_uri=None, signature_method=SIGNATURE_HMAC, signature_type=SIGNATURE_TYPE_AUTH_HEADER, rsa_key=None, verifier=None, decoding="utf-8", client_class=None, force_include_body=False, **kwargs ): try: signature_type = signature_type.upper() except AttributeError: pass client_class = client_class or self.client_class self.force_include_body = force_include_body self.client = client_class( client_key, client_secret, resource_owner_key, resource_owner_secret, callback_uri, signature_method, signature_type, rsa_key, verifier, decoding=decoding, **kwargs ) def __call__(self, r): """Add OAuth parameters to the request. Parameters may be included from the body if the content-type is urlencoded, if no content type is set a guess is made. """ # Overwriting url is safe here as request will not modify it past # this point. log.debug("Signing request %s using client %s", r, self.client) content_type = r.headers.get("Content-Type", "") if ( not content_type and extract_params(r.body) or self.client.signature_type == SIGNATURE_TYPE_BODY ): content_type = CONTENT_TYPE_FORM_URLENCODED if not isinstance(content_type, unicode): content_type = content_type.decode("utf-8") is_form_encoded = CONTENT_TYPE_FORM_URLENCODED in content_type log.debug( "Including body in call to sign: %s", is_form_encoded or self.force_include_body, ) 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 ) 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 ) 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 ) r.prepare_headers(headers) r.url = to_native_string(r.url) log.debug("Updated url: %s", r.url) log.debug("Updated headers: %s", headers) log.debug("Updated body: %r", r.body) return r ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1643482333.0 requests-oauthlib-1.3.1/requests_oauthlib/oauth1_session.py0000644000175100001710000004123700000000000023623 0ustar00runnerdockerfrom __future__ import unicode_literals try: from urlparse import urlparse except ImportError: from urllib.parse import urlparse import logging from oauthlib.common import add_params_to_uri from oauthlib.common import urldecode as _urldecode from oauthlib.oauth1 import SIGNATURE_HMAC, SIGNATURE_RSA, SIGNATURE_TYPE_AUTH_HEADER import requests from . import OAuth1 log = logging.getLogger(__name__) def urldecode(body): """Parse query or json to python dictionary""" try: return _urldecode(body) except Exception: import json return json.loads(body) class TokenRequestDenied(ValueError): def __init__(self, message, response): super(TokenRequestDenied, self).__init__(message) self.response = response @property def status_code(self): """For backwards-compatibility purposes""" return self.response.status_code class TokenMissing(ValueError): def __init__(self, message, response): super(TokenMissing, self).__init__(message) self.response = response class VerifierMissing(ValueError): pass class OAuth1Session(requests.Session): """Request signing and convenience methods for the oauth dance. What is the difference between OAuth1Session and OAuth1? OAuth1Session actually uses OAuth1 internally and its purpose is to assist in the OAuth workflow through convenience methods to prepare authorization URLs and parse the various token and redirection responses. It also provide rudimentary validation of responses. An example of the OAuth workflow using a basic CLI app and Twitter. >>> # Credentials obtained during the registration. >>> client_key = 'client key' >>> client_secret = 'secret' >>> callback_uri = 'https://127.0.0.1/callback' >>> >>> # Endpoints found in the OAuth provider API documentation >>> request_token_url = 'https://api.twitter.com/oauth/request_token' >>> authorization_url = 'https://api.twitter.com/oauth/authorize' >>> access_token_url = 'https://api.twitter.com/oauth/access_token' >>> >>> oauth_session = OAuth1Session(client_key,client_secret=client_secret, callback_uri=callback_uri) >>> >>> # First step, fetch the request token. >>> oauth_session.fetch_request_token(request_token_url) { 'oauth_token': 'kjerht2309u', 'oauth_token_secret': 'lsdajfh923874', } >>> >>> # Second step. Follow this link and authorize >>> oauth_session.authorization_url(authorization_url) '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.') >>> oauth_session.parse_authorization_response(redirect_response) { 'oauth_token: 'kjerht2309u', 'oauth_token_secret: 'lsdajfh923874', 'oauth_verifier: 'w34o8967345', } >>> oauth_session.fetch_access_token(access_token_url) { 'oauth_token': 'sdf0o9823sjdfsdf', 'oauth_token_secret': '2kjshdfp92i34asdasd', } >>> # Done. You can now make OAuth requests. >>> status_url = 'http://api.twitter.com/1/statuses/update.json' >>> new_status = {'status': 'hello world!'} >>> oauth_session.post(status_url, data=new_status) """ def __init__( self, client_key, client_secret=None, resource_owner_key=None, resource_owner_secret=None, callback_uri=None, signature_method=SIGNATURE_HMAC, signature_type=SIGNATURE_TYPE_AUTH_HEADER, rsa_key=None, verifier=None, client_class=None, force_include_body=False, **kwargs ): """Construct the OAuth 1 session. :param client_key: A client specific identifier. :param client_secret: A client specific secret used to create HMAC and plaintext signatures. :param resource_owner_key: A resource owner key, also referred to as request token or access token depending on when in the workflow it is used. :param resource_owner_secret: A resource owner secret obtained with either a request or access token. Often referred to as token secret. :param callback_uri: The URL the user is redirect back to after authorization. :param signature_method: Signature methods determine how the OAuth signature is created. The three options are oauthlib.oauth1.SIGNATURE_HMAC (default), oauthlib.oauth1.SIGNATURE_RSA and oauthlib.oauth1.SIGNATURE_PLAIN. :param signature_type: Signature type decides where the OAuth parameters are added. Either in the Authorization header (default) or to the URL query parameters or the request body. Defined as oauthlib.oauth1.SIGNATURE_TYPE_AUTH_HEADER, oauthlib.oauth1.SIGNATURE_TYPE_QUERY and oauthlib.oauth1.SIGNATURE_TYPE_BODY respectively. :param rsa_key: The private RSA key as a string. Can only be used with signature_method=oauthlib.oauth1.SIGNATURE_RSA. :param verifier: A verifier string to prove authorization was granted. :param client_class: A subclass of `oauthlib.oauth1.Client` to use with `requests_oauthlib.OAuth1` instead of the default :param force_include_body: Always include the request body in the signature creation. :param **kwargs: Additional keyword arguments passed to `OAuth1` """ super(OAuth1Session, self).__init__() self._client = OAuth1( client_key, client_secret=client_secret, resource_owner_key=resource_owner_key, resource_owner_secret=resource_owner_secret, callback_uri=callback_uri, signature_method=signature_method, signature_type=signature_type, rsa_key=rsa_key, verifier=verifier, client_class=client_class, force_include_body=force_include_body, **kwargs ) self.auth = self._client @property def token(self): oauth_token = self._client.client.resource_owner_key oauth_token_secret = self._client.client.resource_owner_secret oauth_verifier = self._client.client.verifier token_dict = {} if oauth_token: token_dict["oauth_token"] = oauth_token if oauth_token_secret: token_dict["oauth_token_secret"] = oauth_token_secret if oauth_verifier: token_dict["oauth_verifier"] = oauth_verifier return token_dict @token.setter def token(self, value): self._populate_attributes(value) @property def authorized(self): """Boolean that indicates whether this session has an OAuth token or not. If `self.authorized` is True, you can reasonably expect OAuth-protected requests to the resource to succeed. If `self.authorized` is False, you need the user to go through the OAuth authentication dance before OAuth-protected requests to the resource will succeed. """ if self._client.client.signature_method == SIGNATURE_RSA: # RSA only uses resource_owner_key return bool(self._client.client.resource_owner_key) else: # other methods of authentication use all three pieces return ( bool(self._client.client.client_secret) and bool(self._client.client.resource_owner_key) and bool(self._client.client.resource_owner_secret) ) def authorization_url(self, url, request_token=None, **kwargs): """Create an authorization URL by appending request_token and optional kwargs to url. This is the second step in the OAuth 1 workflow. The user should be redirected to this authorization URL, grant access to you, and then be redirected back to you. The redirection back can either be specified during client registration or by supplying a callback URI per request. :param url: The authorization endpoint URL. :param request_token: The previously obtained request token. :param kwargs: Optional parameters to append to the URL. :returns: The authorization URL with new parameters embedded. An example using a registered default callback URI. >>> request_token_url = 'https://api.twitter.com/oauth/request_token' >>> authorization_url = 'https://api.twitter.com/oauth/authorize' >>> oauth_session = OAuth1Session('client-key', client_secret='secret') >>> oauth_session.fetch_request_token(request_token_url) { 'oauth_token': 'sdf0o9823sjdfsdf', 'oauth_token_secret': '2kjshdfp92i34asdasd', } >>> oauth_session.authorization_url(authorization_url) 'https://api.twitter.com/oauth/authorize?oauth_token=sdf0o9823sjdfsdf' >>> oauth_session.authorization_url(authorization_url, foo='bar') 'https://api.twitter.com/oauth/authorize?oauth_token=sdf0o9823sjdfsdf&foo=bar' An example using an explicit callback URI. >>> request_token_url = 'https://api.twitter.com/oauth/request_token' >>> authorization_url = 'https://api.twitter.com/oauth/authorize' >>> oauth_session = OAuth1Session('client-key', client_secret='secret', callback_uri='https://127.0.0.1/callback') >>> oauth_session.fetch_request_token(request_token_url) { 'oauth_token': 'sdf0o9823sjdfsdf', 'oauth_token_secret': '2kjshdfp92i34asdasd', } >>> oauth_session.authorization_url(authorization_url) 'https://api.twitter.com/oauth/authorize?oauth_token=sdf0o9823sjdfsdf&oauth_callback=https%3A%2F%2F127.0.0.1%2Fcallback' """ kwargs["oauth_token"] = request_token or self._client.client.resource_owner_key log.debug("Adding parameters %s to url %s", kwargs, url) return add_params_to_uri(url, kwargs.items()) def fetch_request_token(self, url, realm=None, **request_kwargs): r"""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 parsed from the application/x-www-form-urlencoded response and ready to be used to construct an authorization url. :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'' function in ''requests.Session'' :returns: The response in dict format. Note that a previously set callback_uri will be reset for your convenience, or else signature creation will be incorrect on consecutive requests. >>> request_token_url = 'https://api.twitter.com/oauth/request_token' >>> oauth_session = OAuth1Session('client-key', client_secret='secret') >>> oauth_session.fetch_request_token(request_token_url) { 'oauth_token': 'sdf0o9823sjdfsdf', 'oauth_token_secret': '2kjshdfp92i34asdasd', } """ self._client.client.realm = " ".join(realm) if realm else None token = self._fetch_token(url, **request_kwargs) log.debug("Resetting callback_uri and realm (not needed in next phase).") self._client.client.callback_uri = None self._client.client.realm = None return token def fetch_access_token(self, url, verifier=None, **request_kwargs): """Fetch an access token. This is the final step in the OAuth 1 workflow. An access token is obtained using all previously obtained credentials, including the verifier from the authorization step. Note that a previously set verifier will be reset for your convenience, or else signature creation will be incorrect on consecutive requests. >>> access_token_url = 'https://api.twitter.com/oauth/access_token' >>> redirect_response = 'https://127.0.0.1/callback?oauth_token=kjerht2309uf&oauth_token_secret=lsdajfh923874&oauth_verifier=w34o8967345' >>> oauth_session = OAuth1Session('client-key', client_secret='secret') >>> oauth_session.parse_authorization_response(redirect_response) { 'oauth_token: 'kjerht2309u', 'oauth_token_secret: 'lsdajfh923874', 'oauth_verifier: 'w34o8967345', } >>> oauth_session.fetch_access_token(access_token_url) { 'oauth_token': 'sdf0o9823sjdfsdf', 'oauth_token_secret': '2kjshdfp92i34asdasd', } """ if verifier: self._client.client.verifier = verifier if not getattr(self._client.client, "verifier", None): raise VerifierMissing("No client verifier has been set.") token = self._fetch_token(url, **request_kwargs) log.debug("Resetting verifier attribute, should not be used anymore.") self._client.client.verifier = None return token def parse_authorization_response(self, url): """Extract parameters from the post authorization redirect response URL. :param url: The full URL that resulted from the user being redirected back from the OAuth provider to you, the client. :returns: A dict of parameters extracted from the URL. >>> redirect_response = 'https://127.0.0.1/callback?oauth_token=kjerht2309uf&oauth_token_secret=lsdajfh923874&oauth_verifier=w34o8967345' >>> oauth_session = OAuth1Session('client-key', client_secret='secret') >>> oauth_session.parse_authorization_response(redirect_response) { 'oauth_token: 'kjerht2309u', 'oauth_token_secret: 'lsdajfh923874', 'oauth_verifier: 'w34o8967345', } """ log.debug("Parsing token from query part of url %s", url) token = dict(urldecode(urlparse(url).query)) log.debug("Updating internal client token attribute.") self._populate_attributes(token) self.token = token return token def _populate_attributes(self, token): if "oauth_token" in token: self._client.client.resource_owner_key = token["oauth_token"] else: raise TokenMissing( "Response does not contain a token: {resp}".format(resp=token), token ) if "oauth_token_secret" in token: self._client.client.resource_owner_secret = token["oauth_token_secret"] if "oauth_verifier" in token: self._client.client.verifier = token["oauth_verifier"] def _fetch_token(self, url, **request_kwargs): log.debug("Fetching token from %s using client %s", url, self._client.client) r = self.post(url, **request_kwargs) if r.status_code >= 400: error = "Token request failed with code %s, response was '%s'." raise TokenRequestDenied(error % (r.status_code, r.text), r) log.debug('Decoding token from response "%s"', r.text) try: token = dict(urldecode(r.text.strip())) except ValueError as e: error = ( "Unable to decode token from token response. " "This is commonly caused by an unsuccessful request where" " a non urlencoded error message is returned. " "The decoding error was %s" "" % e ) raise ValueError(error) log.debug("Obtained token %s", token) log.debug("Updating internal client attributes from token data.") self._populate_attributes(token) self.token = token return token def rebuild_auth(self, prepared_request, response): """ When being redirected we should always strip Authorization header, since nonce may not be reused as per OAuth spec. """ if "Authorization" in prepared_request.headers: # If we get redirected to a new host, we should strip out # any authentication headers. prepared_request.headers.pop("Authorization", True) prepared_request.prepare_auth(self.auth) return ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1643482333.0 requests-oauthlib-1.3.1/requests_oauthlib/oauth2_auth.py0000644000175100001710000000301400000000000023071 0ustar00runnerdockerfrom __future__ import unicode_literals from oauthlib.oauth2 import WebApplicationClient, InsecureTransportError from oauthlib.oauth2 import is_secure_transport from requests.auth import AuthBase class OAuth2(AuthBase): """Adds proof of authorization (OAuth2 token) to the request.""" def __init__(self, client_id=None, client=None, token=None): """Construct a new OAuth 2 authorization object. :param client_id: Client id obtained during registration :param client: :class:`oauthlib.oauth2.Client` to be used. Default is WebApplicationClient which is useful for any hosted application but not mobile or desktop. :param token: Token dictionary, must include access_token and token_type. """ self._client = client or WebApplicationClient(client_id, token=token) if token: for k, v in token.items(): setattr(self._client, k, v) def __call__(self, r): """Append an OAuth 2 token to the request. Note that currently HTTPS is required for all requests. There may be a token type that allows for plain HTTP in the future and then this should be updated to allow plain HTTP on a white list basis. """ if not is_secure_transport(r.url): raise InsecureTransportError() r.url, r.headers, r.body = self._client.add_token( r.url, http_method=r.method, body=r.body, headers=r.headers ) return r ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1643482333.0 requests-oauthlib-1.3.1/requests_oauthlib/oauth2_session.py0000644000175100001710000005275000000000000023626 0ustar00runnerdockerfrom __future__ import unicode_literals import logging from oauthlib.common import generate_token, urldecode from oauthlib.oauth2 import WebApplicationClient, InsecureTransportError from oauthlib.oauth2 import LegacyApplicationClient from oauthlib.oauth2 import TokenExpiredError, is_secure_transport import requests log = logging.getLogger(__name__) class TokenUpdated(Warning): def __init__(self, token): super(TokenUpdated, self).__init__() self.token = token class OAuth2Session(requests.Session): """Versatile OAuth 2 extension to :class:`requests.Session`. Supports any grant type adhering to :class:`oauthlib.oauth2.Client` spec including the four core OAuth 2 grants. Can be used to create authorization urls, fetch tokens and access protected resources using the :class:`requests.Session` interface you are used to. - :class:`oauthlib.oauth2.WebApplicationClient` (default): Authorization Code Grant - :class:`oauthlib.oauth2.MobileApplicationClient`: Implicit Grant - :class:`oauthlib.oauth2.LegacyApplicationClient`: Password Credentials Grant - :class:`oauthlib.oauth2.BackendApplicationClient`: Client Credentials Grant Note that the only time you will be using Implicit Grant from python is if you are driving a user agent able to obtain URL fragments. """ def __init__( self, client_id=None, client=None, auto_refresh_url=None, auto_refresh_kwargs=None, scope=None, redirect_uri=None, token=None, state=None, token_updater=None, **kwargs ): """Construct a new OAuth 2 client session. :param client_id: Client id obtained during registration :param client: :class:`oauthlib.oauth2.Client` to be used. Default is WebApplicationClient which is useful for any hosted application but not mobile or desktop. :param scope: List of scopes you wish to request access to :param redirect_uri: Redirect URI you registered as callback :param token: Token dictionary, must include access_token and token_type. :param state: State string used to prevent CSRF. This will be given when creating the authorization url and must be supplied when parsing the authorization response. Can be either a string or a no argument callable. :auto_refresh_url: Refresh token endpoint URL, must be HTTPS. Supply this if you wish the client to automatically refresh your access tokens. :auto_refresh_kwargs: Extra arguments to pass to the refresh token endpoint. :token_updater: Method with one argument, token, to be used to update your token database on automatic token refresh. If not set a TokenUpdated warning will be raised when a token has been refreshed. This warning will carry the token in its token argument. :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.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 # Ensure that requests doesn't do any automatic auth. See #278. # The default behavior can be re-enabled by setting auth to None. self.auth = lambda r: r # Allow customizations for non compliant providers through various # hooks to adjust requests and responses. self.compliance_hook = { "access_token_response": set(), "refresh_token_response": set(), "protected_request": set(), } def new_state(self): """Generates a state string to be used in authorizations.""" try: self._state = self.state() log.debug("Generated new state %s.", self._state) except TypeError: self._state = self.state log.debug("Re-using previously supplied state %s.", self._state) return self._state @property def client_id(self): return getattr(self._client, "client_id", None) @client_id.setter def client_id(self, value): self._client.client_id = value @client_id.deleter def client_id(self): del self._client.client_id @property def token(self): return getattr(self._client, "token", None) @token.setter def token(self, value): self._client.token = value self._client.populate_token_attributes(value) @property def access_token(self): return getattr(self._client, "access_token", None) @access_token.setter def access_token(self, value): self._client.access_token = value @access_token.deleter def access_token(self): del self._client.access_token @property def authorized(self): """Boolean that indicates whether this session has an OAuth token or not. If `self.authorized` is True, you can reasonably expect OAuth-protected requests to the resource to succeed. If `self.authorized` is False, you need the user to go through the OAuth authentication dance before OAuth-protected requests to the resource will succeed. """ return bool(self.access_token) def authorization_url(self, url, state=None, **kwargs): """Form an authorization URL. :param url: Authorization endpoint url, must be HTTPS. :param state: An optional state string for CSRF protection. If not given it will be generated for you. :param kwargs: Extra parameters to include. :return: authorization_url, state """ state = state or self.new_state() return ( self._client.prepare_request_uri( url, redirect_uri=self.redirect_uri, scope=self.scope, state=state, **kwargs ), state, ) def fetch_token( self, token_url, code=None, authorization_response=None, body="", auth=None, username=None, password=None, method="POST", force_querystring=False, timeout=None, headers=None, verify=True, proxies=None, include_client_id=None, client_secret=None, cert=None, **kwargs ): """Generic method for fetching an access token from the token endpoint. If you are using the MobileApplicationClient you will want to use `token_from_fragment` instead of `fetch_token`. The current implementation enforces the RFC guidelines. :param token_url: Token endpoint URL, must use HTTPS. :param code: Authorization code (used by WebApplicationClients). :param authorization_response: Authorization response URL, the callback URL of the request back to you. Used by WebApplicationClients instead of code. :param body: Optional application/x-www-form-urlencoded body to add the include in the token request. Prefer kwargs over body. :param auth: An auth tuple or method as accepted by `requests`. :param username: Username required by LegacyApplicationClients to appear in the request body. :param password: Password required by LegacyApplicationClients to appear in the request body. :param method: The HTTP method used to make the request. Defaults to POST, but may also be GET. Other methods should be added as needed. :param force_querystring: If True, force the request body to be sent in the querystring instead. :param timeout: Timeout of the request in seconds. :param headers: Dict to default request headers with. :param verify: Verify SSL certificate. :param proxies: The `proxies` argument is passed onto `requests`. :param include_client_id: Should the request body include the `client_id` parameter. Default is `None`, which will attempt to autodetect. This can be forced to always include (True) or never include (False). :param client_secret: The `client_secret` paired to the `client_id`. This is generally required unless provided in the `auth` tuple. If the value is `None`, it will be omitted from the request, however if the value is an empty string, an empty string will be sent. :param cert: Client certificate to send for OAuth 2.0 Mutual-TLS Client Authentication (draft-ietf-oauth-mtls). Can either be the path of a file containing the private key and certificate or a tuple of two filenames for certificate and key. :param kwargs: Extra parameters to include in the token request. :return: A token dict """ if not is_secure_transport(token_url): raise InsecureTransportError() if not code and authorization_response: self._client.parse_request_uri_response( authorization_response, state=self._state ) code = self._client.code elif not code and isinstance(self._client, WebApplicationClient): code = self._client.code if not code: raise ValueError( "Please supply either code or " "authorization_response parameters." ) # 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. # If an upstream server is not spec compliant and requires them to # appear as an Authorization header, supply an explicit `auth` header # to this function. # This check will allow for empty strings, but not `None`. # # References # 4.3.2 - Resource Owner Password Credentials Grant # https://tools.ietf.org/html/rfc6749#section-4.3.2 if isinstance(self._client, LegacyApplicationClient): if username is None: raise ValueError( "`LegacyApplicationClient` requires both the " "`username` and `password` parameters." ) if password is None: raise ValueError( "The required parameter `username` was supplied, " "but `password` was not." ) # merge username and password into kwargs for `prepare_request_body` if username is not None: kwargs["username"] = username if password is not None: kwargs["password"] = password # is an auth explicitly supplied? if auth is not None: # if we're dealing with the default of `include_client_id` (None): # we will assume the `auth` argument is for an RFC compliant server # and we should not send the `client_id` in the body. # This approach allows us to still force the client_id by submitting # `include_client_id=True` along with an `auth` object. if include_client_id is None: include_client_id = False # otherwise we may need to create an auth header else: # since we don't have an auth header, we MAY need to create one # it is possible that we want to send the `client_id` in the body # if so, `include_client_id` should be set to True # otherwise, we will generate an auth header if include_client_id is not True: client_id = self.client_id if client_id: log.debug( 'Encoding `client_id` "%s" with `client_secret` ' "as Basic auth credentials.", client_id, ) client_secret = client_secret if client_secret is not None else "" auth = requests.auth.HTTPBasicAuth(client_id, client_secret) if include_client_id: # this was pulled out of the params # it needs to be passed into prepare_request_body if client_secret is not None: kwargs["client_secret"] = client_secret body = self._client.prepare_request_body( code=code, body=body, redirect_uri=self.redirect_uri, include_client_id=include_client_id, **kwargs ) headers = headers or { "Accept": "application/json", "Content-Type": "application/x-www-form-urlencoded;charset=UTF-8", } self.token = {} request_kwargs = {} if method.upper() == "POST": request_kwargs["params" if force_querystring else "data"] = dict( urldecode(body) ) elif method.upper() == "GET": request_kwargs["params"] = dict(urldecode(body)) else: raise ValueError("The method kwarg must be POST or GET.") r = self.request( method=method, url=token_url, timeout=timeout, headers=headers, auth=auth, verify=verify, proxies=proxies, cert=cert, **request_kwargs ) log.debug("Request to fetch token completed with status %s.", r.status_code) log.debug("Request url was %s", r.request.url) log.debug("Request headers were %s", r.request.headers) log.debug("Request body was %s", r.request.body) log.debug("Response headers were %s and content %s.", r.headers, r.text) log.debug( "Invoking %d token response hooks.", len(self.compliance_hook["access_token_response"]), ) for hook in self.compliance_hook["access_token_response"]: log.debug("Invoking hook %s.", hook) r = hook(r) self._client.parse_request_body_response(r.text, scope=self.scope) self.token = self._client.token log.debug("Obtained token %s.", self.token) return self.token def token_from_fragment(self, authorization_response): """Parse token from the URI fragment, used by MobileApplicationClients. :param authorization_response: The full URL of the redirect back to you :return: A token dict """ self._client.parse_request_uri_response( authorization_response, state=self._state ) self.token = self._client.token return self.token def refresh_token( self, token_url, refresh_token=None, body="", auth=None, timeout=None, headers=None, verify=True, proxies=None, **kwargs ): """Fetch a new access token using a refresh token. :param token_url: The token endpoint, must be HTTPS. :param refresh_token: The refresh_token to use. :param body: Optional application/x-www-form-urlencoded body to add the include in the token request. Prefer kwargs over body. :param auth: An auth tuple or method as accepted by `requests`. :param timeout: Timeout of the request in seconds. :param headers: A dict of headers to be used by `requests`. :param verify: Verify SSL certificate. :param proxies: The `proxies` argument will be passed to `requests`. :param kwargs: Extra parameters to include in the token request. :return: A token dict """ if not token_url: raise ValueError("No token endpoint set for auto_refresh.") if not is_secure_transport(token_url): raise InsecureTransportError() refresh_token = refresh_token or self.token.get("refresh_token") log.debug( "Adding auto refresh key word arguments %s.", self.auto_refresh_kwargs ) kwargs.update(self.auto_refresh_kwargs) body = self._client.prepare_refresh_body( body=body, refresh_token=refresh_token, scope=self.scope, **kwargs ) log.debug("Prepared refresh token request body %s", body) if headers is None: headers = { "Accept": "application/json", "Content-Type": ("application/x-www-form-urlencoded;charset=UTF-8"), } r = self.post( token_url, data=dict(urldecode(body)), auth=auth, timeout=timeout, headers=headers, verify=verify, withhold_token=True, proxies=proxies, ) log.debug("Request to refresh token completed with status %s.", r.status_code) log.debug("Response headers were %s and content %s.", r.headers, r.text) log.debug( "Invoking %d token response hooks.", len(self.compliance_hook["refresh_token_response"]), ) for hook in self.compliance_hook["refresh_token_response"]: log.debug("Invoking hook %s.", hook) r = hook(r) self.token = self._client.parse_request_body_response(r.text, scope=self.scope) if not "refresh_token" in self.token: log.debug("No new refresh token given. Re-using old.") self.token["refresh_token"] = refresh_token return self.token def request( self, method, url, data=None, headers=None, withhold_token=False, client_id=None, client_secret=None, **kwargs ): """Intercept all requests and add the OAuth 2 token if present.""" if not is_secure_transport(url): raise InsecureTransportError() if self.token and not withhold_token: log.debug( "Invoking %d protected resource request hooks.", len(self.compliance_hook["protected_request"]), ) for hook in self.compliance_hook["protected_request"]: log.debug("Invoking hook %s.", hook) url, headers, data = hook(url, headers, data) log.debug("Adding token %s to request.", self.token) try: url, headers, data = self._client.add_token( url, http_method=method, body=data, headers=headers ) # Attempt to retrieve and save new access token if expired except TokenExpiredError: if self.auto_refresh_url: log.debug( "Auto refresh is set, attempting to refresh at %s.", self.auto_refresh_url, ) # We mustn't pass auth twice. auth = kwargs.pop("auth", None) if client_id and client_secret and (auth is None): log.debug( 'Encoding client_id "%s" with client_secret as Basic auth credentials.', client_id, ) auth = requests.auth.HTTPBasicAuth(client_id, client_secret) token = self.refresh_token( self.auto_refresh_url, auth=auth, **kwargs ) if self.token_updater: log.debug( "Updating token to %s using %s.", token, self.token_updater ) self.token_updater(token) url, headers, data = self._client.add_token( url, http_method=method, body=data, headers=headers ) else: raise TokenUpdated(token) else: raise log.debug("Requesting url %s using method %s.", url, method) 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 ) def register_compliance_hook(self, hook_type, hook): """Register a hook for request/response tweaking. Available hooks are: access_token_response invoked before token parsing. refresh_token_response invoked before refresh token parsing. protected_request invoked before making a request. If you find a new hook is needed please send a GitHub PR request or open an issue. """ if hook_type not in self.compliance_hook: raise ValueError( "Hook type %s is not in %s.", hook_type, self.compliance_hook ) self.compliance_hook[hook_type].add(hook) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1643482341.5994914 requests-oauthlib-1.3.1/requests_oauthlib.egg-info/0000755000175100001710000000000000000000000021770 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1643482341.0 requests-oauthlib-1.3.1/requests_oauthlib.egg-info/PKG-INFO0000644000175100001710000002336500000000000023076 0ustar00runnerdockerMetadata-Version: 2.1 Name: requests-oauthlib Version: 1.3.1 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 Classifier: License :: OSI Approved :: BSD License Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.4 Classifier: Programming Language :: Python :: 3.5 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 :: 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 Provides-Extra: rsa License-File: LICENSE Requests-OAuthlib |build-status| |coverage-status| |docs| ========================================================= This project provides first-class OAuth library support for `Requests `_. The OAuth 1 workflow -------------------- OAuth 1 can seem overly complicated and it sure has its quirks. Luckily, requests_oauthlib hides most of these and let you focus at the task at hand. Accessing protected resources using requests_oauthlib is as simple as: .. code-block:: pycon >>> from requests_oauthlib import OAuth1Session >>> twitter = OAuth1Session('client_key', client_secret='client_secret', resource_owner_key='resource_owner_key', resource_owner_secret='resource_owner_secret') >>> url = 'https://api.twitter.com/1/account/settings.json' >>> r = twitter.get(url) Before accessing resources you will need to obtain a few credentials from your provider (e.g. Twitter) and authorization from the user for whom you wish to retrieve resources for. You can read all about this in the full `OAuth 1 workflow guide on RTD `_. The OAuth 2 workflow -------------------- OAuth 2 is generally simpler than OAuth 1 but comes in more flavours. The most common being the Authorization Code Grant, also known as the WebApplication flow. Fetching a protected resource after obtaining an access token can be extremely simple. However, before accessing resources you will need to obtain a few credentials from your provider (e.g. Google) and authorization from the user for whom you wish to retrieve resources for. You can read all about this in the full `OAuth 2 workflow guide on RTD `_. Installation ------------- To install requests and requests_oauthlib you can use pip: .. code-block:: bash $ 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 .. |coverage-status| image:: https://img.shields.io/coveralls/requests/requests-oauthlib.svg :target: https://coveralls.io/r/requests/requests-oauthlib .. |docs| image:: https://readthedocs.org/projects/requests-oauthlib/badge/ :alt: Documentation Status :scale: 100% :target: https://requests-oauthlib.readthedocs.io/ History ------- v1.3.1 (21 January 2022) ++++++++++++++++++++++++ - Add initial support for OAuth Mutual TLS (draft-ietf-oauth-mtls) - Add eBay compliance fix - Add Spotify OAuth 2 Tutorial - Add support for python 3.8, 3.9 - Fixed LinkedIn Compliance Fixes - Fixed ReadTheDocs Documentation and sphinx errors - Moved pipeline to GitHub Actions v1.3.0 (6 November 2019) ++++++++++++++++++++++++ - Instagram compliance fix - Added ``force_querystring`` argument to fetch_token() method on OAuth2Session v1.2.0 (14 January 2019) ++++++++++++++++++++++++ - This project now depends on OAuthlib 3.0.0 and above. It does **not** support versions of OAuthlib before 3.0.0. - Updated oauth2 tests to use 'sess' for an OAuth2Session instance instead of `auth` because OAuth2Session objects and methods acceept an `auth` paramether which is typically an instance of `requests.auth.HTTPBasicAuth` - `OAuth2Session.fetch_token` previously tried to guess how and where to provide "client" and "user" credentials incorrectly. This was incompatible with some OAuth servers and incompatible with breaking changes in oauthlib that seek to correctly provide the `client_id`. The older implementation also did not raise the correct exceptions when username and password are not present on Legacy clients. - Avoid automatic netrc authentication for OAuth2Session. v1.1.0 (9 January 2019) +++++++++++++++++++++++ - Adjusted version specifier for ``oauthlib`` dependency: this project is not yet compatible with ``oauthlib`` 3.0.0. - Dropped dependency on ``nose``. - Minor changes to clean up the code and make it more readable/maintainable. v1.0.0 (4 June 2018) ++++++++++++++++++++ - **Removed support for Python 2.6 and Python 3.3.** This project now supports Python 2.7, and Python 3.4 and above. - Added several examples to the documentation. - Added plentymarkets compliance fix. - Added a ``token`` property to OAuth1Session, to match the corresponding ``token`` property on OAuth2Session. v0.8.0 (14 February 2017) +++++++++++++++++++++++++ - Added Fitbit compliance fix. - Fixed an issue where newlines in the response body for the access token request would cause errors when trying to extract the token. - Fixed an issue introduced in v0.7.0 where users passing ``auth`` to several methods would encounter conflicts with the ``client_id`` and ``client_secret``-derived auth. The user-supplied ``auth`` argument is now used in preference to those options. v0.7.0 (22 September 2016) ++++++++++++++++++++++++++ - Allowed ``OAuth2Session.request`` to take the ``client_id`` and ``client_secret`` parameters for the purposes of automatic token refresh, which may need them. v0.6.2 (12 July 2016) +++++++++++++++++++++ - Use ``client_id`` and ``client_secret`` for the Authorization header if provided. - Allow explicit bypass of the Authorization header by setting ``auth=False``. - Pass through the ``proxies`` kwarg when refreshing tokens. - Miscellaneous cleanups. v0.6.1 (19 February 2016) +++++++++++++++++++++++++ - Fixed a bug when sending authorization in headers with no username and password present. - Make sure we clear the session token before obtaining a new one. - Some improvements to the Slack compliance fix. - Avoid timing problems around token refresh. - Allow passing arbitrary arguments to requests when calling ``fetch_request_token`` and ``fetch_access_token``. v0.6.0 (14 December 2015) +++++++++++++++++++++++++ - Add compliance fix for Slack. - Add compliance fix for Mailchimp. - ``TokenRequestDenied`` exceptions now carry the entire response, not just the status code. - Pass through keyword arguments when refreshing tokens automatically. - Send authorization in headers, not just body, to maximize compatibility. - More getters/setters available for OAuth2 session client values. - Allow sending custom headers when refreshing tokens, and set some defaults. v0.5.0 (4 May 2015) +++++++++++++++++++ - Fix ``TypeError`` being raised instead of ``TokenMissing`` error. - Raise requests exceptions on 4XX and 5XX responses in the OAuth2 flow. - Avoid ``AttributeError`` when initializing the ``OAuth2Session`` class without complete client information. v0.4.2 (16 October 2014) ++++++++++++++++++++++++ - New ``authorized`` property on OAuth1Session and OAuth2Session, which allows you to easily determine if the session is already authorized with OAuth tokens or not. - New ``TokenMissing`` and ``VerifierMissing`` exception classes for OAuth1Session: this will make it easier to catch and identify these exceptions. v0.4.1 (6 June 2014) ++++++++++++++++++++ - New install target ``[rsa]`` for people using OAuth1 RSA-SHA1 signature method. - Fixed bug in OAuth2 where supplied state param was not used in auth url. - OAuth2 HTTPS checking can be disabled by setting environment variable ``OAUTHLIB_INSECURE_TRANSPORT``. - OAuth1 now re-authorize upon redirects. - OAuth1 token fetching now raise a detailed error message when the response body is incorrectly encoded or the request was denied. - Added support for custom OAuth1 clients. - OAuth2 compliance fix for Sina Weibo. - Multiple fixes to facebook compliance fix. - Compliance fixes now re-encode body properly as bytes in Python 3. - Logging now properly done under ``requests_oauthlib`` namespace instead of piggybacking on oauthlib namespace. - Logging introduced for OAuth1 auth and session. v0.4.0 (29 September 2013) ++++++++++++++++++++++++++ - OAuth1Session methods only return unicode strings. #55. - Renamed requests_oauthlib.core to requests_oauthlib.oauth1_auth for consistency. #79. - Added Facebook compliance fix and access_token_response hook to OAuth2Session. #63. - Added LinkedIn compliance fix. - Added refresh_token_response compliance hook, invoked before parsing the refresh token. - Correctly limit compliance hooks to running only once! - Content type guessing should only be done when no content type is given - 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 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1643482341.0 requests-oauthlib-1.3.1/requests_oauthlib.egg-info/SOURCES.txt0000644000175100001710000000315300000000000023656 0ustar00runnerdockerAUTHORS.rst HISTORY.rst LICENSE MANIFEST.in README.rst requirements.txt setup.cfg setup.py docs/Makefile docs/api.rst docs/conf.py docs/contributing.rst docs/index.rst docs/make.bat docs/oauth1_workflow.rst docs/oauth2_workflow.rst docs/requirements.txt docs/examples/bitbucket.rst docs/examples/examples.rst docs/examples/facebook.rst docs/examples/fitbit.rst docs/examples/github.rst docs/examples/google.rst docs/examples/linkedin.rst docs/examples/outlook.rst docs/examples/real_world_example.rst docs/examples/real_world_example_with_refresh.rst docs/examples/spotify.rst docs/examples/tumblr.rst requests_oauthlib/__init__.py requests_oauthlib/oauth1_auth.py requests_oauthlib/oauth1_session.py requests_oauthlib/oauth2_auth.py requests_oauthlib/oauth2_session.py requests_oauthlib.egg-info/PKG-INFO requests_oauthlib.egg-info/SOURCES.txt requests_oauthlib.egg-info/dependency_links.txt requests_oauthlib.egg-info/not-zip-safe requests_oauthlib.egg-info/requires.txt requests_oauthlib.egg-info/top_level.txt requests_oauthlib/compliance_fixes/__init__.py requests_oauthlib/compliance_fixes/douban.py requests_oauthlib/compliance_fixes/ebay.py requests_oauthlib/compliance_fixes/facebook.py requests_oauthlib/compliance_fixes/fitbit.py requests_oauthlib/compliance_fixes/instagram.py requests_oauthlib/compliance_fixes/mailchimp.py requests_oauthlib/compliance_fixes/plentymarkets.py requests_oauthlib/compliance_fixes/slack.py requests_oauthlib/compliance_fixes/weibo.py tests/__init__.py tests/test.bin tests/test_compliance_fixes.py tests/test_core.py tests/test_oauth1_session.py tests/test_oauth2_auth.py tests/test_oauth2_session.py././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1643482341.0 requests-oauthlib-1.3.1/requests_oauthlib.egg-info/dependency_links.txt0000644000175100001710000000000100000000000026036 0ustar00runnerdocker ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1643482341.0 requests-oauthlib-1.3.1/requests_oauthlib.egg-info/not-zip-safe0000644000175100001710000000000100000000000024216 0ustar00runnerdocker ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1643482341.0 requests-oauthlib-1.3.1/requests_oauthlib.egg-info/requires.txt0000644000175100001710000000010400000000000024363 0ustar00runnerdockeroauthlib>=3.0.0 requests>=2.0.0 [rsa] oauthlib[signedtoken]>=3.0.0 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1643482341.0 requests-oauthlib-1.3.1/requests_oauthlib.egg-info/top_level.txt0000644000175100001710000000002200000000000024514 0ustar00runnerdockerrequests_oauthlib ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1643482333.0 requests-oauthlib-1.3.1/requirements.txt0000644000175100001710000000005600000000000020021 0ustar00runnerdockerrequests==2.26.0 oauthlib[signedtoken]==3.1.1 ././@PaxHeader0000000000000000000000000000003200000000000010210 xustar0026 mtime=1643482341.60749 requests-oauthlib-1.3.1/setup.cfg0000644000175100001710000000014600000000000016356 0ustar00runnerdocker[bdist_wheel] universal = 1 [metadata] license_file = LICENSE [egg_info] tag_build = tag_date = 0 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1643482333.0 requests-oauthlib-1.3.1/setup.py0000644000175100001710000000422200000000000016246 0ustar00runnerdocker#!/usr/bin/env python # -*- coding: utf-8 -*- import os import sys import re from setuptools import setup # Get the version version_regex = r'__version__ = ["\']([^"\']*)["\']' with open("requests_oauthlib/__init__.py", "r") as f: text = f.read() match = re.search(version_regex, text) if match: VERSION = match.group(1) else: raise RuntimeError("No version number found!") APP_NAME = "requests-oauthlib" # Publish Helper. if sys.argv[-1] == "publish": os.system("python setup.py sdist upload") sys.exit() def readall(path): with open(path) as fp: return fp.read() setup( name=APP_NAME, version=VERSION, description="OAuthlib authentication support for Requests.", long_description=readall("README.rst") + "\n\n" + readall("HISTORY.rst"), long_description_content_type="text/x-rst", author="Kenneth Reitz", author_email="me@kennethreitz.com", url="https://github.com/requests/requests-oauthlib", packages=["requests_oauthlib", "requests_oauthlib.compliance_fixes"], python_requires=">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*", install_requires=["oauthlib>=3.0.0", "requests>=2.0.0"], extras_require={"rsa": ["oauthlib[signedtoken]>=3.0.0"]}, license="ISC", classifiers=[ "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "Natural Language :: English", "License :: OSI Approved :: BSD License", "Programming Language :: Python", "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: PyPy", ], zip_safe=False, tests_require=["mock", "requests-mock"], test_suite="tests", ) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1643482341.6034906 requests-oauthlib-1.3.1/tests/0000755000175100001710000000000000000000000015676 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1643482333.0 requests-oauthlib-1.3.1/tests/__init__.py0000644000175100001710000000000000000000000017775 0ustar00runnerdocker././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1643482333.0 requests-oauthlib-1.3.1/tests/test.bin0000644000175100001710000000000200000000000017337 0ustar00runnerdocker¥Æ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1643482333.0 requests-oauthlib-1.3.1/tests/test_compliance_fixes.py0000644000175100001710000003020300000000000022615 0ustar00runnerdockerfrom __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 oauthlib.oauth2.rfc6749.errors import InvalidGrantError from requests_oauthlib import OAuth2Session from requests_oauthlib.compliance_fixes import facebook_compliance_fix from requests_oauthlib.compliance_fixes import fitbit_compliance_fix from requests_oauthlib.compliance_fixes import mailchimp_compliance_fix from requests_oauthlib.compliance_fixes import weibo_compliance_fix from requests_oauthlib.compliance_fixes import slack_compliance_fix from requests_oauthlib.compliance_fixes import instagram_compliance_fix from requests_oauthlib.compliance_fixes import plentymarkets_compliance_fix from requests_oauthlib.compliance_fixes import ebay_compliance_fix class FacebookComplianceFixTest(TestCase): def setUp(self): mocker = requests_mock.Mocker() mocker.post( "https://graph.facebook.com/oauth/access_token", text="access_token=urlencoded", headers={"Content-Type": "text/plain"}, ) mocker.start() self.addCleanup(mocker.stop) facebook = OAuth2Session("someclientid", redirect_uri="https://i.b") self.session = facebook_compliance_fix(facebook) def test_fetch_access_token(self): token = self.session.fetch_token( "https://graph.facebook.com/oauth/access_token", client_secret="someclientsecret", authorization_response="https://i.b/?code=hello", ) self.assertEqual(token, {"access_token": "urlencoded", "token_type": "Bearer"}) class FitbitComplianceFixTest(TestCase): def setUp(self): self.mocker = requests_mock.Mocker() self.mocker.post( "https://api.fitbit.com/oauth2/token", json={"errors": [{"errorType": "invalid_grant"}]}, ) self.mocker.start() self.addCleanup(self.mocker.stop) fitbit = OAuth2Session("someclientid", redirect_uri="https://i.b") self.session = fitbit_compliance_fix(fitbit) def test_fetch_access_token(self): self.assertRaises( InvalidGrantError, self.session.fetch_token, "https://api.fitbit.com/oauth2/token", client_secret="someclientsecret", authorization_response="https://i.b/?code=hello", ) self.mocker.post( "https://api.fitbit.com/oauth2/token", json={"access_token": "fitbit"} ) token = self.session.fetch_token( "https://api.fitbit.com/oauth2/token", client_secret="good" ) self.assertEqual(token, {"access_token": "fitbit"}) def test_refresh_token(self): self.assertRaises( InvalidGrantError, self.session.refresh_token, "https://api.fitbit.com/oauth2/token", auth=requests.auth.HTTPBasicAuth("someclientid", "someclientsecret"), ) self.mocker.post( "https://api.fitbit.com/oauth2/token", json={"access_token": "access", "refresh_token": "refresh"}, ) token = self.session.refresh_token( "https://api.fitbit.com/oauth2/token", auth=requests.auth.HTTPBasicAuth("someclientid", "someclientsecret"), ) self.assertEqual(token["access_token"], "access") self.assertEqual(token["refresh_token"], "refresh") class MailChimpComplianceFixTest(TestCase): def setUp(self): mocker = requests_mock.Mocker() mocker.post( "https://login.mailchimp.com/oauth2/token", json={"access_token": "mailchimp", "expires_in": 0, "scope": None}, ) mocker.start() self.addCleanup(mocker.stop) mailchimp = OAuth2Session("someclientid", redirect_uri="https://i.b") self.session = mailchimp_compliance_fix(mailchimp) def test_fetch_access_token(self): token = self.session.fetch_token( "https://login.mailchimp.com/oauth2/token", client_secret="someclientsecret", authorization_response="https://i.b/?code=hello", ) # Times should be close approx_expires_at = time.time() + 3600 actual_expires_at = token.pop("expires_at") self.assertAlmostEqual(actual_expires_at, approx_expires_at, places=2) # Other token values exact self.assertEqual(token, {"access_token": "mailchimp", "expires_in": 3600}) # And no scope at all self.assertNotIn("scope", token) class WeiboComplianceFixTest(TestCase): def setUp(self): mocker = requests_mock.Mocker() mocker.post( "https://api.weibo.com/oauth2/access_token", json={"access_token": "weibo"} ) mocker.start() self.addCleanup(mocker.stop) weibo = OAuth2Session("someclientid", redirect_uri="https://i.b") self.session = weibo_compliance_fix(weibo) def test_fetch_access_token(self): token = self.session.fetch_token( "https://api.weibo.com/oauth2/access_token", client_secret="someclientsecret", authorization_response="https://i.b/?code=hello", ) self.assertEqual(token, {"access_token": "weibo", "token_type": "Bearer"}) class SlackComplianceFixTest(TestCase): def setUp(self): mocker = requests_mock.Mocker() mocker.post( "https://slack.com/api/oauth.access", json={"access_token": "xoxt-23984754863-2348975623103", "scope": "read"}, ) for method in ("GET", "POST"): mocker.request( method=method, url="https://slack.com/api/auth.test", json={ "ok": True, "url": "https://myteam.slack.com/", "team": "My Team", "user": "cal", "team_id": "T12345", "user_id": "U12345", }, ) mocker.start() self.addCleanup(mocker.stop) slack = OAuth2Session("someclientid", redirect_uri="https://i.b") self.session = slack_compliance_fix(slack) def test_protected_request(self): self.session.token = {"access_token": "dummy-access-token"} response = self.session.get("https://slack.com/api/auth.test") url = response.request.url query = parse_qs(urlparse(url).query) self.assertNotIn("token", query) body = response.request.body data = parse_qs(body) self.assertEqual(data["token"], ["dummy-access-token"]) def test_protected_request_override_token_get(self): self.session.token = {"access_token": "dummy-access-token"} response = self.session.get( "https://slack.com/api/auth.test", data={"token": "different-token"} ) url = response.request.url query = parse_qs(urlparse(url).query) self.assertNotIn("token", query) body = response.request.body data = parse_qs(body) self.assertEqual(data["token"], ["different-token"]) def test_protected_request_override_token_post(self): self.session.token = {"access_token": "dummy-access-token"} response = self.session.post( "https://slack.com/api/auth.test", data={"token": "different-token"} ) url = response.request.url query = parse_qs(urlparse(url).query) self.assertNotIn("token", query) body = response.request.body data = parse_qs(body) self.assertEqual(data["token"], ["different-token"]) def test_protected_request_override_token_url(self): self.session.token = {"access_token": "dummy-access-token"} response = self.session.get( "https://slack.com/api/auth.test?token=different-token" ) url = response.request.url query = parse_qs(urlparse(url).query) self.assertEqual(query["token"], ["different-token"]) self.assertIsNone(response.request.body) class InstagramComplianceFixTest(TestCase): def setUp(self): mocker = requests_mock.Mocker() mocker.request( method="GET", url="https://api.instagram.com/v1/users/self", json={ "data": { "id": "1574083", "username": "snoopdogg", "full_name": "Snoop Dogg", "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_1574083_75sq_1295469061.jpg", "bio": "This is my bio", "website": "http://snoopdogg.com", "is_business": False, "counts": {"media": 1320, "follows": 420, "followed_by": 3410}, } }, ) mocker.start() self.addCleanup(mocker.stop) instagram = OAuth2Session("someclientid", redirect_uri="https://i.b") self.session = instagram_compliance_fix(instagram) def test_protected_request(self): self.session.token = {"access_token": "dummy-access-token"} response = self.session.get("https://api.instagram.com/v1/users/self") url = response.request.url query = parse_qs(urlparse(url).query) self.assertIn("access_token", query) self.assertEqual(query["access_token"], ["dummy-access-token"]) def test_protected_request_dont_override(self): """check that if the access_token param already exist we don't override it""" self.session.token = {"access_token": "dummy-access-token"} response = self.session.get( "https://api.instagram.com/v1/users/self?access_token=correct-access-token" ) url = response.request.url query = parse_qs(urlparse(url).query) self.assertIn("access_token", query) self.assertEqual(query["access_token"], ["correct-access-token"]) class PlentymarketsComplianceFixTest(TestCase): def setUp(self): mocker = requests_mock.Mocker() mocker.post( "https://shop.plentymarkets-cloud02.com", json={ "accessToken": "ecUN1r8KhJewMCdLAmpHOdZ4O0ofXKB9zf6CXK61", "tokenType": "Bearer", "expiresIn": 86400, "refreshToken": "iG2kBGIjcXaRE4xmTVUnv7xwxX7XMcWCHqJmFaSX", }, headers={"Content-Type": "application/json"}, ) mocker.start() self.addCleanup(mocker.stop) plentymarkets = OAuth2Session("someclientid", redirect_uri="https://i.b") self.session = plentymarkets_compliance_fix(plentymarkets) def test_fetch_access_token(self): token = self.session.fetch_token( "https://shop.plentymarkets-cloud02.com", authorization_response="https://i.b/?code=hello", ) approx_expires_at = time.time() + 86400 actual_expires_at = token.pop("expires_at") self.assertAlmostEqual(actual_expires_at, approx_expires_at, places=2) self.assertEqual( token, { "access_token": "ecUN1r8KhJewMCdLAmpHOdZ4O0ofXKB9zf6CXK61", "expires_in": 86400, "token_type": "Bearer", "refresh_token": "iG2kBGIjcXaRE4xmTVUnv7xwxX7XMcWCHqJmFaSX", }, ) class EbayComplianceFixTest(TestCase): def setUp(self): mocker = requests_mock.Mocker() mocker.post( "https://api.ebay.com/identity/v1/oauth2/token", json={ "access_token": "this is the access token", "expires_in": 7200, "token_type": "Application Access Token", }, headers={"Content-Type": "application/json"}, ) mocker.start() self.addCleanup(mocker.stop) session = OAuth2Session() self.fixed_session = ebay_compliance_fix(session) def test_fetch_access_token(self): token = self.fixed_session.fetch_token( "https://api.ebay.com/identity/v1/oauth2/token", authorization_response="https://i.b/?code=hello", ) assert token["token_type"] == "Bearer" ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1643482333.0 requests-oauthlib-1.3.1/tests/test_core.py0000644000175100001710000001353600000000000020247 0ustar00runnerdocker# -*- coding: utf-8 -*- from __future__ import unicode_literals import requests import requests_oauthlib import oauthlib import os.path from io import StringIO import unittest try: import mock except ImportError: from unittest import mock @mock.patch("oauthlib.oauth1.rfc5849.generate_timestamp") @mock.patch("oauthlib.oauth1.rfc5849.generate_nonce") class OAuth1Test(unittest.TestCase): def testFormEncoded(self, generate_nonce, generate_timestamp): """OAuth1 assumes form encoded if content type is not specified.""" generate_nonce.return_value = "abc" generate_timestamp.return_value = "1" oauth = requests_oauthlib.OAuth1("client_key") headers = {"Content-type": "application/x-www-form-urlencoded"} r = requests.Request( method="POST", url="http://a.b/path?query=retain", auth=oauth, data="this=really&is=&+form=encoded", headers=headers, ) a = r.prepare() self.assertEqual(a.url, "http://a.b/path?query=retain") self.assertEqual(a.body, b"this=really&is=&+form=encoded") self.assertEqual( a.headers.get("Content-Type"), b"application/x-www-form-urlencoded" ) # guess content-type r = requests.Request( method="POST", url="http://a.b/path?query=retain", auth=oauth, data="this=really&is=&+form=encoded", ) b = r.prepare() self.assertEqual(b.url, "http://a.b/path?query=retain") self.assertEqual(b.body, b"this=really&is=&+form=encoded") self.assertEqual( b.headers.get("Content-Type"), b"application/x-www-form-urlencoded" ) self.assertEqual(a.headers.get("Authorization"), b.headers.get("Authorization")) def testNonFormEncoded(self, generate_nonce, generate_timestamp): """OAuth signature only depend on body if it is form encoded.""" generate_nonce.return_value = "abc" generate_timestamp.return_value = "1" oauth = requests_oauthlib.OAuth1("client_key") r = requests.Request( method="POST", url="http://a.b/path?query=retain", auth=oauth, data="this really is not form encoded", ) a = r.prepare() r = requests.Request( method="POST", url="http://a.b/path?query=retain", auth=oauth ) b = r.prepare() self.assertEqual(a.headers.get("Authorization"), b.headers.get("Authorization")) r = requests.Request( method="POST", url="http://a.b/path?query=retain", auth=oauth, files={"test": StringIO("hello")}, ) c = r.prepare() self.assertEqual(b.headers.get("Authorization"), c.headers.get("Authorization")) def testCanPostBinaryData(self, generate_nonce, generate_timestamp): """ Test we can post binary data. Should prevent regression of the UnicodeDecodeError issue. """ generate_nonce.return_value = "abc" generate_timestamp.return_value = "1" oauth = requests_oauthlib.OAuth1("client_key") dirname = os.path.dirname(__file__) fname = os.path.join(dirname, "test.bin") with open(fname, "rb") as f: r = requests.post( "http://httpbin.org/post", data={"hi": "there"}, files={"media": (os.path.basename(f.name), f)}, headers={"content-type": "application/octet-stream"}, auth=oauth, ) self.assertEqual(r.status_code, 200) def test_url_is_native_str(self, generate_nonce, generate_timestamp): """ Test that the URL is always a native string. """ generate_nonce.return_value = "abc" generate_timestamp.return_value = "1" oauth = requests_oauthlib.OAuth1("client_key") r = requests.get("http://httpbin.org/get", auth=oauth) self.assertIsInstance(r.request.url, str) def test_content_type_override(self, generate_nonce, generate_timestamp): """ Content type should only be guessed if none is given. """ generate_nonce.return_value = "abc" generate_timestamp.return_value = "1" oauth = requests_oauthlib.OAuth1("client_key") data = "a" r = requests.post("http://httpbin.org/get", data=data, auth=oauth) self.assertEqual( r.request.headers.get("Content-Type"), b"application/x-www-form-urlencoded" ) r = requests.post( "http://httpbin.org/get", auth=oauth, data=data, headers={"Content-type": "application/json"}, ) self.assertEqual(r.request.headers.get("Content-Type"), b"application/json") def test_register_client_class(self, generate_timestamp, generate_nonce): class ClientSubclass(oauthlib.oauth1.Client): pass self.assertTrue(hasattr(requests_oauthlib.OAuth1, "client_class")) self.assertEqual(requests_oauthlib.OAuth1.client_class, oauthlib.oauth1.Client) normal = requests_oauthlib.OAuth1("client_key") self.assertIsInstance(normal.client, oauthlib.oauth1.Client) self.assertNotIsInstance(normal.client, ClientSubclass) requests_oauthlib.OAuth1.client_class = ClientSubclass self.assertEqual(requests_oauthlib.OAuth1.client_class, ClientSubclass) custom = requests_oauthlib.OAuth1("client_key") self.assertIsInstance(custom.client, oauthlib.oauth1.Client) self.assertIsInstance(custom.client, ClientSubclass) overridden = requests_oauthlib.OAuth1( "client_key", client_class=oauthlib.oauth1.Client ) self.assertIsInstance(overridden.client, oauthlib.oauth1.Client) self.assertNotIsInstance(normal.client, ClientSubclass) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1643482333.0 requests-oauthlib-1.3.1/tests/test_oauth1_session.py0000644000175100001710000003526600000000000022267 0ustar00runnerdockerfrom __future__ import unicode_literals, print_function import unittest import sys import requests from io import StringIO 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 try: import jwt except ImportError: jwt = None if sys.version[0] == "3": unicode_type = str else: unicode_type = unicode TEST_RSA_KEY = ( "-----BEGIN RSA PRIVATE KEY-----\n" "MIIEogIBAAKCAQEApF1JaMSN8TEsh4N4O/5SpEAVLivJyLH+Cgl3OQBPGgJkt8cg\n" "49oasl+5iJS+VdrILxWM9/JCJyURpUuslX4Eb4eUBtQ0x5BaPa8+S2NLdGTaL7nB\n" "OO8o8n0C5FEUU+qlEip79KE8aqOj+OC44VsIquSmOvWIQD26n3fCVlgwoRBD1gzz\n" "sDOeaSyzpKrZR851Kh6rEmF2qjJ8jt6EkxMsRNACmBomzgA4M1TTsisSUO87444p\n" "e35Z4/n5c735o2fZMrGgMwiJNh7rT8SYxtIkxngioiGnwkxGQxQ4NzPAHg+XSY0J\n" "04pNm7KqTkgtxyrqOANJLIjXlR+U9SQ90NjHVQIDAQABAoIBABuBPOKaWcJt3yzC\n" "NGGduoif7KtwSnEaUA+v69KPGa2Zju8uFHPssKD+4dZYRc2qMeunKJLpaGaSjnRh\n" "yHyvvOBJCN1nr3lhz6gY5kzJTfwpUFXCOPJlGy4Q+2Xnp4YvcvYqQ9n5DVovDiZ8\n" "vJOBn16xqpudMPLHIa7D5LJ8SY76HBjE+imTXw1EShdh5TOV9bmPFQqH6JFzowRH\n" "hyH2DPHuyHJj6cl8FyqJw5lVWzG3n6Prvk7bYHsjmGjurN35UsumNAp6VouNyUP1\n" "RAEcUJega49aIs6/FJ0ENJzQjlsAzVbTleHkpez2aIok+wsWJGJ4SVxAjADOWAaZ\n" "uEJPc3UCgYEA1g4ZGrXOuo75p9/MRIepXGpBWxip4V7B9XmO9WzPCv8nMorJntWB\n" "msYV1I01aITxadHatO4Gl2xLniNkDyrEQzJ7w38RQgsVK+CqbnC0K9N77QPbHeC1\n" "YQd9RCNyUohOimKvb7jyv798FBU1GO5QI2eNgfnnfteSVXhD2iOoTOsCgYEAxJJ+\n" "8toxJdnLa0uUsAbql6zeNXGbUBMzu3FomKlyuWuq841jS2kIalaO/TRj5hbnE45j\n" "mCjeLgTVO6Ach3Wfk4zrqajqfFJ0zUg/Wexp49lC3RWiV4icBb85Q6bzeJD9Dn9v\n" "hjpfWVkczf/NeA1fGH/pcgfkT6Dm706GFFttLL8CgYBl/HeXk1H47xAiHO4dJKnb\n" "v0B+X8To/RXamF01r+8BpUoOubOQetdyX7ic+d6deuHu8i6LD/GSCeYJZYFR/KVg\n" "AtiW757QYalnq3ZogkhFrVCZP8IRfTPOFBxp752TlyAcrSI7T9pQ47IBe4094KXM\n" "CJWSfPgAJkOxd0iU0XJpmwKBgGfQxuMTgSlwYRKFlD1zKap5TdID8fbUbVnth0Q5\n" "GbH7vwlp/qrxCdS/aj0n0irOpbOaW9ccnlrHiqY25VpVMLYIkt3DrDOEiNNx+KNR\n" "TItdTwbcSiTYrS4L0/56ydM/H6bsfsXxRjI18hSJqMZiqXqS84OZz2aOn+h7HCzc\n" "LEiZAoGASk20wFvilpRKHq79xxFWiDUPHi0x0pp82dYIEntGQkKUWkbSlhgf3MAi\n" "5NEQTDmXdnB+rVeWIvEi+BXfdnNgdn8eC4zSdtF4sIAhYr5VWZo0WVWDhT7u2ccv\n" "ZBFymiz8lo3gN57wGUCi9pbZqzV1+ZppX6YTNDdDCE0q+KO3Cec=\n" "-----END RSA PRIVATE KEY-----" ) TEST_RSA_OAUTH_SIGNATURE = ( "j8WF8PGjojT82aUDd2EL%2Bz7HCoHInFzWUpiEKMCy%2BJ2cYHWcBS7mXlmFDLgAKV0" "P%2FyX4TrpXODYnJ6dRWdfghqwDpi%2FlQmB2jxCiGMdJoYxh3c5zDf26gEbGdP6D7O" "Ssp5HUnzH6sNkmVjuE%2FxoJcHJdc23H6GhOs7VJ2LWNdbhKWP%2FMMlTrcoQDn8lz" "%2Fb24WsJ6ae1txkUzpFOOlLM8aTdNtGL4OtsubOlRhNqnAFq93FyhXg0KjzUyIZzmMX" "9Vx90jTks5QeBGYcLE0Op2iHb2u%2FO%2BEgdwFchgEwE5LgMUyHUI4F3Wglp28yHOAM" "jPkI%2FkWMvpxtMrU3Z3KN31WQ%3D%3D" ) class OAuth1SessionTest(unittest.TestCase): def test_signature_types(self): def verify_signature(getter): def fake_send(r, **kwargs): signature = getter(r) if isinstance(signature, bytes): signature = signature.decode("utf-8") self.assertIn("oauth_signature", signature) resp = mock.MagicMock(spec=requests.Response) resp.cookies = [] return resp return fake_send header = OAuth1Session("foo") header.send = verify_signature(lambda r: r.headers["Authorization"]) header.post("https://i.b") query = OAuth1Session("foo", signature_type=SIGNATURE_TYPE_QUERY) query.send = verify_signature(lambda r: r.url) query.post("https://i.b") body = OAuth1Session("foo", signature_type=SIGNATURE_TYPE_BODY) headers = {"Content-Type": "application/x-www-form-urlencoded"} body.send = verify_signature(lambda r: r.body) body.post("https://i.b", headers=headers, data="") @mock.patch("oauthlib.oauth1.rfc5849.generate_timestamp") @mock.patch("oauthlib.oauth1.rfc5849.generate_nonce") def test_signature_methods(self, generate_nonce, generate_timestamp): if not cryptography: raise unittest.SkipTest("cryptography module is required") if not jwt: raise unittest.SkipTest("pyjwt module is required") 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="HMAC-SHA1", oauth_consumer_key="foo", oauth_signature="h2sRqLArjhlc5p3FTkuNogVHlKE%3D"' auth = OAuth1Session("foo") auth.send = self.verify_signature(signature) auth.post("https://i.b") signature = 'OAuth oauth_nonce="abc", oauth_timestamp="123", oauth_version="1.0", oauth_signature_method="PLAINTEXT", oauth_consumer_key="foo", oauth_signature="%26"' auth = OAuth1Session("foo", signature_method=SIGNATURE_PLAINTEXT) auth.send = self.verify_signature(signature) auth.post("https://i.b") signature = ( "OAuth " 'oauth_nonce="abc", oauth_timestamp="123", oauth_version="1.0", ' 'oauth_signature_method="RSA-SHA1", oauth_consumer_key="foo", ' 'oauth_signature="{sig}"' ).format(sig=TEST_RSA_OAUTH_SIGNATURE) auth = OAuth1Session( "foo", signature_method=SIGNATURE_RSA, rsa_key=TEST_RSA_KEY ) auth.send = self.verify_signature(signature) auth.post("https://i.b") @mock.patch("oauthlib.oauth1.rfc5849.generate_timestamp") @mock.patch("oauthlib.oauth1.rfc5849.generate_nonce") def test_binary_upload(self, generate_nonce, generate_timestamp): generate_nonce.return_value = "abc" generate_timestamp.return_value = "123" fake_xml = StringIO("hello world") headers = {"Content-Type": "application/xml"} signature = 'OAuth oauth_nonce="abc", oauth_timestamp="123", oauth_version="1.0", oauth_signature_method="HMAC-SHA1", oauth_consumer_key="foo", oauth_signature="h2sRqLArjhlc5p3FTkuNogVHlKE%3D"' auth = OAuth1Session("foo") auth.send = self.verify_signature(signature) auth.post("https://i.b", headers=headers, files=[("fake", fake_xml)]) @mock.patch("oauthlib.oauth1.rfc5849.generate_timestamp") @mock.patch("oauthlib.oauth1.rfc5849.generate_nonce") def test_nonascii(self, generate_nonce, generate_timestamp): 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="HMAC-SHA1", oauth_consumer_key="foo", oauth_signature="W0haoue5IZAZoaJiYCtfqwMf8x8%3D"' auth = OAuth1Session("foo") auth.send = self.verify_signature(signature) auth.post("https://i.b?cjk=%E5%95%A6%E5%95%A6") def test_authorization_url(self): auth = OAuth1Session("foo") url = "https://example.comm/authorize" token = "asluif023sf" auth_url = auth.authorization_url(url, request_token=token) self.assertEqual(auth_url, url + "?oauth_token=" + token) def test_parse_response_url(self): url = "https://i.b/callback?oauth_token=foo&oauth_verifier=bar" auth = OAuth1Session("foo") resp = auth.parse_authorization_response(url) 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) def test_fetch_request_token(self): auth = OAuth1Session("foo") auth.send = self.fake_body("oauth_token=foo") 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) def test_fetch_request_token_with_optional_arguments(self): auth = OAuth1Session("foo") auth.send = self.fake_body("oauth_token=foo") resp = auth.fetch_request_token( "https://example.com/token", verify=False, stream=True ) self.assertEqual(resp["oauth_token"], "foo") for k, v in resp.items(): self.assertIsInstance(k, unicode_type) self.assertIsInstance(v, unicode_type) def test_fetch_access_token(self): auth = OAuth1Session("foo", verifier="bar") auth.send = self.fake_body("oauth_token=foo") 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) def test_fetch_access_token_with_optional_arguments(self): auth = OAuth1Session("foo", verifier="bar") auth.send = self.fake_body("oauth_token=foo") resp = auth.fetch_access_token( "https://example.com/token", verify=False, stream=True ) self.assertEqual(resp["oauth_token"], "foo") for k, v in resp.items(): self.assertIsInstance(k, unicode_type) self.assertIsInstance(v, unicode_type) def _test_fetch_access_token_raises_error(self, auth): """Assert that an error is being raised whenever there's no verifier passed in to the client. """ auth.send = self.fake_body("oauth_token=foo") with self.assertRaises(ValueError) as cm: auth.fetch_access_token("https://example.com/token") self.assertEqual("No client verifier has been set.", str(cm.exception)) def test_fetch_token_invalid_response(self): auth = OAuth1Session("foo") auth.send = self.fake_body("not valid urlencoded response!") self.assertRaises( ValueError, auth.fetch_request_token, "https://example.com/token" ) for code in (400, 401, 403): auth.send = self.fake_body("valid=response", code) with self.assertRaises(ValueError) as cm: auth.fetch_request_token("https://example.com/token") self.assertEqual(cm.exception.status_code, code) self.assertIsInstance(cm.exception.response, requests.Response) def test_fetch_access_token_missing_verifier(self): self._test_fetch_access_token_raises_error(OAuth1Session("foo")) def test_fetch_access_token_has_verifier_is_none(self): auth = OAuth1Session("foo") del auth._client.client.verifier self._test_fetch_access_token_raises_error(auth) def test_token_proxy_set(self): token = { "oauth_token": "fake-key", "oauth_token_secret": "fake-secret", "oauth_verifier": "fake-verifier", } sess = OAuth1Session("foo") self.assertIsNone(sess._client.client.resource_owner_key) self.assertIsNone(sess._client.client.resource_owner_secret) self.assertIsNone(sess._client.client.verifier) self.assertEqual(sess.token, {}) sess.token = token self.assertEqual(sess._client.client.resource_owner_key, "fake-key") self.assertEqual(sess._client.client.resource_owner_secret, "fake-secret") self.assertEqual(sess._client.client.verifier, "fake-verifier") def test_token_proxy_get(self): token = { "oauth_token": "fake-key", "oauth_token_secret": "fake-secret", "oauth_verifier": "fake-verifier", } sess = OAuth1Session( "foo", resource_owner_key=token["oauth_token"], resource_owner_secret=token["oauth_token_secret"], verifier=token["oauth_verifier"], ) self.assertEqual(sess.token, token) sess._client.client.resource_owner_key = "different-key" token["oauth_token"] = "different-key" self.assertEqual(sess.token, token) def test_authorized_false(self): sess = OAuth1Session("foo") self.assertIs(sess.authorized, False) def test_authorized_false_rsa(self): signature = ( "OAuth " 'oauth_nonce="abc", oauth_timestamp="123", oauth_version="1.0", ' 'oauth_signature_method="RSA-SHA1", oauth_consumer_key="foo", ' 'oauth_signature="{sig}"' ).format(sig=TEST_RSA_OAUTH_SIGNATURE) sess = OAuth1Session( "foo", signature_method=SIGNATURE_RSA, rsa_key=TEST_RSA_KEY ) sess.send = self.verify_signature(signature) self.assertIs(sess.authorized, False) def test_authorized_true(self): sess = OAuth1Session("key", "secret", verifier="bar") sess.send = self.fake_body("oauth_token=foo&oauth_token_secret=bar") sess.fetch_access_token("https://example.com/token") self.assertIs(sess.authorized, True) @mock.patch("oauthlib.oauth1.rfc5849.generate_timestamp") @mock.patch("oauthlib.oauth1.rfc5849.generate_nonce") def test_authorized_true_rsa(self, generate_nonce, generate_timestamp): if not cryptography: raise unittest.SkipTest("cryptography module is required") if not jwt: raise unittest.SkipTest("pyjwt module is required") 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", signature_method=SIGNATURE_RSA, rsa_key=TEST_RSA_KEY, verifier="bar", ) sess.send = self.fake_body("oauth_token=foo&oauth_token_secret=bar") sess.fetch_access_token("https://example.com/token") self.assertIs(sess.authorized, True) def verify_signature(self, signature): def fake_send(r, **kwargs): auth_header = r.headers["Authorization"] if isinstance(auth_header, bytes): auth_header = auth_header.decode("utf-8") self.assertEqual(auth_header, signature) resp = mock.MagicMock(spec=requests.Response) resp.cookies = [] return resp return fake_send def fake_body(self, body, status_code=200): def fake_send(r, **kwargs): resp = mock.MagicMock(spec=requests.Response) resp.cookies = [] resp.text = body resp.status_code = status_code return resp return fake_send ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1643482333.0 requests-oauthlib-1.3.1/tests/test_oauth2_auth.py0000644000175100001710000000414200000000000021533 0ustar00runnerdockerfrom __future__ import unicode_literals import unittest from oauthlib.oauth2 import WebApplicationClient, MobileApplicationClient from oauthlib.oauth2 import LegacyApplicationClient, BackendApplicationClient from requests import Request from requests_oauthlib import OAuth2 class OAuth2AuthTest(unittest.TestCase): def setUp(self): self.token = { "token_type": "Bearer", "access_token": "asdfoiw37850234lkjsdfsdf", "expires_in": "3600", } self.client_id = "foo" self.clients = [ WebApplicationClient(self.client_id), MobileApplicationClient(self.client_id), LegacyApplicationClient(self.client_id), BackendApplicationClient(self.client_id), ] def test_add_token_to_url(self): url = "https://example.com/resource?foo=bar" new_url = url + "&access_token=" + self.token["access_token"] for client in self.clients: client.default_token_placement = "query" auth = OAuth2(client=client, token=self.token) r = Request("GET", url, auth=auth).prepare() self.assertEqual(r.url, new_url) def test_add_token_to_headers(self): token = "Bearer " + self.token["access_token"] for client in self.clients: auth = OAuth2(client=client, token=self.token) r = Request("GET", "https://i.b", auth=auth).prepare() self.assertEqual(r.headers["Authorization"], token) def test_add_token_to_body(self): body = "foo=bar" new_body = body + "&access_token=" + self.token["access_token"] for client in self.clients: client.default_token_placement = "body" auth = OAuth2(client=client, token=self.token) r = Request("GET", "https://i.b", data=body, auth=auth).prepare() self.assertEqual(r.body, new_body) def test_add_nonexisting_token(self): for client in self.clients: auth = OAuth2(client=client) r = Request("GET", "https://i.b", auth=auth) self.assertRaises(ValueError, r.prepare) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1643482333.0 requests-oauthlib-1.3.1/tests/test_oauth2_session.py0000644000175100001710000005022100000000000022254 0ustar00runnerdockerfrom __future__ import unicode_literals import json import time import tempfile import shutil import os from base64 import b64encode from copy import deepcopy from unittest import TestCase try: import mock except ImportError: from unittest import mock from oauthlib.common import urlencode from oauthlib.oauth2 import TokenExpiredError, OAuth2Error from oauthlib.oauth2 import MismatchingStateError from oauthlib.oauth2 import WebApplicationClient, MobileApplicationClient from oauthlib.oauth2 import LegacyApplicationClient, BackendApplicationClient from requests_oauthlib import OAuth2Session, TokenUpdated import requests from requests.auth import _basic_auth_str fake_time = time.time() CODE = "asdf345xdf" def fake_token(token): def fake_send(r, **kwargs): resp = mock.MagicMock() resp.text = json.dumps(token) return resp return fake_send class OAuth2SessionTest(TestCase): def setUp(self): self.token = { "token_type": "Bearer", "access_token": "asdfoiw37850234lkjsdfsdf", "refresh_token": "sldvafkjw34509s8dfsdf", "expires_in": 3600, "expires_at": fake_time + 3600, } # use someclientid:someclientsecret to easily differentiate between client and user credentials # these are the values used in oauthlib tests self.client_id = "someclientid" self.client_secret = "someclientsecret" self.user_username = "user_username" self.user_password = "user_password" self.client_WebApplication = WebApplicationClient(self.client_id, code=CODE) self.client_LegacyApplication = LegacyApplicationClient(self.client_id) self.client_BackendApplication = BackendApplicationClient(self.client_id) self.client_MobileApplication = MobileApplicationClient(self.client_id) self.clients = [ self.client_WebApplication, self.client_LegacyApplication, self.client_BackendApplication, ] self.all_clients = self.clients + [self.client_MobileApplication] def test_add_token(self): token = "Bearer " + self.token["access_token"] def verifier(r, **kwargs): auth_header = r.headers.get(str("Authorization"), None) self.assertEqual(auth_header, token) resp = mock.MagicMock() resp.cookes = [] return resp for client in self.all_clients: sess = OAuth2Session(client=client, token=self.token) sess.send = verifier sess.get("https://i.b") def test_mtls(self): cert = ( "testsomething.example-client.pem", "testsomething.example-client-key.pem", ) def verifier(r, **kwargs): self.assertIn("cert", kwargs) self.assertEqual(cert, kwargs["cert"]) self.assertIn("client_id=" + self.client_id, r.body) resp = mock.MagicMock() resp.text = json.dumps(self.token) return resp for client in self.clients: sess = OAuth2Session(client=client) sess.send = verifier if isinstance(client, LegacyApplicationClient): sess.fetch_token( "https://i.b", include_client_id=True, cert=cert, username="username1", password="password1", ) else: sess.fetch_token("https://i.b", include_client_id=True, cert=cert) def test_authorization_url(self): url = "https://example.com/authorize?foo=bar" web = WebApplicationClient(self.client_id) s = OAuth2Session(client=web) 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) mobile = MobileApplicationClient(self.client_id) s = OAuth2Session(client=mobile) 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) @mock.patch("time.time", new=lambda: fake_time) def test_refresh_token_request(self): self.expired_token = dict(self.token) self.expired_token["expires_in"] = "-1" del self.expired_token["expires_at"] def fake_refresh(r, **kwargs): if "/refresh" in r.url: self.assertNotIn("Authorization", r.headers) resp = mock.MagicMock() resp.text = json.dumps(self.token) return resp # No auto refresh setup for client in self.clients: sess = OAuth2Session(client=client, token=self.expired_token) self.assertRaises(TokenExpiredError, sess.get, "https://i.b") # Auto refresh but no auto update for client in self.clients: sess = OAuth2Session( client=client, token=self.expired_token, auto_refresh_url="https://i.b/refresh", ) sess.send = fake_refresh self.assertRaises(TokenUpdated, sess.get, "https://i.b") # Auto refresh and auto update def token_updater(token): self.assertEqual(token, self.token) for client in self.clients: sess = OAuth2Session( client=client, token=self.expired_token, auto_refresh_url="https://i.b/refresh", token_updater=token_updater, ) sess.send = fake_refresh sess.get("https://i.b") def fake_refresh_with_auth(r, **kwargs): if "/refresh" in r.url: self.assertIn("Authorization", r.headers) encoded = b64encode( "{client_id}:{client_secret}".format( client_id=self.client_id, client_secret=self.client_secret ).encode("latin1") ) content = "Basic {encoded}".format(encoded=encoded.decode("latin1")) self.assertEqual(r.headers["Authorization"], content) resp = mock.MagicMock() resp.text = json.dumps(self.token) return resp for client in self.clients: sess = OAuth2Session( client=client, token=self.expired_token, auto_refresh_url="https://i.b/refresh", token_updater=token_updater, ) sess.send = fake_refresh_with_auth sess.get( "https://i.b", client_id=self.client_id, client_secret=self.client_secret, ) @mock.patch("time.time", new=lambda: fake_time) def test_token_from_fragment(self): mobile = MobileApplicationClient(self.client_id) response_url = "https://i.b/callback#" + urlencode(self.token.items()) sess = OAuth2Session(client=mobile) self.assertEqual(sess.token_from_fragment(response_url), self.token) @mock.patch("time.time", new=lambda: fake_time) def test_fetch_token(self): url = "https://example.com/token" for client in self.clients: sess = OAuth2Session(client=client, token=self.token) sess.send = fake_token(self.token) if isinstance(client, LegacyApplicationClient): # this client requires a username+password # if unset, an error will be raised self.assertRaises(ValueError, sess.fetch_token, url) self.assertRaises( ValueError, sess.fetch_token, url, username="username1" ) self.assertRaises( ValueError, sess.fetch_token, url, password="password1" ) # otherwise it will pass self.assertEqual( sess.fetch_token(url, username="username1", password="password1"), self.token, ) else: self.assertEqual(sess.fetch_token(url), self.token) error = {"error": "invalid_request"} for client in self.clients: sess = OAuth2Session(client=client, token=self.token) sess.send = fake_token(error) if isinstance(client, LegacyApplicationClient): # this client requires a username+password # if unset, an error will be raised self.assertRaises(ValueError, sess.fetch_token, url) self.assertRaises( ValueError, sess.fetch_token, url, username="username1" ) self.assertRaises( ValueError, sess.fetch_token, url, password="password1" ) # otherwise it will pass self.assertRaises( OAuth2Error, sess.fetch_token, url, username="username1", password="password1", ) else: self.assertRaises(OAuth2Error, sess.fetch_token, url) # there are different scenarios in which the `client_id` can be specified # reference `oauthlib.tests.oauth2.rfc6749.clients.test_web_application.WebApplicationClientTest.test_prepare_request_body` # this only needs to test WebApplicationClient client = self.client_WebApplication client.tester = True # this should be a tuple of (r.url, r.body, r.headers.get('Authorization')) _fetch_history = [] def fake_token_history(token): def fake_send(r, **kwargs): resp = mock.MagicMock() resp.text = json.dumps(token) _fetch_history.append( (r.url, r.body, r.headers.get("Authorization", None)) ) return resp return fake_send sess = OAuth2Session(client=client, token=self.token) sess.send = fake_token_history(self.token) expected_auth_header = _basic_auth_str(self.client_id, self.client_secret) # scenario 1 - default request # this should send the `client_id` in the headers, as that is recommended by the RFC self.assertEqual( sess.fetch_token(url, client_secret="someclientsecret"), self.token ) self.assertEqual(len(_fetch_history), 1) self.assertNotIn( "client_id", _fetch_history[0][1] ) # no `client_id` in the body self.assertNotIn( "client_secret", _fetch_history[0][1] ) # no `client_secret` in the body self.assertEqual( _fetch_history[0][2], expected_auth_header ) # ensure a Basic Authorization header # scenario 2 - force the `client_id` into the body self.assertEqual( sess.fetch_token( url, client_secret="someclientsecret", include_client_id=True ), self.token, ) self.assertEqual(len(_fetch_history), 2) self.assertIn("client_id=%s" % self.client_id, _fetch_history[1][1]) self.assertIn("client_secret=%s" % self.client_secret, _fetch_history[1][1]) self.assertEqual( _fetch_history[1][2], None ) # ensure NO Basic Authorization header # scenario 3 - send in an auth object auth = requests.auth.HTTPBasicAuth(self.client_id, self.client_secret) self.assertEqual(sess.fetch_token(url, auth=auth), self.token) self.assertEqual(len(_fetch_history), 3) self.assertNotIn( "client_id", _fetch_history[2][1] ) # no `client_id` in the body self.assertNotIn( "client_secret", _fetch_history[2][1] ) # no `client_secret` in the body self.assertEqual( _fetch_history[2][2], expected_auth_header ) # ensure a Basic Authorization header # scenario 4 - send in a username/password combo # this should send the `client_id` in the headers, like scenario 1 self.assertEqual( sess.fetch_token( url, username=self.user_username, password=self.user_password ), self.token, ) self.assertEqual(len(_fetch_history), 4) self.assertNotIn( "client_id", _fetch_history[3][1] ) # no `client_id` in the body self.assertNotIn( "client_secret", _fetch_history[3][1] ) # no `client_secret` in the body self.assertEqual( _fetch_history[0][2], expected_auth_header ) # ensure a Basic Authorization header self.assertIn("username=%s" % self.user_username, _fetch_history[3][1]) self.assertIn("password=%s" % self.user_password, _fetch_history[3][1]) # scenario 5 - send data in `params` and not in `data` for providers # that expect data in URL self.assertEqual( sess.fetch_token(url, client_secret="somesecret", force_querystring=True), self.token, ) self.assertIn("code=%s" % CODE, _fetch_history[4][0]) # some quick tests for valid ways of supporting `client_secret` # scenario 2b - force the `client_id` into the body; but the `client_secret` is `None` self.assertEqual( sess.fetch_token(url, client_secret=None, include_client_id=True), self.token, ) self.assertEqual(len(_fetch_history), 6) self.assertIn("client_id=%s" % self.client_id, _fetch_history[5][1]) self.assertNotIn( "client_secret=", _fetch_history[5][1] ) # no `client_secret` in the body self.assertEqual( _fetch_history[5][2], None ) # ensure NO Basic Authorization header # scenario 2c - force the `client_id` into the body; but the `client_secret` is an empty string self.assertEqual( sess.fetch_token(url, client_secret="", include_client_id=True), self.token ) self.assertEqual(len(_fetch_history), 7) self.assertIn("client_id=%s" % self.client_id, _fetch_history[6][1]) self.assertIn("client_secret=", _fetch_history[6][1]) self.assertEqual( _fetch_history[6][2], None ) # ensure NO Basic Authorization header def test_cleans_previous_token_before_fetching_new_one(self): """Makes sure the previous token is cleaned before fetching a new one. The reason behind it is that, if the previous token is expired, this method shouldn't fail with a TokenExpiredError, since it's attempting to get a new one (which shouldn't be expired). """ new_token = deepcopy(self.token) past = time.time() - 7200 now = time.time() self.token["expires_at"] = past new_token["expires_at"] = now + 3600 url = "https://example.com/token" with mock.patch("time.time", lambda: now): for client in self.clients: sess = OAuth2Session(client=client, token=self.token) sess.send = fake_token(new_token) if isinstance(client, LegacyApplicationClient): # this client requires a username+password # if unset, an error will be raised self.assertRaises(ValueError, sess.fetch_token, url) self.assertRaises( ValueError, sess.fetch_token, url, username="username1" ) self.assertRaises( ValueError, sess.fetch_token, url, password="password1" ) # otherwise it will pass self.assertEqual( sess.fetch_token( url, username="username1", password="password1" ), new_token, ) else: self.assertEqual(sess.fetch_token(url), new_token) def test_web_app_fetch_token(self): # Ensure the state parameter is used, see issue #105. client = OAuth2Session("someclientid", state="somestate") self.assertRaises( MismatchingStateError, client.fetch_token, "https://i.b/token", authorization_response="https://i.b/no-state?code=abc", ) def test_client_id_proxy(self): sess = OAuth2Session("test-id") self.assertEqual(sess.client_id, "test-id") sess.client_id = "different-id" self.assertEqual(sess.client_id, "different-id") sess._client.client_id = "something-else" self.assertEqual(sess.client_id, "something-else") del sess.client_id self.assertIsNone(sess.client_id) def test_access_token_proxy(self): sess = OAuth2Session("test-id") self.assertIsNone(sess.access_token) sess.access_token = "test-token" self.assertEqual(sess.access_token, "test-token") sess._client.access_token = "different-token" self.assertEqual(sess.access_token, "different-token") del sess.access_token self.assertIsNone(sess.access_token) def test_token_proxy(self): token = {"access_token": "test-access"} sess = OAuth2Session("test-id", token=token) self.assertEqual(sess.access_token, "test-access") self.assertEqual(sess.token, token) token["access_token"] = "something-else" sess.token = token self.assertEqual(sess.access_token, "something-else") self.assertEqual(sess.token, token) sess._client.access_token = "different-token" token["access_token"] = "different-token" self.assertEqual(sess.access_token, "different-token") self.assertEqual(sess.token, token) # can't delete token attribute with self.assertRaises(AttributeError): del sess.token def test_authorized_false(self): sess = OAuth2Session("someclientid") self.assertFalse(sess.authorized) @mock.patch("time.time", new=lambda: fake_time) def test_authorized_true(self): def fake_token(token): def fake_send(r, **kwargs): resp = mock.MagicMock() resp.text = json.dumps(token) return resp return fake_send url = "https://example.com/token" for client in self.clients: sess = OAuth2Session(client=client) sess.send = fake_token(self.token) self.assertFalse(sess.authorized) if isinstance(client, LegacyApplicationClient): # this client requires a username+password # if unset, an error will be raised self.assertRaises(ValueError, sess.fetch_token, url) self.assertRaises( ValueError, sess.fetch_token, url, username="username1" ) self.assertRaises( ValueError, sess.fetch_token, url, password="password1" ) # otherwise it will pass sess.fetch_token(url, username="username1", password="password1") else: sess.fetch_token(url) self.assertTrue(sess.authorized) class OAuth2SessionNetrcTest(OAuth2SessionTest): """Ensure that there is no magic auth handling. By default, requests sessions have magic handling of netrc files, which is undesirable for this library because it will take precedence over manually set authentication headers. """ def setUp(self): # Set up a temporary home directory self.homedir = tempfile.mkdtemp() self.prehome = os.environ.get("HOME", None) os.environ["HOME"] = self.homedir # Write a .netrc file that will cause problems netrc_loc = os.path.expanduser("~/.netrc") with open(netrc_loc, "w") as f: f.write("machine i.b\n" " password abc123\n" " login spam@eggs.co\n") super(OAuth2SessionNetrcTest, self).setUp() def tearDown(self): super(OAuth2SessionNetrcTest, self).tearDown() if self.prehome is not None: os.environ["HOME"] = self.prehome shutil.rmtree(self.homedir)