Import Upstream version 3.2.2

This commit is contained in:
luoyaoming 2024-04-30 17:48:06 +08:00
parent 5d789ab01a
commit dabc06c9f7
141 changed files with 3531 additions and 2291 deletions

View File

@ -1,33 +1,112 @@
Changelog
=========
3.1.0 (TBD)
3.2.2 (2022-10-17)
------------------
OAuth2.0 Provider:
* CVE-2022-36087
OAuth2.0 Provider - Features
* #660: OIDC add support of nonce, c_hash, at_hash fields
- New RequestValidator.fill_id_token method
- Deprecated RequestValidator.get_id_token method
* #677: OIDC add UserInfo endpoint
- New RequestValidator.get_userinfo_claims method
3.2.1 (2022-09-09)
------------------
OAuth2.0 Provider:
* #803: Metadata endpoint support of non-HTTPS
OAuth2.0 Provider - Security
* #665: Enhance data leak to logs
- New default to not expose request content in logs
- New function `oauthlib.set_debug(True)`
* #666: Disabling query parameters for POST requests
OAuth1.0:
* #818: Allow IPv6 being parsed by signature
General:
* Improved and fixed documentation warnings.
* Cosmetic changes based on isort
3.2.0 (2022-01-29)
------------------
OAuth2.0 Client:
* #795: Add Device Authorization Flow for Web Application
* #786: Add PKCE support for Client
* #783: Fallback to none in case of wrong expires_at format.
OAuth2.0 Provider:
* #790: Add support for CORS to metadata endpoint.
* #791: Add support for CORS to token endpoint.
* #787: Remove comma after Bearer in WWW-Authenticate
OAuth2.0 Provider - OIDC:
* #755: Call save_token in Hybrid code flow
* #751: OIDC add support of refreshing ID Tokens with `refresh_id_token`
* #751: The RefreshTokenGrant modifiers now take the same arguments as the
AuthorizationCodeGrant modifiers (`token`, `token_handler`, `request`).
General:
* Added Python 3.9, 3.10, 3.11
* Improve Travis & Coverage
3.1.1 (2021-05-31)
------------------
OAuth2.0 Provider - Bugfixes
* #670: Fix validate_authorization_request to return the new PKCE fields
* #674: Fix token_type to be case-insensitive (bearer and Bearer)
* #753: Fix acceptance of valid IPv6 addresses in URI validation
OAuth2.0 Client - Bugfixes
* #290: Fix Authorization Code's errors processing
* #603: BackendApplication.Client.prepare_request_body use the "scope" argument as intended.
* #672: Fix edge case when expires_in=Null
* #730: Base OAuth2 Client now has a consistent way of managing the `scope`: it consistently
relies on the `scope` provided in the constructor if any, except if overridden temporarily
in a method call. Note that in particular providing a non-None `scope` in
`prepare_authorization_request` or `prepare_refresh_token` does not override anymore
`self.scope` forever, it is just used temporarily.
* #726: MobileApplicationClient.prepare_request_uri and MobileApplicationClient.parse_request_uri_response,
ServiceApplicationClient.prepare_request_body,
and WebApplicationClient.prepare_request_uri now correctly use the default `scope` provided in
constructor.
* #725: LegacyApplicationClient.prepare_request_body now correctly uses the default `scope` provided in constructor
OAuth2.0 Provider - Bugfixes
* #711: client_credentials grant: fix log message
* #746: OpenID Connect Hybrid - fix nonce not passed to add_id_token
* #756: Different prompt values are now handled according to spec (e.g. prompt=none)
* #759: OpenID Connect - fix Authorization: Basic parsing
General
* #716: improved skeleton validator for public vs private client
* #720: replace mock library with standard unittest.mock
* #727: build isort integration
* #734: python2 code removal
* #735, #750: add python3.8 support
* #749: bump minimum versions of pyjwt and cryptography
3.1.0 (2019-08-06)
------------------
OAuth2.0 Provider - Features
* #660: OIDC add support of `nonce`, `c_hash`, `at_hash fields`
- New `RequestValidator.fill_id_token` method
- Deprecated `RequestValidator.get_id_token` method
* #677: OIDC add `UserInfo` endpoint - New `RequestValidator.get_userinfo_claims` method
OAuth2.0 Provider - Security
* #665: Enhance data leak to logs
* New default to not expose request content in logs
* New function `oauthlib.set_debug(True)`
* #666: Disabling query parameters for POST requests
OAuth2.0 Provider - Bugfixes
* #670: Fix `validate_authorization_request` to return the new PKCE fields
* #674: Fix `token_type` to be case-insensitive (`bearer` and `Bearer`)
OAuth2.0 Client - Bugfixes
* #290: Fix Authorization Code's errors processing
* #603: BackendApplicationClient.prepare_request_body use the `scope` argument as intended.
* #672: Fix edge case when `expires_in=Null`
OAuth1.0 Client
* #669: Add case-insensitive headers to oauth1 BaseEndpoint
* #669: Add case-insensitive headers to oauth1 `BaseEndpoint`
OAuth1.0
* #722: Added support for HMAC-SHA512, RSA-SHA256 and RSA-SHA512 signature methods.
3.0.2 (2019-07-04)
------------------
@ -84,7 +163,7 @@ OAuth1.0 Client:
General fixes:
* $ and ' are allowed to be unencoded in query strings #564
* Request attributes are no longer overriden by HTTP Headers #409
* Request attributes are no longer overridden by HTTP Headers #409
* Removed unnecessary code for handling python2.6
* Add support of python3.7 #621
* Several minors updates to setup.py and tox
@ -142,7 +221,7 @@ General fixes:
* Added log statements to except clauses.
* According to RC7009 Section 2.1, a client should include authentication credentials when revoking its tokens.
As discussed in #339, this is not make sense for public clients.
However, in that case, the public client should still be checked that is infact a public client (authenticate_client_id).
However, in that case, the public client should still be checked that is in fact a public client (authenticate_client_id).
* Improved prompt parameter validation.
* Added two error codes from RFC 6750.
* Hybrid response types are now be fragment-encoded.
@ -292,7 +371,7 @@ Quick fix. OAuth 1 client repr in 0.6.2 overwrote secrets when scrubbing for pri
Draft revocation endpoint features and numerous fixes including:
* (OAuth 2 Provider) is_within_original_scope to check whether a refresh token
is trying to aquire a new set of scopes that are a subset of the original scope.
is trying to acquire a new set of scopes that are a subset of the original scope.
* (OAuth 2 Provider) expires_in token lifetime can be set per request.

286
PKG-INFO
View File

@ -1,6 +1,6 @@
Metadata-Version: 2.1
Name: oauthlib
Version: 3.1.0
Version: 3.2.2
Summary: A generic, spec-compliant, thorough implementation of the OAuth request-signing logic
Home-page: https://github.com/oauthlib/oauthlib
Author: The OAuthlib Community
@ -8,138 +8,6 @@ Author-email: idan@gazit.me
Maintainer: Ib Lundgren
Maintainer-email: ib.lundgren@gmail.com
License: BSD
Description: OAuthLib - Python Framework for OAuth1 & OAuth2
===============================================
*A generic, spec-compliant, thorough implementation of the OAuth request-signing
logic for Python 2.7 and 3.4+.*
.. image:: https://travis-ci.org/oauthlib/oauthlib.svg?branch=master
:target: https://travis-ci.org/oauthlib/oauthlib
:alt: Travis
.. image:: https://coveralls.io/repos/oauthlib/oauthlib/badge.svg?branch=master
:target: https://coveralls.io/r/oauthlib/oauthlib
:alt: Coveralls
.. image:: https://img.shields.io/pypi/pyversions/oauthlib.svg
:target: https://pypi.org/project/oauthlib/
:alt: Download from PyPI
.. image:: https://img.shields.io/pypi/l/oauthlib.svg
:target: https://pypi.org/project/oauthlib/
:alt: License
.. image:: https://app.fossa.io/api/projects/git%2Bgithub.com%2Foauthlib%2Foauthlib.svg?type=shield
:target: https://app.fossa.io/projects/git%2Bgithub.com%2Foauthlib%2Foauthlib?ref=badge_shield
:alt: FOSSA Status
.. image:: https://img.shields.io/readthedocs/oauthlib.svg
:target: https://oauthlib.readthedocs.io/en/latest/index.html
:alt: Read the Docs
.. image:: https://badges.gitter.im/oauthlib/oauthlib.svg
:target: https://gitter.im/oauthlib/Lobby
:alt: Chat on Gitter
OAuth often seems complicated and difficult-to-implement. There are several
prominent libraries for handling OAuth requests, but they all suffer from one or
both of the following:
1. They predate the `OAuth 1.0 spec`_, AKA RFC 5849.
2. They predate the `OAuth 2.0 spec`_, AKA RFC 6749.
3. They assume the usage of a specific HTTP request library.
.. _`OAuth 1.0 spec`: https://tools.ietf.org/html/rfc5849
.. _`OAuth 2.0 spec`: https://tools.ietf.org/html/rfc6749
OAuthLib is a framework which implements the logic of OAuth1 or OAuth2 without
assuming a specific HTTP request object or web framework. Use it to graft OAuth
client support onto your favorite HTTP library, or provide support onto your
favourite web framework. If you're a maintainer of such a library, write a thin
veneer on top of OAuthLib and get OAuth support for very little effort.
Documentation
--------------
Full documentation is available on `Read the Docs`_. All contributions are very
welcome! The documentation is still quite sparse, please open an issue for what
you'd like to know, or discuss it in our `Gitter community`_, or even better, send a
pull request!
.. _`Gitter community`: https://gitter.im/oauthlib/Lobby
.. _`Read the Docs`: https://oauthlib.readthedocs.io/en/latest/index.html
Interested in making OAuth requests?
------------------------------------
Then you might be more interested in using `requests`_ which has OAuthLib
powered OAuth support provided by the `requests-oauthlib`_ library.
.. _`requests`: https://github.com/requests/requests
.. _`requests-oauthlib`: https://github.com/requests/requests-oauthlib
Which web frameworks are supported?
-----------------------------------
The following packages provide OAuth support using OAuthLib.
- For Django there is `django-oauth-toolkit`_, which includes `Django REST framework`_ support.
- For Flask there is `flask-oauthlib`_ and `Flask-Dance`_.
- For Pyramid there is `pyramid-oauthlib`_.
- For Bottle there is `bottle-oauthlib`_.
If you have written an OAuthLib package that supports your favorite framework,
please open a Pull Request, updating the documentation.
.. _`django-oauth-toolkit`: https://github.com/evonove/django-oauth-toolkit
.. _`flask-oauthlib`: https://github.com/lepture/flask-oauthlib
.. _`Django REST framework`: http://django-rest-framework.org
.. _`Flask-Dance`: https://github.com/singingwolfboy/flask-dance
.. _`pyramid-oauthlib`: https://github.com/tilgovi/pyramid-oauthlib
.. _`bottle-oauthlib`: https://github.com/thomsonreuters/bottle-oauthlib
Using OAuthLib? Please get in touch!
------------------------------------
Patching OAuth support onto an http request framework? Creating an OAuth
provider extension for a web framework? Simply using OAuthLib to Get Things Done
or to learn?
No matter which we'd love to hear from you in our `Gitter community`_ or if you have
anything in particular you would like to have, change or comment on don't
hesitate for a second to send a pull request or open an issue. We might be quite
busy and therefore slow to reply but we love feedback!
Chances are you have run into something annoying that you wish there was
documentation for, if you wish to gain eternal fame and glory, and a drink if we
have the pleasure to run into eachother, please send a docs pull request =)
.. _`Gitter community`: https://gitter.im/oauthlib/Lobby
License
-------
OAuthLib is yours to use and abuse according to the terms of the BSD license.
Check the LICENSE file for full details.
Credits
-------
OAuthLib has been started and maintained several years by Idan Gazit and other
amazing `AUTHORS`_. Thanks to their wonderful work, the open-source `community`_
creation has been possible and the project can stay active and reactive to users
requests.
.. _`AUTHORS`: https://github.com/oauthlib/oauthlib/blob/master/AUTHORS
.. _`community`: https://github.com/oauthlib/
Changelog
---------
*OAuthLib is in active development, with the core of both OAuth1 and OAuth2
completed, for providers as well as clients.* See `supported features`_ for
details.
.. _`supported features`: https://oauthlib.readthedocs.io/en/latest/feature_matrix.html
For a full changelog see ``CHANGELOG.rst``.
Platform: any
Classifier: Development Status :: 5 - Production/Stable
Classifier: Environment :: Web Environment
@ -150,18 +18,158 @@ Classifier: Operating System :: MacOS
Classifier: Operating System :: POSIX
Classifier: Operating System :: POSIX :: Linux
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 :: 3.10
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: Implementation
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Programming Language :: Python :: Implementation :: PyPy
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*
Requires-Python: >=3.6
Description-Content-Type: text/x-rst
Provides-Extra: rsa
Provides-Extra: signedtoken
Provides-Extra: signals
Provides-Extra: rsa
License-File: LICENSE
OAuthLib - Python Framework for OAuth1 & OAuth2
===============================================
*A generic, spec-compliant, thorough implementation of the OAuth request-signing
logic for Python 3.6+.*
.. image:: https://app.travis-ci.com/oauthlib/oauthlib.svg?branch=master
:target: https://app.travis-ci.com/oauthlib/oauthlib
:alt: Travis
.. image:: https://coveralls.io/repos/oauthlib/oauthlib/badge.svg?branch=master
:target: https://coveralls.io/r/oauthlib/oauthlib
:alt: Coveralls
.. image:: https://img.shields.io/pypi/pyversions/oauthlib.svg
:target: https://pypi.org/project/oauthlib/
:alt: Download from PyPI
.. image:: https://img.shields.io/pypi/l/oauthlib.svg
:target: https://pypi.org/project/oauthlib/
:alt: License
.. image:: https://app.fossa.io/api/projects/git%2Bgithub.com%2Foauthlib%2Foauthlib.svg?type=shield
:target: https://app.fossa.io/projects/git%2Bgithub.com%2Foauthlib%2Foauthlib?ref=badge_shield
:alt: FOSSA Status
.. image:: https://img.shields.io/readthedocs/oauthlib.svg
:target: https://oauthlib.readthedocs.io/en/latest/index.html
:alt: Read the Docs
.. image:: https://badges.gitter.im/oauthlib/oauthlib.svg
:target: https://gitter.im/oauthlib/Lobby
:alt: Chat on Gitter
.. image:: https://raw.githubusercontent.com/oauthlib/oauthlib/8d71b161fd145d11c40d55c9ab66ac134a303253/docs/logo/oauthlib-banner-700x192.png
:target: https://github.com/oauthlib/oauthlib/
:alt: OAuth + Python = OAuthlib Python Framework
OAuth often seems complicated and difficult-to-implement. There are several
prominent libraries for handling OAuth requests, but they all suffer from one or
both of the following:
1. They predate the `OAuth 1.0 spec`_, AKA RFC 5849.
2. They predate the `OAuth 2.0 spec`_, AKA RFC 6749.
3. They assume the usage of a specific HTTP request library.
.. _`OAuth 1.0 spec`: https://tools.ietf.org/html/rfc5849
.. _`OAuth 2.0 spec`: https://tools.ietf.org/html/rfc6749
OAuthLib is a framework which implements the logic of OAuth1 or OAuth2 without
assuming a specific HTTP request object or web framework. Use it to graft OAuth
client support onto your favorite HTTP library, or provide support onto your
favourite web framework. If you're a maintainer of such a library, write a thin
veneer on top of OAuthLib and get OAuth support for very little effort.
Documentation
--------------
Full documentation is available on `Read the Docs`_. All contributions are very
welcome! The documentation is still quite sparse, please open an issue for what
you'd like to know, or discuss it in our `Gitter community`_, or even better, send a
pull request!
.. _`Gitter community`: https://gitter.im/oauthlib/Lobby
.. _`Read the Docs`: https://oauthlib.readthedocs.io/en/latest/index.html
Interested in making OAuth requests?
------------------------------------
Then you might be more interested in using `requests`_ which has OAuthLib
powered OAuth support provided by the `requests-oauthlib`_ library.
.. _`requests`: https://github.com/requests/requests
.. _`requests-oauthlib`: https://github.com/requests/requests-oauthlib
Which web frameworks are supported?
-----------------------------------
The following packages provide OAuth support using OAuthLib.
- For Django there is `django-oauth-toolkit`_, which includes `Django REST framework`_ support.
- For Flask there is `flask-oauthlib`_ and `Flask-Dance`_.
- For Pyramid there is `pyramid-oauthlib`_.
- For Bottle there is `bottle-oauthlib`_.
If you have written an OAuthLib package that supports your favorite framework,
please open a Pull Request, updating the documentation.
.. _`django-oauth-toolkit`: https://github.com/evonove/django-oauth-toolkit
.. _`flask-oauthlib`: https://github.com/lepture/flask-oauthlib
.. _`Django REST framework`: http://django-rest-framework.org
.. _`Flask-Dance`: https://github.com/singingwolfboy/flask-dance
.. _`pyramid-oauthlib`: https://github.com/tilgovi/pyramid-oauthlib
.. _`bottle-oauthlib`: https://github.com/thomsonreuters/bottle-oauthlib
Using OAuthLib? Please get in touch!
------------------------------------
Patching OAuth support onto an http request framework? Creating an OAuth
provider extension for a web framework? Simply using OAuthLib to Get Things Done
or to learn?
No matter which we'd love to hear from you in our `Gitter community`_ or if you have
anything in particular you would like to have, change or comment on don't
hesitate for a second to send a pull request or open an issue. We might be quite
busy and therefore slow to reply but we love feedback!
Chances are you have run into something annoying that you wish there was
documentation for, if you wish to gain eternal fame and glory, and a drink if we
have the pleasure to run into each other, please send a docs pull request =)
.. _`Gitter community`: https://gitter.im/oauthlib/Lobby
License
-------
OAuthLib is yours to use and abuse according to the terms of the BSD license.
Check the LICENSE file for full details.
Credits
-------
OAuthLib has been started and maintained several years by Idan Gazit and other
amazing `AUTHORS`_. Thanks to their wonderful work, the open-source `community`_
creation has been possible and the project can stay active and reactive to users
requests.
.. _`AUTHORS`: https://github.com/oauthlib/oauthlib/blob/master/AUTHORS
.. _`community`: https://github.com/oauthlib/
Changelog
---------
*OAuthLib is in active development, with the core of both OAuth1 and OAuth2
completed, for providers as well as clients.* See `supported features`_ for
details.
.. _`supported features`: https://oauthlib.readthedocs.io/en/latest/feature_matrix.html
For a full changelog see ``CHANGELOG.rst``.

View File

@ -2,10 +2,10 @@ OAuthLib - Python Framework for OAuth1 & OAuth2
===============================================
*A generic, spec-compliant, thorough implementation of the OAuth request-signing
logic for Python 2.7 and 3.4+.*
logic for Python 3.6+.*
.. image:: https://travis-ci.org/oauthlib/oauthlib.svg?branch=master
:target: https://travis-ci.org/oauthlib/oauthlib
.. image:: https://app.travis-ci.com/oauthlib/oauthlib.svg?branch=master
:target: https://app.travis-ci.com/oauthlib/oauthlib
:alt: Travis
.. image:: https://coveralls.io/repos/oauthlib/oauthlib/badge.svg?branch=master
:target: https://coveralls.io/r/oauthlib/oauthlib
@ -26,6 +26,12 @@ logic for Python 2.7 and 3.4+.*
:target: https://gitter.im/oauthlib/Lobby
:alt: Chat on Gitter
.. image:: https://raw.githubusercontent.com/oauthlib/oauthlib/8d71b161fd145d11c40d55c9ab66ac134a303253/docs/logo/oauthlib-banner-700x192.png
:target: https://github.com/oauthlib/oauthlib/
:alt: OAuth + Python = OAuthlib Python Framework
OAuth often seems complicated and difficult-to-implement. There are several
prominent libraries for handling OAuth requests, but they all suffer from one or
both of the following:
@ -97,7 +103,7 @@ busy and therefore slow to reply but we love feedback!
Chances are you have run into something annoying that you wish there was
documentation for, if you wish to gain eternal fame and glory, and a drink if we
have the pleasure to run into eachother, please send a docs pull request =)
have the pleasure to run into each other, please send a docs pull request =)
.. _`Gitter community`: https://gitter.im/oauthlib/Lobby

11
debian/changelog vendored
View File

@ -1,11 +0,0 @@
python-oauthlib (3.1.0-ok2) yangtze; urgency=medium
* update version info
-- luzhiping <luzhiping@kylinos.cn> Mon, 22 Aug 2022 14:19:08 +0800
python-oauthlib (3.1.0-ok1) yangtze; urgency=medium
* Build for openKylin.
-- openKylinBot <openKylinBot@openkylin.com> Mon, 25 Apr 2022 22:03:04 +0800

1
debian/clean vendored
View File

@ -1 +0,0 @@
oauthlib.egg-info/*

50
debian/control vendored
View File

@ -1,50 +0,0 @@
Source: python-oauthlib
Maintainer: Openkylin Developers <packaging@lists.openkylin.top>
XSBC-Original-Maintainer: Debian Python Modules Team <python-modules-team@lists.alioth.debian.org>
Uploaders: Daniele Tricoli <eriol@debian.org>
Section: python
Priority: optional
Build-Depends:
debhelper-compat (= 12),
dh-python,
python3-all,
python3-blinker,
python3-cryptography,
python3-jwt (>= 1.0.0),
python3-mock,
python3-pytest (>= 4.0),
python3-setuptools
Standards-Version: 4.4.1
Rules-Requires-Root: no
Homepage: https://github.com/idan/oauthlib
Vcs-Git: https://salsa.debian.org/python-team/modules/python-oauthlib.git
Vcs-Browser: https://salsa.debian.org/python-team/modules/python-oauthlib
Package: python3-oauthlib
Architecture: all
Depends:
${misc:Depends},
${python3:Depends},
python3-blinker,
python3-cryptography,
python3-jwt (>= 1.0.0)
Description: generic, spec-compliant implementation of OAuth for Python3
OAuthLib is a generic utility which implements the logic of OAuth without
assuming a specific HTTP request object. It can be used to graft OAuth support
onto HTTP libraries.
.
OAuth 1 is fully supported per the RFC for both clients and providers.
.
OAuth 2 client and provider support for:
.
- Authorization Code Grant
- Implicit Grant
- Client Credentials Grant
- Resource Owner Password Credentials Grant
- Refresh Tokens
- Bearer Tokens
- Draft MAC tokens
- Token Revocation
- OpenID Connect Authentication
.
This package contains the Python 3 version of the library.

40
debian/copyright vendored
View File

@ -1,40 +0,0 @@
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: oauthlib
Upstream-Contact: Idan Gazit <idan@gazit.me>
Source: http://pypi.python.org/pypi/oauthlib
Files: *
Copyright: 2011, Idan Gazit and contributors
License: BSD-3-clause
Files: debian/*
Copyright: 2012-2019 Daniele Tricoli <eriol@debian.org>
License: BSD-3-clause
License: BSD-3-clause
All rights reserved.
.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
.
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
.
3. Neither the name of this project nor the names of its contributors may
be used to endorse or promote products derived from this software
without specific prior written permission.
.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

2
debian/docs vendored
View File

@ -1,2 +0,0 @@
README.rst

8
debian/rules vendored
View File

@ -1,8 +0,0 @@
#!/usr/bin/make -f
export PYBUILD_NAME=oauthlib
export PYTHONWARNINGS=d
%:
dh $@ --with python3 --buildsystem=pybuild

View File

@ -1 +0,0 @@
3.0 (native)

View File

@ -1,6 +0,0 @@
Tests: python3-oauthlib
Depends:
python3-all,
python3-mock,
python3-pytest (>= 4.0),
python3-oauthlib

View File

@ -1,6 +0,0 @@
#!/bin/sh -e
cp -r tests "$AUTOPKGTEST_TMP"
cd "$AUTOPKGTEST_TMP"
py3versions -i | tr ' ' '\n' | xargs -I {} env {} -Wd -m pytest -v -x -rs 2>&1

3
debian/watch vendored
View File

@ -1,3 +0,0 @@
version=3
opts=uversionmangle=s/(rc|a|b|c)/~$1/ \
https://pypi.debian.net/oauthlib/oauthlib-(.+)\.(?:zip|tgz|tbz|txz|(?:tar\.(?:gz|bz2|xz)))

View File

@ -1,6 +1,6 @@
Metadata-Version: 2.1
Name: oauthlib
Version: 3.1.0
Version: 3.2.2
Summary: A generic, spec-compliant, thorough implementation of the OAuth request-signing logic
Home-page: https://github.com/oauthlib/oauthlib
Author: The OAuthlib Community
@ -8,138 +8,6 @@ Author-email: idan@gazit.me
Maintainer: Ib Lundgren
Maintainer-email: ib.lundgren@gmail.com
License: BSD
Description: OAuthLib - Python Framework for OAuth1 & OAuth2
===============================================
*A generic, spec-compliant, thorough implementation of the OAuth request-signing
logic for Python 2.7 and 3.4+.*
.. image:: https://travis-ci.org/oauthlib/oauthlib.svg?branch=master
:target: https://travis-ci.org/oauthlib/oauthlib
:alt: Travis
.. image:: https://coveralls.io/repos/oauthlib/oauthlib/badge.svg?branch=master
:target: https://coveralls.io/r/oauthlib/oauthlib
:alt: Coveralls
.. image:: https://img.shields.io/pypi/pyversions/oauthlib.svg
:target: https://pypi.org/project/oauthlib/
:alt: Download from PyPI
.. image:: https://img.shields.io/pypi/l/oauthlib.svg
:target: https://pypi.org/project/oauthlib/
:alt: License
.. image:: https://app.fossa.io/api/projects/git%2Bgithub.com%2Foauthlib%2Foauthlib.svg?type=shield
:target: https://app.fossa.io/projects/git%2Bgithub.com%2Foauthlib%2Foauthlib?ref=badge_shield
:alt: FOSSA Status
.. image:: https://img.shields.io/readthedocs/oauthlib.svg
:target: https://oauthlib.readthedocs.io/en/latest/index.html
:alt: Read the Docs
.. image:: https://badges.gitter.im/oauthlib/oauthlib.svg
:target: https://gitter.im/oauthlib/Lobby
:alt: Chat on Gitter
OAuth often seems complicated and difficult-to-implement. There are several
prominent libraries for handling OAuth requests, but they all suffer from one or
both of the following:
1. They predate the `OAuth 1.0 spec`_, AKA RFC 5849.
2. They predate the `OAuth 2.0 spec`_, AKA RFC 6749.
3. They assume the usage of a specific HTTP request library.
.. _`OAuth 1.0 spec`: https://tools.ietf.org/html/rfc5849
.. _`OAuth 2.0 spec`: https://tools.ietf.org/html/rfc6749
OAuthLib is a framework which implements the logic of OAuth1 or OAuth2 without
assuming a specific HTTP request object or web framework. Use it to graft OAuth
client support onto your favorite HTTP library, or provide support onto your
favourite web framework. If you're a maintainer of such a library, write a thin
veneer on top of OAuthLib and get OAuth support for very little effort.
Documentation
--------------
Full documentation is available on `Read the Docs`_. All contributions are very
welcome! The documentation is still quite sparse, please open an issue for what
you'd like to know, or discuss it in our `Gitter community`_, or even better, send a
pull request!
.. _`Gitter community`: https://gitter.im/oauthlib/Lobby
.. _`Read the Docs`: https://oauthlib.readthedocs.io/en/latest/index.html
Interested in making OAuth requests?
------------------------------------
Then you might be more interested in using `requests`_ which has OAuthLib
powered OAuth support provided by the `requests-oauthlib`_ library.
.. _`requests`: https://github.com/requests/requests
.. _`requests-oauthlib`: https://github.com/requests/requests-oauthlib
Which web frameworks are supported?
-----------------------------------
The following packages provide OAuth support using OAuthLib.
- For Django there is `django-oauth-toolkit`_, which includes `Django REST framework`_ support.
- For Flask there is `flask-oauthlib`_ and `Flask-Dance`_.
- For Pyramid there is `pyramid-oauthlib`_.
- For Bottle there is `bottle-oauthlib`_.
If you have written an OAuthLib package that supports your favorite framework,
please open a Pull Request, updating the documentation.
.. _`django-oauth-toolkit`: https://github.com/evonove/django-oauth-toolkit
.. _`flask-oauthlib`: https://github.com/lepture/flask-oauthlib
.. _`Django REST framework`: http://django-rest-framework.org
.. _`Flask-Dance`: https://github.com/singingwolfboy/flask-dance
.. _`pyramid-oauthlib`: https://github.com/tilgovi/pyramid-oauthlib
.. _`bottle-oauthlib`: https://github.com/thomsonreuters/bottle-oauthlib
Using OAuthLib? Please get in touch!
------------------------------------
Patching OAuth support onto an http request framework? Creating an OAuth
provider extension for a web framework? Simply using OAuthLib to Get Things Done
or to learn?
No matter which we'd love to hear from you in our `Gitter community`_ or if you have
anything in particular you would like to have, change or comment on don't
hesitate for a second to send a pull request or open an issue. We might be quite
busy and therefore slow to reply but we love feedback!
Chances are you have run into something annoying that you wish there was
documentation for, if you wish to gain eternal fame and glory, and a drink if we
have the pleasure to run into eachother, please send a docs pull request =)
.. _`Gitter community`: https://gitter.im/oauthlib/Lobby
License
-------
OAuthLib is yours to use and abuse according to the terms of the BSD license.
Check the LICENSE file for full details.
Credits
-------
OAuthLib has been started and maintained several years by Idan Gazit and other
amazing `AUTHORS`_. Thanks to their wonderful work, the open-source `community`_
creation has been possible and the project can stay active and reactive to users
requests.
.. _`AUTHORS`: https://github.com/oauthlib/oauthlib/blob/master/AUTHORS
.. _`community`: https://github.com/oauthlib/
Changelog
---------
*OAuthLib is in active development, with the core of both OAuth1 and OAuth2
completed, for providers as well as clients.* See `supported features`_ for
details.
.. _`supported features`: https://oauthlib.readthedocs.io/en/latest/feature_matrix.html
For a full changelog see ``CHANGELOG.rst``.
Platform: any
Classifier: Development Status :: 5 - Production/Stable
Classifier: Environment :: Web Environment
@ -150,18 +18,158 @@ Classifier: Operating System :: MacOS
Classifier: Operating System :: POSIX
Classifier: Operating System :: POSIX :: Linux
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 :: 3.10
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: Implementation
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Programming Language :: Python :: Implementation :: PyPy
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*
Requires-Python: >=3.6
Description-Content-Type: text/x-rst
Provides-Extra: rsa
Provides-Extra: signedtoken
Provides-Extra: signals
Provides-Extra: rsa
License-File: LICENSE
OAuthLib - Python Framework for OAuth1 & OAuth2
===============================================
*A generic, spec-compliant, thorough implementation of the OAuth request-signing
logic for Python 3.6+.*
.. image:: https://app.travis-ci.com/oauthlib/oauthlib.svg?branch=master
:target: https://app.travis-ci.com/oauthlib/oauthlib
:alt: Travis
.. image:: https://coveralls.io/repos/oauthlib/oauthlib/badge.svg?branch=master
:target: https://coveralls.io/r/oauthlib/oauthlib
:alt: Coveralls
.. image:: https://img.shields.io/pypi/pyversions/oauthlib.svg
:target: https://pypi.org/project/oauthlib/
:alt: Download from PyPI
.. image:: https://img.shields.io/pypi/l/oauthlib.svg
:target: https://pypi.org/project/oauthlib/
:alt: License
.. image:: https://app.fossa.io/api/projects/git%2Bgithub.com%2Foauthlib%2Foauthlib.svg?type=shield
:target: https://app.fossa.io/projects/git%2Bgithub.com%2Foauthlib%2Foauthlib?ref=badge_shield
:alt: FOSSA Status
.. image:: https://img.shields.io/readthedocs/oauthlib.svg
:target: https://oauthlib.readthedocs.io/en/latest/index.html
:alt: Read the Docs
.. image:: https://badges.gitter.im/oauthlib/oauthlib.svg
:target: https://gitter.im/oauthlib/Lobby
:alt: Chat on Gitter
.. image:: https://raw.githubusercontent.com/oauthlib/oauthlib/8d71b161fd145d11c40d55c9ab66ac134a303253/docs/logo/oauthlib-banner-700x192.png
:target: https://github.com/oauthlib/oauthlib/
:alt: OAuth + Python = OAuthlib Python Framework
OAuth often seems complicated and difficult-to-implement. There are several
prominent libraries for handling OAuth requests, but they all suffer from one or
both of the following:
1. They predate the `OAuth 1.0 spec`_, AKA RFC 5849.
2. They predate the `OAuth 2.0 spec`_, AKA RFC 6749.
3. They assume the usage of a specific HTTP request library.
.. _`OAuth 1.0 spec`: https://tools.ietf.org/html/rfc5849
.. _`OAuth 2.0 spec`: https://tools.ietf.org/html/rfc6749
OAuthLib is a framework which implements the logic of OAuth1 or OAuth2 without
assuming a specific HTTP request object or web framework. Use it to graft OAuth
client support onto your favorite HTTP library, or provide support onto your
favourite web framework. If you're a maintainer of such a library, write a thin
veneer on top of OAuthLib and get OAuth support for very little effort.
Documentation
--------------
Full documentation is available on `Read the Docs`_. All contributions are very
welcome! The documentation is still quite sparse, please open an issue for what
you'd like to know, or discuss it in our `Gitter community`_, or even better, send a
pull request!
.. _`Gitter community`: https://gitter.im/oauthlib/Lobby
.. _`Read the Docs`: https://oauthlib.readthedocs.io/en/latest/index.html
Interested in making OAuth requests?
------------------------------------
Then you might be more interested in using `requests`_ which has OAuthLib
powered OAuth support provided by the `requests-oauthlib`_ library.
.. _`requests`: https://github.com/requests/requests
.. _`requests-oauthlib`: https://github.com/requests/requests-oauthlib
Which web frameworks are supported?
-----------------------------------
The following packages provide OAuth support using OAuthLib.
- For Django there is `django-oauth-toolkit`_, which includes `Django REST framework`_ support.
- For Flask there is `flask-oauthlib`_ and `Flask-Dance`_.
- For Pyramid there is `pyramid-oauthlib`_.
- For Bottle there is `bottle-oauthlib`_.
If you have written an OAuthLib package that supports your favorite framework,
please open a Pull Request, updating the documentation.
.. _`django-oauth-toolkit`: https://github.com/evonove/django-oauth-toolkit
.. _`flask-oauthlib`: https://github.com/lepture/flask-oauthlib
.. _`Django REST framework`: http://django-rest-framework.org
.. _`Flask-Dance`: https://github.com/singingwolfboy/flask-dance
.. _`pyramid-oauthlib`: https://github.com/tilgovi/pyramid-oauthlib
.. _`bottle-oauthlib`: https://github.com/thomsonreuters/bottle-oauthlib
Using OAuthLib? Please get in touch!
------------------------------------
Patching OAuth support onto an http request framework? Creating an OAuth
provider extension for a web framework? Simply using OAuthLib to Get Things Done
or to learn?
No matter which we'd love to hear from you in our `Gitter community`_ or if you have
anything in particular you would like to have, change or comment on don't
hesitate for a second to send a pull request or open an issue. We might be quite
busy and therefore slow to reply but we love feedback!
Chances are you have run into something annoying that you wish there was
documentation for, if you wish to gain eternal fame and glory, and a drink if we
have the pleasure to run into each other, please send a docs pull request =)
.. _`Gitter community`: https://gitter.im/oauthlib/Lobby
License
-------
OAuthLib is yours to use and abuse according to the terms of the BSD license.
Check the LICENSE file for full details.
Credits
-------
OAuthLib has been started and maintained several years by Idan Gazit and other
amazing `AUTHORS`_. Thanks to their wonderful work, the open-source `community`_
creation has been possible and the project can stay active and reactive to users
requests.
.. _`AUTHORS`: https://github.com/oauthlib/oauthlib/blob/master/AUTHORS
.. _`community`: https://github.com/oauthlib/
Changelog
---------
*OAuthLib is in active development, with the core of both OAuth1 and OAuth2
completed, for providers as well as clients.* See `supported features`_ for
details.
.. _`supported features`: https://oauthlib.readthedocs.io/en/latest/feature_matrix.html
For a full changelog see ``CHANGELOG.rst``.

View File

@ -58,6 +58,9 @@ oauthlib/oauth2/rfc6749/grant_types/client_credentials.py
oauthlib/oauth2/rfc6749/grant_types/implicit.py
oauthlib/oauth2/rfc6749/grant_types/refresh_token.py
oauthlib/oauth2/rfc6749/grant_types/resource_owner_password_credentials.py
oauthlib/oauth2/rfc8628/__init__.py
oauthlib/oauth2/rfc8628/clients/__init__.py
oauthlib/oauth2/rfc8628/clients/device.py
oauthlib/openid/__init__.py
oauthlib/openid/connect/__init__.py
oauthlib/openid/connect/core/__init__.py
@ -71,11 +74,12 @@ oauthlib/openid/connect/core/grant_types/__init__.py
oauthlib/openid/connect/core/grant_types/authorization_code.py
oauthlib/openid/connect/core/grant_types/base.py
oauthlib/openid/connect/core/grant_types/dispatchers.py
oauthlib/openid/connect/core/grant_types/exceptions.py
oauthlib/openid/connect/core/grant_types/hybrid.py
oauthlib/openid/connect/core/grant_types/implicit.py
oauthlib/openid/connect/core/grant_types/refresh_token.py
tests/__init__.py
tests/test_common.py
tests/test_uri_validate.py
tests/oauth1/__init__.py
tests/oauth1/rfc5849/__init__.py
tests/oauth1/rfc5849/test_client.py
@ -122,6 +126,9 @@ tests/oauth2/rfc6749/grant_types/test_client_credentials.py
tests/oauth2/rfc6749/grant_types/test_implicit.py
tests/oauth2/rfc6749/grant_types/test_refresh_token.py
tests/oauth2/rfc6749/grant_types/test_resource_owner_password.py
tests/oauth2/rfc8628/__init__.py
tests/oauth2/rfc8628/clients/__init__.py
tests/oauth2/rfc8628/clients/test_device.py
tests/openid/__init__.py
tests/openid/connect/__init__.py
tests/openid/connect/core/__init__.py
@ -138,4 +145,5 @@ tests/openid/connect/core/grant_types/test_base.py
tests/openid/connect/core/grant_types/test_dispatchers.py
tests/openid/connect/core/grant_types/test_hybrid.py
tests/openid/connect/core/grant_types/test_implicit.py
tests/openid/connect/core/grant_types/test_refresh_token.py
tests/unittest/__init__.py

View File

@ -1,10 +1,10 @@
[rsa]
cryptography
cryptography>=3.0.0
[signals]
blinker
blinker>=1.4.0
[signedtoken]
cryptography
pyjwt>=1.0.0
cryptography>=3.0.0
pyjwt<3,>=2.0.0

View File

@ -12,7 +12,7 @@ import logging
from logging import NullHandler
__author__ = 'The OAuthlib Community'
__version__ = '3.1.0'
__version__ = '3.2.2'
logging.getLogger('oauthlib').addHandler(NullHandler())

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
"""
oauthlib.common
~~~~~~~~~~~~~~
@ -6,34 +5,22 @@ oauthlib.common
This module provides data structures and utilities common
to all implementations of OAuth.
"""
from __future__ import absolute_import, unicode_literals
import collections
import datetime
import logging
import re
import sys
import time
import urllib.parse as urlparse
from urllib.parse import (
quote as _quote, unquote as _unquote, urlencode as _urlencode,
)
from . import get_debug
try:
from secrets import randbits
from secrets import SystemRandom
from secrets import SystemRandom, randbits
except ImportError:
from random import getrandbits as randbits
from random import SystemRandom
try:
from urllib import quote as _quote
from urllib import unquote as _unquote
from urllib import urlencode as _urlencode
except ImportError:
from urllib.parse import quote as _quote
from urllib.parse import unquote as _unquote
from urllib.parse import urlencode as _urlencode
try:
import urlparse
except ImportError:
import urllib.parse as urlparse
from random import SystemRandom, getrandbits as randbits
UNICODE_ASCII_CHARACTER_SET = ('abcdefghijklmnopqrstuvwxyz'
'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
@ -51,17 +38,10 @@ always_safe = ('ABCDEFGHIJKLMNOPQRSTUVWXYZ'
log = logging.getLogger('oauthlib')
PY3 = sys.version_info[0] == 3
if PY3:
unicode_type = str
else:
unicode_type = unicode
# 'safe' must be bytes (Python 2.6 requires bytes, other versions allow either)
def quote(s, safe=b'/'):
s = s.encode('utf-8') if isinstance(s, unicode_type) else s
s = s.encode('utf-8') if isinstance(s, str) else s
s = _quote(s, safe)
# PY3 always returns unicode. PY2 may return either, depending on whether
# it had to modify the string.
@ -83,7 +63,7 @@ def unquote(s):
def urlencode(params):
utf8_params = encode_params_utf8(params)
urlencoded = _urlencode(utf8_params)
if isinstance(urlencoded, unicode_type): # PY3 returns unicode
if isinstance(urlencoded, str):
return urlencoded
else:
return urlencoded.decode("utf-8")
@ -96,8 +76,8 @@ def encode_params_utf8(params):
encoded = []
for k, v in params:
encoded.append((
k.encode('utf-8') if isinstance(k, unicode_type) else k,
v.encode('utf-8') if isinstance(v, unicode_type) else v))
k.encode('utf-8') if isinstance(k, str) else k,
v.encode('utf-8') if isinstance(v, str) else v))
return encoded
@ -141,22 +121,6 @@ def urldecode(query):
if INVALID_HEX_PATTERN.search(query):
raise ValueError('Invalid hex encoding in query string.')
# We encode to utf-8 prior to parsing because parse_qsl behaves
# differently on unicode input in python 2 and 3.
# Python 2.7
# >>> urlparse.parse_qsl(u'%E5%95%A6%E5%95%A6')
# u'\xe5\x95\xa6\xe5\x95\xa6'
# Python 2.7, non unicode input gives the same
# >>> urlparse.parse_qsl('%E5%95%A6%E5%95%A6')
# '\xe5\x95\xa6\xe5\x95\xa6'
# but now we can decode it to unicode
# >>> urlparse.parse_qsl('%E5%95%A6%E5%95%A6').decode('utf-8')
# u'\u5566\u5566'
# Python 3.3 however
# >>> urllib.parse.parse_qsl(u'%E5%95%A6%E5%95%A6')
# u'\u5566\u5566'
query = query.encode(
'utf-8') if not PY3 and isinstance(query, unicode_type) else query
# We want to allow queries such as "c2" whereas urlparse.parse_qsl
# with the strict_parsing flag will not.
params = urlparse.parse_qsl(query, keep_blank_values=True)
@ -173,7 +137,7 @@ def extract_params(raw):
empty list of parameters. Any other input will result in a return
value of None.
"""
if isinstance(raw, (bytes, unicode_type)):
if isinstance(raw, (bytes, str)):
try:
params = urldecode(raw)
except ValueError:
@ -206,7 +170,7 @@ def generate_nonce():
.. _`section 3.2.1`: https://tools.ietf.org/html/draft-ietf-oauth-v2-http-mac-01#section-3.2.1
.. _`section 3.3`: https://tools.ietf.org/html/rfc5849#section-3.3
"""
return unicode_type(unicode_type(randbits(64)) + generate_timestamp())
return str(str(randbits(64)) + generate_timestamp())
def generate_timestamp():
@ -218,7 +182,7 @@ def generate_timestamp():
.. _`section 3.2.1`: https://tools.ietf.org/html/draft-ietf-oauth-v2-http-mac-01#section-3.2.1
.. _`section 3.3`: https://tools.ietf.org/html/rfc5849#section-3.3
"""
return unicode_type(int(time.time()))
return str(int(time.time()))
def generate_token(length=30, chars=UNICODE_ASCII_CHARACTER_SET):
@ -305,11 +269,11 @@ def safe_string_equals(a, b):
def to_unicode(data, encoding='UTF-8'):
"""Convert a number of different types of objects to unicode."""
if isinstance(data, unicode_type):
if isinstance(data, str):
return data
if isinstance(data, bytes):
return unicode_type(data, encoding=encoding)
return str(data, encoding=encoding)
if hasattr(data, '__iter__'):
try:
@ -323,7 +287,7 @@ def to_unicode(data, encoding='UTF-8'):
# We support 2.6 which lacks dict comprehensions
if hasattr(data, 'items'):
data = data.items()
return dict(((to_unicode(k, encoding), to_unicode(v, encoding)) for k, v in data))
return {to_unicode(k, encoding): to_unicode(v, encoding) for k, v in data}
return data
@ -335,7 +299,7 @@ class CaseInsensitiveDict(dict):
proxy = {}
def __init__(self, data):
self.proxy = dict((k.lower(), k) for k in data)
self.proxy = {k.lower(): k for k in data}
for k in data:
self[k] = data[k]
@ -344,27 +308,27 @@ class CaseInsensitiveDict(dict):
def __delitem__(self, k):
key = self.proxy[k.lower()]
super(CaseInsensitiveDict, self).__delitem__(key)
super().__delitem__(key)
del self.proxy[k.lower()]
def __getitem__(self, k):
key = self.proxy[k.lower()]
return super(CaseInsensitiveDict, self).__getitem__(key)
return super().__getitem__(key)
def get(self, k, default=None):
return self[k] if k in self else default
def __setitem__(self, k, v):
super(CaseInsensitiveDict, self).__setitem__(k, v)
super().__setitem__(k, v)
self.proxy[k.lower()] = k
def update(self, *args, **kwargs):
super(CaseInsensitiveDict, self).update(*args, **kwargs)
super().update(*args, **kwargs)
for k in dict(*args, **kwargs):
self.proxy[k.lower()] = k
class Request(object):
class Request:
"""A malleable representation of a signable HTTP request.
@ -444,7 +408,7 @@ class Request(object):
body = SANITIZE_PATTERN.sub('\1<SANITIZED>', str(body))
if 'Authorization' in headers:
headers['Authorization'] = '<SANITIZED>'
return '<oauthlib.Request url="%s", http_method="%s", headers="%s", body="%s">' % (
return '<oauthlib.Request url="{}", http_method="{}", headers="{}", body="{}">'.format(
self.uri, self.http_method, headers, body)
@property

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
"""
oauthlib.oauth1
~~~~~~~~~~~~~~
@ -6,14 +5,19 @@ oauthlib.oauth1
This module is a wrapper for the most recent implementation of OAuth 1.0 Client
and Server classes.
"""
from __future__ import absolute_import, unicode_literals
from .rfc5849 import Client
from .rfc5849 import SIGNATURE_HMAC, SIGNATURE_HMAC_SHA1, SIGNATURE_HMAC_SHA256, SIGNATURE_RSA, SIGNATURE_PLAINTEXT
from .rfc5849 import SIGNATURE_TYPE_AUTH_HEADER, SIGNATURE_TYPE_QUERY
from .rfc5849 import SIGNATURE_TYPE_BODY
from .rfc5849 import (
SIGNATURE_HMAC, SIGNATURE_HMAC_SHA1, SIGNATURE_HMAC_SHA256,
SIGNATURE_HMAC_SHA512, SIGNATURE_PLAINTEXT, SIGNATURE_RSA,
SIGNATURE_RSA_SHA1, SIGNATURE_RSA_SHA256, SIGNATURE_RSA_SHA512,
SIGNATURE_TYPE_AUTH_HEADER, SIGNATURE_TYPE_BODY, SIGNATURE_TYPE_QUERY,
Client,
)
from .rfc5849.endpoints import (
AccessTokenEndpoint, AuthorizationEndpoint, RequestTokenEndpoint,
ResourceEndpoint, SignatureOnlyEndpoint, WebApplicationServer,
)
from .rfc5849.errors import (
InsecureTransportError, InvalidClientError, InvalidRequestError,
InvalidSignatureMethodError, OAuth1Error,
)
from .rfc5849.request_validator import RequestValidator
from .rfc5849.endpoints import RequestTokenEndpoint, AuthorizationEndpoint
from .rfc5849.endpoints import AccessTokenEndpoint, ResourceEndpoint
from .rfc5849.endpoints import SignatureOnlyEndpoint, WebApplicationServer
from .rfc5849.errors import InsecureTransportError, InvalidClientError, InvalidRequestError, InvalidSignatureMethodError, OAuth1Error

View File

@ -1,33 +1,68 @@
# -*- coding: utf-8 -*-
"""
oauthlib.oauth1.rfc5849
~~~~~~~~~~~~~~
This module is an implementation of various logic needed
for signing and checking OAuth 1.0 RFC 5849 requests.
It supports all three standard signature methods defined in RFC 5849:
- HMAC-SHA1
- RSA-SHA1
- PLAINTEXT
It also supports signature methods that are not defined in RFC 5849. These are
based on the standard ones but replace SHA-1 with the more secure SHA-256:
- HMAC-SHA256
- RSA-SHA256
"""
from __future__ import absolute_import, unicode_literals
import base64
import hashlib
import logging
import urllib.parse as urlparse
from oauthlib.common import (
Request, generate_nonce, generate_timestamp, to_unicode, urlencode,
)
from . import parameters, signature
log = logging.getLogger(__name__)
import sys
try:
import urlparse
except ImportError:
import urllib.parse as urlparse
from oauthlib.common import Request, urlencode, generate_nonce
from oauthlib.common import generate_timestamp, to_unicode
from . import parameters, signature
# Available signature methods
#
# Note: SIGNATURE_HMAC and SIGNATURE_RSA are kept for backward compatibility
# with previous versions of this library, when it the only HMAC-based and
# RSA-based signature methods were HMAC-SHA1 and RSA-SHA1. But now that it
# supports other hashing algorithms besides SHA1, explicitly identifying which
# hashing algorithm is being used is recommended.
#
# Note: if additional values are defined here, don't forget to update the
# imports in "../__init__.py" so they are available outside this module.
SIGNATURE_HMAC_SHA1 = "HMAC-SHA1"
SIGNATURE_HMAC_SHA256 = "HMAC-SHA256"
SIGNATURE_HMAC = SIGNATURE_HMAC_SHA1
SIGNATURE_RSA = "RSA-SHA1"
SIGNATURE_HMAC_SHA512 = "HMAC-SHA512"
SIGNATURE_HMAC = SIGNATURE_HMAC_SHA1 # deprecated variable for HMAC-SHA1
SIGNATURE_RSA_SHA1 = "RSA-SHA1"
SIGNATURE_RSA_SHA256 = "RSA-SHA256"
SIGNATURE_RSA_SHA512 = "RSA-SHA512"
SIGNATURE_RSA = SIGNATURE_RSA_SHA1 # deprecated variable for RSA-SHA1
SIGNATURE_PLAINTEXT = "PLAINTEXT"
SIGNATURE_METHODS = (SIGNATURE_HMAC_SHA1, SIGNATURE_HMAC_SHA256, SIGNATURE_RSA, SIGNATURE_PLAINTEXT)
SIGNATURE_METHODS = (
SIGNATURE_HMAC_SHA1,
SIGNATURE_HMAC_SHA256,
SIGNATURE_HMAC_SHA512,
SIGNATURE_RSA_SHA1,
SIGNATURE_RSA_SHA256,
SIGNATURE_RSA_SHA512,
SIGNATURE_PLAINTEXT
)
SIGNATURE_TYPE_AUTH_HEADER = 'AUTH_HEADER'
SIGNATURE_TYPE_QUERY = 'QUERY'
@ -36,13 +71,16 @@ SIGNATURE_TYPE_BODY = 'BODY'
CONTENT_TYPE_FORM_URLENCODED = 'application/x-www-form-urlencoded'
class Client(object):
class Client:
"""A client used to sign OAuth 1.0 RFC 5849 requests."""
SIGNATURE_METHODS = {
SIGNATURE_HMAC_SHA1: signature.sign_hmac_sha1_with_client,
SIGNATURE_HMAC_SHA256: signature.sign_hmac_sha256_with_client,
SIGNATURE_RSA: signature.sign_rsa_sha1_with_client,
SIGNATURE_HMAC_SHA512: signature.sign_hmac_sha512_with_client,
SIGNATURE_RSA_SHA1: signature.sign_rsa_sha1_with_client,
SIGNATURE_RSA_SHA256: signature.sign_rsa_sha256_with_client,
SIGNATURE_RSA_SHA512: signature.sign_rsa_sha512_with_client,
SIGNATURE_PLAINTEXT: signature.sign_plaintext_with_client
}
@ -106,8 +144,8 @@ class Client(object):
attrs['rsa_key'] = '****' if attrs['rsa_key'] else None
attrs[
'resource_owner_secret'] = '****' if attrs['resource_owner_secret'] else None
attribute_str = ', '.join('%s=%s' % (k, v) for k, v in attrs.items())
return '<%s %s>' % (self.__class__.__name__, attribute_str)
attribute_str = ', '.join('{}={}'.format(k, v) for k, v in attrs.items())
return '<{} {}>'.format(self.__class__.__name__, attribute_str)
def get_oauth_signature(self, request):
"""Get an OAuth signature to be used in signing a request
@ -130,24 +168,24 @@ class Client(object):
uri_query=urlparse.urlparse(uri).query,
body=body,
headers=headers)
log.debug("Collected params: {0}".format(collected_params))
log.debug("Collected params: {}".format(collected_params))
normalized_params = signature.normalize_parameters(collected_params)
normalized_uri = signature.base_string_uri(uri, headers.get('Host', None))
log.debug("Normalized params: {0}".format(normalized_params))
log.debug("Normalized URI: {0}".format(normalized_uri))
log.debug("Normalized params: {}".format(normalized_params))
log.debug("Normalized URI: {}".format(normalized_uri))
base_string = signature.signature_base_string(request.http_method,
normalized_uri, normalized_params)
log.debug("Signing: signature base string: {0}".format(base_string))
log.debug("Signing: signature base string: {}".format(base_string))
if self.signature_method not in self.SIGNATURE_METHODS:
raise ValueError('Invalid signature method.')
sig = self.SIGNATURE_METHODS[self.signature_method](base_string, self)
log.debug("Signature: {0}".format(sig))
log.debug("Signature: {}".format(sig))
return sig
def get_oauth_params(self, request):
@ -278,8 +316,8 @@ class Client(object):
# header field set to "application/x-www-form-urlencoded".
elif not should_have_params and has_params:
raise ValueError(
"Body contains parameters but Content-Type header was {0} "
"instead of {1}".format(content_type or "not set",
"Body contains parameters but Content-Type header was {} "
"instead of {}".format(content_type or "not set",
CONTENT_TYPE_FORM_URLENCODED))
# 3.5.2. Form-Encoded Body

View File

@ -1,9 +1,8 @@
from __future__ import absolute_import
from .access_token import AccessTokenEndpoint
from .authorization import AuthorizationEndpoint
from .base import BaseEndpoint
from .request_token import RequestTokenEndpoint
from .authorization import AuthorizationEndpoint
from .access_token import AccessTokenEndpoint
from .resource import ResourceEndpoint
from .signature_only import SignatureOnlyEndpoint
from .pre_configured import WebApplicationServer
from .pre_configured import WebApplicationServer # isort:skip

View File

@ -8,8 +8,6 @@ OAuth 1.0 RFC 5849. It validates the correctness of access token requests,
creates and persists tokens as well as create the proper response to be
returned to the client.
"""
from __future__ import absolute_import, unicode_literals
import logging
from oauthlib.common import urlencode

View File

@ -6,18 +6,13 @@ oauthlib.oauth1.rfc5849.endpoints.authorization
This module is an implementation of various logic needed
for signing and checking OAuth 1.0 RFC 5849 requests.
"""
from __future__ import absolute_import, unicode_literals
from urllib.parse import urlencode
from oauthlib.common import Request, add_params_to_uri
from oauthlib.common import add_params_to_uri
from .. import errors
from .base import BaseEndpoint
try:
from urllib import urlencode
except ImportError:
from urllib.parse import urlencode
class AuthorizationEndpoint(BaseEndpoint):

View File

@ -6,18 +6,19 @@ oauthlib.oauth1.rfc5849.endpoints.base
This module is an implementation of various logic needed
for signing and checking OAuth 1.0 RFC 5849 requests.
"""
from __future__ import absolute_import, unicode_literals
import time
from oauthlib.common import CaseInsensitiveDict, Request, generate_token
from .. import (CONTENT_TYPE_FORM_URLENCODED, SIGNATURE_HMAC_SHA1, SIGNATURE_HMAC_SHA256, SIGNATURE_RSA,
SIGNATURE_TYPE_AUTH_HEADER, SIGNATURE_TYPE_BODY,
SIGNATURE_TYPE_QUERY, errors, signature, utils)
from .. import (
CONTENT_TYPE_FORM_URLENCODED, SIGNATURE_HMAC_SHA1, SIGNATURE_HMAC_SHA256,
SIGNATURE_HMAC_SHA512, SIGNATURE_PLAINTEXT, SIGNATURE_RSA_SHA1,
SIGNATURE_RSA_SHA256, SIGNATURE_RSA_SHA512, SIGNATURE_TYPE_AUTH_HEADER,
SIGNATURE_TYPE_BODY, SIGNATURE_TYPE_QUERY, errors, signature, utils,
)
class BaseEndpoint(object):
class BaseEndpoint:
def __init__(self, request_validator, token_generator=None):
self.request_validator = request_validator
@ -131,7 +132,7 @@ class BaseEndpoint(object):
if (not request.signature_method in
self.request_validator.allowed_signature_methods):
raise errors.InvalidSignatureMethodError(
description="Invalid signature, %s not in %r." % (
description="Invalid signature, {} not in {!r}.".format(
request.signature_method,
self.request_validator.allowed_signature_methods))
@ -179,38 +180,65 @@ class BaseEndpoint(object):
def _check_signature(self, request, is_token_request=False):
# ---- RSA Signature verification ----
if request.signature_method == SIGNATURE_RSA:
if request.signature_method == SIGNATURE_RSA_SHA1 or \
request.signature_method == SIGNATURE_RSA_SHA256 or \
request.signature_method == SIGNATURE_RSA_SHA512:
# RSA-based signature method
# The server verifies the signature per `[RFC3447] section 8.2.2`_
# .. _`[RFC3447] section 8.2.2`: https://tools.ietf.org/html/rfc3447#section-8.2.1
rsa_key = self.request_validator.get_rsa_key(
request.client_key, request)
valid_signature = signature.verify_rsa_sha1(request, rsa_key)
if request.signature_method == SIGNATURE_RSA_SHA1:
valid_signature = signature.verify_rsa_sha1(request, rsa_key)
elif request.signature_method == SIGNATURE_RSA_SHA256:
valid_signature = signature.verify_rsa_sha256(request, rsa_key)
elif request.signature_method == SIGNATURE_RSA_SHA512:
valid_signature = signature.verify_rsa_sha512(request, rsa_key)
else:
valid_signature = False
# ---- HMAC or Plaintext Signature verification ----
else:
# Non-RSA based signature method
# Servers receiving an authenticated request MUST validate it by:
# Recalculating the request signature independently as described in
# `Section 3.4`_ and comparing it to the value received from the
# client via the "oauth_signature" parameter.
# .. _`Section 3.4`: https://tools.ietf.org/html/rfc5849#section-3.4
client_secret = self.request_validator.get_client_secret(
request.client_key, request)
resource_owner_secret = None
if request.resource_owner_key:
if is_token_request:
resource_owner_secret = self.request_validator.get_request_token_secret(
request.client_key, request.resource_owner_key, request)
resource_owner_secret = \
self.request_validator.get_request_token_secret(
request.client_key, request.resource_owner_key,
request)
else:
resource_owner_secret = self.request_validator.get_access_token_secret(
request.client_key, request.resource_owner_key, request)
resource_owner_secret = \
self.request_validator.get_access_token_secret(
request.client_key, request.resource_owner_key,
request)
if request.signature_method == SIGNATURE_HMAC_SHA1:
valid_signature = signature.verify_hmac_sha1(request,
client_secret, resource_owner_secret)
valid_signature = signature.verify_hmac_sha1(
request, client_secret, resource_owner_secret)
elif request.signature_method == SIGNATURE_HMAC_SHA256:
valid_signature = signature.verify_hmac_sha256(request,
client_secret, resource_owner_secret)
valid_signature = signature.verify_hmac_sha256(
request, client_secret, resource_owner_secret)
elif request.signature_method == SIGNATURE_HMAC_SHA512:
valid_signature = signature.verify_hmac_sha512(
request, client_secret, resource_owner_secret)
elif request.signature_method == SIGNATURE_PLAINTEXT:
valid_signature = signature.verify_plaintext(
request, client_secret, resource_owner_secret)
else:
valid_signature = signature.verify_plaintext(request,
client_secret, resource_owner_secret)
valid_signature = False
return valid_signature

View File

@ -1,7 +1,7 @@
from __future__ import absolute_import, unicode_literals
from . import (AccessTokenEndpoint, AuthorizationEndpoint,
RequestTokenEndpoint, ResourceEndpoint)
from . import (
AccessTokenEndpoint, AuthorizationEndpoint, RequestTokenEndpoint,
ResourceEndpoint,
)
class WebApplicationServer(RequestTokenEndpoint, AuthorizationEndpoint,

View File

@ -8,8 +8,6 @@ OAuth 1.0 RFC 5849. It validates the correctness of request token requests,
creates and persists tokens as well as create the proper response to be
returned to the client.
"""
from __future__ import absolute_import, unicode_literals
import logging
from oauthlib.common import urlencode
@ -129,7 +127,7 @@ class RequestTokenEndpoint(BaseEndpoint):
request.client_key, request)
if not self.request_validator.check_realms(request.realms):
raise errors.InvalidRequestError(
description='Invalid realm %s. Allowed are %r.' % (
description='Invalid realm {}. Allowed are {!r}.'.format(
request.realms, self.request_validator.realms))
if not request.redirect_uri:
@ -154,7 +152,7 @@ class RequestTokenEndpoint(BaseEndpoint):
request.client_key = self.request_validator.dummy_client
# Note that `realm`_ is only used in authorization headers and how
# it should be interepreted is not included in the OAuth spec.
# it should be interpreted is not included in the OAuth spec.
# However they could be seen as a scope or realm to which the
# client has access and as such every client should be checked
# to ensure it is authorized access to that scope or realm.
@ -166,7 +164,7 @@ class RequestTokenEndpoint(BaseEndpoint):
# workflow where a client requests access to a specific realm.
# This first step (obtaining request token) need not require a realm
# and can then be identified by checking the require_resource_owner
# flag and abscence of realm.
# flag and absence of realm.
#
# Clients obtaining an access token will not supply a realm and it will
# not be checked. Instead the previously requested realm should be

View File

@ -6,8 +6,6 @@ oauthlib.oauth1.rfc5849.endpoints.resource
This module is an implementation of the resource protection provider logic of
OAuth 1.0 RFC 5849.
"""
from __future__ import absolute_import, unicode_literals
import logging
from .. import errors
@ -115,7 +113,7 @@ class ResourceEndpoint(BaseEndpoint):
request.resource_owner_key = self.request_validator.dummy_access_token
# Note that `realm`_ is only used in authorization headers and how
# it should be interepreted is not included in the OAuth spec.
# it should be interpreted is not included in the OAuth spec.
# However they could be seen as a scope or realm to which the
# client has access and as such every client should be checked
# to ensure it is authorized access to that scope or realm.
@ -127,7 +125,7 @@ class ResourceEndpoint(BaseEndpoint):
# workflow where a client requests access to a specific realm.
# This first step (obtaining request token) need not require a realm
# and can then be identified by checking the require_resource_owner
# flag and abscence of realm.
# flag and absence of realm.
#
# Clients obtaining an access token will not supply a realm and it will
# not be checked. Instead the previously requested realm should be

View File

@ -6,8 +6,6 @@ oauthlib.oauth1.rfc5849.endpoints.signature_only
This module is an implementation of the signing logic of OAuth 1.0 RFC 5849.
"""
from __future__ import absolute_import, unicode_literals
import logging
from .. import errors

View File

@ -1,4 +1,3 @@
# coding=utf-8
"""
oauthlib.oauth1.rfc5849.errors
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -6,8 +5,6 @@ oauthlib.oauth1.rfc5849.errors
Error used both by OAuth 1 clients and provicers to represent the spec
defined error responses for all four core grant types.
"""
from __future__ import unicode_literals
from oauthlib.common import add_params_to_uri, urlencode
@ -37,10 +34,10 @@ class OAuth1Error(Exception):
request: Oauthlib Request object
"""
self.description = description or self.description
message = '(%s) %s' % (self.error, self.description)
message = '({}) {}'.format(self.error, self.description)
if request:
message += ' ' + repr(request)
super(OAuth1Error, self).__init__(message)
super().__init__(message)
self.uri = uri
self.status_code = status_code

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
"""
oauthlib.parameters
~~~~~~~~~~~~~~~~~~~
@ -7,17 +6,12 @@ This module contains methods related to `section 3.5`_ of the OAuth 1.0a spec.
.. _`section 3.5`: https://tools.ietf.org/html/rfc5849#section-3.5
"""
from __future__ import absolute_import, unicode_literals
from urllib.parse import urlparse, urlunparse
from oauthlib.common import extract_params, urlencode
from . import utils
try:
from urlparse import urlparse, urlunparse
except ImportError: # noqa
from urllib.parse import urlparse, urlunparse
# TODO: do we need filter_params now that oauth_params are handled by Request?
# We can easily pass in just oauth protocol params.
@ -61,7 +55,7 @@ def prepare_headers(oauth_params, headers=None, realm=None):
# 2. Each parameter's name is immediately followed by an "=" character
# (ASCII code 61), a """ character (ASCII code 34), the parameter
# value (MAY be empty), and another """ character (ASCII code 34).
part = '{0}="{1}"'.format(escaped_name, escaped_value)
part = '{}="{}"'.format(escaped_name, escaped_value)
authorization_header_parameters_parts.append(part)

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
"""
oauthlib.oauth1.rfc5849
~~~~~~~~~~~~~~
@ -6,14 +5,10 @@ oauthlib.oauth1.rfc5849
This module is an implementation of various logic needed
for signing and checking OAuth 1.0 RFC 5849 requests.
"""
from __future__ import absolute_import, unicode_literals
import sys
from . import SIGNATURE_METHODS, utils
class RequestValidator(object):
class RequestValidator:
"""A validator/datastore interaction base class for OAuth 1 providers.
@ -24,7 +19,7 @@ class RequestValidator(object):
Methods used to check the format of input parameters. Common tests include
length, character set, membership, range or pattern. These tests are
referred to as `whitelisting or blacklisting`_. Whitelisting is better
but blacklisting can be usefull to spot malicious activity.
but blacklisting can be useful to spot malicious activity.
The following have methods a default implementation:
- check_client_key
@ -197,7 +192,7 @@ class RequestValidator(object):
def check_realms(self, realms):
"""Check that the realm is one of a set allowed realms."""
return all((r in self.realms for r in realms))
return all(r in self.realms for r in realms)
def _subclass_must_implement(self, fn):
"""
@ -448,7 +443,7 @@ class RequestValidator(object):
:type request: oauthlib.common.Request
:returns: None
Per `Section 2.3`__ of the spec:
Per `Section 2.3`_ of the spec:
"The server MUST (...) ensure that the temporary
credentials have not expired or been used before."
@ -836,7 +831,7 @@ class RequestValidator(object):
"""Associate an authorization verifier with a request token.
:param token: A request token string.
:param verifier A dictionary containing the oauth_verifier and
:param verifier: A dictionary containing the oauth_verifier and
oauth_token
:param request: OAuthlib request.
:type request: oauthlib.common.Request

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
"""
oauthlib.utils
~~~~~~~~~~~~~~
@ -6,15 +5,9 @@ oauthlib.utils
This module contains utility methods used by various parts of the OAuth
spec.
"""
from __future__ import absolute_import, unicode_literals
from oauthlib.common import quote, unicode_type, unquote
try:
import urllib2
except ImportError:
import urllib.request as urllib2
import urllib.request as urllib2
from oauthlib.common import quote, unquote
UNICODE_ASCII_CHARACTER_SET = ('abcdefghijklmnopqrstuvwxyz'
'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
@ -52,16 +45,16 @@ def escape(u):
.. _`section 3.6`: https://tools.ietf.org/html/rfc5849#section-3.6
"""
if not isinstance(u, unicode_type):
if not isinstance(u, str):
raise ValueError('Only unicode objects are escapable. ' +
'Got %r of type %s.' % (u, type(u)))
'Got {!r} of type {}.'.format(u, type(u)))
# Letters, digits, and the characters '_.-' are already treated as safe
# by urllib.quote(). We need to add '~' to fully support rfc5849.
return quote(u, safe=b'~')
def unescape(u):
if not isinstance(u, unicode_type):
if not isinstance(u, str):
raise ValueError('Only unicode objects are unescapable.')
return unquote(u)

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
"""
oauthlib.oauth2
~~~~~~~~~~~~~~
@ -6,31 +5,32 @@ oauthlib.oauth2
This module is a wrapper for the most recent implementation of OAuth 2.0 Client
and Server classes.
"""
from __future__ import absolute_import, unicode_literals
from .rfc6749.clients import Client
from .rfc6749.clients import WebApplicationClient
from .rfc6749.clients import MobileApplicationClient
from .rfc6749.clients import LegacyApplicationClient
from .rfc6749.clients import BackendApplicationClient
from .rfc6749.clients import ServiceApplicationClient
from .rfc6749.endpoints import AuthorizationEndpoint
from .rfc6749.endpoints import IntrospectEndpoint
from .rfc6749.endpoints import MetadataEndpoint
from .rfc6749.endpoints import TokenEndpoint
from .rfc6749.endpoints import ResourceEndpoint
from .rfc6749.endpoints import RevocationEndpoint
from .rfc6749.endpoints import Server
from .rfc6749.endpoints import WebApplicationServer
from .rfc6749.endpoints import MobileApplicationServer
from .rfc6749.endpoints import LegacyApplicationServer
from .rfc6749.endpoints import BackendApplicationServer
from .rfc6749.errors import AccessDeniedError, OAuth2Error, FatalClientError, InsecureTransportError, InvalidClientError, InvalidClientIdError, InvalidGrantError, InvalidRedirectURIError, InvalidRequestError, InvalidRequestFatalError, InvalidScopeError, MismatchingRedirectURIError, MismatchingStateError, MissingClientIdError, MissingCodeError, MissingRedirectURIError, MissingResponseTypeError, MissingTokenError, MissingTokenTypeError, ServerError, TemporarilyUnavailableError, TokenExpiredError, UnauthorizedClientError, UnsupportedGrantTypeError, UnsupportedResponseTypeError, UnsupportedTokenTypeError
from .rfc6749.grant_types import AuthorizationCodeGrant
from .rfc6749.grant_types import ImplicitGrant
from .rfc6749.grant_types import ResourceOwnerPasswordCredentialsGrant
from .rfc6749.grant_types import ClientCredentialsGrant
from .rfc6749.grant_types import RefreshTokenGrant
from .rfc6749.clients import (
BackendApplicationClient, Client, LegacyApplicationClient,
MobileApplicationClient, ServiceApplicationClient, WebApplicationClient,
)
from .rfc6749.endpoints import (
AuthorizationEndpoint, BackendApplicationServer, IntrospectEndpoint,
LegacyApplicationServer, MetadataEndpoint, MobileApplicationServer,
ResourceEndpoint, RevocationEndpoint, Server, TokenEndpoint,
WebApplicationServer,
)
from .rfc6749.errors import (
AccessDeniedError, FatalClientError, InsecureTransportError,
InvalidClientError, InvalidClientIdError, InvalidGrantError,
InvalidRedirectURIError, InvalidRequestError, InvalidRequestFatalError,
InvalidScopeError, MismatchingRedirectURIError, MismatchingStateError,
MissingClientIdError, MissingCodeError, MissingRedirectURIError,
MissingResponseTypeError, MissingTokenError, MissingTokenTypeError,
OAuth2Error, ServerError, TemporarilyUnavailableError, TokenExpiredError,
UnauthorizedClientError, UnsupportedGrantTypeError,
UnsupportedResponseTypeError, UnsupportedTokenTypeError,
)
from .rfc6749.grant_types import (
AuthorizationCodeGrant, ClientCredentialsGrant, ImplicitGrant,
RefreshTokenGrant, ResourceOwnerPasswordCredentialsGrant,
)
from .rfc6749.request_validator import RequestValidator
from .rfc6749.tokens import BearerToken, OAuth2Token
from .rfc6749.utils import is_secure_transport
from .rfc8628.clients import DeviceClient

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
"""
oauthlib.oauth2.rfc6749
~~~~~~~~~~~~~~~~~~~~~~~
@ -6,15 +5,12 @@ oauthlib.oauth2.rfc6749
This module is an implementation of various logic needed
for consuming and providing OAuth 2.0 RFC6749.
"""
from __future__ import absolute_import, unicode_literals
import functools
import logging
from .endpoints.base import BaseEndpoint
from .endpoints.base import catch_errors_and_unavailability
from .errors import TemporarilyUnavailableError, ServerError
from .errors import FatalClientError, OAuth2Error
from .endpoints.base import BaseEndpoint, catch_errors_and_unavailability
from .errors import (
FatalClientError, OAuth2Error, ServerError, TemporarilyUnavailableError,
)
log = logging.getLogger(__name__)

View File

@ -6,11 +6,9 @@ oauthlib.oauth2.rfc6749
This module is an implementation of various logic needed
for consuming OAuth 2.0 RFC6749.
"""
from __future__ import absolute_import, unicode_literals
from .base import Client, AUTH_HEADER, URI_QUERY, BODY
from .web_application import WebApplicationClient
from .mobile_application import MobileApplicationClient
from .legacy_application import LegacyApplicationClient
from .backend_application import BackendApplicationClient
from .base import AUTH_HEADER, BODY, URI_QUERY, Client
from .legacy_application import LegacyApplicationClient
from .mobile_application import MobileApplicationClient
from .service_application import ServiceApplicationClient
from .web_application import WebApplicationClient

View File

@ -6,9 +6,7 @@ oauthlib.oauth2.rfc6749
This module is an implementation of various logic needed
for consuming and providing OAuth 2.0 RFC6749.
"""
from __future__ import absolute_import, unicode_literals
from ..parameters import parse_token_response, prepare_token_request
from ..parameters import prepare_token_request
from .base import Client
@ -41,7 +39,7 @@ class BackendApplicationClient(Client):
format per `Appendix B`_ in the HTTP request entity-body:
:param body: Existing request body (URL encoded string) to embed parameters
into. This may contain extra paramters. Default ''.
into. This may contain extra parameters. Default ''.
:param scope: The scope of the access request as described by
`Section 3.3`_.

View File

@ -6,18 +6,22 @@ oauthlib.oauth2.rfc6749
This module is an implementation of various logic needed
for consuming OAuth 2.0 RFC6749.
"""
from __future__ import absolute_import, unicode_literals
import base64
import hashlib
import re
import secrets
import time
import warnings
from oauthlib.common import generate_token
from oauthlib.oauth2.rfc6749 import tokens
from oauthlib.oauth2.rfc6749.errors import (InsecureTransportError,
TokenExpiredError)
from oauthlib.oauth2.rfc6749.parameters import (parse_token_response,
prepare_token_request,
prepare_token_revocation_request)
from oauthlib.oauth2.rfc6749.errors import (
InsecureTransportError, TokenExpiredError,
)
from oauthlib.oauth2.rfc6749.parameters import (
parse_token_response, prepare_token_request,
prepare_token_revocation_request,
)
from oauthlib.oauth2.rfc6749.utils import is_secure_transport
AUTH_HEADER = 'auth_header'
@ -29,7 +33,7 @@ FORM_ENC_HEADERS = {
}
class Client(object):
class Client:
"""Base OAuth2 client responsible for access token management.
This class also acts as a generic interface providing methods common to all
@ -61,6 +65,9 @@ class Client(object):
state=None,
redirect_url=None,
state_generator=generate_token,
code_verifier=None,
code_challenge=None,
code_challenge_method=None,
**kwargs):
"""Initialize a client with commonly used attributes.
@ -99,6 +106,15 @@ class Client(object):
:param state_generator: A no argument state generation callable. Defaults
to :py:meth:`oauthlib.common.generate_token`.
:param code_verifier: PKCE parameter. A cryptographically random string that is used to correlate the
authorization request to the token request.
:param code_challenge: PKCE parameter. A challenge derived from the code verifier that is sent in the
authorization request, to be verified against later.
:param code_challenge_method: PKCE parameter. A method that was used to derive code challenge.
Defaults to "plain" if not present in the request.
"""
self.client_id = client_id
@ -113,6 +129,9 @@ class Client(object):
self.state_generator = state_generator
self.state = state
self.redirect_url = redirect_url
self.code_verifier = code_verifier
self.code_challenge = code_challenge
self.code_challenge_method = code_challenge_method
self.code = None
self.expires_in = None
self._expires_at = None
@ -186,8 +205,8 @@ class Client(object):
token_placement = token_placement or self.default_token_placement
case_insensitive_token_types = dict(
(k.lower(), v) for k, v in self.token_types.items())
case_insensitive_token_types = {
k.lower(): v for k, v in self.token_types.items()}
if not self.token_type.lower() in case_insensitive_token_types:
raise ValueError("Unsupported token type: %s" % self.token_type)
@ -209,23 +228,21 @@ class Client(object):
required parameters to the authorization URL.
:param authorization_url: Provider authorization endpoint URL.
:param state: CSRF protection string. Will be automatically created if
not provided. The generated state is available via the ``state``
attribute. Clients should verify that the state is unchanged and
present in the authorization response. This verification is done
automatically if using the ``authorization_response`` parameter
with ``prepare_token_request``.
not provided. The generated state is available via the ``state``
attribute. Clients should verify that the state is unchanged and
present in the authorization response. This verification is done
automatically if using the ``authorization_response`` parameter
with ``prepare_token_request``.
:param redirect_url: Redirect URL to which the user will be returned
after authorization. Must be provided unless previously setup with
the provider. If provided then it must also be provided in the
token request.
:param scope:
after authorization. Must be provided unless previously setup with
the provider. If provided then it must also be provided in the
token request.
:param scope: List of scopes to request. Must be equal to
or a subset of the scopes granted when obtaining the refresh
token. If none is provided, the ones provided in the constructor are
used.
:param kwargs: Additional parameters to included in the request.
:returns: The prepared request tuple with (url, headers, body).
"""
if not is_secure_transport(authorization_url):
@ -233,10 +250,11 @@ class Client(object):
self.state = state or self.state_generator()
self.redirect_url = redirect_url or self.redirect_url
self.scope = scope or self.scope
# do not assign scope to self automatically anymore
scope = self.scope if scope is None else scope
auth_url = self.prepare_request_uri(
authorization_url, redirect_uri=self.redirect_url,
scope=self.scope, state=self.state, **kwargs)
scope=scope, state=self.state, **kwargs)
return auth_url, FORM_ENC_HEADERS, ''
def prepare_token_request(self, token_url, authorization_response=None,
@ -248,22 +266,16 @@ class Client(object):
credentials.
:param token_url: Provider token creation endpoint URL.
:param authorization_response: The full redirection URL string, i.e.
the location to which the user was redirected after successfull
authorization. Used to mine credentials needed to obtain a token
in this step, such as authorization code.
the location to which the user was redirected after successful
authorization. Used to mine credentials needed to obtain a token
in this step, such as authorization code.
:param redirect_url: The redirect_url supplied with the authorization
request (if there was one).
request (if there was one).
:param state:
:param body: Existing request body (URL encoded string) to embed parameters
into. This may contain extra paramters. Default ''.
into. This may contain extra parameters. Default ''.
:param kwargs: Additional parameters to included in the request.
:returns: The prepared request tuple with (url, headers, body).
"""
if not is_secure_transport(token_url):
@ -289,26 +301,23 @@ class Client(object):
obtain a new access token, and possibly a new refresh token.
:param token_url: Provider token refresh endpoint URL.
:param refresh_token: Refresh token string.
:param body: Existing request body (URL encoded string) to embed parameters
into. This may contain extra paramters. Default ''.
into. This may contain extra parameters. Default ''.
:param scope: List of scopes to request. Must be equal to
or a subset of the scopes granted when obtaining the refresh
token.
or a subset of the scopes granted when obtaining the refresh
token. If none is provided, the ones provided in the constructor are
used.
:param kwargs: Additional parameters to included in the request.
:returns: The prepared request tuple with (url, headers, body).
"""
if not is_secure_transport(token_url):
raise InsecureTransportError()
self.scope = scope or self.scope
# do not assign scope to self automatically anymore
scope = self.scope if scope is None else scope
body = self.prepare_refresh_body(body=body,
refresh_token=refresh_token, scope=self.scope, **kwargs)
refresh_token=refresh_token, scope=scope, **kwargs)
return token_url, FORM_ENC_HEADERS, body
def prepare_token_revocation_request(self, revocation_url, token,
@ -316,20 +325,14 @@ class Client(object):
"""Prepare a token revocation request.
:param revocation_url: Provider token revocation endpoint URL.
:param token: The access or refresh token to be revoked (string).
:param token_type_hint: ``"access_token"`` (default) or
``"refresh_token"``. This is optional and if you wish to not pass it you
must provide ``token_type_hint=None``.
``"refresh_token"``. This is optional and if you wish to not pass it you
must provide ``token_type_hint=None``.
:param body:
:param callback: A jsonp callback such as ``package.callback`` to be invoked
upon receiving the response. Not that it should not include a () suffix.
upon receiving the response. Not that it should not include a () suffix.
:param kwargs: Additional parameters to included in the request.
:returns: The prepared request tuple with (url, headers, body).
Note that JSONP request may use GET requests as the parameters will
@ -337,7 +340,7 @@ class Client(object):
An example of a revocation request
.. code-block: http
.. code-block:: http
POST /revoke HTTP/1.1
Host: server.example.com
@ -348,7 +351,7 @@ class Client(object):
An example of a jsonp revocation request
.. code-block: http
.. code-block:: http
GET /revoke?token=agabcdefddddafdd&callback=package.myCallback HTTP/1.1
Host: server.example.com
@ -357,9 +360,9 @@ class Client(object):
and an error response
.. code-block: http
.. code-block:: javascript
package.myCallback({"error":"unsupported_token_type"});
package.myCallback({"error":"unsupported_token_type"});
Note that these requests usually require client credentials, client_id in
the case for public clients and provider specific authentication
@ -382,9 +385,11 @@ class Client(object):
returns an error response as described in `Section 5.2`_.
:param body: The response body from the token request.
:param scope: Scopes originally requested.
:param scope: Scopes originally requested. If none is provided, the ones
provided in the constructor are used.
:return: Dictionary of token parameters.
:raises: Warning if scope has changed. OAuth2Error if response is invalid.
:raises: Warning if scope has changed. :py:class:`oauthlib.oauth2.errors.OAuth2Error`
if response is invalid.
These response are json encoded and could easily be parsed without
the assistance of OAuthLib. However, there are a few subtle issues
@ -410,7 +415,7 @@ class Client(object):
If omitted, the authorization server SHOULD provide the
expiration time via other means or document the default value.
**scope**
**scope**
Providers may supply this in all responses but are required to only
if it has changed since the authorization request.
@ -418,6 +423,7 @@ class Client(object):
.. _`Section 5.2`: https://tools.ietf.org/html/rfc6749#section-5.2
.. _`Section 7.1`: https://tools.ietf.org/html/rfc6749#section-7.1
"""
scope = self.scope if scope is None else scope
self.token = parse_token_response(body, scope=scope)
self.populate_token_attributes(self.token)
return self.token
@ -427,21 +433,19 @@ class Client(object):
If the authorization server issued a refresh token to the client, the
client makes a refresh request to the token endpoint by adding the
following parameters using the "application/x-www-form-urlencoded"
following parameters using the `application/x-www-form-urlencoded`
format in the HTTP request entity-body:
grant_type
REQUIRED. Value MUST be set to "refresh_token".
refresh_token
REQUIRED. The refresh token issued to the client.
scope
OPTIONAL. The scope of the access request as described by
Section 3.3. The requested scope MUST NOT include any scope
not originally granted by the resource owner, and if omitted is
treated as equal to the scope originally granted by the
resource owner.
:param refresh_token: REQUIRED. The refresh token issued to the client.
:param scope: OPTIONAL. The scope of the access request as described by
Section 3.3. The requested scope MUST NOT include any scope
not originally granted by the resource owner, and if omitted is
treated as equal to the scope originally granted by the
resource owner. Note that if none is provided, the ones provided
in the constructor are used if any.
"""
refresh_token = refresh_token or self.refresh_token
scope = self.scope if scope is None else scope
return prepare_token_request(self.refresh_token_key, body=body, scope=scope,
refresh_token=refresh_token, **kwargs)
@ -461,6 +465,91 @@ class Client(object):
raise ValueError("Invalid token placement.")
return uri, headers, body
def create_code_verifier(self, length):
"""Create PKCE **code_verifier** used in computing **code_challenge**.
See `RFC7636 Section 4.1`_
:param length: REQUIRED. The length of the code_verifier.
The client first creates a code verifier, "code_verifier", for each
OAuth 2.0 [RFC6749] Authorization Request, in the following manner:
.. code-block:: text
code_verifier = high-entropy cryptographic random STRING using the
unreserved characters [A-Z] / [a-z] / [0-9] / "-" / "." / "_" / "~"
from Section 2.3 of [RFC3986], with a minimum length of 43 characters
and a maximum length of 128 characters.
.. _`RFC7636 Section 4.1`: https://tools.ietf.org/html/rfc7636#section-4.1
"""
code_verifier = None
if not length >= 43:
raise ValueError("Length must be greater than or equal to 43")
if not length <= 128:
raise ValueError("Length must be less than or equal to 128")
allowed_characters = re.compile('^[A-Zaa-z0-9-._~]')
code_verifier = secrets.token_urlsafe(length)
if not re.search(allowed_characters, code_verifier):
raise ValueError("code_verifier contains invalid characters")
self.code_verifier = code_verifier
return code_verifier
def create_code_challenge(self, code_verifier, code_challenge_method=None):
"""Create PKCE **code_challenge** derived from the **code_verifier**.
See `RFC7636 Section 4.2`_
:param code_verifier: REQUIRED. The **code_verifier** generated from `create_code_verifier()`.
:param code_challenge_method: OPTIONAL. The method used to derive the **code_challenge**. Acceptable values include `S256`. DEFAULT is `plain`.
The client then creates a code challenge derived from the code
verifier by using one of the following transformations on the code
verifier::
plain
code_challenge = code_verifier
S256
code_challenge = BASE64URL-ENCODE(SHA256(ASCII(code_verifier)))
If the client is capable of using `S256`, it MUST use `S256`, as
`S256` is Mandatory To Implement (MTI) on the server. Clients are
permitted to use `plain` only if they cannot support `S256` for some
technical reason and know via out-of-band configuration that the
server supports `plain`.
The plain transformation is for compatibility with existing
deployments and for constrained environments that can't use the S256 transformation.
.. _`RFC7636 Section 4.2`: https://tools.ietf.org/html/rfc7636#section-4.2
"""
code_challenge = None
if code_verifier == None:
raise ValueError("Invalid code_verifier")
if code_challenge_method == None:
code_challenge_method = "plain"
self.code_challenge_method = code_challenge_method
code_challenge = code_verifier
self.code_challenge = code_challenge
if code_challenge_method == "S256":
h = hashlib.sha256()
h.update(code_verifier.encode(encoding='ascii'))
sha256_val = h.digest()
code_challenge = bytes.decode(base64.urlsafe_b64encode(sha256_val))
# replace '+' with '-', '/' with '_', and remove trailing '='
code_challenge = code_challenge.replace("+", "-").replace("/", "_").replace("=", "")
self.code_challenge = code_challenge
return code_challenge
def _add_mac_token(self, uri, http_method='GET', body=None,
headers=None, token_placement=AUTH_HEADER, ext=None, **kwargs):
"""Add a MAC token to the request authorization header.
@ -503,7 +592,10 @@ class Client(object):
self._expires_at = time.time() + int(self.expires_in)
if 'expires_at' in response:
self._expires_at = int(response.get('expires_at'))
try:
self._expires_at = int(response.get('expires_at'))
except:
self._expires_at = None
if 'mac_key' in response:
self.mac_key = response.get('mac_key')

View File

@ -6,9 +6,7 @@ oauthlib.oauth2.rfc6749
This module is an implementation of various logic needed
for consuming and providing OAuth 2.0 RFC6749.
"""
from __future__ import absolute_import, unicode_literals
from ..parameters import parse_token_response, prepare_token_request
from ..parameters import prepare_token_request
from .base import Client
@ -38,7 +36,7 @@ class LegacyApplicationClient(Client):
grant_type = 'password'
def __init__(self, client_id, **kwargs):
super(LegacyApplicationClient, self).__init__(client_id, **kwargs)
super().__init__(client_id, **kwargs)
def prepare_request_body(self, username, password, body='', scope=None,
include_client_id=False, **kwargs):
@ -51,7 +49,7 @@ class LegacyApplicationClient(Client):
:param username: The resource owner username.
:param password: The resource owner password.
:param body: Existing request body (URL encoded string) to embed parameters
into. This may contain extra paramters. Default ''.
into. This may contain extra parameters. Default ''.
:param scope: The scope of the access request as described by
`Section 3.3`_.
:param include_client_id: `True` to send the `client_id` in the
@ -81,5 +79,6 @@ class LegacyApplicationClient(Client):
"""
kwargs['client_id'] = self.client_id
kwargs['include_client_id'] = include_client_id
scope = self.scope if scope is None else scope
return prepare_token_request(self.grant_type, body=body, username=username,
password=password, scope=scope, **kwargs)

View File

@ -6,8 +6,6 @@ oauthlib.oauth2.rfc6749
This module is an implementation of various logic needed
for consuming and providing OAuth 2.0 RFC6749.
"""
from __future__ import absolute_import, unicode_literals
from ..parameters import parse_implicit_response, prepare_grant_uri
from .base import Client
@ -57,7 +55,7 @@ class MobileApplicationClient(Client):
using the "application/x-www-form-urlencoded" format, per `Appendix B`_:
:param redirect_uri: OPTIONAL. The redirect URI must be an absolute URI
and it should have been registerd with the OAuth
and it should have been registered with the OAuth
provider prior to use. As described in `Section 3.1.2`_.
:param scope: OPTIONAL. The scope of the access request as described by
@ -93,6 +91,7 @@ class MobileApplicationClient(Client):
.. _`Section 3.3`: https://tools.ietf.org/html/rfc6749#section-3.3
.. _`Section 10.12`: https://tools.ietf.org/html/rfc6749#section-10.12
"""
scope = self.scope if scope is None else scope
return prepare_grant_uri(uri, self.client_id, self.response_type,
redirect_uri=redirect_uri, state=state, scope=scope, **kwargs)
@ -169,6 +168,7 @@ class MobileApplicationClient(Client):
.. _`Section 7.1`: https://tools.ietf.org/html/rfc6749#section-7.1
.. _`Section 3.3`: https://tools.ietf.org/html/rfc6749#section-3.3
"""
scope = self.scope if scope is None else scope
self.token = parse_implicit_response(uri, state=state, scope=scope)
self.populate_token_attributes(self.token)
return self.token

View File

@ -6,13 +6,11 @@ oauthlib.oauth2.rfc6749
This module is an implementation of various logic needed
for consuming and providing OAuth 2.0 RFC6749.
"""
from __future__ import absolute_import, unicode_literals
import time
from oauthlib.common import to_unicode
from ..parameters import parse_token_response, prepare_token_request
from ..parameters import prepare_token_request
from .base import Client
@ -33,7 +31,7 @@ class ServiceApplicationClient(Client):
def __init__(self, client_id, private_key=None, subject=None, issuer=None,
audience=None, **kwargs):
"""Initalize a JWT client with defaults for implicit use later.
"""Initialize a JWT client with defaults for implicit use later.
:param client_id: Client identifier given by the OAuth provider upon
registration.
@ -57,7 +55,7 @@ class ServiceApplicationClient(Client):
state and token. See ``Client.__init__.__doc__`` for
details.
"""
super(ServiceApplicationClient, self).__init__(client_id, **kwargs)
super().__init__(client_id, **kwargs)
self.private_key = private_key
self.subject = subject
self.issuer = issuer
@ -101,7 +99,7 @@ class ServiceApplicationClient(Client):
:param extra_claims: A dict of additional claims to include in the JWT.
:param body: Existing request body (URL encoded string) to embed parameters
into. This may contain extra paramters. Default ''.
into. This may contain extra parameters. Default ''.
:param scope: The scope of the access request.
@ -183,6 +181,7 @@ class ServiceApplicationClient(Client):
kwargs['client_id'] = self.client_id
kwargs['include_client_id'] = include_client_id
scope = self.scope if scope is None else scope
return prepare_token_request(self.grant_type,
body=body,
assertion=assertion,

View File

@ -6,13 +6,12 @@ oauthlib.oauth2.rfc6749
This module is an implementation of various logic needed
for consuming and providing OAuth 2.0 RFC6749.
"""
from __future__ import absolute_import, unicode_literals
import warnings
from ..parameters import (parse_authorization_code_response,
parse_token_response, prepare_grant_uri,
prepare_token_request)
from ..parameters import (
parse_authorization_code_response, prepare_grant_uri,
prepare_token_request,
)
from .base import Client
@ -38,11 +37,11 @@ class WebApplicationClient(Client):
grant_type = 'authorization_code'
def __init__(self, client_id, code=None, **kwargs):
super(WebApplicationClient, self).__init__(client_id, **kwargs)
super().__init__(client_id, **kwargs)
self.code = code
def prepare_request_uri(self, uri, redirect_uri=None, scope=None,
state=None, **kwargs):
state=None, code_challenge=None, code_challenge_method='plain', **kwargs):
"""Prepare the authorization code request URI
The client constructs the request URI by adding the following
@ -50,7 +49,7 @@ class WebApplicationClient(Client):
using the "application/x-www-form-urlencoded" format, per `Appendix B`_:
:param redirect_uri: OPTIONAL. The redirect URI must be an absolute URI
and it should have been registerd with the OAuth
and it should have been registered with the OAuth
provider prior to use. As described in `Section 3.1.2`_.
:param scope: OPTIONAL. The scope of the access request as described by
@ -63,6 +62,13 @@ class WebApplicationClient(Client):
to the client. The parameter SHOULD be used for preventing
cross-site request forgery as described in `Section 10.12`_.
:param code_challenge: OPTIONAL. PKCE parameter. REQUIRED if PKCE is enforced.
A challenge derived from the code_verifier that is sent in the
authorization request, to be verified against later.
:param code_challenge_method: OPTIONAL. PKCE parameter. A method that was used to derive code challenge.
Defaults to "plain" if not present in the request.
:param kwargs: Extra arguments to include in the request URI.
In addition to supplied parameters, OAuthLib will append the ``client_id``
@ -77,6 +83,10 @@ class WebApplicationClient(Client):
'https://example.com?client_id=your_id&response_type=code&redirect_uri=https%3A%2F%2Fa.b%2Fcallback'
>>> client.prepare_request_uri('https://example.com', scope=['profile', 'pictures'])
'https://example.com?client_id=your_id&response_type=code&scope=profile+pictures'
>>> client.prepare_request_uri('https://example.com', code_challenge='kjasBS523KdkAILD2k78NdcJSk2k3KHG6')
'https://example.com?client_id=your_id&response_type=code&code_challenge=kjasBS523KdkAILD2k78NdcJSk2k3KHG6'
>>> client.prepare_request_uri('https://example.com', code_challenge_method='S256')
'https://example.com?client_id=your_id&response_type=code&code_challenge_method=S256'
>>> client.prepare_request_uri('https://example.com', foo='bar')
'https://example.com?client_id=your_id&response_type=code&foo=bar'
@ -86,11 +96,13 @@ class WebApplicationClient(Client):
.. _`Section 3.3`: https://tools.ietf.org/html/rfc6749#section-3.3
.. _`Section 10.12`: https://tools.ietf.org/html/rfc6749#section-10.12
"""
scope = self.scope if scope is None else scope
return prepare_grant_uri(uri, self.client_id, 'code',
redirect_uri=redirect_uri, scope=scope, state=state, **kwargs)
redirect_uri=redirect_uri, scope=scope, state=state, code_challenge=code_challenge,
code_challenge_method=code_challenge_method, **kwargs)
def prepare_request_body(self, code=None, redirect_uri=None, body='',
include_client_id=True, **kwargs):
include_client_id=True, code_verifier=None, **kwargs):
"""Prepare the access token request body.
The client makes a request to the token endpoint by adding the
@ -105,7 +117,7 @@ class WebApplicationClient(Client):
values MUST be identical.
:param body: Existing request body (URL encoded string) to embed parameters
into. This may contain extra paramters. Default ''.
into. This may contain extra parameters. Default ''.
:param include_client_id: `True` (default) to send the `client_id` in the
body of the upstream request. This is required
@ -113,6 +125,9 @@ class WebApplicationClient(Client):
authorization server as described in `Section 3.2.1`_.
:type include_client_id: Boolean
:param code_verifier: OPTIONAL. A cryptographically random string that is used to correlate the
authorization request to the token request.
:param kwargs: Extra parameters to include in the token request.
In addition OAuthLib will add the ``grant_type`` parameter set to
@ -127,6 +142,8 @@ class WebApplicationClient(Client):
>>> client = WebApplicationClient('your_id')
>>> client.prepare_request_body(code='sh35ksdf09sf')
'grant_type=authorization_code&code=sh35ksdf09sf'
>>> client.prepare_request_body(code_verifier='KB46DCKJ873NCGXK5GD682NHDKK34GR')
'grant_type=authorization_code&code_verifier=KB46DCKJ873NCGXK5GD682NHDKK34GR'
>>> client.prepare_request_body(code='sh35ksdf09sf', foo='bar')
'grant_type=authorization_code&code=sh35ksdf09sf&foo=bar'
@ -154,7 +171,7 @@ class WebApplicationClient(Client):
kwargs['client_id'] = self.client_id
kwargs['include_client_id'] = include_client_id
return prepare_token_request(self.grant_type, code=code, body=body,
redirect_uri=redirect_uri, **kwargs)
redirect_uri=redirect_uri, code_verifier=code_verifier, **kwargs)
def parse_request_uri_response(self, uri, state=None):
"""Parse the URI query for code and state.

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
"""
oauthlib.oauth2.rfc6749
~~~~~~~~~~~~~~~~~~~~~~~
@ -6,16 +5,13 @@ oauthlib.oauth2.rfc6749
This module is an implementation of various logic needed
for consuming and providing OAuth 2.0 RFC6749.
"""
from __future__ import absolute_import, unicode_literals
from .authorization import AuthorizationEndpoint
from .introspect import IntrospectEndpoint
from .metadata import MetadataEndpoint
from .token import TokenEndpoint
from .pre_configured import (
BackendApplicationServer, LegacyApplicationServer, MobileApplicationServer,
Server, WebApplicationServer,
)
from .resource import ResourceEndpoint
from .revocation import RevocationEndpoint
from .pre_configured import Server
from .pre_configured import WebApplicationServer
from .pre_configured import MobileApplicationServer
from .pre_configured import LegacyApplicationServer
from .pre_configured import BackendApplicationServer
from .token import TokenEndpoint

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
"""
oauthlib.oauth2.rfc6749
~~~~~~~~~~~~~~~~~~~~~~~
@ -6,8 +5,6 @@ oauthlib.oauth2.rfc6749
This module is an implementation of various logic needed
for consuming and providing OAuth 2.0 RFC6749.
"""
from __future__ import absolute_import, unicode_literals
import logging
from oauthlib.common import Request

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
"""
oauthlib.oauth2.rfc6749
~~~~~~~~~~~~~~~~~~~~~~~
@ -6,21 +5,18 @@ oauthlib.oauth2.rfc6749
This module is an implementation of various logic needed
for consuming and providing OAuth 2.0 RFC6749.
"""
from __future__ import absolute_import, unicode_literals
import functools
import logging
from ..errors import (FatalClientError, OAuth2Error, ServerError,
TemporarilyUnavailableError, InvalidRequestError,
InvalidClientError, UnsupportedTokenTypeError)
from oauthlib.common import CaseInsensitiveDict, urldecode
from ..errors import (
FatalClientError, InvalidClientError, InvalidRequestError, OAuth2Error,
ServerError, TemporarilyUnavailableError, UnsupportedTokenTypeError,
)
log = logging.getLogger(__name__)
class BaseEndpoint(object):
class BaseEndpoint:
def __init__(self):
self._available = True

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
"""
oauthlib.oauth2.rfc6749.endpoint.introspect
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -7,14 +6,12 @@ An implementation of the OAuth 2.0 `Token Introspection`.
.. _`Token Introspection`: https://tools.ietf.org/html/rfc7662
"""
from __future__ import absolute_import, unicode_literals
import json
import logging
from oauthlib.common import Request
from ..errors import OAuth2Error, UnsupportedTokenTypeError
from ..errors import OAuth2Error
from .base import BaseEndpoint, catch_errors_and_unavailability
log = logging.getLogger(__name__)
@ -89,9 +86,9 @@ class IntrospectEndpoint(BaseEndpoint):
an HTTP POST request with parameters sent as
"application/x-www-form-urlencoded".
token REQUIRED. The string value of the token.
* token REQUIRED. The string value of the token.
* token_type_hint OPTIONAL.
token_type_hint OPTIONAL.
A hint about the type of the token submitted for
introspection. The protected resource MAY pass this parameter to
help the authorization server optimize the token lookup. If the
@ -99,11 +96,9 @@ class IntrospectEndpoint(BaseEndpoint):
extend its search across all of its supported token types. An
authorization server MAY ignore this parameter, particularly if it
is able to detect the token type automatically.
* access_token: An Access Token as defined in [`RFC6749`],
`section 1.4`_
* refresh_token: A Refresh Token as defined in [`RFC6749`],
`section 1.5`_
* access_token: An Access Token as defined in [`RFC6749`], `section 1.4`_
* refresh_token: A Refresh Token as defined in [`RFC6749`], `section 1.5`_
The introspection endpoint MAY accept other OPTIONAL
parameters to provide further context to the query. For

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
"""
oauthlib.oauth2.rfc6749.endpoint.metadata
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -7,20 +6,16 @@ An implementation of the `OAuth 2.0 Authorization Server Metadata`.
.. _`OAuth 2.0 Authorization Server Metadata`: https://tools.ietf.org/html/rfc8414
"""
from __future__ import absolute_import, unicode_literals
import copy
import json
import logging
from ....common import unicode_type
from .base import BaseEndpoint, catch_errors_and_unavailability
from .. import grant_types, utils
from .authorization import AuthorizationEndpoint
from .base import BaseEndpoint, catch_errors_and_unavailability
from .introspect import IntrospectEndpoint
from .token import TokenEndpoint
from .revocation import RevocationEndpoint
from .. import grant_types
from .token import TokenEndpoint
log = logging.getLogger(__name__)
@ -59,7 +54,8 @@ class MetadataEndpoint(BaseEndpoint):
"""Create metadata response
"""
headers = {
'Content-Type': 'application/json'
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*',
}
return headers, json.dumps(self.claims), 200
@ -72,7 +68,7 @@ class MetadataEndpoint(BaseEndpoint):
raise ValueError("key {} is a mandatory metadata.".format(key))
elif is_issuer:
if not array[key].startswith("https"):
if not utils.is_secure_transport(array[key]):
raise ValueError("key {}: {} must be an HTTPS URL".format(key, array[key]))
if "?" in array[key] or "&" in array[key] or "#" in array[key]:
raise ValueError("key {}: {} must not contain query or fragment components".format(key, array[key]))
@ -85,7 +81,7 @@ class MetadataEndpoint(BaseEndpoint):
if not isinstance(array[key], list):
raise ValueError("key {}: {} must be an Array".format(key, array[key]))
for elem in array[key]:
if not isinstance(elem, unicode_type):
if not isinstance(elem, str):
raise ValueError("array {}: {} must contains only string (not {})".format(key, array[key], elem))
def validate_metadata_token(self, claims, endpoint):
@ -166,10 +162,10 @@ class MetadataEndpoint(BaseEndpoint):
response_types_supported
REQUIRED.
* Other OPTIONAL fields:
jwks_uri
registration_endpoint
response_modes_supported
Other OPTIONAL fields:
jwks_uri,
registration_endpoint,
response_modes_supported
grant_types_supported
OPTIONAL. JSON array containing a list of the OAuth 2.0 grant

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
"""
oauthlib.oauth2.rfc6749.endpoints.pre_configured
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -6,13 +5,10 @@ oauthlib.oauth2.rfc6749.endpoints.pre_configured
This module is an implementation of various endpoints needed
for providing OAuth 2.0 RFC6749 servers.
"""
from __future__ import absolute_import, unicode_literals
from ..grant_types import (AuthorizationCodeGrant,
ClientCredentialsGrant,
ImplicitGrant,
RefreshTokenGrant,
ResourceOwnerPasswordCredentialsGrant)
from ..grant_types import (
AuthorizationCodeGrant, ClientCredentialsGrant, ImplicitGrant,
RefreshTokenGrant, ResourceOwnerPasswordCredentialsGrant,
)
from ..tokens import BearerToken
from .authorization import AuthorizationEndpoint
from .introspect import IntrospectEndpoint
@ -42,34 +38,34 @@ class Server(AuthorizationEndpoint, IntrospectEndpoint, TokenEndpoint,
:param kwargs: Extra parameters to pass to authorization-,
token-, resource-, and revocation-endpoint constructors.
"""
auth_grant = AuthorizationCodeGrant(request_validator)
implicit_grant = ImplicitGrant(request_validator)
password_grant = ResourceOwnerPasswordCredentialsGrant(
self.auth_grant = AuthorizationCodeGrant(request_validator)
self.implicit_grant = ImplicitGrant(request_validator)
self.password_grant = ResourceOwnerPasswordCredentialsGrant(
request_validator)
credentials_grant = ClientCredentialsGrant(request_validator)
refresh_grant = RefreshTokenGrant(request_validator)
self.credentials_grant = ClientCredentialsGrant(request_validator)
self.refresh_grant = RefreshTokenGrant(request_validator)
bearer = BearerToken(request_validator, token_generator,
self.bearer = BearerToken(request_validator, token_generator,
token_expires_in, refresh_token_generator)
AuthorizationEndpoint.__init__(self, default_response_type='code',
response_types={
'code': auth_grant,
'token': implicit_grant,
'none': auth_grant
'code': self.auth_grant,
'token': self.implicit_grant,
'none': self.auth_grant
},
default_token_type=bearer)
default_token_type=self.bearer)
TokenEndpoint.__init__(self, default_grant_type='authorization_code',
grant_types={
'authorization_code': auth_grant,
'password': password_grant,
'client_credentials': credentials_grant,
'refresh_token': refresh_grant,
'authorization_code': self.auth_grant,
'password': self.password_grant,
'client_credentials': self.credentials_grant,
'refresh_token': self.refresh_grant,
},
default_token_type=bearer)
default_token_type=self.bearer)
ResourceEndpoint.__init__(self, default_token='Bearer',
token_types={'Bearer': bearer})
token_types={'Bearer': self.bearer})
RevocationEndpoint.__init__(self, request_validator)
IntrospectEndpoint.__init__(self, request_validator)
@ -94,21 +90,21 @@ class WebApplicationServer(AuthorizationEndpoint, IntrospectEndpoint, TokenEndpo
:param kwargs: Extra parameters to pass to authorization-,
token-, resource-, and revocation-endpoint constructors.
"""
auth_grant = AuthorizationCodeGrant(request_validator)
refresh_grant = RefreshTokenGrant(request_validator)
bearer = BearerToken(request_validator, token_generator,
self.auth_grant = AuthorizationCodeGrant(request_validator)
self.refresh_grant = RefreshTokenGrant(request_validator)
self.bearer = BearerToken(request_validator, token_generator,
token_expires_in, refresh_token_generator)
AuthorizationEndpoint.__init__(self, default_response_type='code',
response_types={'code': auth_grant},
default_token_type=bearer)
response_types={'code': self.auth_grant},
default_token_type=self.bearer)
TokenEndpoint.__init__(self, default_grant_type='authorization_code',
grant_types={
'authorization_code': auth_grant,
'refresh_token': refresh_grant,
'authorization_code': self.auth_grant,
'refresh_token': self.refresh_grant,
},
default_token_type=bearer)
default_token_type=self.bearer)
ResourceEndpoint.__init__(self, default_token='Bearer',
token_types={'Bearer': bearer})
token_types={'Bearer': self.bearer})
RevocationEndpoint.__init__(self, request_validator)
IntrospectEndpoint.__init__(self, request_validator)
@ -133,15 +129,15 @@ class MobileApplicationServer(AuthorizationEndpoint, IntrospectEndpoint,
:param kwargs: Extra parameters to pass to authorization-,
token-, resource-, and revocation-endpoint constructors.
"""
implicit_grant = ImplicitGrant(request_validator)
bearer = BearerToken(request_validator, token_generator,
self.implicit_grant = ImplicitGrant(request_validator)
self.bearer = BearerToken(request_validator, token_generator,
token_expires_in, refresh_token_generator)
AuthorizationEndpoint.__init__(self, default_response_type='token',
response_types={
'token': implicit_grant},
default_token_type=bearer)
'token': self.implicit_grant},
default_token_type=self.bearer)
ResourceEndpoint.__init__(self, default_token='Bearer',
token_types={'Bearer': bearer})
token_types={'Bearer': self.bearer})
RevocationEndpoint.__init__(self, request_validator,
supported_token_types=['access_token'])
IntrospectEndpoint.__init__(self, request_validator,
@ -168,19 +164,19 @@ class LegacyApplicationServer(TokenEndpoint, IntrospectEndpoint,
:param kwargs: Extra parameters to pass to authorization-,
token-, resource-, and revocation-endpoint constructors.
"""
password_grant = ResourceOwnerPasswordCredentialsGrant(
self.password_grant = ResourceOwnerPasswordCredentialsGrant(
request_validator)
refresh_grant = RefreshTokenGrant(request_validator)
bearer = BearerToken(request_validator, token_generator,
self.refresh_grant = RefreshTokenGrant(request_validator)
self.bearer = BearerToken(request_validator, token_generator,
token_expires_in, refresh_token_generator)
TokenEndpoint.__init__(self, default_grant_type='password',
grant_types={
'password': password_grant,
'refresh_token': refresh_grant,
'password': self.password_grant,
'refresh_token': self.refresh_grant,
},
default_token_type=bearer)
default_token_type=self.bearer)
ResourceEndpoint.__init__(self, default_token='Bearer',
token_types={'Bearer': bearer})
token_types={'Bearer': self.bearer})
RevocationEndpoint.__init__(self, request_validator)
IntrospectEndpoint.__init__(self, request_validator)
@ -205,15 +201,15 @@ class BackendApplicationServer(TokenEndpoint, IntrospectEndpoint,
:param kwargs: Extra parameters to pass to authorization-,
token-, resource-, and revocation-endpoint constructors.
"""
credentials_grant = ClientCredentialsGrant(request_validator)
bearer = BearerToken(request_validator, token_generator,
self.credentials_grant = ClientCredentialsGrant(request_validator)
self.bearer = BearerToken(request_validator, token_generator,
token_expires_in, refresh_token_generator)
TokenEndpoint.__init__(self, default_grant_type='client_credentials',
grant_types={
'client_credentials': credentials_grant},
default_token_type=bearer)
'client_credentials': self.credentials_grant},
default_token_type=self.bearer)
ResourceEndpoint.__init__(self, default_token='Bearer',
token_types={'Bearer': bearer})
token_types={'Bearer': self.bearer})
RevocationEndpoint.__init__(self, request_validator,
supported_token_types=['access_token'])
IntrospectEndpoint.__init__(self, request_validator,

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
"""
oauthlib.oauth2.rfc6749
~~~~~~~~~~~~~~~~~~~~~~~
@ -6,8 +5,6 @@ oauthlib.oauth2.rfc6749
This module is an implementation of various logic needed
for consuming and providing OAuth 2.0 RFC6749.
"""
from __future__ import absolute_import, unicode_literals
import logging
from oauthlib.common import Request

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
"""
oauthlib.oauth2.rfc6749.endpoint.revocation
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -7,13 +6,11 @@ An implementation of the OAuth 2 `Token Revocation`_ spec (draft 11).
.. _`Token Revocation`: https://tools.ietf.org/html/draft-ietf-oauth-revocation-11
"""
from __future__ import absolute_import, unicode_literals
import logging
from oauthlib.common import Request
from ..errors import OAuth2Error, UnsupportedTokenTypeError
from ..errors import OAuth2Error
from .base import BaseEndpoint, catch_errors_and_unavailability
log = logging.getLogger(__name__)
@ -45,7 +42,7 @@ class RevocationEndpoint(BaseEndpoint):
The authorization server responds with HTTP status code 200 if the
token has been revoked sucessfully or if the client submitted an
token has been revoked successfully or if the client submitted an
invalid token.
Note: invalid tokens do not cause an error response since the client
@ -73,7 +70,7 @@ class RevocationEndpoint(BaseEndpoint):
log.debug('Client error during validation of %r. %r.', request, e)
response_body = e.json
if self.enable_jsonp and request.callback:
response_body = '%s(%s);' % (request.callback, response_body)
response_body = '{}({});'.format(request.callback, response_body)
resp_headers.update(e.headers)
return resp_headers, response_body, e.status_code
@ -98,7 +95,7 @@ class RevocationEndpoint(BaseEndpoint):
submitted for revocation. Clients MAY pass this parameter in order to
help the authorization server to optimize the token lookup. If the
server is unable to locate the token using the given hint, it MUST
extend its search accross all of its supported token types. An
extend its search across all of its supported token types. An
authorization server MAY ignore this parameter, particularly if it is
able to detect the token type automatically. This specification
defines two such values:

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
"""
oauthlib.oauth2.rfc6749
~~~~~~~~~~~~~~~~~~~~~~~
@ -6,8 +5,6 @@ oauthlib.oauth2.rfc6749
This module is an implementation of various logic needed
for consuming and providing OAuth 2.0 RFC6749.
"""
from __future__ import absolute_import, unicode_literals
import logging
from oauthlib.common import Request
@ -39,7 +36,6 @@ class TokenEndpoint(BaseEndpoint):
https://example.com/path?query=component # OK
https://example.com/path?query=component#fragment # Not OK
Since requests to the authorization endpoint result in user
Since requests to the token endpoint result in the transmission of
clear-text credentials (in the HTTP request and response), the
authorization server MUST require the use of TLS as described in

View File

@ -1,4 +1,3 @@
# coding=utf-8
"""
oauthlib.oauth2.rfc6749.errors
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -6,8 +5,6 @@ oauthlib.oauth2.rfc6749.errors
Error used both by OAuth 2 clients and providers to represent the spec
defined error responses for all four core grant types.
"""
from __future__ import unicode_literals
import json
from oauthlib.common import add_params_to_uri, urlencode
@ -45,10 +42,10 @@ class OAuth2Error(Exception):
if description is not None:
self.description = description
message = '(%s) %s' % (self.error, self.description)
message = '({}) {}'.format(self.error, self.description)
if request:
message += ' ' + repr(request)
super(OAuth2Error, self).__init__(message)
super().__init__(message)
self.uri = uri
self.state = state
@ -106,15 +103,12 @@ class OAuth2Error(Exception):
value "Bearer". This scheme MUST be followed by one or more
auth-param values.
"""
authvalues = [
"Bearer",
'error="{}"'.format(self.error)
]
authvalues = ['error="{}"'.format(self.error)]
if self.description:
authvalues.append('error_description="{}"'.format(self.description))
if self.uri:
authvalues.append('error_uri="{}"'.format(self.uri))
return {"WWW-Authenticate": ", ".join(authvalues)}
return {"WWW-Authenticate": "Bearer " + ", ".join(authvalues)}
return {}
@ -389,7 +383,7 @@ class CustomOAuth2Error(OAuth2Error):
"""
def __init__(self, error, *args, **kwargs):
self.error = error
super(CustomOAuth2Error, self).__init__(*args, **kwargs)
super().__init__(*args, **kwargs)
def raise_from_error(error, params=None):

View File

@ -1,12 +1,11 @@
# -*- coding: utf-8 -*-
"""
oauthlib.oauth2.rfc6749.grant_types
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
"""
from __future__ import unicode_literals, absolute_import
from .authorization_code import AuthorizationCodeGrant
from .implicit import ImplicitGrant
from .resource_owner_password_credentials import ResourceOwnerPasswordCredentialsGrant
from .client_credentials import ClientCredentialsGrant
from .implicit import ImplicitGrant
from .refresh_token import RefreshTokenGrant
from .resource_owner_password_credentials import (
ResourceOwnerPasswordCredentialsGrant,
)

View File

@ -1,10 +1,7 @@
# -*- coding: utf-8 -*-
"""
oauthlib.oauth2.rfc6749.grant_types
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
"""
from __future__ import absolute_import, unicode_literals
import base64
import hashlib
import json
@ -275,6 +272,8 @@ class AuthorizationCodeGrant(GrantTypeBase):
grant = self.create_authorization_code(request)
for modifier in self._code_modifiers:
grant = modifier(grant, token_handler, request)
if 'access_token' in grant:
self.request_validator.save_token(grant, request)
log.debug('Saving grant %r for %r.', grant, request)
self.request_validator.save_authorization_code(
request.client_id, grant, request)
@ -313,6 +312,7 @@ class AuthorizationCodeGrant(GrantTypeBase):
self.request_validator.save_token(token, request)
self.request_validator.invalidate_authorization_code(
request.client_id, request.code, request)
headers.update(self._create_cors_headers(request))
return headers, json.dumps(token), 200
def validate_authorization_request(self, request):

View File

@ -1,23 +1,21 @@
# -*- coding: utf-8 -*-
"""
oauthlib.oauth2.rfc6749.grant_types
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
"""
from __future__ import absolute_import, unicode_literals
import logging
from itertools import chain
from oauthlib.common import add_params_to_uri
from oauthlib.uri_validate import is_absolute_uri
from oauthlib.oauth2.rfc6749 import errors, utils
from oauthlib.uri_validate import is_absolute_uri
from ..request_validator import RequestValidator
from ..utils import is_secure_transport
log = logging.getLogger(__name__)
class ValidatorsContainer(object):
class ValidatorsContainer:
"""
Container object for holding custom validator callables to be invoked
as part of the grant type `validate_authorization_request()` or
@ -74,7 +72,7 @@ class ValidatorsContainer(object):
return chain(self.post_auth, self.post_token)
class GrantTypeBase(object):
class GrantTypeBase:
error_uri = None
request_validator = None
default_response_mode = 'fragment'
@ -251,3 +249,20 @@ class GrantTypeBase(object):
raise errors.MissingRedirectURIError(request=request)
if not is_absolute_uri(request.redirect_uri):
raise errors.InvalidRedirectURIError(request=request)
def _create_cors_headers(self, request):
"""If CORS is allowed, create the appropriate headers."""
if 'origin' not in request.headers:
return {}
origin = request.headers['origin']
if not is_secure_transport(origin):
log.debug('Origin "%s" is not HTTPS, CORS not allowed.', origin)
return {}
elif not self.request_validator.is_origin_allowed(
request.client_id, origin, request):
log.debug('Invalid origin "%s", CORS not allowed.', origin)
return {}
else:
log.debug('Valid origin "%s", injecting CORS headers.', origin)
return {'Access-Control-Allow-Origin': origin}

View File

@ -1,15 +1,11 @@
# -*- coding: utf-8 -*-
"""
oauthlib.oauth2.rfc6749.grant_types
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
"""
from __future__ import absolute_import, unicode_literals
import json
import logging
from .. import errors
from ..request_validator import RequestValidator
from .base import GrantTypeBase
log = logging.getLogger(__name__)
@ -119,8 +115,8 @@ class ClientCredentialsGrant(GrantTypeBase):
# Ensure client is authorized use of this grant type
self.validate_grant_type(request)
log.debug('Authorizing access to user %r.', request.user)
request.client_id = request.client_id or request.client.client_id
log.debug('Authorizing access to client %r.', request.client_id)
self.validate_scopes(request)
for validator in self.custom_validators.post_token:

View File

@ -1,10 +1,7 @@
# -*- coding: utf-8 -*-
"""
oauthlib.oauth2.rfc6749.grant_types
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
"""
from __future__ import absolute_import, unicode_literals
import logging
from oauthlib import common

View File

@ -1,15 +1,11 @@
# -*- coding: utf-8 -*-
"""
oauthlib.oauth2.rfc6749.grant_types
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
"""
from __future__ import absolute_import, unicode_literals
import json
import logging
from .. import errors, utils
from ..request_validator import RequestValidator
from .base import GrantTypeBase
log = logging.getLogger(__name__)
@ -25,7 +21,7 @@ class RefreshTokenGrant(GrantTypeBase):
def __init__(self, request_validator=None,
issue_new_refresh_tokens=True,
**kwargs):
super(RefreshTokenGrant, self).__init__(
super().__init__(
request_validator,
issue_new_refresh_tokens=issue_new_refresh_tokens,
**kwargs)
@ -67,12 +63,13 @@ class RefreshTokenGrant(GrantTypeBase):
refresh_token=self.issue_new_refresh_tokens)
for modifier in self._token_modifiers:
token = modifier(token)
token = modifier(token, token_handler, request)
self.request_validator.save_token(token, request)
log.debug('Issuing new token to client id %r (%r), %r.',
request.client_id, request.client, token)
headers.update(self._create_cors_headers(request))
return headers, json.dumps(token), 200
def validate_token_request(self, request):
@ -126,7 +123,7 @@ class RefreshTokenGrant(GrantTypeBase):
if request.scope:
request.scopes = utils.scope_to_list(request.scope)
if (not all((s in original_scopes for s in request.scopes))
if (not all(s in original_scopes for s in request.scopes)
and not self.request_validator.is_within_original_scope(
request.scopes, request.refresh_token, request)):
log.debug('Refresh token %s lack requested scopes, %r.',

View File

@ -1,15 +1,11 @@
# -*- coding: utf-8 -*-
"""
oauthlib.oauth2.rfc6749.grant_types
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
"""
from __future__ import absolute_import, unicode_literals
import json
import logging
from .. import errors
from ..request_validator import RequestValidator
from .base import GrantTypeBase
log = logging.getLogger(__name__)

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
"""
oauthlib.oauth2.rfc6749.parameters
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -7,29 +6,24 @@ This module contains methods related to `Section 4`_ of the OAuth 2 RFC.
.. _`Section 4`: https://tools.ietf.org/html/rfc6749#section-4
"""
from __future__ import absolute_import, unicode_literals
import json
import os
import time
import urllib.parse as urlparse
from oauthlib.common import add_params_to_qs, add_params_to_uri, unicode_type
from oauthlib.common import add_params_to_qs, add_params_to_uri
from oauthlib.signals import scope_changed
from .errors import (InsecureTransportError, MismatchingStateError,
MissingCodeError, MissingTokenError,
MissingTokenTypeError, raise_from_error)
from .errors import (
InsecureTransportError, MismatchingStateError, MissingCodeError,
MissingTokenError, MissingTokenTypeError, raise_from_error,
)
from .tokens import OAuth2Token
from .utils import is_secure_transport, list_to_scope, scope_to_list
try:
import urlparse
except ImportError:
import urllib.parse as urlparse
def prepare_grant_uri(uri, client_id, response_type, redirect_uri=None,
scope=None, state=None, **kwargs):
scope=None, state=None, code_challenge=None, code_challenge_method='plain', **kwargs):
"""Prepare the authorization grant request URI.
The client constructs the request URI by adding the following
@ -51,6 +45,11 @@ def prepare_grant_uri(uri, client_id, response_type, redirect_uri=None,
back to the client. The parameter SHOULD be used for
preventing cross-site request forgery as described in
`Section 10.12`_.
:param code_challenge: PKCE parameter. A challenge derived from the
code_verifier that is sent in the authorization
request, to be verified against later.
:param code_challenge_method: PKCE parameter. A method that was used to derive the
code_challenge. Defaults to "plain" if not present in the request.
:param kwargs: Extra arguments to embed in the grant/authorization URL.
An example of an authorization code grant authorization URL:
@ -58,6 +57,7 @@ def prepare_grant_uri(uri, client_id, response_type, redirect_uri=None,
.. code-block:: http
GET /authorize?response_type=code&client_id=s6BhdRkqt3&state=xyz
&code_challenge=kjasBS523KdkAILD2k78NdcJSk2k3KHG6&code_challenge_method=S256
&redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb HTTP/1.1
Host: server.example.com
@ -79,15 +79,18 @@ def prepare_grant_uri(uri, client_id, response_type, redirect_uri=None,
params.append(('scope', list_to_scope(scope)))
if state:
params.append(('state', state))
if code_challenge is not None:
params.append(('code_challenge', code_challenge))
params.append(('code_challenge_method', code_challenge_method))
for k in kwargs:
if kwargs[k]:
params.append((unicode_type(k), kwargs[k]))
params.append((str(k), kwargs[k]))
return add_params_to_uri(uri, params)
def prepare_token_request(grant_type, body='', include_client_id=True, **kwargs):
def prepare_token_request(grant_type, body='', include_client_id=True, code_verifier=None, **kwargs):
"""Prepare the access token request.
The client makes a request to the token endpoint by adding the
@ -122,6 +125,9 @@ def prepare_token_request(grant_type, body='', include_client_id=True, **kwargs)
authorization request as described in
`Section 4.1.1`_, and their values MUST be identical. *
:param code_verifier: PKCE parameter. A cryptographically random string that is used to correlate the
authorization request to the token request.
:param kwargs: Extra arguments to embed in the request body.
Parameters marked with a `*` above are not explicit arguments in the
@ -146,18 +152,22 @@ def prepare_token_request(grant_type, body='', include_client_id=True, **kwargs)
client_id = kwargs.pop('client_id', None)
if include_client_id:
if client_id is not None:
params.append((unicode_type('client_id'), client_id))
params.append(('client_id', client_id))
# use code_verifier if code_challenge was passed in the authorization request
if code_verifier is not None:
params.append(('code_verifier', code_verifier))
# the kwargs iteration below only supports including boolean truth (truthy)
# values, but some servers may require an empty string for `client_secret`
client_secret = kwargs.pop('client_secret', None)
if client_secret is not None:
params.append((unicode_type('client_secret'), client_secret))
params.append(('client_secret', client_secret))
# this handles: `code`, `redirect_uri`, and other undocumented params
for k in kwargs:
if kwargs[k]:
params.append((unicode_type(k), kwargs[k]))
params.append((str(k), kwargs[k]))
return add_params_to_qs(body, params)
@ -167,7 +177,7 @@ def prepare_token_revocation_request(url, token, token_type_hint="access_token",
"""Prepare a token revocation request.
The client constructs the request by including the following parameters
using the "application/x-www-form-urlencoded" format in the HTTP request
using the ``application/x-www-form-urlencoded`` format in the HTTP request
entity-body:
:param token: REQUIRED. The token that the client wants to get revoked.
@ -209,7 +219,7 @@ def prepare_token_revocation_request(url, token, token_type_hint="access_token",
for k in kwargs:
if kwargs[k]:
params.append((unicode_type(k), kwargs[k]))
params.append((str(k), kwargs[k]))
headers = {'Content-Type': 'application/x-www-form-urlencoded'}
@ -433,7 +443,7 @@ def parse_token_response(body, scope=None):
def validate_token_parameters(params):
"""Ensures token precence, token type, expiration and scope in params."""
"""Ensures token presence, token type, expiration and scope in params."""
if 'error' in params:
raise_from_error(params.get('error'), params)

View File

@ -1,16 +1,13 @@
# -*- coding: utf-8 -*-
"""
oauthlib.oauth2.rfc6749.request_validator
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
"""
from __future__ import absolute_import, unicode_literals
import logging
log = logging.getLogger(__name__)
class RequestValidator(object):
class RequestValidator:
def client_authentication_required(self, request, *args, **kwargs):
"""Determine if client authentication is required for current request.
@ -51,6 +48,17 @@ class RequestValidator(object):
Headers may be accesses through request.headers and parameters found in
both body and query can be obtained by direct attribute access, i.e.
request.client_id for client_id in the URL query.
The authentication process is required to contain the identification of
the client (i.e. search the database based on the client_id). In case the
client doesn't exist based on the received client_id, this method has to
return False and the HTTP response created by the library will contain
'invalid_client' message.
After the client identification succeeds, this method needs to set the
client on the request, i.e. request.client = client. A client object's
class must contain the 'client_id' attribute and the 'client_id' must have
a value.
:param request: OAuthlib request.
:type request: oauthlib.common.Request
@ -183,6 +191,7 @@ class RequestValidator(object):
claims associated, or `None` in case the token is unknown.
Below the list of registered claims you should be interested in:
- scope : space-separated list of scopes
- client_id : client identifier
- username : human-readable identifier for the resource owner
@ -196,10 +205,10 @@ class RequestValidator(object):
- jti : string identifier for the token
Note that most of them are coming directly from JWT RFC. More details
can be found in `Introspect Claims`_ or `_JWT Claims`_.
can be found in `Introspect Claims`_ or `JWT Claims`_.
The implementation can use *token_type_hint* to improve lookup
efficency, but must fallback to other types to be compliant with RFC.
efficiency, but must fallback to other types to be compliant with RFC.
The dict of claims is added to request.token after this method.
@ -435,6 +444,7 @@ class RequestValidator(object):
- request.user
- request.scopes
- request.claims (if given)
OBS! The request.user attribute should be set to the resource owner
associated with this authorization code. Similarly request.scopes
must also be set.
@ -443,6 +453,7 @@ class RequestValidator(object):
If PKCE is enabled (see 'is_pkce_required' and 'save_authorization_code')
you MUST set the following based on the information stored:
- request.code_challenge
- request.code_challenge_method
@ -553,7 +564,7 @@ class RequestValidator(object):
OBS! The validation should also set the user attribute of the request
to a valid resource owner, i.e. request.user = username or similar. If
not set you will be unable to associate a token with a user in the
persistance method used (commonly, save_bearer_token).
persistence method used (commonly, save_bearer_token).
:param username: Unicode username.
:param password: Unicode password.
@ -641,3 +652,29 @@ class RequestValidator(object):
"""
raise NotImplementedError('Subclasses must implement this method.')
def is_origin_allowed(self, client_id, origin, request, *args, **kwargs):
"""Indicate if the given origin is allowed to access the token endpoint
via Cross-Origin Resource Sharing (CORS). CORS is used by browser-based
clients, such as Single-Page Applications, to perform the Authorization
Code Grant.
(Note: If performing Authorization Code Grant via a public client such
as a browser, you should use PKCE as well.)
If this method returns true, the appropriate CORS headers will be added
to the response. By default this method always returns False, meaning
CORS is disabled.
:param client_id: Unicode client identifier.
:param redirect_uri: Unicode origin.
:param request: OAuthlib request.
:type request: oauthlib.common.Request
:rtype: bool
Method is used by:
- Authorization Code Grant
- Refresh Token Grant
"""
return False

View File

@ -7,28 +7,22 @@ This module contains methods for adding two types of access tokens to requests.
- Bearer https://tools.ietf.org/html/rfc6750
- MAC https://tools.ietf.org/html/draft-ietf-oauth-v2-http-mac-01
"""
from __future__ import absolute_import, unicode_literals
import hashlib
import hmac
from binascii import b2a_base64
import warnings
from binascii import b2a_base64
from urllib.parse import urlparse
from oauthlib import common
from oauthlib.common import add_params_to_qs, add_params_to_uri, unicode_type
from oauthlib.common import add_params_to_qs, add_params_to_uri
from . import utils
try:
from urlparse import urlparse
except ImportError:
from urllib.parse import urlparse
class OAuth2Token(dict):
def __init__(self, params, old_scope=None):
super(OAuth2Token, self).__init__(params)
super().__init__(params)
self._new_scope = None
if 'scope' in params and params['scope']:
self._new_scope = set(utils.scope_to_list(params['scope']))
@ -121,7 +115,7 @@ def prepare_mac_header(token, uri, key, http_method,
raise ValueError('unknown hash algorithm')
if draft == 0:
nonce = nonce or '{0}:{1}'.format(utils.generate_age(issue_time),
nonce = nonce or '{}:{}'.format(utils.generate_age(issue_time),
common.generate_nonce())
else:
ts = common.generate_timestamp()
@ -158,7 +152,7 @@ def prepare_mac_header(token, uri, key, http_method,
base_string = '\n'.join(base) + '\n'
# hmac struggles with unicode strings - http://bugs.python.org/issue5285
if isinstance(key, unicode_type):
if isinstance(key, str):
key = key.encode('utf-8')
sign = hmac.new(key, base_string.encode('utf-8'), h)
sign = b2a_base64(sign.digest())[:-1].decode('utf-8')
@ -262,7 +256,8 @@ def get_token_from_header(request):
return token
class TokenBase(object):
class TokenBase:
__slots__ = ()
def __call__(self, request, refresh_token=False):
raise NotImplementedError('Subclasses must implement this method.')

View File

@ -1,33 +1,22 @@
# -*- coding: utf-8 -*-
"""
oauthlib.utils
~~~~~~~~~~~~~~
This module contains utility methods used by various parts of the OAuth 2 spec.
"""
from __future__ import absolute_import, unicode_literals
import datetime
import os
from urllib.parse import quote, urlparse
from oauthlib.common import unicode_type, urldecode
try:
from urllib import quote
except ImportError:
from urllib.parse import quote
try:
from urlparse import urlparse
except ImportError:
from urllib.parse import urlparse
from oauthlib.common import urldecode
def list_to_scope(scope):
"""Convert a list of scopes to a space separated string."""
if isinstance(scope, unicode_type) or scope is None:
if isinstance(scope, str) or scope is None:
return scope
elif isinstance(scope, (set, tuple, list)):
return " ".join([unicode_type(s) for s in scope])
return " ".join([str(s) for s in scope])
else:
raise ValueError("Invalid scope (%s), must be string, tuple, set, or list." % scope)
@ -35,7 +24,7 @@ def list_to_scope(scope):
def scope_to_list(scope):
"""Convert a space separated string to a list of scopes."""
if isinstance(scope, (tuple, list, set)):
return [unicode_type(s) for s in scope]
return [str(s) for s in scope]
elif scope is None:
return None
else:
@ -74,7 +63,7 @@ def escape(u):
TODO: verify whether this can in fact be used for OAuth 2
"""
if not isinstance(u, unicode_type):
if not isinstance(u, str):
raise ValueError('Only unicode objects are escapable.')
return quote(u.encode('utf-8'), safe=b'~')
@ -84,7 +73,7 @@ def generate_age(issue_time):
td = datetime.datetime.now() - issue_time
age = (td.microseconds + (td.seconds + td.days * 24 * 3600)
* 10 ** 6) / 10 ** 6
return unicode_type(age)
return str(age)
def is_secure_transport(uri):

View File

@ -0,0 +1,10 @@
"""
oauthlib.oauth2.rfc8628
~~~~~~~~~~~~~~~~~~~~~~~
This module is an implementation of various logic needed
for consuming and providing OAuth 2.0 Device Authorization RFC8628.
"""
import logging
log = logging.getLogger(__name__)

View File

@ -0,0 +1,8 @@
"""
oauthlib.oauth2.rfc8628
~~~~~~~~~~~~~~~~~~~~~~~
This module is an implementation of various logic needed
for consuming OAuth 2.0 Device Authorization RFC8628.
"""
from .device import DeviceClient

View File

@ -0,0 +1,95 @@
"""
oauthlib.oauth2.rfc8628
~~~~~~~~~~~~~~~~~~~~~~~
This module is an implementation of various logic needed
for consuming and providing OAuth 2.0 Device Authorization RFC8628.
"""
from oauthlib.common import add_params_to_uri
from oauthlib.oauth2 import BackendApplicationClient, Client
from oauthlib.oauth2.rfc6749.errors import InsecureTransportError
from oauthlib.oauth2.rfc6749.parameters import prepare_token_request
from oauthlib.oauth2.rfc6749.utils import is_secure_transport, list_to_scope
class DeviceClient(Client):
"""A public client utilizing the device authorization workflow.
The client can request an access token using a device code and
a public client id associated with the device code as defined
in RFC8628.
The device authorization grant type can be used to obtain both
access tokens and refresh tokens and is intended to be used in
a scenario where the device being authorized does not have a
user interface that is suitable for performing authentication.
"""
grant_type = 'urn:ietf:params:oauth:grant-type:device_code'
def __init__(self, client_id, **kwargs):
super().__init__(client_id, **kwargs)
self.client_secret = kwargs.get('client_secret')
def prepare_request_uri(self, uri, scope=None, **kwargs):
if not is_secure_transport(uri):
raise InsecureTransportError()
scope = self.scope if scope is None else scope
params = [(('client_id', self.client_id)), (('grant_type', self.grant_type))]
if self.client_secret is not None:
params.append(('client_secret', self.client_secret))
if scope:
params.append(('scope', list_to_scope(scope)))
for k in kwargs:
if kwargs[k]:
params.append((str(k), kwargs[k]))
return add_params_to_uri(uri, params)
def prepare_request_body(self, device_code, body='', scope=None,
include_client_id=False, **kwargs):
"""Add device_code to request body
The client makes a request to the token endpoint by adding the
device_code as a parameter using the
"application/x-www-form-urlencoded" format to the HTTP request
body.
:param body: Existing request body (URL encoded string) to embed parameters
into. This may contain extra parameters. Default ''.
:param scope: The scope of the access request as described by
`Section 3.3`_.
:param include_client_id: `True` to send the `client_id` in the
body of the upstream request. This is required
if the client is not authenticating with the
authorization server as described in
`Section 3.2.1`_. False otherwise (default).
:type include_client_id: Boolean
:param kwargs: Extra credentials to include in the token request.
The prepared body will include all provided device_code as well as
the ``grant_type`` parameter set to
``urn:ietf:params:oauth:grant-type:device_code``::
>>> from oauthlib.oauth2 import DeviceClient
>>> client = DeviceClient('your_id', 'your_code')
>>> client.prepare_request_body(scope=['hello', 'world'])
'grant_type=urn:ietf:params:oauth:grant-type:device_code&scope=hello+world'
.. _`Section 3.2.1`: https://datatracker.ietf.org/doc/html/rfc6749#section-3.2.1
.. _`Section 3.3`: https://datatracker.ietf.org/doc/html/rfc6749#section-3.3
.. _`Section 3.4`: https://datatracker.ietf.org/doc/html/rfc8628#section-3.4
"""
kwargs['client_id'] = self.client_id
kwargs['include_client_id'] = include_client_id
scope = self.scope if scope is None else scope
return prepare_token_request(self.grant_type, body=body, device_code=device_code,
scope=scope, **kwargs)

View File

@ -1,11 +1,7 @@
# -*- coding: utf-8 -*-
"""
oauthlib.openid
~~~~~~~~~~~~~~
"""
from __future__ import absolute_import, unicode_literals
from .connect.core.endpoints import Server
from .connect.core.endpoints import UserInfoEndpoint
from .connect.core.endpoints import Server, UserInfoEndpoint
from .connect.core.request_validator import RequestValidator

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
"""
oauthlib.oopenid.core
~~~~~~~~~~~~~~~~~~~~~~~
@ -6,7 +5,5 @@ oauthlib.oopenid.core
This module is an implementation of various logic needed
for consuming and providing OpenID Connect
"""
from __future__ import absolute_import, unicode_literals
from .pre_configured import Server
from .userinfo import UserInfoEndpoint

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
"""
oauthlib.openid.connect.core.endpoints.pre_configured
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -6,32 +5,21 @@ oauthlib.openid.connect.core.endpoints.pre_configured
This module is an implementation of various endpoints needed
for providing OpenID Connect servers.
"""
from __future__ import absolute_import, unicode_literals
from oauthlib.oauth2.rfc6749.endpoints import (
AuthorizationEndpoint,
IntrospectEndpoint,
ResourceEndpoint,
RevocationEndpoint,
TokenEndpoint
AuthorizationEndpoint, IntrospectEndpoint, ResourceEndpoint,
RevocationEndpoint, TokenEndpoint,
)
from oauthlib.oauth2.rfc6749.grant_types import (
AuthorizationCodeGrant as OAuth2AuthorizationCodeGrant,
ImplicitGrant as OAuth2ImplicitGrant,
ClientCredentialsGrant,
RefreshTokenGrant,
ResourceOwnerPasswordCredentialsGrant
ClientCredentialsGrant, ImplicitGrant as OAuth2ImplicitGrant,
RefreshTokenGrant, ResourceOwnerPasswordCredentialsGrant,
)
from oauthlib.oauth2.rfc6749.tokens import BearerToken
from ..grant_types import (
AuthorizationCodeGrant,
ImplicitGrant,
HybridGrant,
)
from ..grant_types import AuthorizationCodeGrant, HybridGrant, ImplicitGrant
from ..grant_types.dispatchers import (
AuthorizationCodeGrantDispatcher,
AuthorizationCodeGrantDispatcher, AuthorizationTokenGrantDispatcher,
ImplicitTokenGrantDispatcher,
AuthorizationTokenGrantDispatcher
)
from ..tokens import JWTToken
from .userinfo import UserInfoEndpoint
@ -58,52 +46,52 @@ class Server(AuthorizationEndpoint, IntrospectEndpoint, TokenEndpoint,
:param kwargs: Extra parameters to pass to authorization-,
token-, resource-, and revocation-endpoint constructors.
"""
auth_grant = OAuth2AuthorizationCodeGrant(request_validator)
implicit_grant = OAuth2ImplicitGrant(request_validator)
password_grant = ResourceOwnerPasswordCredentialsGrant(
self.auth_grant = OAuth2AuthorizationCodeGrant(request_validator)
self.implicit_grant = OAuth2ImplicitGrant(request_validator)
self.password_grant = ResourceOwnerPasswordCredentialsGrant(
request_validator)
credentials_grant = ClientCredentialsGrant(request_validator)
refresh_grant = RefreshTokenGrant(request_validator)
openid_connect_auth = AuthorizationCodeGrant(request_validator)
openid_connect_implicit = ImplicitGrant(request_validator)
openid_connect_hybrid = HybridGrant(request_validator)
self.credentials_grant = ClientCredentialsGrant(request_validator)
self.refresh_grant = RefreshTokenGrant(request_validator)
self.openid_connect_auth = AuthorizationCodeGrant(request_validator)
self.openid_connect_implicit = ImplicitGrant(request_validator)
self.openid_connect_hybrid = HybridGrant(request_validator)
bearer = BearerToken(request_validator, token_generator,
self.bearer = BearerToken(request_validator, token_generator,
token_expires_in, refresh_token_generator)
jwt = JWTToken(request_validator, token_generator,
self.jwt = JWTToken(request_validator, token_generator,
token_expires_in, refresh_token_generator)
auth_grant_choice = AuthorizationCodeGrantDispatcher(default_grant=auth_grant, oidc_grant=openid_connect_auth)
implicit_grant_choice = ImplicitTokenGrantDispatcher(default_grant=implicit_grant, oidc_grant=openid_connect_implicit)
self.auth_grant_choice = AuthorizationCodeGrantDispatcher(default_grant=self.auth_grant, oidc_grant=self.openid_connect_auth)
self.implicit_grant_choice = ImplicitTokenGrantDispatcher(default_grant=self.implicit_grant, oidc_grant=self.openid_connect_implicit)
# See http://openid.net/specs/oauth-v2-multiple-response-types-1_0.html#Combinations for valid combinations
# internally our AuthorizationEndpoint will ensure they can appear in any order for any valid combination
AuthorizationEndpoint.__init__(self, default_response_type='code',
response_types={
'code': auth_grant_choice,
'token': implicit_grant_choice,
'id_token': openid_connect_implicit,
'id_token token': openid_connect_implicit,
'code token': openid_connect_hybrid,
'code id_token': openid_connect_hybrid,
'code id_token token': openid_connect_hybrid,
'none': auth_grant
'code': self.auth_grant_choice,
'token': self.implicit_grant_choice,
'id_token': self.openid_connect_implicit,
'id_token token': self.openid_connect_implicit,
'code token': self.openid_connect_hybrid,
'code id_token': self.openid_connect_hybrid,
'code id_token token': self.openid_connect_hybrid,
'none': self.auth_grant
},
default_token_type=bearer)
default_token_type=self.bearer)
token_grant_choice = AuthorizationTokenGrantDispatcher(request_validator, default_grant=auth_grant, oidc_grant=openid_connect_auth)
self.token_grant_choice = AuthorizationTokenGrantDispatcher(request_validator, default_grant=self.auth_grant, oidc_grant=self.openid_connect_auth)
TokenEndpoint.__init__(self, default_grant_type='authorization_code',
grant_types={
'authorization_code': token_grant_choice,
'password': password_grant,
'client_credentials': credentials_grant,
'refresh_token': refresh_grant,
'authorization_code': self.token_grant_choice,
'password': self.password_grant,
'client_credentials': self.credentials_grant,
'refresh_token': self.refresh_grant,
},
default_token_type=bearer)
default_token_type=self.bearer)
ResourceEndpoint.__init__(self, default_token='Bearer',
token_types={'Bearer': bearer, 'JWT': jwt})
token_types={'Bearer': self.bearer, 'JWT': self.jwt})
RevocationEndpoint.__init__(self, request_validator)
IntrospectEndpoint.__init__(self, request_validator)
UserInfoEndpoint.__init__(self, request_validator)

View File

@ -4,18 +4,15 @@ oauthlib.openid.connect.core.endpoints.userinfo
This module is an implementation of userinfo endpoint.
"""
from __future__ import absolute_import, unicode_literals
import json
import logging
from oauthlib.common import Request
from oauthlib.common import unicode_type
from oauthlib.oauth2.rfc6749.endpoints.base import BaseEndpoint
from oauthlib.oauth2.rfc6749.endpoints.base import catch_errors_and_unavailability
from oauthlib.oauth2.rfc6749.tokens import BearerToken
from oauthlib.oauth2.rfc6749 import errors
from oauthlib.oauth2.rfc6749.endpoints.base import (
BaseEndpoint, catch_errors_and_unavailability,
)
from oauthlib.oauth2.rfc6749.tokens import BearerToken
log = logging.getLogger(__name__)
@ -55,7 +52,7 @@ class UserInfoEndpoint(BaseEndpoint):
log.error('Userinfo MUST have "sub" for %r.', request)
raise errors.ServerError(status_code=500)
body = json.dumps(claims)
elif isinstance(claims, unicode_type):
elif isinstance(claims, str):
resp_headers = {
'Content-Type': 'application/jwt'
}
@ -72,7 +69,7 @@ class UserInfoEndpoint(BaseEndpoint):
5.3.1. UserInfo Request
The Client sends the UserInfo Request using either HTTP GET or HTTP
POST. The Access Token obtained from an OpenID Connect Authentication
Request MUST be sent as a Bearer Token, per Section 2 of OAuth 2.0
Request MUST be sent as a Bearer Token, per `Section 2`_ of OAuth 2.0
Bearer Token Usage [RFC6750].
It is RECOMMENDED that the request use the HTTP GET method and the
@ -80,21 +77,28 @@ class UserInfoEndpoint(BaseEndpoint):
The following is a non-normative example of a UserInfo Request:
GET /userinfo HTTP/1.1
Host: server.example.com
Authorization: Bearer SlAV32hkKG
.. code-block:: http
GET /userinfo HTTP/1.1
Host: server.example.com
Authorization: Bearer SlAV32hkKG
5.3.3. UserInfo Error Response
When an error condition occurs, the UserInfo Endpoint returns an Error
Response as defined in Section 3 of OAuth 2.0 Bearer Token Usage
Response as defined in `Section 3`_ of OAuth 2.0 Bearer Token Usage
[RFC6750]. (HTTP errors unrelated to RFC 6750 are returned to the User
Agent using the appropriate HTTP status code.)
The following is a non-normative example of a UserInfo Error Response:
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer error="invalid_token",
.. code-block:: http
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer error="invalid_token",
error_description="The Access Token expired"
.. _`Section 2`: https://datatracker.ietf.org/doc/html/rfc6750#section-2
.. _`Section 3`: https://datatracker.ietf.org/doc/html/rfc6750#section-3
"""
if not self.bearer.validate_request(request):
raise errors.InvalidTokenError()

View File

@ -1,4 +1,3 @@
# coding=utf-8
"""
oauthlib.oauth2.rfc6749.errors
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -6,8 +5,6 @@ oauthlib.oauth2.rfc6749.errors
Error used both by OAuth 2 clients and providers to represent the spec
defined error responses for all four core grant types.
"""
from __future__ import unicode_literals
from oauthlib.oauth2.rfc6749.errors import FatalClientError, OAuth2Error

View File

@ -1,17 +1,13 @@
# -*- coding: utf-8 -*-
"""
oauthlib.openid.connect.core.grant_types
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
"""
from __future__ import unicode_literals, absolute_import
from .authorization_code import AuthorizationCodeGrant
from .implicit import ImplicitGrant
from .base import GrantTypeBase
from .hybrid import HybridGrant
from .exceptions import OIDCNoPrompt
from .dispatchers import (
AuthorizationCodeGrantDispatcher,
AuthorizationCodeGrantDispatcher, AuthorizationTokenGrantDispatcher,
ImplicitTokenGrantDispatcher,
AuthorizationTokenGrantDispatcher
)
from .hybrid import HybridGrant
from .implicit import ImplicitGrant
from .refresh_token import RefreshTokenGrant

View File

@ -1,13 +1,12 @@
# -*- coding: utf-8 -*-
"""
oauthlib.openid.connect.core.grant_types
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
"""
from __future__ import absolute_import, unicode_literals
import logging
from oauthlib.oauth2.rfc6749.grant_types.authorization_code import AuthorizationCodeGrant as OAuth2AuthorizationCodeGrant
from oauthlib.oauth2.rfc6749.grant_types.authorization_code import (
AuthorizationCodeGrant as OAuth2AuthorizationCodeGrant,
)
from .base import GrantTypeBase
@ -41,4 +40,4 @@ class AuthorizationCodeGrant(GrantTypeBase):
request.redirect_uri,
request
)
return super(AuthorizationCodeGrant, self).add_id_token(token, token_handler, request, nonce=nonce)
return super().add_id_token(token, token_handler, request, nonce=nonce)

View File

@ -1,17 +1,17 @@
from .exceptions import OIDCNoPrompt
import base64
import hashlib
import logging
import time
from json import loads
from oauthlib.oauth2.rfc6749.errors import ConsentRequired, InvalidRequestError, LoginRequired
from oauthlib.oauth2.rfc6749.errors import (
ConsentRequired, InvalidRequestError, LoginRequired,
)
log = logging.getLogger(__name__)
class GrantTypeBase(object):
class GrantTypeBase:
# Just proxy the majority of method calls through to the
# proxy_target grant type handler, which will usually be either
@ -20,7 +20,7 @@ class GrantTypeBase(object):
return getattr(self.proxy_target, attr)
def __setattr__(self, attr, value):
proxied_attrs = set(('refresh_token', 'response_types'))
proxied_attrs = {'refresh_token', 'response_types'}
if attr in proxied_attrs:
setattr(self.proxy_target, attr, value)
else:
@ -31,13 +31,7 @@ class GrantTypeBase(object):
:returns: (list of scopes, dict of request info)
"""
# If request.prompt is 'none' then no login/authorization form should
# be presented to the user. Instead, a silent login/authorization
# should be performed.
if request.prompt == 'none':
raise OIDCNoPrompt()
else:
return self.proxy_target.validate_authorization_request(request)
return self.proxy_target.validate_authorization_request(request)
def _inflate_claims(self, request):
# this may be called multiple times in a single request so make sure we only de-serialize the claims once

View File

@ -1,16 +1,19 @@
import logging
log = logging.getLogger(__name__)
class Dispatcher(object):
class Dispatcher:
default_grant = None
oidc_grant = None
class AuthorizationCodeGrantDispatcher(Dispatcher):
"""
This is an adapter class that will route simple Authorization Code requests, those that have response_type=code and a scope
including 'openid' to either the default_grant or the oidc_grant based on the scopes requested.
This is an adapter class that will route simple Authorization Code
requests, those that have `response_type=code` and a scope including
`openid` to either the `default_grant` or the `oidc_grant` based on
the scopes requested.
"""
def __init__(self, default_grant=None, oidc_grant=None):
self.default_grant = default_grant
@ -26,16 +29,20 @@ class AuthorizationCodeGrantDispatcher(Dispatcher):
return handler
def create_authorization_response(self, request, token_handler):
"""Read scope and route to the designated handler."""
return self._handler_for_request(request).create_authorization_response(request, token_handler)
def validate_authorization_request(self, request):
"""Read scope and route to the designated handler."""
return self._handler_for_request(request).validate_authorization_request(request)
class ImplicitTokenGrantDispatcher(Dispatcher):
"""
This is an adapter class that will route simple Authorization Code requests, those that have response_type=code and a scope
including 'openid' to either the default_grant or the oidc_grant based on the scopes requested.
This is an adapter class that will route simple Authorization
requests, those that have `id_token` in `response_type` and a scope
including `openid` to either the `default_grant` or the `oidc_grant`
based on the scopes requested.
"""
def __init__(self, default_grant=None, oidc_grant=None):
self.default_grant = default_grant
@ -51,9 +58,11 @@ class ImplicitTokenGrantDispatcher(Dispatcher):
return handler
def create_authorization_response(self, request, token_handler):
"""Read scope and route to the designated handler."""
return self._handler_for_request(request).create_authorization_response(request, token_handler)
def validate_authorization_request(self, request):
"""Read scope and route to the designated handler."""
return self._handler_for_request(request).validate_authorization_request(request)
@ -75,7 +84,7 @@ class AuthorizationTokenGrantDispatcher(Dispatcher):
code = parameters.get('code', None)
redirect_uri = parameters.get('redirect_uri', None)
# If code is not pressent fallback to `default_grant` wich will
# If code is not present fallback to `default_grant` which will
# raise an error for the missing `code` in `create_token_response` step.
if code:
scopes = self.request_validator.get_authorization_code_scopes(client_id, code, redirect_uri, request)
@ -87,5 +96,6 @@ class AuthorizationTokenGrantDispatcher(Dispatcher):
return handler
def create_token_response(self, request, token_handler):
"""Read scope and route to the designated handler."""
handler = self._handler_for_request(request)
return handler.create_token_response(request, token_handler)

View File

@ -1,32 +0,0 @@
class OIDCNoPrompt(Exception):
"""Exception used to inform users that no explicit authorization is needed.
Normally users authorize requests after validation of the request is done.
Then post-authorization validation is again made and a response containing
an auth code or token is created. However, when OIDC clients request
no prompting of user authorization the final response is created directly.
Example (without the shortcut for no prompt)
scopes, req_info = endpoint.validate_authorization_request(url, ...)
authorization_view = create_fancy_auth_form(scopes, req_info)
return authorization_view
Example (with the no prompt shortcut)
try:
scopes, req_info = endpoint.validate_authorization_request(url, ...)
authorization_view = create_fancy_auth_form(scopes, req_info)
return authorization_view
except OIDCNoPrompt:
# Note: Location will be set for you
headers, body, status = endpoint.create_authorization_response(url, ...)
redirect_view = create_redirect(headers, body, status)
return redirect_view
"""
def __init__(self):
msg = ("OIDC request for no user interaction received. Do not ask user "
"for authorization, it should been done using silent "
"authentication through create_authorization_response. "
"See OIDCNoPrompt.__doc__ for more details.")
super(OIDCNoPrompt, self).__init__(msg)

View File

@ -1,17 +1,16 @@
# -*- coding: utf-8 -*-
"""
oauthlib.openid.connect.core.grant_types
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
"""
from __future__ import absolute_import, unicode_literals
import logging
from oauthlib.oauth2.rfc6749.grant_types.authorization_code import AuthorizationCodeGrant as OAuth2AuthorizationCodeGrant
from oauthlib.oauth2.rfc6749.errors import InvalidRequestError
from oauthlib.oauth2.rfc6749.grant_types.authorization_code import (
AuthorizationCodeGrant as OAuth2AuthorizationCodeGrant,
)
from .base import GrantTypeBase
from ..request_validator import RequestValidator
from .base import GrantTypeBase
log = logging.getLogger(__name__)
@ -36,10 +35,13 @@ class HybridGrant(GrantTypeBase):
self.register_code_modifier(self.add_id_token)
self.register_token_modifier(self.add_id_token)
def add_id_token(self, token, token_handler, request):
return super().add_id_token(token, token_handler, request, nonce=request.nonce)
def openid_authorization_validator(self, request):
"""Additional validation when following the Authorization Code flow.
"""
request_info = super(HybridGrant, self).openid_authorization_validator(request)
request_info = super().openid_authorization_validator(request)
if not request_info: # returns immediately if OAuth2.0
return request_info

View File

@ -1,16 +1,15 @@
# -*- coding: utf-8 -*-
"""
oauthlib.openid.connect.core.grant_types
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
"""
from __future__ import absolute_import, unicode_literals
import logging
from .base import GrantTypeBase
from oauthlib.oauth2.rfc6749.grant_types.implicit import ImplicitGrant as OAuth2ImplicitGrant
from oauthlib.oauth2.rfc6749.errors import InvalidRequestError
from oauthlib.oauth2.rfc6749.grant_types.implicit import (
ImplicitGrant as OAuth2ImplicitGrant,
)
from .base import GrantTypeBase
log = logging.getLogger(__name__)
@ -29,12 +28,12 @@ class ImplicitGrant(GrantTypeBase):
def add_id_token(self, token, token_handler, request):
if 'state' not in token and request.state:
token['state'] = request.state
return super(ImplicitGrant, self).add_id_token(token, token_handler, request, nonce=request.nonce)
return super().add_id_token(token, token_handler, request, nonce=request.nonce)
def openid_authorization_validator(self, request):
"""Additional validation when following the implicit flow.
"""
request_info = super(ImplicitGrant, self).openid_authorization_validator(request)
request_info = super().openid_authorization_validator(request)
if not request_info: # returns immediately if OAuth2.0
return request_info

View File

@ -0,0 +1,34 @@
"""
oauthlib.openid.connect.core.grant_types
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
"""
import logging
from oauthlib.oauth2.rfc6749.grant_types.refresh_token import (
RefreshTokenGrant as OAuth2RefreshTokenGrant,
)
from .base import GrantTypeBase
log = logging.getLogger(__name__)
class RefreshTokenGrant(GrantTypeBase):
def __init__(self, request_validator=None, **kwargs):
self.proxy_target = OAuth2RefreshTokenGrant(
request_validator=request_validator, **kwargs)
self.register_token_modifier(self.add_id_token)
def add_id_token(self, token, token_handler, request):
"""
Construct an initial version of id_token, and let the
request_validator sign or encrypt it.
The authorization_code version of this method is used to
retrieve the nonce accordingly to the code storage.
"""
if not self.request_validator.refresh_id_token(request):
return token
return super().add_id_token(token, token_handler, request)

View File

@ -1,13 +1,12 @@
# -*- coding: utf-8 -*-
"""
oauthlib.openid.connect.core.request_validator
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
"""
from __future__ import absolute_import, unicode_literals
import logging
from oauthlib.oauth2.rfc6749.request_validator import RequestValidator as OAuth2RequestValidator
from oauthlib.oauth2.rfc6749.request_validator import (
RequestValidator as OAuth2RequestValidator,
)
log = logging.getLogger(__name__)
@ -307,3 +306,15 @@ class RequestValidator(OAuth2RequestValidator):
Method is used by:
UserInfoEndpoint
"""
def refresh_id_token(self, request):
"""Whether the id token should be refreshed. Default, True
:param request: OAuthlib request.
:type request: oauthlib.common.Request
:rtype: True or False
Method is used by:
RefreshTokenGrant
"""
return True

View File

@ -4,10 +4,9 @@ authlib.openid.connect.core.tokens
This module contains methods for adding JWT tokens to requests.
"""
from __future__ import absolute_import, unicode_literals
from oauthlib.oauth2.rfc6749.tokens import TokenBase, random_token_generator
from oauthlib.oauth2.rfc6749.tokens import (
TokenBase, get_token_from_header, random_token_generator,
)
class JWTToken(TokenBase):
@ -38,17 +37,12 @@ class JWTToken(TokenBase):
return self.request_validator.get_jwt_bearer_token(None, None, request)
def validate_request(self, request):
token = None
if 'Authorization' in request.headers:
token = request.headers.get('Authorization')[7:]
else:
token = request.access_token
token = get_token_from_header(request)
return self.request_validator.validate_jwt_bearer_token(
token, request.scopes, request)
def estimate_type(self, request):
token = request.headers.get('Authorization', '')[7:]
if token.startswith('ey') and token.count('.') in (2, 4):
token = get_token_from_header(request)
if token and token.startswith('ey') and token.count('.') in (2, 4):
return 10
else:
return 0
return 0

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
"""
Implements signals based on blinker if available, otherwise
falls silently back to a noop. Shamelessly stolen from flask.signals:
@ -9,11 +8,11 @@ try:
from blinker import Namespace
signals_available = True
except ImportError: # noqa
class Namespace(object):
class Namespace:
def signal(self, name, doc=None):
return _FakeSignal(name, doc)
class _FakeSignal(object):
class _FakeSignal:
"""If blinker is unavailable, create a fake class with the same
interface that allows sending of signals but will fail with an
error on anything else. Instead of doing anything on send, it

View File

@ -8,8 +8,6 @@ They should be processed with re.VERBOSE.
Thanks Mark Nottingham for this code - https://gist.github.com/138549
"""
from __future__ import unicode_literals
import re
# basics
@ -67,32 +65,8 @@ dec_octet = r"""(?: %(DIGIT)s |
IPv4address = r"%(dec_octet)s \. %(dec_octet)s \. %(dec_octet)s \. %(dec_octet)s" % locals(
)
# h16 = 1*4HEXDIG
h16 = r"(?: %(HEXDIG)s ){1,4}" % locals()
# ls32 = ( h16 ":" h16 ) / IPv4address
ls32 = r"(?: (?: %(h16)s : %(h16)s ) | %(IPv4address)s )" % locals()
# IPv6address = 6( h16 ":" ) ls32
# / "::" 5( h16 ":" ) ls32
# / [ h16 ] "::" 4( h16 ":" ) ls32
# / [ *1( h16 ":" ) h16 ] "::" 3( h16 ":" ) ls32
# / [ *2( h16 ":" ) h16 ] "::" 2( h16 ":" ) ls32
# / [ *3( h16 ":" ) h16 ] "::" h16 ":" ls32
# / [ *4( h16 ":" ) h16 ] "::" ls32
# / [ *5( h16 ":" ) h16 ] "::" h16
# / [ *6( h16 ":" ) h16 ] "::"
IPv6address = r"""(?: (?: %(h16)s : ){6} %(ls32)s |
:: (?: %(h16)s : ){5} %(ls32)s |
%(h16)s :: (?: %(h16)s : ){4} %(ls32)s |
(?: %(h16)s : ) %(h16)s :: (?: %(h16)s : ){3} %(ls32)s |
(?: %(h16)s : ){2} %(h16)s :: (?: %(h16)s : ){2} %(ls32)s |
(?: %(h16)s : ){3} %(h16)s :: %(h16)s : %(ls32)s |
(?: %(h16)s : ){4} %(h16)s :: %(ls32)s |
(?: %(h16)s : ){5} %(h16)s :: %(h16)s |
(?: %(h16)s : ){6} %(h16)s ::
)
""" % locals()
# IPv6address
IPv6address = r"([A-Fa-f0-9:]+[:$])[A-Fa-f0-9]{1,4}"
# IPvFuture = "v" 1*HEXDIG "." 1*( unreserved / sub-delims / ":" )
IPvFuture = r"v %(HEXDIG)s+ \. (?: %(unreserved)s | %(sub_delims)s | : )+" % locals()

View File

@ -1,9 +1,16 @@
[bdist_wheel]
universal = 1
[metadata]
license_file = LICENSE
[isort]
combine_as_imports = true
default_section = THIRDPARTY
include_trailing_comma = true
known_first_party = oauthlib
known_tests = tests
sections = FUTURE,STDLIB,THIRDPARTY,FIRSTPARTY,TESTS,LOCALFOLDER
line_length = 79
multi_line_output = 5
[egg_info]
tag_build =
tag_date = 0

View File

@ -1,12 +1,9 @@
# -*- coding: utf-8 -*-
# Hack because logging + setuptools sucks.
try:
import multiprocessing
except ImportError:
pass
import sys
from os.path import dirname, join
from setuptools import find_packages, setup
@ -19,15 +16,16 @@ def fread(fn):
return f.read()
rsa_require = ['cryptography']
signedtoken_require = ['cryptography', 'pyjwt>=1.0.0']
signals_require = ['blinker']
rsa_require = ['cryptography>=3.0.0']
signedtoken_require = ['cryptography>=3.0.0', 'pyjwt>=2.0.0,<3']
signals_require = ['blinker>=1.4.0']
setup(
name='oauthlib',
version=oauthlib.__version__,
description='A generic, spec-compliant, thorough implementation of the OAuth request-signing logic',
long_description=fread('README.rst'),
long_description_content_type='text/x-rst',
author='The OAuthlib Community',
author_email='idan@gazit.me',
maintainer='Ib Lundgren',
@ -36,7 +34,7 @@ setup(
platforms='any',
license='BSD',
packages=find_packages(exclude=('docs', 'tests', 'tests.*')),
python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*',
python_requires='>=3.6',
extras_require={
'rsa': rsa_require,
'signedtoken': signedtoken_require,
@ -52,13 +50,13 @@ setup(
'Operating System :: POSIX',
'Operating System :: POSIX :: Linux',
'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 :: 3.10',
'Programming Language :: Python :: 3 :: Only',
'Programming Language :: Python :: Implementation',
'Programming Language :: Python :: Implementation :: CPython',
'Programming Language :: Python :: Implementation :: PyPy',

View File

@ -1,12 +1,10 @@
from __future__ import absolute_import, unicode_literals
from mock import ANY, MagicMock
from unittest.mock import ANY, MagicMock
from oauthlib.oauth1 import RequestValidator
from oauthlib.oauth1.rfc5849 import Client
from oauthlib.oauth1.rfc5849.endpoints import AccessTokenEndpoint
from ....unittest import TestCase
from tests.unittest import TestCase
class AccessTokenEndpointTest(TestCase):

View File

@ -1,6 +1,4 @@
from __future__ import absolute_import, unicode_literals
from mock import MagicMock
from unittest.mock import MagicMock
from oauthlib.oauth1 import RequestValidator
from oauthlib.oauth1.rfc5849 import errors

View File

@ -1,17 +1,16 @@
from __future__ import absolute_import, unicode_literals
from re import sub
from mock import MagicMock
from unittest.mock import MagicMock
from oauthlib.common import CaseInsensitiveDict, safe_string_equals
from oauthlib.oauth1 import Client, RequestValidator
from oauthlib.oauth1.rfc5849 import (SIGNATURE_HMAC, SIGNATURE_PLAINTEXT,
SIGNATURE_RSA, errors)
from oauthlib.oauth1.rfc5849.endpoints import (BaseEndpoint,
RequestTokenEndpoint)
from oauthlib.oauth1.rfc5849 import (
SIGNATURE_HMAC, SIGNATURE_PLAINTEXT, SIGNATURE_RSA, errors,
)
from oauthlib.oauth1.rfc5849.endpoints import (
BaseEndpoint, RequestTokenEndpoint,
)
from ....unittest import TestCase
from tests.unittest import TestCase
URLENCODED = {"Content-Type": "application/x-www-form-urlencoded"}

View File

@ -1,12 +1,10 @@
from __future__ import absolute_import, unicode_literals
from mock import ANY, MagicMock
from unittest.mock import ANY, MagicMock
from oauthlib.oauth1 import RequestValidator
from oauthlib.oauth1.rfc5849 import Client
from oauthlib.oauth1.rfc5849.endpoints import RequestTokenEndpoint
from ....unittest import TestCase
from tests.unittest import TestCase
class RequestTokenEndpointTest(TestCase):

View File

@ -1,12 +1,10 @@
from __future__ import absolute_import, unicode_literals
from mock import ANY, MagicMock
from unittest.mock import ANY, MagicMock
from oauthlib.oauth1 import RequestValidator
from oauthlib.oauth1.rfc5849 import Client
from oauthlib.oauth1.rfc5849.endpoints import ResourceEndpoint
from ....unittest import TestCase
from tests.unittest import TestCase
class ResourceEndpointTest(TestCase):

View File

@ -1,12 +1,10 @@
from __future__ import absolute_import, unicode_literals
from mock import ANY, MagicMock
from unittest.mock import ANY, MagicMock
from oauthlib.oauth1 import RequestValidator
from oauthlib.oauth1.rfc5849 import Client
from oauthlib.oauth1.rfc5849.endpoints import SignatureOnlyEndpoint
from ....unittest import TestCase
from tests.unittest import TestCase
class SignatureOnlyEndpointTest(TestCase):

View File

@ -1,13 +1,12 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, unicode_literals
from oauthlib.common import Request
from oauthlib.oauth1 import (SIGNATURE_PLAINTEXT, SIGNATURE_HMAC_SHA1,
SIGNATURE_HMAC_SHA256, SIGNATURE_RSA,
SIGNATURE_TYPE_BODY, SIGNATURE_TYPE_QUERY)
from oauthlib.oauth1 import (
SIGNATURE_HMAC_SHA1, SIGNATURE_HMAC_SHA256, SIGNATURE_PLAINTEXT,
SIGNATURE_RSA, SIGNATURE_TYPE_BODY, SIGNATURE_TYPE_QUERY,
)
from oauthlib.oauth1.rfc5849 import Client
from ...unittest import TestCase
from tests.unittest import TestCase
class ClientRealmTests(TestCase):
@ -163,7 +162,7 @@ class SignatureMethodTest(TestCase):
Client.register_signature_method('PIZZA',
lambda base_string, client: 'PIZZA')
self.assertTrue('PIZZA' in Client.SIGNATURE_METHODS)
self.assertIn('PIZZA', Client.SIGNATURE_METHODS)
client = Client('client_key', signature_method='PIZZA',
timestamp='1234567890', nonce='abc')

View File

@ -1,13 +1,11 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, unicode_literals
from oauthlib.common import urlencode
from oauthlib.oauth1.rfc5849.parameters import (_append_params,
prepare_form_encoded_body,
prepare_headers,
prepare_request_uri_query)
from oauthlib.oauth1.rfc5849.parameters import (
_append_params, prepare_form_encoded_body, prepare_headers,
prepare_request_uri_query,
)
from ...unittest import TestCase
from tests.unittest import TestCase
class ParameterTests(TestCase):

View File

@ -1,9 +1,7 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, unicode_literals
from oauthlib.oauth1 import RequestValidator
from ...unittest import TestCase
from tests.unittest import TestCase
class RequestValidatorTests(TestCase):

File diff suppressed because it is too large Load Diff

View File

@ -1,10 +1,7 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, unicode_literals
from oauthlib.common import unicode_type
from oauthlib.oauth1.rfc5849.utils import *
from ...unittest import TestCase
from tests.unittest import TestCase
class UtilsTests(TestCase):
@ -102,12 +99,12 @@ class UtilsTests(TestCase):
def test_escape(self):
self.assertRaises(ValueError, escape, b"I am a string type. Not a unicode type.")
self.assertEqual(escape("I am a unicode type."), "I%20am%20a%20unicode%20type.")
self.assertIsInstance(escape("I am a unicode type."), unicode_type)
self.assertIsInstance(escape("I am a unicode type."), str)
def test_unescape(self):
self.assertRaises(ValueError, unescape, b"I am a string type. Not a unicode type.")
self.assertEqual(unescape("I%20am%20a%20unicode%20type."), 'I am a unicode type.')
self.assertIsInstance(unescape("I%20am%20a%20unicode%20type."), unicode_type)
self.assertIsInstance(unescape("I%20am%20a%20unicode%20type."), str)
def test_parse_authorization_header(self):
# make us some headers
@ -122,8 +119,8 @@ class UtilsTests(TestCase):
# are the internal components of each tuple unicode?
for k, v in authorization_headers:
self.assertIsInstance(k, unicode_type)
self.assertIsInstance(v, unicode_type)
self.assertIsInstance(k, str)
self.assertIsInstance(v, str)
# let's check the parsed headers created
correct_headers = [

View File

@ -1,14 +1,11 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, unicode_literals
import os
from mock import patch
from unittest.mock import patch
from oauthlib import signals
from oauthlib.oauth2 import BackendApplicationClient
from ....unittest import TestCase
from tests.unittest import TestCase
@patch('time.time', new=lambda: 1000)

View File

@ -1,6 +1,4 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, unicode_literals
import datetime
from oauthlib import common
@ -8,7 +6,7 @@ from oauthlib.oauth2 import Client, InsecureTransportError, TokenExpiredError
from oauthlib.oauth2.rfc6749 import utils
from oauthlib.oauth2.rfc6749.clients import AUTH_HEADER, BODY, URI_QUERY
from ....unittest import TestCase
from tests.unittest import TestCase
class ClientTest(TestCase):
@ -295,11 +293,63 @@ class ClientTest(TestCase):
u, h, b = client.prepare_refresh_token_request(url, token, scope=scope)
self.assertEqual(u, url)
self.assertEqual(h, {'Content-Type': 'application/x-www-form-urlencoded'})
self.assertFormBodyEqual(b, 'grant_type=refresh_token&scope=%s&refresh_token=%s' % (scope, token))
self.assertFormBodyEqual(b, 'grant_type=refresh_token&scope={}&refresh_token={}'.format(scope, token))
# provide scope while init
client = Client(self.client_id, scope=scope)
u, h, b = client.prepare_refresh_token_request(url, token, scope=scope)
self.assertEqual(u, url)
self.assertEqual(h, {'Content-Type': 'application/x-www-form-urlencoded'})
self.assertFormBodyEqual(b, 'grant_type=refresh_token&scope=%s&refresh_token=%s' % (scope, token))
self.assertFormBodyEqual(b, 'grant_type=refresh_token&scope={}&refresh_token={}'.format(scope, token))
def test_parse_token_response_invalid_expires_at(self):
token_json = ('{ "access_token":"2YotnFZFEjr1zCsicMWpAA",'
' "token_type":"example",'
' "expires_at":"2006-01-02T15:04:05Z",'
' "scope":"/profile",'
' "example_parameter":"example_value"}')
token = {
"access_token": "2YotnFZFEjr1zCsicMWpAA",
"token_type": "example",
"expires_at": "2006-01-02T15:04:05Z",
"scope": ["/profile"],
"example_parameter": "example_value"
}
client = Client(self.client_id)
# Parse code and state
response = client.parse_request_body_response(token_json, scope=["/profile"])
self.assertEqual(response, token)
self.assertEqual(None, client._expires_at)
self.assertEqual(client.access_token, response.get("access_token"))
self.assertEqual(client.refresh_token, response.get("refresh_token"))
self.assertEqual(client.token_type, response.get("token_type"))
def test_create_code_verifier_min_length(self):
client = Client(self.client_id)
length = 43
code_verifier = client.create_code_verifier(length=length)
self.assertEqual(client.code_verifier, code_verifier)
def test_create_code_verifier_max_length(self):
client = Client(self.client_id)
length = 128
code_verifier = client.create_code_verifier(length=length)
self.assertEqual(client.code_verifier, code_verifier)
def test_create_code_challenge_plain(self):
client = Client(self.client_id)
code_verifier = client.create_code_verifier(length=128)
code_challenge_plain = client.create_code_challenge(code_verifier=code_verifier)
# if no code_challenge_method specified, code_challenge = code_verifier
self.assertEqual(code_challenge_plain, client.code_verifier)
self.assertEqual(client.code_challenge_method, "plain")
def test_create_code_challenge_s256(self):
client = Client(self.client_id)
code_verifier = client.create_code_verifier(length=128)
code_challenge_s256 = client.create_code_challenge(code_verifier=code_verifier, code_challenge_method='S256')
self.assertEqual(code_challenge_s256, client.code_challenge)

View File

@ -1,20 +1,12 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, unicode_literals
import os
from mock import patch
import urllib.parse as urlparse
from unittest.mock import patch
from oauthlib import signals
from oauthlib.oauth2 import LegacyApplicationClient
from ....unittest import TestCase
# this is the same import method used in oauthlib/oauth2/rfc6749/parameters.py
try:
import urlparse
except ImportError:
import urllib.parse as urlparse
from tests.unittest import TestCase
@patch('time.time', new=lambda: 1000)
@ -32,7 +24,7 @@ class LegacyApplicationClientTest(TestCase):
password = "user_password"
body = "not=empty"
body_up = "not=empty&grant_type=password&username=%s&password=%s" % (username, password)
body_up = "not=empty&grant_type=password&username={}&password={}".format(username, password)
body_kwargs = body_up + "&some=providers&require=extra+arguments"
token_json = ('{ "access_token":"2YotnFZFEjr1zCsicMWpAA",'
@ -105,8 +97,8 @@ class LegacyApplicationClientTest(TestCase):
# scenario 1, default behavior to not include `client_id`
r1 = client.prepare_request_body(username=self.username, password=self.password)
self.assertIn(r1, ('grant_type=password&username=%s&password=%s' % (self.username, self.password, ),
'grant_type=password&password=%s&username=%s' % (self.password, self.username, ),
self.assertIn(r1, ('grant_type=password&username={}&password={}'.format(self.username, self.password),
'grant_type=password&password={}&username={}'.format(self.password, self.username),
))
# scenario 2, include `client_id` in the body

View File

@ -1,14 +1,11 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, unicode_literals
import os
from mock import patch
from unittest.mock import patch
from oauthlib import signals
from oauthlib.oauth2 import MobileApplicationClient
from ....unittest import TestCase
from tests.unittest import TestCase
@patch('time.time', new=lambda: 1000)

Some files were not shown because too many files have changed in this diff Show More