Import Upstream version 3.2.2
This commit is contained in:
parent
5d789ab01a
commit
dabc06c9f7
121
CHANGELOG.rst
121
CHANGELOG.rst
|
@ -1,33 +1,112 @@
|
||||||
Changelog
|
Changelog
|
||||||
=========
|
=========
|
||||||
|
|
||||||
3.1.0 (TBD)
|
3.2.2 (2022-10-17)
|
||||||
------------------
|
------------------
|
||||||
|
OAuth2.0 Provider:
|
||||||
|
* CVE-2022-36087
|
||||||
|
|
||||||
OAuth2.0 Provider - Features
|
3.2.1 (2022-09-09)
|
||||||
* #660: OIDC add support of nonce, c_hash, at_hash fields
|
------------------
|
||||||
- New RequestValidator.fill_id_token method
|
OAuth2.0 Provider:
|
||||||
- Deprecated RequestValidator.get_id_token method
|
* #803: Metadata endpoint support of non-HTTPS
|
||||||
* #677: OIDC add UserInfo endpoint
|
|
||||||
- New RequestValidator.get_userinfo_claims method
|
|
||||||
|
|
||||||
OAuth2.0 Provider - Security
|
OAuth1.0:
|
||||||
* #665: Enhance data leak to logs
|
* #818: Allow IPv6 being parsed by signature
|
||||||
- New default to not expose request content in logs
|
|
||||||
- New function `oauthlib.set_debug(True)`
|
|
||||||
* #666: Disabling query parameters for POST requests
|
|
||||||
|
|
||||||
|
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
|
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
|
OAuth2.0 Client - Bugfixes
|
||||||
* #290: Fix Authorization Code's errors processing
|
|
||||||
* #603: BackendApplication.Client.prepare_request_body use the "scope" argument as intended.
|
* #730: Base OAuth2 Client now has a consistent way of managing the `scope`: it consistently
|
||||||
* #672: Fix edge case when expires_in=Null
|
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
|
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)
|
3.0.2 (2019-07-04)
|
||||||
------------------
|
------------------
|
||||||
|
@ -84,7 +163,7 @@ OAuth1.0 Client:
|
||||||
General fixes:
|
General fixes:
|
||||||
|
|
||||||
* $ and ' are allowed to be unencoded in query strings #564
|
* $ 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
|
* Removed unnecessary code for handling python2.6
|
||||||
* Add support of python3.7 #621
|
* Add support of python3.7 #621
|
||||||
* Several minors updates to setup.py and tox
|
* Several minors updates to setup.py and tox
|
||||||
|
@ -142,7 +221,7 @@ General fixes:
|
||||||
* Added log statements to except clauses.
|
* Added log statements to except clauses.
|
||||||
* According to RC7009 Section 2.1, a client should include authentication credentials when revoking its tokens.
|
* 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.
|
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.
|
* Improved prompt parameter validation.
|
||||||
* Added two error codes from RFC 6750.
|
* Added two error codes from RFC 6750.
|
||||||
* Hybrid response types are now be fragment-encoded.
|
* 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:
|
Draft revocation endpoint features and numerous fixes including:
|
||||||
|
|
||||||
* (OAuth 2 Provider) is_within_original_scope to check whether a refresh token
|
* (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.
|
* (OAuth 2 Provider) expires_in token lifetime can be set per request.
|
||||||
|
|
||||||
|
|
286
PKG-INFO
286
PKG-INFO
|
@ -1,6 +1,6 @@
|
||||||
Metadata-Version: 2.1
|
Metadata-Version: 2.1
|
||||||
Name: oauthlib
|
Name: oauthlib
|
||||||
Version: 3.1.0
|
Version: 3.2.2
|
||||||
Summary: A generic, spec-compliant, thorough implementation of the OAuth request-signing logic
|
Summary: A generic, spec-compliant, thorough implementation of the OAuth request-signing logic
|
||||||
Home-page: https://github.com/oauthlib/oauthlib
|
Home-page: https://github.com/oauthlib/oauthlib
|
||||||
Author: The OAuthlib Community
|
Author: The OAuthlib Community
|
||||||
|
@ -8,138 +8,6 @@ Author-email: idan@gazit.me
|
||||||
Maintainer: Ib Lundgren
|
Maintainer: Ib Lundgren
|
||||||
Maintainer-email: ib.lundgren@gmail.com
|
Maintainer-email: ib.lundgren@gmail.com
|
||||||
License: BSD
|
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
|
Platform: any
|
||||||
Classifier: Development Status :: 5 - Production/Stable
|
Classifier: Development Status :: 5 - Production/Stable
|
||||||
Classifier: Environment :: Web Environment
|
Classifier: Environment :: Web Environment
|
||||||
|
@ -150,18 +18,158 @@ Classifier: Operating System :: MacOS
|
||||||
Classifier: Operating System :: POSIX
|
Classifier: Operating System :: POSIX
|
||||||
Classifier: Operating System :: POSIX :: Linux
|
Classifier: Operating System :: POSIX :: Linux
|
||||||
Classifier: Programming Language :: Python
|
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
|
||||||
Classifier: Programming Language :: Python :: 3.4
|
|
||||||
Classifier: Programming Language :: Python :: 3.5
|
|
||||||
Classifier: Programming Language :: Python :: 3.6
|
Classifier: Programming Language :: Python :: 3.6
|
||||||
Classifier: Programming Language :: Python :: 3.7
|
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
|
||||||
Classifier: Programming Language :: Python :: Implementation :: CPython
|
Classifier: Programming Language :: Python :: Implementation :: CPython
|
||||||
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
||||||
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
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: signedtoken
|
||||||
Provides-Extra: signals
|
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``.
|
||||||
|
|
14
README.rst
14
README.rst
|
@ -2,10 +2,10 @@ OAuthLib - Python Framework for OAuth1 & OAuth2
|
||||||
===============================================
|
===============================================
|
||||||
|
|
||||||
*A generic, spec-compliant, thorough implementation of the OAuth request-signing
|
*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
|
.. image:: https://app.travis-ci.com/oauthlib/oauthlib.svg?branch=master
|
||||||
:target: https://travis-ci.org/oauthlib/oauthlib
|
:target: https://app.travis-ci.com/oauthlib/oauthlib
|
||||||
:alt: Travis
|
:alt: Travis
|
||||||
.. image:: https://coveralls.io/repos/oauthlib/oauthlib/badge.svg?branch=master
|
.. image:: https://coveralls.io/repos/oauthlib/oauthlib/badge.svg?branch=master
|
||||||
:target: https://coveralls.io/r/oauthlib/oauthlib
|
: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
|
:target: https://gitter.im/oauthlib/Lobby
|
||||||
:alt: Chat on Gitter
|
: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
|
OAuth often seems complicated and difficult-to-implement. There are several
|
||||||
prominent libraries for handling OAuth requests, but they all suffer from one or
|
prominent libraries for handling OAuth requests, but they all suffer from one or
|
||||||
both of the following:
|
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
|
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
|
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
|
.. _`Gitter community`: https://gitter.im/oauthlib/Lobby
|
||||||
|
|
||||||
|
|
|
@ -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 +0,0 @@
|
||||||
oauthlib.egg-info/*
|
|
|
@ -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.
|
|
|
@ -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.
|
|
|
@ -1,2 +0,0 @@
|
||||||
README.rst
|
|
||||||
|
|
|
@ -1,8 +0,0 @@
|
||||||
#!/usr/bin/make -f
|
|
||||||
|
|
||||||
export PYBUILD_NAME=oauthlib
|
|
||||||
|
|
||||||
export PYTHONWARNINGS=d
|
|
||||||
|
|
||||||
%:
|
|
||||||
dh $@ --with python3 --buildsystem=pybuild
|
|
|
@ -1 +0,0 @@
|
||||||
3.0 (native)
|
|
|
@ -1,6 +0,0 @@
|
||||||
Tests: python3-oauthlib
|
|
||||||
Depends:
|
|
||||||
python3-all,
|
|
||||||
python3-mock,
|
|
||||||
python3-pytest (>= 4.0),
|
|
||||||
python3-oauthlib
|
|
|
@ -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
|
|
|
@ -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)))
|
|
|
@ -1,6 +1,6 @@
|
||||||
Metadata-Version: 2.1
|
Metadata-Version: 2.1
|
||||||
Name: oauthlib
|
Name: oauthlib
|
||||||
Version: 3.1.0
|
Version: 3.2.2
|
||||||
Summary: A generic, spec-compliant, thorough implementation of the OAuth request-signing logic
|
Summary: A generic, spec-compliant, thorough implementation of the OAuth request-signing logic
|
||||||
Home-page: https://github.com/oauthlib/oauthlib
|
Home-page: https://github.com/oauthlib/oauthlib
|
||||||
Author: The OAuthlib Community
|
Author: The OAuthlib Community
|
||||||
|
@ -8,138 +8,6 @@ Author-email: idan@gazit.me
|
||||||
Maintainer: Ib Lundgren
|
Maintainer: Ib Lundgren
|
||||||
Maintainer-email: ib.lundgren@gmail.com
|
Maintainer-email: ib.lundgren@gmail.com
|
||||||
License: BSD
|
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
|
Platform: any
|
||||||
Classifier: Development Status :: 5 - Production/Stable
|
Classifier: Development Status :: 5 - Production/Stable
|
||||||
Classifier: Environment :: Web Environment
|
Classifier: Environment :: Web Environment
|
||||||
|
@ -150,18 +18,158 @@ Classifier: Operating System :: MacOS
|
||||||
Classifier: Operating System :: POSIX
|
Classifier: Operating System :: POSIX
|
||||||
Classifier: Operating System :: POSIX :: Linux
|
Classifier: Operating System :: POSIX :: Linux
|
||||||
Classifier: Programming Language :: Python
|
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
|
||||||
Classifier: Programming Language :: Python :: 3.4
|
|
||||||
Classifier: Programming Language :: Python :: 3.5
|
|
||||||
Classifier: Programming Language :: Python :: 3.6
|
Classifier: Programming Language :: Python :: 3.6
|
||||||
Classifier: Programming Language :: Python :: 3.7
|
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
|
||||||
Classifier: Programming Language :: Python :: Implementation :: CPython
|
Classifier: Programming Language :: Python :: Implementation :: CPython
|
||||||
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
||||||
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
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: signedtoken
|
||||||
Provides-Extra: signals
|
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``.
|
||||||
|
|
|
@ -58,6 +58,9 @@ oauthlib/oauth2/rfc6749/grant_types/client_credentials.py
|
||||||
oauthlib/oauth2/rfc6749/grant_types/implicit.py
|
oauthlib/oauth2/rfc6749/grant_types/implicit.py
|
||||||
oauthlib/oauth2/rfc6749/grant_types/refresh_token.py
|
oauthlib/oauth2/rfc6749/grant_types/refresh_token.py
|
||||||
oauthlib/oauth2/rfc6749/grant_types/resource_owner_password_credentials.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/__init__.py
|
||||||
oauthlib/openid/connect/__init__.py
|
oauthlib/openid/connect/__init__.py
|
||||||
oauthlib/openid/connect/core/__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/authorization_code.py
|
||||||
oauthlib/openid/connect/core/grant_types/base.py
|
oauthlib/openid/connect/core/grant_types/base.py
|
||||||
oauthlib/openid/connect/core/grant_types/dispatchers.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/hybrid.py
|
||||||
oauthlib/openid/connect/core/grant_types/implicit.py
|
oauthlib/openid/connect/core/grant_types/implicit.py
|
||||||
|
oauthlib/openid/connect/core/grant_types/refresh_token.py
|
||||||
tests/__init__.py
|
tests/__init__.py
|
||||||
tests/test_common.py
|
tests/test_common.py
|
||||||
|
tests/test_uri_validate.py
|
||||||
tests/oauth1/__init__.py
|
tests/oauth1/__init__.py
|
||||||
tests/oauth1/rfc5849/__init__.py
|
tests/oauth1/rfc5849/__init__.py
|
||||||
tests/oauth1/rfc5849/test_client.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_implicit.py
|
||||||
tests/oauth2/rfc6749/grant_types/test_refresh_token.py
|
tests/oauth2/rfc6749/grant_types/test_refresh_token.py
|
||||||
tests/oauth2/rfc6749/grant_types/test_resource_owner_password.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/__init__.py
|
||||||
tests/openid/connect/__init__.py
|
tests/openid/connect/__init__.py
|
||||||
tests/openid/connect/core/__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_dispatchers.py
|
||||||
tests/openid/connect/core/grant_types/test_hybrid.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_implicit.py
|
||||||
|
tests/openid/connect/core/grant_types/test_refresh_token.py
|
||||||
tests/unittest/__init__.py
|
tests/unittest/__init__.py
|
|
@ -1,10 +1,10 @@
|
||||||
|
|
||||||
[rsa]
|
[rsa]
|
||||||
cryptography
|
cryptography>=3.0.0
|
||||||
|
|
||||||
[signals]
|
[signals]
|
||||||
blinker
|
blinker>=1.4.0
|
||||||
|
|
||||||
[signedtoken]
|
[signedtoken]
|
||||||
cryptography
|
cryptography>=3.0.0
|
||||||
pyjwt>=1.0.0
|
pyjwt<3,>=2.0.0
|
||||||
|
|
|
@ -12,7 +12,7 @@ import logging
|
||||||
from logging import NullHandler
|
from logging import NullHandler
|
||||||
|
|
||||||
__author__ = 'The OAuthlib Community'
|
__author__ = 'The OAuthlib Community'
|
||||||
__version__ = '3.1.0'
|
__version__ = '3.2.2'
|
||||||
|
|
||||||
logging.getLogger('oauthlib').addHandler(NullHandler())
|
logging.getLogger('oauthlib').addHandler(NullHandler())
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
"""
|
"""
|
||||||
oauthlib.common
|
oauthlib.common
|
||||||
~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~
|
||||||
|
@ -6,34 +5,22 @@ oauthlib.common
|
||||||
This module provides data structures and utilities common
|
This module provides data structures and utilities common
|
||||||
to all implementations of OAuth.
|
to all implementations of OAuth.
|
||||||
"""
|
"""
|
||||||
from __future__ import absolute_import, unicode_literals
|
|
||||||
|
|
||||||
import collections
|
import collections
|
||||||
import datetime
|
import datetime
|
||||||
import logging
|
import logging
|
||||||
import re
|
import re
|
||||||
import sys
|
|
||||||
import time
|
import time
|
||||||
|
import urllib.parse as urlparse
|
||||||
|
from urllib.parse import (
|
||||||
|
quote as _quote, unquote as _unquote, urlencode as _urlencode,
|
||||||
|
)
|
||||||
|
|
||||||
from . import get_debug
|
from . import get_debug
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from secrets import randbits
|
from secrets import SystemRandom, randbits
|
||||||
from secrets import SystemRandom
|
|
||||||
except ImportError:
|
except ImportError:
|
||||||
from random import getrandbits as randbits
|
from random import SystemRandom, 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
|
|
||||||
|
|
||||||
UNICODE_ASCII_CHARACTER_SET = ('abcdefghijklmnopqrstuvwxyz'
|
UNICODE_ASCII_CHARACTER_SET = ('abcdefghijklmnopqrstuvwxyz'
|
||||||
'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
|
'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
|
||||||
|
@ -51,17 +38,10 @@ always_safe = ('ABCDEFGHIJKLMNOPQRSTUVWXYZ'
|
||||||
|
|
||||||
log = logging.getLogger('oauthlib')
|
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)
|
# 'safe' must be bytes (Python 2.6 requires bytes, other versions allow either)
|
||||||
def quote(s, safe=b'/'):
|
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)
|
s = _quote(s, safe)
|
||||||
# PY3 always returns unicode. PY2 may return either, depending on whether
|
# PY3 always returns unicode. PY2 may return either, depending on whether
|
||||||
# it had to modify the string.
|
# it had to modify the string.
|
||||||
|
@ -83,7 +63,7 @@ def unquote(s):
|
||||||
def urlencode(params):
|
def urlencode(params):
|
||||||
utf8_params = encode_params_utf8(params)
|
utf8_params = encode_params_utf8(params)
|
||||||
urlencoded = _urlencode(utf8_params)
|
urlencoded = _urlencode(utf8_params)
|
||||||
if isinstance(urlencoded, unicode_type): # PY3 returns unicode
|
if isinstance(urlencoded, str):
|
||||||
return urlencoded
|
return urlencoded
|
||||||
else:
|
else:
|
||||||
return urlencoded.decode("utf-8")
|
return urlencoded.decode("utf-8")
|
||||||
|
@ -96,8 +76,8 @@ def encode_params_utf8(params):
|
||||||
encoded = []
|
encoded = []
|
||||||
for k, v in params:
|
for k, v in params:
|
||||||
encoded.append((
|
encoded.append((
|
||||||
k.encode('utf-8') if isinstance(k, unicode_type) else k,
|
k.encode('utf-8') if isinstance(k, str) else k,
|
||||||
v.encode('utf-8') if isinstance(v, unicode_type) else v))
|
v.encode('utf-8') if isinstance(v, str) else v))
|
||||||
return encoded
|
return encoded
|
||||||
|
|
||||||
|
|
||||||
|
@ -141,22 +121,6 @@ def urldecode(query):
|
||||||
if INVALID_HEX_PATTERN.search(query):
|
if INVALID_HEX_PATTERN.search(query):
|
||||||
raise ValueError('Invalid hex encoding in query string.')
|
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
|
# We want to allow queries such as "c2" whereas urlparse.parse_qsl
|
||||||
# with the strict_parsing flag will not.
|
# with the strict_parsing flag will not.
|
||||||
params = urlparse.parse_qsl(query, keep_blank_values=True)
|
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
|
empty list of parameters. Any other input will result in a return
|
||||||
value of None.
|
value of None.
|
||||||
"""
|
"""
|
||||||
if isinstance(raw, (bytes, unicode_type)):
|
if isinstance(raw, (bytes, str)):
|
||||||
try:
|
try:
|
||||||
params = urldecode(raw)
|
params = urldecode(raw)
|
||||||
except ValueError:
|
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.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
|
.. _`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():
|
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.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
|
.. _`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):
|
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'):
|
def to_unicode(data, encoding='UTF-8'):
|
||||||
"""Convert a number of different types of objects to unicode."""
|
"""Convert a number of different types of objects to unicode."""
|
||||||
if isinstance(data, unicode_type):
|
if isinstance(data, str):
|
||||||
return data
|
return data
|
||||||
|
|
||||||
if isinstance(data, bytes):
|
if isinstance(data, bytes):
|
||||||
return unicode_type(data, encoding=encoding)
|
return str(data, encoding=encoding)
|
||||||
|
|
||||||
if hasattr(data, '__iter__'):
|
if hasattr(data, '__iter__'):
|
||||||
try:
|
try:
|
||||||
|
@ -323,7 +287,7 @@ def to_unicode(data, encoding='UTF-8'):
|
||||||
# We support 2.6 which lacks dict comprehensions
|
# We support 2.6 which lacks dict comprehensions
|
||||||
if hasattr(data, 'items'):
|
if hasattr(data, 'items'):
|
||||||
data = 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
|
return data
|
||||||
|
|
||||||
|
@ -335,7 +299,7 @@ class CaseInsensitiveDict(dict):
|
||||||
proxy = {}
|
proxy = {}
|
||||||
|
|
||||||
def __init__(self, data):
|
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:
|
for k in data:
|
||||||
self[k] = data[k]
|
self[k] = data[k]
|
||||||
|
|
||||||
|
@ -344,27 +308,27 @@ class CaseInsensitiveDict(dict):
|
||||||
|
|
||||||
def __delitem__(self, k):
|
def __delitem__(self, k):
|
||||||
key = self.proxy[k.lower()]
|
key = self.proxy[k.lower()]
|
||||||
super(CaseInsensitiveDict, self).__delitem__(key)
|
super().__delitem__(key)
|
||||||
del self.proxy[k.lower()]
|
del self.proxy[k.lower()]
|
||||||
|
|
||||||
def __getitem__(self, k):
|
def __getitem__(self, k):
|
||||||
key = self.proxy[k.lower()]
|
key = self.proxy[k.lower()]
|
||||||
return super(CaseInsensitiveDict, self).__getitem__(key)
|
return super().__getitem__(key)
|
||||||
|
|
||||||
def get(self, k, default=None):
|
def get(self, k, default=None):
|
||||||
return self[k] if k in self else default
|
return self[k] if k in self else default
|
||||||
|
|
||||||
def __setitem__(self, k, v):
|
def __setitem__(self, k, v):
|
||||||
super(CaseInsensitiveDict, self).__setitem__(k, v)
|
super().__setitem__(k, v)
|
||||||
self.proxy[k.lower()] = k
|
self.proxy[k.lower()] = k
|
||||||
|
|
||||||
def update(self, *args, **kwargs):
|
def update(self, *args, **kwargs):
|
||||||
super(CaseInsensitiveDict, self).update(*args, **kwargs)
|
super().update(*args, **kwargs)
|
||||||
for k in dict(*args, **kwargs):
|
for k in dict(*args, **kwargs):
|
||||||
self.proxy[k.lower()] = k
|
self.proxy[k.lower()] = k
|
||||||
|
|
||||||
|
|
||||||
class Request(object):
|
class Request:
|
||||||
|
|
||||||
"""A malleable representation of a signable HTTP request.
|
"""A malleable representation of a signable HTTP request.
|
||||||
|
|
||||||
|
@ -444,7 +408,7 @@ class Request(object):
|
||||||
body = SANITIZE_PATTERN.sub('\1<SANITIZED>', str(body))
|
body = SANITIZE_PATTERN.sub('\1<SANITIZED>', str(body))
|
||||||
if 'Authorization' in headers:
|
if 'Authorization' in headers:
|
||||||
headers['Authorization'] = '<SANITIZED>'
|
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)
|
self.uri, self.http_method, headers, body)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
"""
|
"""
|
||||||
oauthlib.oauth1
|
oauthlib.oauth1
|
||||||
~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~
|
||||||
|
@ -6,14 +5,19 @@ oauthlib.oauth1
|
||||||
This module is a wrapper for the most recent implementation of OAuth 1.0 Client
|
This module is a wrapper for the most recent implementation of OAuth 1.0 Client
|
||||||
and Server classes.
|
and Server classes.
|
||||||
"""
|
"""
|
||||||
from __future__ import absolute_import, unicode_literals
|
from .rfc5849 import (
|
||||||
|
SIGNATURE_HMAC, SIGNATURE_HMAC_SHA1, SIGNATURE_HMAC_SHA256,
|
||||||
from .rfc5849 import Client
|
SIGNATURE_HMAC_SHA512, SIGNATURE_PLAINTEXT, SIGNATURE_RSA,
|
||||||
from .rfc5849 import SIGNATURE_HMAC, SIGNATURE_HMAC_SHA1, SIGNATURE_HMAC_SHA256, SIGNATURE_RSA, SIGNATURE_PLAINTEXT
|
SIGNATURE_RSA_SHA1, SIGNATURE_RSA_SHA256, SIGNATURE_RSA_SHA512,
|
||||||
from .rfc5849 import SIGNATURE_TYPE_AUTH_HEADER, SIGNATURE_TYPE_QUERY
|
SIGNATURE_TYPE_AUTH_HEADER, SIGNATURE_TYPE_BODY, SIGNATURE_TYPE_QUERY,
|
||||||
from .rfc5849 import SIGNATURE_TYPE_BODY
|
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.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
|
|
||||||
|
|
|
@ -1,33 +1,68 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
"""
|
"""
|
||||||
oauthlib.oauth1.rfc5849
|
oauthlib.oauth1.rfc5849
|
||||||
~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~
|
||||||
|
|
||||||
This module is an implementation of various logic needed
|
This module is an implementation of various logic needed
|
||||||
for signing and checking OAuth 1.0 RFC 5849 requests.
|
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 base64
|
||||||
import hashlib
|
import hashlib
|
||||||
import logging
|
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__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
import sys
|
# Available signature methods
|
||||||
try:
|
#
|
||||||
import urlparse
|
# Note: SIGNATURE_HMAC and SIGNATURE_RSA are kept for backward compatibility
|
||||||
except ImportError:
|
# with previous versions of this library, when it the only HMAC-based and
|
||||||
import urllib.parse as urlparse
|
# RSA-based signature methods were HMAC-SHA1 and RSA-SHA1. But now that it
|
||||||
|
# supports other hashing algorithms besides SHA1, explicitly identifying which
|
||||||
from oauthlib.common import Request, urlencode, generate_nonce
|
# hashing algorithm is being used is recommended.
|
||||||
from oauthlib.common import generate_timestamp, to_unicode
|
#
|
||||||
from . import parameters, signature
|
# 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_SHA1 = "HMAC-SHA1"
|
||||||
SIGNATURE_HMAC_SHA256 = "HMAC-SHA256"
|
SIGNATURE_HMAC_SHA256 = "HMAC-SHA256"
|
||||||
SIGNATURE_HMAC = SIGNATURE_HMAC_SHA1
|
SIGNATURE_HMAC_SHA512 = "HMAC-SHA512"
|
||||||
SIGNATURE_RSA = "RSA-SHA1"
|
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_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_AUTH_HEADER = 'AUTH_HEADER'
|
||||||
SIGNATURE_TYPE_QUERY = 'QUERY'
|
SIGNATURE_TYPE_QUERY = 'QUERY'
|
||||||
|
@ -36,13 +71,16 @@ SIGNATURE_TYPE_BODY = 'BODY'
|
||||||
CONTENT_TYPE_FORM_URLENCODED = 'application/x-www-form-urlencoded'
|
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."""
|
"""A client used to sign OAuth 1.0 RFC 5849 requests."""
|
||||||
SIGNATURE_METHODS = {
|
SIGNATURE_METHODS = {
|
||||||
SIGNATURE_HMAC_SHA1: signature.sign_hmac_sha1_with_client,
|
SIGNATURE_HMAC_SHA1: signature.sign_hmac_sha1_with_client,
|
||||||
SIGNATURE_HMAC_SHA256: signature.sign_hmac_sha256_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
|
SIGNATURE_PLAINTEXT: signature.sign_plaintext_with_client
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -106,8 +144,8 @@ class Client(object):
|
||||||
attrs['rsa_key'] = '****' if attrs['rsa_key'] else None
|
attrs['rsa_key'] = '****' if attrs['rsa_key'] else None
|
||||||
attrs[
|
attrs[
|
||||||
'resource_owner_secret'] = '****' if attrs['resource_owner_secret'] else None
|
'resource_owner_secret'] = '****' if attrs['resource_owner_secret'] else None
|
||||||
attribute_str = ', '.join('%s=%s' % (k, v) for k, v in attrs.items())
|
attribute_str = ', '.join('{}={}'.format(k, v) for k, v in attrs.items())
|
||||||
return '<%s %s>' % (self.__class__.__name__, attribute_str)
|
return '<{} {}>'.format(self.__class__.__name__, attribute_str)
|
||||||
|
|
||||||
def get_oauth_signature(self, request):
|
def get_oauth_signature(self, request):
|
||||||
"""Get an OAuth signature to be used in signing a 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,
|
uri_query=urlparse.urlparse(uri).query,
|
||||||
body=body,
|
body=body,
|
||||||
headers=headers)
|
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_params = signature.normalize_parameters(collected_params)
|
||||||
normalized_uri = signature.base_string_uri(uri, headers.get('Host', None))
|
normalized_uri = signature.base_string_uri(uri, headers.get('Host', None))
|
||||||
log.debug("Normalized params: {0}".format(normalized_params))
|
log.debug("Normalized params: {}".format(normalized_params))
|
||||||
log.debug("Normalized URI: {0}".format(normalized_uri))
|
log.debug("Normalized URI: {}".format(normalized_uri))
|
||||||
|
|
||||||
base_string = signature.signature_base_string(request.http_method,
|
base_string = signature.signature_base_string(request.http_method,
|
||||||
normalized_uri, normalized_params)
|
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:
|
if self.signature_method not in self.SIGNATURE_METHODS:
|
||||||
raise ValueError('Invalid signature method.')
|
raise ValueError('Invalid signature method.')
|
||||||
|
|
||||||
sig = self.SIGNATURE_METHODS[self.signature_method](base_string, self)
|
sig = self.SIGNATURE_METHODS[self.signature_method](base_string, self)
|
||||||
|
|
||||||
log.debug("Signature: {0}".format(sig))
|
log.debug("Signature: {}".format(sig))
|
||||||
return sig
|
return sig
|
||||||
|
|
||||||
def get_oauth_params(self, request):
|
def get_oauth_params(self, request):
|
||||||
|
@ -278,8 +316,8 @@ class Client(object):
|
||||||
# header field set to "application/x-www-form-urlencoded".
|
# header field set to "application/x-www-form-urlencoded".
|
||||||
elif not should_have_params and has_params:
|
elif not should_have_params and has_params:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
"Body contains parameters but Content-Type header was {0} "
|
"Body contains parameters but Content-Type header was {} "
|
||||||
"instead of {1}".format(content_type or "not set",
|
"instead of {}".format(content_type or "not set",
|
||||||
CONTENT_TYPE_FORM_URLENCODED))
|
CONTENT_TYPE_FORM_URLENCODED))
|
||||||
|
|
||||||
# 3.5.2. Form-Encoded Body
|
# 3.5.2. Form-Encoded Body
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
from __future__ import absolute_import
|
from .access_token import AccessTokenEndpoint
|
||||||
|
from .authorization import AuthorizationEndpoint
|
||||||
from .base import BaseEndpoint
|
from .base import BaseEndpoint
|
||||||
from .request_token import RequestTokenEndpoint
|
from .request_token import RequestTokenEndpoint
|
||||||
from .authorization import AuthorizationEndpoint
|
|
||||||
from .access_token import AccessTokenEndpoint
|
|
||||||
from .resource import ResourceEndpoint
|
from .resource import ResourceEndpoint
|
||||||
from .signature_only import SignatureOnlyEndpoint
|
from .signature_only import SignatureOnlyEndpoint
|
||||||
from .pre_configured import WebApplicationServer
|
|
||||||
|
from .pre_configured import WebApplicationServer # isort:skip
|
||||||
|
|
|
@ -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
|
creates and persists tokens as well as create the proper response to be
|
||||||
returned to the client.
|
returned to the client.
|
||||||
"""
|
"""
|
||||||
from __future__ import absolute_import, unicode_literals
|
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from oauthlib.common import urlencode
|
from oauthlib.common import urlencode
|
||||||
|
|
|
@ -6,18 +6,13 @@ oauthlib.oauth1.rfc5849.endpoints.authorization
|
||||||
This module is an implementation of various logic needed
|
This module is an implementation of various logic needed
|
||||||
for signing and checking OAuth 1.0 RFC 5849 requests.
|
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 .. import errors
|
||||||
from .base import BaseEndpoint
|
from .base import BaseEndpoint
|
||||||
|
|
||||||
try:
|
|
||||||
from urllib import urlencode
|
|
||||||
except ImportError:
|
|
||||||
from urllib.parse import urlencode
|
|
||||||
|
|
||||||
|
|
||||||
class AuthorizationEndpoint(BaseEndpoint):
|
class AuthorizationEndpoint(BaseEndpoint):
|
||||||
|
|
||||||
|
|
|
@ -6,18 +6,19 @@ oauthlib.oauth1.rfc5849.endpoints.base
|
||||||
This module is an implementation of various logic needed
|
This module is an implementation of various logic needed
|
||||||
for signing and checking OAuth 1.0 RFC 5849 requests.
|
for signing and checking OAuth 1.0 RFC 5849 requests.
|
||||||
"""
|
"""
|
||||||
from __future__ import absolute_import, unicode_literals
|
|
||||||
|
|
||||||
import time
|
import time
|
||||||
|
|
||||||
from oauthlib.common import CaseInsensitiveDict, Request, generate_token
|
from oauthlib.common import CaseInsensitiveDict, Request, generate_token
|
||||||
|
|
||||||
from .. import (CONTENT_TYPE_FORM_URLENCODED, SIGNATURE_HMAC_SHA1, SIGNATURE_HMAC_SHA256, SIGNATURE_RSA,
|
from .. import (
|
||||||
SIGNATURE_TYPE_AUTH_HEADER, SIGNATURE_TYPE_BODY,
|
CONTENT_TYPE_FORM_URLENCODED, SIGNATURE_HMAC_SHA1, SIGNATURE_HMAC_SHA256,
|
||||||
SIGNATURE_TYPE_QUERY, errors, signature, utils)
|
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):
|
def __init__(self, request_validator, token_generator=None):
|
||||||
self.request_validator = request_validator
|
self.request_validator = request_validator
|
||||||
|
@ -131,7 +132,7 @@ class BaseEndpoint(object):
|
||||||
if (not request.signature_method in
|
if (not request.signature_method in
|
||||||
self.request_validator.allowed_signature_methods):
|
self.request_validator.allowed_signature_methods):
|
||||||
raise errors.InvalidSignatureMethodError(
|
raise errors.InvalidSignatureMethodError(
|
||||||
description="Invalid signature, %s not in %r." % (
|
description="Invalid signature, {} not in {!r}.".format(
|
||||||
request.signature_method,
|
request.signature_method,
|
||||||
self.request_validator.allowed_signature_methods))
|
self.request_validator.allowed_signature_methods))
|
||||||
|
|
||||||
|
@ -179,38 +180,65 @@ class BaseEndpoint(object):
|
||||||
|
|
||||||
def _check_signature(self, request, is_token_request=False):
|
def _check_signature(self, request, is_token_request=False):
|
||||||
# ---- RSA Signature verification ----
|
# ---- 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`_
|
# 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
|
# .. _`[RFC3447] section 8.2.2`: https://tools.ietf.org/html/rfc3447#section-8.2.1
|
||||||
|
|
||||||
rsa_key = self.request_validator.get_rsa_key(
|
rsa_key = self.request_validator.get_rsa_key(
|
||||||
request.client_key, request)
|
request.client_key, request)
|
||||||
|
|
||||||
|
if request.signature_method == SIGNATURE_RSA_SHA1:
|
||||||
valid_signature = signature.verify_rsa_sha1(request, rsa_key)
|
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 ----
|
# ---- HMAC or Plaintext Signature verification ----
|
||||||
else:
|
else:
|
||||||
|
# Non-RSA based signature method
|
||||||
|
|
||||||
# Servers receiving an authenticated request MUST validate it by:
|
# Servers receiving an authenticated request MUST validate it by:
|
||||||
# Recalculating the request signature independently as described in
|
# Recalculating the request signature independently as described in
|
||||||
# `Section 3.4`_ and comparing it to the value received from the
|
# `Section 3.4`_ and comparing it to the value received from the
|
||||||
# client via the "oauth_signature" parameter.
|
# client via the "oauth_signature" parameter.
|
||||||
# .. _`Section 3.4`: https://tools.ietf.org/html/rfc5849#section-3.4
|
# .. _`Section 3.4`: https://tools.ietf.org/html/rfc5849#section-3.4
|
||||||
|
|
||||||
client_secret = self.request_validator.get_client_secret(
|
client_secret = self.request_validator.get_client_secret(
|
||||||
request.client_key, request)
|
request.client_key, request)
|
||||||
|
|
||||||
resource_owner_secret = None
|
resource_owner_secret = None
|
||||||
if request.resource_owner_key:
|
if request.resource_owner_key:
|
||||||
if is_token_request:
|
if is_token_request:
|
||||||
resource_owner_secret = self.request_validator.get_request_token_secret(
|
resource_owner_secret = \
|
||||||
request.client_key, request.resource_owner_key, request)
|
self.request_validator.get_request_token_secret(
|
||||||
|
request.client_key, request.resource_owner_key,
|
||||||
|
request)
|
||||||
else:
|
else:
|
||||||
resource_owner_secret = self.request_validator.get_access_token_secret(
|
resource_owner_secret = \
|
||||||
request.client_key, request.resource_owner_key, request)
|
self.request_validator.get_access_token_secret(
|
||||||
|
request.client_key, request.resource_owner_key,
|
||||||
|
request)
|
||||||
|
|
||||||
if request.signature_method == SIGNATURE_HMAC_SHA1:
|
if request.signature_method == SIGNATURE_HMAC_SHA1:
|
||||||
valid_signature = signature.verify_hmac_sha1(request,
|
valid_signature = signature.verify_hmac_sha1(
|
||||||
client_secret, resource_owner_secret)
|
request, client_secret, resource_owner_secret)
|
||||||
elif request.signature_method == SIGNATURE_HMAC_SHA256:
|
elif request.signature_method == SIGNATURE_HMAC_SHA256:
|
||||||
valid_signature = signature.verify_hmac_sha256(request,
|
valid_signature = signature.verify_hmac_sha256(
|
||||||
client_secret, resource_owner_secret)
|
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:
|
else:
|
||||||
valid_signature = signature.verify_plaintext(request,
|
valid_signature = False
|
||||||
client_secret, resource_owner_secret)
|
|
||||||
return valid_signature
|
return valid_signature
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
from __future__ import absolute_import, unicode_literals
|
from . import (
|
||||||
|
AccessTokenEndpoint, AuthorizationEndpoint, RequestTokenEndpoint,
|
||||||
from . import (AccessTokenEndpoint, AuthorizationEndpoint,
|
ResourceEndpoint,
|
||||||
RequestTokenEndpoint, ResourceEndpoint)
|
)
|
||||||
|
|
||||||
|
|
||||||
class WebApplicationServer(RequestTokenEndpoint, AuthorizationEndpoint,
|
class WebApplicationServer(RequestTokenEndpoint, AuthorizationEndpoint,
|
||||||
|
|
|
@ -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
|
creates and persists tokens as well as create the proper response to be
|
||||||
returned to the client.
|
returned to the client.
|
||||||
"""
|
"""
|
||||||
from __future__ import absolute_import, unicode_literals
|
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from oauthlib.common import urlencode
|
from oauthlib.common import urlencode
|
||||||
|
@ -129,7 +127,7 @@ class RequestTokenEndpoint(BaseEndpoint):
|
||||||
request.client_key, request)
|
request.client_key, request)
|
||||||
if not self.request_validator.check_realms(request.realms):
|
if not self.request_validator.check_realms(request.realms):
|
||||||
raise errors.InvalidRequestError(
|
raise errors.InvalidRequestError(
|
||||||
description='Invalid realm %s. Allowed are %r.' % (
|
description='Invalid realm {}. Allowed are {!r}.'.format(
|
||||||
request.realms, self.request_validator.realms))
|
request.realms, self.request_validator.realms))
|
||||||
|
|
||||||
if not request.redirect_uri:
|
if not request.redirect_uri:
|
||||||
|
@ -154,7 +152,7 @@ class RequestTokenEndpoint(BaseEndpoint):
|
||||||
request.client_key = self.request_validator.dummy_client
|
request.client_key = self.request_validator.dummy_client
|
||||||
|
|
||||||
# Note that `realm`_ is only used in authorization headers and how
|
# 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
|
# However they could be seen as a scope or realm to which the
|
||||||
# client has access and as such every client should be checked
|
# client has access and as such every client should be checked
|
||||||
# to ensure it is authorized access to that scope or realm.
|
# 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.
|
# workflow where a client requests access to a specific realm.
|
||||||
# This first step (obtaining request token) need not require a realm
|
# This first step (obtaining request token) need not require a realm
|
||||||
# and can then be identified by checking the require_resource_owner
|
# 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
|
# Clients obtaining an access token will not supply a realm and it will
|
||||||
# not be checked. Instead the previously requested realm should be
|
# not be checked. Instead the previously requested realm should be
|
||||||
|
|
|
@ -6,8 +6,6 @@ oauthlib.oauth1.rfc5849.endpoints.resource
|
||||||
This module is an implementation of the resource protection provider logic of
|
This module is an implementation of the resource protection provider logic of
|
||||||
OAuth 1.0 RFC 5849.
|
OAuth 1.0 RFC 5849.
|
||||||
"""
|
"""
|
||||||
from __future__ import absolute_import, unicode_literals
|
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from .. import errors
|
from .. import errors
|
||||||
|
@ -115,7 +113,7 @@ class ResourceEndpoint(BaseEndpoint):
|
||||||
request.resource_owner_key = self.request_validator.dummy_access_token
|
request.resource_owner_key = self.request_validator.dummy_access_token
|
||||||
|
|
||||||
# Note that `realm`_ is only used in authorization headers and how
|
# 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
|
# However they could be seen as a scope or realm to which the
|
||||||
# client has access and as such every client should be checked
|
# client has access and as such every client should be checked
|
||||||
# to ensure it is authorized access to that scope or realm.
|
# 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.
|
# workflow where a client requests access to a specific realm.
|
||||||
# This first step (obtaining request token) need not require a realm
|
# This first step (obtaining request token) need not require a realm
|
||||||
# and can then be identified by checking the require_resource_owner
|
# 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
|
# Clients obtaining an access token will not supply a realm and it will
|
||||||
# not be checked. Instead the previously requested realm should be
|
# not be checked. Instead the previously requested realm should be
|
||||||
|
|
|
@ -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.
|
This module is an implementation of the signing logic of OAuth 1.0 RFC 5849.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from __future__ import absolute_import, unicode_literals
|
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from .. import errors
|
from .. import errors
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
# coding=utf-8
|
|
||||||
"""
|
"""
|
||||||
oauthlib.oauth1.rfc5849.errors
|
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
|
Error used both by OAuth 1 clients and provicers to represent the spec
|
||||||
defined error responses for all four core grant types.
|
defined error responses for all four core grant types.
|
||||||
"""
|
"""
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
from oauthlib.common import add_params_to_uri, urlencode
|
from oauthlib.common import add_params_to_uri, urlencode
|
||||||
|
|
||||||
|
|
||||||
|
@ -37,10 +34,10 @@ class OAuth1Error(Exception):
|
||||||
request: Oauthlib Request object
|
request: Oauthlib Request object
|
||||||
"""
|
"""
|
||||||
self.description = description or self.description
|
self.description = description or self.description
|
||||||
message = '(%s) %s' % (self.error, self.description)
|
message = '({}) {}'.format(self.error, self.description)
|
||||||
if request:
|
if request:
|
||||||
message += ' ' + repr(request)
|
message += ' ' + repr(request)
|
||||||
super(OAuth1Error, self).__init__(message)
|
super().__init__(message)
|
||||||
|
|
||||||
self.uri = uri
|
self.uri = uri
|
||||||
self.status_code = status_code
|
self.status_code = status_code
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
"""
|
"""
|
||||||
oauthlib.parameters
|
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
|
.. _`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 oauthlib.common import extract_params, urlencode
|
||||||
|
|
||||||
from . import utils
|
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?
|
# TODO: do we need filter_params now that oauth_params are handled by Request?
|
||||||
# We can easily pass in just oauth protocol params.
|
# 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
|
# 2. Each parameter's name is immediately followed by an "=" character
|
||||||
# (ASCII code 61), a """ character (ASCII code 34), the parameter
|
# (ASCII code 61), a """ character (ASCII code 34), the parameter
|
||||||
# value (MAY be empty), and another """ character (ASCII code 34).
|
# 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)
|
authorization_header_parameters_parts.append(part)
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
"""
|
"""
|
||||||
oauthlib.oauth1.rfc5849
|
oauthlib.oauth1.rfc5849
|
||||||
~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~
|
||||||
|
@ -6,14 +5,10 @@ oauthlib.oauth1.rfc5849
|
||||||
This module is an implementation of various logic needed
|
This module is an implementation of various logic needed
|
||||||
for signing and checking OAuth 1.0 RFC 5849 requests.
|
for signing and checking OAuth 1.0 RFC 5849 requests.
|
||||||
"""
|
"""
|
||||||
from __future__ import absolute_import, unicode_literals
|
|
||||||
|
|
||||||
import sys
|
|
||||||
|
|
||||||
from . import SIGNATURE_METHODS, utils
|
from . import SIGNATURE_METHODS, utils
|
||||||
|
|
||||||
|
|
||||||
class RequestValidator(object):
|
class RequestValidator:
|
||||||
|
|
||||||
"""A validator/datastore interaction base class for OAuth 1 providers.
|
"""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
|
Methods used to check the format of input parameters. Common tests include
|
||||||
length, character set, membership, range or pattern. These tests are
|
length, character set, membership, range or pattern. These tests are
|
||||||
referred to as `whitelisting or blacklisting`_. Whitelisting is better
|
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:
|
The following have methods a default implementation:
|
||||||
|
|
||||||
- check_client_key
|
- check_client_key
|
||||||
|
@ -197,7 +192,7 @@ class RequestValidator(object):
|
||||||
|
|
||||||
def check_realms(self, realms):
|
def check_realms(self, realms):
|
||||||
"""Check that the realm is one of a set allowed 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):
|
def _subclass_must_implement(self, fn):
|
||||||
"""
|
"""
|
||||||
|
@ -448,7 +443,7 @@ class RequestValidator(object):
|
||||||
:type request: oauthlib.common.Request
|
:type request: oauthlib.common.Request
|
||||||
:returns: None
|
:returns: None
|
||||||
|
|
||||||
Per `Section 2.3`__ of the spec:
|
Per `Section 2.3`_ of the spec:
|
||||||
|
|
||||||
"The server MUST (...) ensure that the temporary
|
"The server MUST (...) ensure that the temporary
|
||||||
credentials have not expired or been used before."
|
credentials have not expired or been used before."
|
||||||
|
@ -836,7 +831,7 @@ class RequestValidator(object):
|
||||||
"""Associate an authorization verifier with a request token.
|
"""Associate an authorization verifier with a request token.
|
||||||
|
|
||||||
:param token: A request token string.
|
: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
|
oauth_token
|
||||||
:param request: OAuthlib request.
|
:param request: OAuthlib request.
|
||||||
:type request: oauthlib.common.Request
|
:type request: oauthlib.common.Request
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,4 +1,3 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
"""
|
"""
|
||||||
oauthlib.utils
|
oauthlib.utils
|
||||||
~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~
|
||||||
|
@ -6,15 +5,9 @@ oauthlib.utils
|
||||||
This module contains utility methods used by various parts of the OAuth
|
This module contains utility methods used by various parts of the OAuth
|
||||||
spec.
|
spec.
|
||||||
"""
|
"""
|
||||||
from __future__ import absolute_import, unicode_literals
|
import urllib.request as urllib2
|
||||||
|
|
||||||
from oauthlib.common import quote, unicode_type, unquote
|
|
||||||
|
|
||||||
try:
|
|
||||||
import urllib2
|
|
||||||
except ImportError:
|
|
||||||
import urllib.request as urllib2
|
|
||||||
|
|
||||||
|
from oauthlib.common import quote, unquote
|
||||||
|
|
||||||
UNICODE_ASCII_CHARACTER_SET = ('abcdefghijklmnopqrstuvwxyz'
|
UNICODE_ASCII_CHARACTER_SET = ('abcdefghijklmnopqrstuvwxyz'
|
||||||
'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
|
'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
|
||||||
|
@ -52,16 +45,16 @@ def escape(u):
|
||||||
.. _`section 3.6`: https://tools.ietf.org/html/rfc5849#section-3.6
|
.. _`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. ' +
|
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
|
# Letters, digits, and the characters '_.-' are already treated as safe
|
||||||
# by urllib.quote(). We need to add '~' to fully support rfc5849.
|
# by urllib.quote(). We need to add '~' to fully support rfc5849.
|
||||||
return quote(u, safe=b'~')
|
return quote(u, safe=b'~')
|
||||||
|
|
||||||
|
|
||||||
def unescape(u):
|
def unescape(u):
|
||||||
if not isinstance(u, unicode_type):
|
if not isinstance(u, str):
|
||||||
raise ValueError('Only unicode objects are unescapable.')
|
raise ValueError('Only unicode objects are unescapable.')
|
||||||
return unquote(u)
|
return unquote(u)
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
"""
|
"""
|
||||||
oauthlib.oauth2
|
oauthlib.oauth2
|
||||||
~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~
|
||||||
|
@ -6,31 +5,32 @@ oauthlib.oauth2
|
||||||
This module is a wrapper for the most recent implementation of OAuth 2.0 Client
|
This module is a wrapper for the most recent implementation of OAuth 2.0 Client
|
||||||
and Server classes.
|
and Server classes.
|
||||||
"""
|
"""
|
||||||
from __future__ import absolute_import, unicode_literals
|
from .rfc6749.clients import (
|
||||||
|
BackendApplicationClient, Client, LegacyApplicationClient,
|
||||||
from .rfc6749.clients import Client
|
MobileApplicationClient, ServiceApplicationClient, WebApplicationClient,
|
||||||
from .rfc6749.clients import WebApplicationClient
|
)
|
||||||
from .rfc6749.clients import MobileApplicationClient
|
from .rfc6749.endpoints import (
|
||||||
from .rfc6749.clients import LegacyApplicationClient
|
AuthorizationEndpoint, BackendApplicationServer, IntrospectEndpoint,
|
||||||
from .rfc6749.clients import BackendApplicationClient
|
LegacyApplicationServer, MetadataEndpoint, MobileApplicationServer,
|
||||||
from .rfc6749.clients import ServiceApplicationClient
|
ResourceEndpoint, RevocationEndpoint, Server, TokenEndpoint,
|
||||||
from .rfc6749.endpoints import AuthorizationEndpoint
|
WebApplicationServer,
|
||||||
from .rfc6749.endpoints import IntrospectEndpoint
|
)
|
||||||
from .rfc6749.endpoints import MetadataEndpoint
|
from .rfc6749.errors import (
|
||||||
from .rfc6749.endpoints import TokenEndpoint
|
AccessDeniedError, FatalClientError, InsecureTransportError,
|
||||||
from .rfc6749.endpoints import ResourceEndpoint
|
InvalidClientError, InvalidClientIdError, InvalidGrantError,
|
||||||
from .rfc6749.endpoints import RevocationEndpoint
|
InvalidRedirectURIError, InvalidRequestError, InvalidRequestFatalError,
|
||||||
from .rfc6749.endpoints import Server
|
InvalidScopeError, MismatchingRedirectURIError, MismatchingStateError,
|
||||||
from .rfc6749.endpoints import WebApplicationServer
|
MissingClientIdError, MissingCodeError, MissingRedirectURIError,
|
||||||
from .rfc6749.endpoints import MobileApplicationServer
|
MissingResponseTypeError, MissingTokenError, MissingTokenTypeError,
|
||||||
from .rfc6749.endpoints import LegacyApplicationServer
|
OAuth2Error, ServerError, TemporarilyUnavailableError, TokenExpiredError,
|
||||||
from .rfc6749.endpoints import BackendApplicationServer
|
UnauthorizedClientError, UnsupportedGrantTypeError,
|
||||||
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
|
UnsupportedResponseTypeError, UnsupportedTokenTypeError,
|
||||||
from .rfc6749.grant_types import AuthorizationCodeGrant
|
)
|
||||||
from .rfc6749.grant_types import ImplicitGrant
|
from .rfc6749.grant_types import (
|
||||||
from .rfc6749.grant_types import ResourceOwnerPasswordCredentialsGrant
|
AuthorizationCodeGrant, ClientCredentialsGrant, ImplicitGrant,
|
||||||
from .rfc6749.grant_types import ClientCredentialsGrant
|
RefreshTokenGrant, ResourceOwnerPasswordCredentialsGrant,
|
||||||
from .rfc6749.grant_types import RefreshTokenGrant
|
)
|
||||||
from .rfc6749.request_validator import RequestValidator
|
from .rfc6749.request_validator import RequestValidator
|
||||||
from .rfc6749.tokens import BearerToken, OAuth2Token
|
from .rfc6749.tokens import BearerToken, OAuth2Token
|
||||||
from .rfc6749.utils import is_secure_transport
|
from .rfc6749.utils import is_secure_transport
|
||||||
|
from .rfc8628.clients import DeviceClient
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
"""
|
"""
|
||||||
oauthlib.oauth2.rfc6749
|
oauthlib.oauth2.rfc6749
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
@ -6,15 +5,12 @@ oauthlib.oauth2.rfc6749
|
||||||
This module is an implementation of various logic needed
|
This module is an implementation of various logic needed
|
||||||
for consuming and providing OAuth 2.0 RFC6749.
|
for consuming and providing OAuth 2.0 RFC6749.
|
||||||
"""
|
"""
|
||||||
from __future__ import absolute_import, unicode_literals
|
|
||||||
|
|
||||||
import functools
|
import functools
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from .endpoints.base import BaseEndpoint
|
from .endpoints.base import BaseEndpoint, catch_errors_and_unavailability
|
||||||
from .endpoints.base import catch_errors_and_unavailability
|
from .errors import (
|
||||||
from .errors import TemporarilyUnavailableError, ServerError
|
FatalClientError, OAuth2Error, ServerError, TemporarilyUnavailableError,
|
||||||
from .errors import FatalClientError, OAuth2Error
|
)
|
||||||
|
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
|
@ -6,11 +6,9 @@ oauthlib.oauth2.rfc6749
|
||||||
This module is an implementation of various logic needed
|
This module is an implementation of various logic needed
|
||||||
for consuming OAuth 2.0 RFC6749.
|
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 .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 .service_application import ServiceApplicationClient
|
||||||
|
from .web_application import WebApplicationClient
|
||||||
|
|
|
@ -6,9 +6,7 @@ oauthlib.oauth2.rfc6749
|
||||||
This module is an implementation of various logic needed
|
This module is an implementation of various logic needed
|
||||||
for consuming and providing OAuth 2.0 RFC6749.
|
for consuming and providing OAuth 2.0 RFC6749.
|
||||||
"""
|
"""
|
||||||
from __future__ import absolute_import, unicode_literals
|
from ..parameters import prepare_token_request
|
||||||
|
|
||||||
from ..parameters import parse_token_response, prepare_token_request
|
|
||||||
from .base import Client
|
from .base import Client
|
||||||
|
|
||||||
|
|
||||||
|
@ -41,7 +39,7 @@ class BackendApplicationClient(Client):
|
||||||
format per `Appendix B`_ in the HTTP request entity-body:
|
format per `Appendix B`_ in the HTTP request entity-body:
|
||||||
|
|
||||||
:param body: Existing request body (URL encoded string) to embed parameters
|
: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
|
:param scope: The scope of the access request as described by
|
||||||
`Section 3.3`_.
|
`Section 3.3`_.
|
||||||
|
|
||||||
|
|
|
@ -6,18 +6,22 @@ oauthlib.oauth2.rfc6749
|
||||||
This module is an implementation of various logic needed
|
This module is an implementation of various logic needed
|
||||||
for consuming OAuth 2.0 RFC6749.
|
for consuming OAuth 2.0 RFC6749.
|
||||||
"""
|
"""
|
||||||
from __future__ import absolute_import, unicode_literals
|
import base64
|
||||||
|
import hashlib
|
||||||
|
import re
|
||||||
|
import secrets
|
||||||
import time
|
import time
|
||||||
import warnings
|
import warnings
|
||||||
|
|
||||||
from oauthlib.common import generate_token
|
from oauthlib.common import generate_token
|
||||||
from oauthlib.oauth2.rfc6749 import tokens
|
from oauthlib.oauth2.rfc6749 import tokens
|
||||||
from oauthlib.oauth2.rfc6749.errors import (InsecureTransportError,
|
from oauthlib.oauth2.rfc6749.errors import (
|
||||||
TokenExpiredError)
|
InsecureTransportError, TokenExpiredError,
|
||||||
from oauthlib.oauth2.rfc6749.parameters import (parse_token_response,
|
)
|
||||||
prepare_token_request,
|
from oauthlib.oauth2.rfc6749.parameters import (
|
||||||
prepare_token_revocation_request)
|
parse_token_response, prepare_token_request,
|
||||||
|
prepare_token_revocation_request,
|
||||||
|
)
|
||||||
from oauthlib.oauth2.rfc6749.utils import is_secure_transport
|
from oauthlib.oauth2.rfc6749.utils import is_secure_transport
|
||||||
|
|
||||||
AUTH_HEADER = 'auth_header'
|
AUTH_HEADER = 'auth_header'
|
||||||
|
@ -29,7 +33,7 @@ FORM_ENC_HEADERS = {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class Client(object):
|
class Client:
|
||||||
"""Base OAuth2 client responsible for access token management.
|
"""Base OAuth2 client responsible for access token management.
|
||||||
|
|
||||||
This class also acts as a generic interface providing methods common to all
|
This class also acts as a generic interface providing methods common to all
|
||||||
|
@ -61,6 +65,9 @@ class Client(object):
|
||||||
state=None,
|
state=None,
|
||||||
redirect_url=None,
|
redirect_url=None,
|
||||||
state_generator=generate_token,
|
state_generator=generate_token,
|
||||||
|
code_verifier=None,
|
||||||
|
code_challenge=None,
|
||||||
|
code_challenge_method=None,
|
||||||
**kwargs):
|
**kwargs):
|
||||||
"""Initialize a client with commonly used attributes.
|
"""Initialize a client with commonly used attributes.
|
||||||
|
|
||||||
|
@ -99,6 +106,15 @@ class Client(object):
|
||||||
|
|
||||||
:param state_generator: A no argument state generation callable. Defaults
|
:param state_generator: A no argument state generation callable. Defaults
|
||||||
to :py:meth:`oauthlib.common.generate_token`.
|
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
|
self.client_id = client_id
|
||||||
|
@ -113,6 +129,9 @@ class Client(object):
|
||||||
self.state_generator = state_generator
|
self.state_generator = state_generator
|
||||||
self.state = state
|
self.state = state
|
||||||
self.redirect_url = redirect_url
|
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.code = None
|
||||||
self.expires_in = None
|
self.expires_in = None
|
||||||
self._expires_at = None
|
self._expires_at = None
|
||||||
|
@ -186,8 +205,8 @@ class Client(object):
|
||||||
|
|
||||||
token_placement = token_placement or self.default_token_placement
|
token_placement = token_placement or self.default_token_placement
|
||||||
|
|
||||||
case_insensitive_token_types = dict(
|
case_insensitive_token_types = {
|
||||||
(k.lower(), v) for k, v in self.token_types.items())
|
k.lower(): v for k, v in self.token_types.items()}
|
||||||
if not self.token_type.lower() in case_insensitive_token_types:
|
if not self.token_type.lower() in case_insensitive_token_types:
|
||||||
raise ValueError("Unsupported token type: %s" % self.token_type)
|
raise ValueError("Unsupported token type: %s" % self.token_type)
|
||||||
|
|
||||||
|
@ -209,23 +228,21 @@ class Client(object):
|
||||||
required parameters to the authorization URL.
|
required parameters to the authorization URL.
|
||||||
|
|
||||||
:param authorization_url: Provider authorization endpoint URL.
|
:param authorization_url: Provider authorization endpoint URL.
|
||||||
|
|
||||||
:param state: CSRF protection string. Will be automatically created if
|
:param state: CSRF protection string. Will be automatically created if
|
||||||
not provided. The generated state is available via the ``state``
|
not provided. The generated state is available via the ``state``
|
||||||
attribute. Clients should verify that the state is unchanged and
|
attribute. Clients should verify that the state is unchanged and
|
||||||
present in the authorization response. This verification is done
|
present in the authorization response. This verification is done
|
||||||
automatically if using the ``authorization_response`` parameter
|
automatically if using the ``authorization_response`` parameter
|
||||||
with ``prepare_token_request``.
|
with ``prepare_token_request``.
|
||||||
|
|
||||||
:param redirect_url: Redirect URL to which the user will be returned
|
:param redirect_url: Redirect URL to which the user will be returned
|
||||||
after authorization. Must be provided unless previously setup with
|
after authorization. Must be provided unless previously setup with
|
||||||
the provider. If provided then it must also be provided in the
|
the provider. If provided then it must also be provided in the
|
||||||
token request.
|
token request.
|
||||||
|
:param scope: List of scopes to request. Must be equal to
|
||||||
:param scope:
|
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.
|
:param kwargs: Additional parameters to included in the request.
|
||||||
|
|
||||||
:returns: The prepared request tuple with (url, headers, body).
|
:returns: The prepared request tuple with (url, headers, body).
|
||||||
"""
|
"""
|
||||||
if not is_secure_transport(authorization_url):
|
if not is_secure_transport(authorization_url):
|
||||||
|
@ -233,10 +250,11 @@ class Client(object):
|
||||||
|
|
||||||
self.state = state or self.state_generator()
|
self.state = state or self.state_generator()
|
||||||
self.redirect_url = redirect_url or self.redirect_url
|
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(
|
auth_url = self.prepare_request_uri(
|
||||||
authorization_url, redirect_uri=self.redirect_url,
|
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, ''
|
return auth_url, FORM_ENC_HEADERS, ''
|
||||||
|
|
||||||
def prepare_token_request(self, token_url, authorization_response=None,
|
def prepare_token_request(self, token_url, authorization_response=None,
|
||||||
|
@ -248,22 +266,16 @@ class Client(object):
|
||||||
credentials.
|
credentials.
|
||||||
|
|
||||||
:param token_url: Provider token creation endpoint URL.
|
:param token_url: Provider token creation endpoint URL.
|
||||||
|
|
||||||
:param authorization_response: The full redirection URL string, i.e.
|
:param authorization_response: The full redirection URL string, i.e.
|
||||||
the location to which the user was redirected after successfull
|
the location to which the user was redirected after successful
|
||||||
authorization. Used to mine credentials needed to obtain a token
|
authorization. Used to mine credentials needed to obtain a token
|
||||||
in this step, such as authorization code.
|
in this step, such as authorization code.
|
||||||
|
|
||||||
:param redirect_url: The redirect_url supplied with the authorization
|
:param redirect_url: The redirect_url supplied with the authorization
|
||||||
request (if there was one).
|
request (if there was one).
|
||||||
|
|
||||||
:param state:
|
:param state:
|
||||||
|
|
||||||
:param body: Existing request body (URL encoded string) to embed parameters
|
: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.
|
:param kwargs: Additional parameters to included in the request.
|
||||||
|
|
||||||
:returns: The prepared request tuple with (url, headers, body).
|
:returns: The prepared request tuple with (url, headers, body).
|
||||||
"""
|
"""
|
||||||
if not is_secure_transport(token_url):
|
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.
|
obtain a new access token, and possibly a new refresh token.
|
||||||
|
|
||||||
:param token_url: Provider token refresh endpoint URL.
|
:param token_url: Provider token refresh endpoint URL.
|
||||||
|
|
||||||
:param refresh_token: Refresh token string.
|
:param refresh_token: Refresh token string.
|
||||||
|
|
||||||
:param body: Existing request body (URL encoded string) to embed parameters
|
: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
|
:param scope: List of scopes to request. Must be equal to
|
||||||
or a subset of the scopes granted when obtaining the refresh
|
or a subset of the scopes granted when obtaining the refresh
|
||||||
token.
|
token. If none is provided, the ones provided in the constructor are
|
||||||
|
used.
|
||||||
:param kwargs: Additional parameters to included in the request.
|
:param kwargs: Additional parameters to included in the request.
|
||||||
|
|
||||||
:returns: The prepared request tuple with (url, headers, body).
|
:returns: The prepared request tuple with (url, headers, body).
|
||||||
"""
|
"""
|
||||||
if not is_secure_transport(token_url):
|
if not is_secure_transport(token_url):
|
||||||
raise InsecureTransportError()
|
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,
|
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
|
return token_url, FORM_ENC_HEADERS, body
|
||||||
|
|
||||||
def prepare_token_revocation_request(self, revocation_url, token,
|
def prepare_token_revocation_request(self, revocation_url, token,
|
||||||
|
@ -316,20 +325,14 @@ class Client(object):
|
||||||
"""Prepare a token revocation request.
|
"""Prepare a token revocation request.
|
||||||
|
|
||||||
:param revocation_url: Provider token revocation endpoint URL.
|
:param revocation_url: Provider token revocation endpoint URL.
|
||||||
|
|
||||||
:param token: The access or refresh token to be revoked (string).
|
:param token: The access or refresh token to be revoked (string).
|
||||||
|
|
||||||
:param token_type_hint: ``"access_token"`` (default) or
|
:param token_type_hint: ``"access_token"`` (default) or
|
||||||
``"refresh_token"``. This is optional and if you wish to not pass it you
|
``"refresh_token"``. This is optional and if you wish to not pass it you
|
||||||
must provide ``token_type_hint=None``.
|
must provide ``token_type_hint=None``.
|
||||||
|
|
||||||
:param body:
|
:param body:
|
||||||
|
|
||||||
:param callback: A jsonp callback such as ``package.callback`` to be invoked
|
: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.
|
:param kwargs: Additional parameters to included in the request.
|
||||||
|
|
||||||
:returns: The prepared request tuple with (url, headers, body).
|
:returns: The prepared request tuple with (url, headers, body).
|
||||||
|
|
||||||
Note that JSONP request may use GET requests as the parameters will
|
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
|
An example of a revocation request
|
||||||
|
|
||||||
.. code-block: http
|
.. code-block:: http
|
||||||
|
|
||||||
POST /revoke HTTP/1.1
|
POST /revoke HTTP/1.1
|
||||||
Host: server.example.com
|
Host: server.example.com
|
||||||
|
@ -348,7 +351,7 @@ class Client(object):
|
||||||
|
|
||||||
An example of a jsonp revocation request
|
An example of a jsonp revocation request
|
||||||
|
|
||||||
.. code-block: http
|
.. code-block:: http
|
||||||
|
|
||||||
GET /revoke?token=agabcdefddddafdd&callback=package.myCallback HTTP/1.1
|
GET /revoke?token=agabcdefddddafdd&callback=package.myCallback HTTP/1.1
|
||||||
Host: server.example.com
|
Host: server.example.com
|
||||||
|
@ -357,7 +360,7 @@ class Client(object):
|
||||||
|
|
||||||
and an error response
|
and an error response
|
||||||
|
|
||||||
.. code-block: http
|
.. code-block:: javascript
|
||||||
|
|
||||||
package.myCallback({"error":"unsupported_token_type"});
|
package.myCallback({"error":"unsupported_token_type"});
|
||||||
|
|
||||||
|
@ -382,9 +385,11 @@ class Client(object):
|
||||||
returns an error response as described in `Section 5.2`_.
|
returns an error response as described in `Section 5.2`_.
|
||||||
|
|
||||||
:param body: The response body from the token request.
|
: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.
|
: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
|
These response are json encoded and could easily be parsed without
|
||||||
the assistance of OAuthLib. However, there are a few subtle issues
|
the assistance of OAuthLib. However, there are a few subtle issues
|
||||||
|
@ -418,6 +423,7 @@ class Client(object):
|
||||||
.. _`Section 5.2`: https://tools.ietf.org/html/rfc6749#section-5.2
|
.. _`Section 5.2`: https://tools.ietf.org/html/rfc6749#section-5.2
|
||||||
.. _`Section 7.1`: https://tools.ietf.org/html/rfc6749#section-7.1
|
.. _`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.token = parse_token_response(body, scope=scope)
|
||||||
self.populate_token_attributes(self.token)
|
self.populate_token_attributes(self.token)
|
||||||
return self.token
|
return self.token
|
||||||
|
@ -427,21 +433,19 @@ class Client(object):
|
||||||
|
|
||||||
If the authorization server issued a refresh token to the client, the
|
If the authorization server issued a refresh token to the client, the
|
||||||
client makes a refresh request to the token endpoint by adding 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:
|
format in the HTTP request entity-body:
|
||||||
|
|
||||||
grant_type
|
:param refresh_token: REQUIRED. The refresh token issued to the client.
|
||||||
REQUIRED. Value MUST be set to "refresh_token".
|
:param scope: OPTIONAL. The scope of the access request as described by
|
||||||
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
|
Section 3.3. The requested scope MUST NOT include any scope
|
||||||
not originally granted by the resource owner, and if omitted is
|
not originally granted by the resource owner, and if omitted is
|
||||||
treated as equal to the scope originally granted by the
|
treated as equal to the scope originally granted by the
|
||||||
resource owner.
|
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
|
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,
|
return prepare_token_request(self.refresh_token_key, body=body, scope=scope,
|
||||||
refresh_token=refresh_token, **kwargs)
|
refresh_token=refresh_token, **kwargs)
|
||||||
|
|
||||||
|
@ -461,6 +465,91 @@ class Client(object):
|
||||||
raise ValueError("Invalid token placement.")
|
raise ValueError("Invalid token placement.")
|
||||||
return uri, headers, body
|
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,
|
def _add_mac_token(self, uri, http_method='GET', body=None,
|
||||||
headers=None, token_placement=AUTH_HEADER, ext=None, **kwargs):
|
headers=None, token_placement=AUTH_HEADER, ext=None, **kwargs):
|
||||||
"""Add a MAC token to the request authorization header.
|
"""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)
|
self._expires_at = time.time() + int(self.expires_in)
|
||||||
|
|
||||||
if 'expires_at' in response:
|
if 'expires_at' in response:
|
||||||
|
try:
|
||||||
self._expires_at = int(response.get('expires_at'))
|
self._expires_at = int(response.get('expires_at'))
|
||||||
|
except:
|
||||||
|
self._expires_at = None
|
||||||
|
|
||||||
if 'mac_key' in response:
|
if 'mac_key' in response:
|
||||||
self.mac_key = response.get('mac_key')
|
self.mac_key = response.get('mac_key')
|
||||||
|
|
|
@ -6,9 +6,7 @@ oauthlib.oauth2.rfc6749
|
||||||
This module is an implementation of various logic needed
|
This module is an implementation of various logic needed
|
||||||
for consuming and providing OAuth 2.0 RFC6749.
|
for consuming and providing OAuth 2.0 RFC6749.
|
||||||
"""
|
"""
|
||||||
from __future__ import absolute_import, unicode_literals
|
from ..parameters import prepare_token_request
|
||||||
|
|
||||||
from ..parameters import parse_token_response, prepare_token_request
|
|
||||||
from .base import Client
|
from .base import Client
|
||||||
|
|
||||||
|
|
||||||
|
@ -38,7 +36,7 @@ class LegacyApplicationClient(Client):
|
||||||
grant_type = 'password'
|
grant_type = 'password'
|
||||||
|
|
||||||
def __init__(self, client_id, **kwargs):
|
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,
|
def prepare_request_body(self, username, password, body='', scope=None,
|
||||||
include_client_id=False, **kwargs):
|
include_client_id=False, **kwargs):
|
||||||
|
@ -51,7 +49,7 @@ class LegacyApplicationClient(Client):
|
||||||
:param username: The resource owner username.
|
:param username: The resource owner username.
|
||||||
:param password: The resource owner password.
|
:param password: The resource owner password.
|
||||||
:param body: Existing request body (URL encoded string) to embed parameters
|
: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
|
:param scope: The scope of the access request as described by
|
||||||
`Section 3.3`_.
|
`Section 3.3`_.
|
||||||
:param include_client_id: `True` to send the `client_id` in the
|
: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['client_id'] = self.client_id
|
||||||
kwargs['include_client_id'] = include_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,
|
return prepare_token_request(self.grant_type, body=body, username=username,
|
||||||
password=password, scope=scope, **kwargs)
|
password=password, scope=scope, **kwargs)
|
||||||
|
|
|
@ -6,8 +6,6 @@ oauthlib.oauth2.rfc6749
|
||||||
This module is an implementation of various logic needed
|
This module is an implementation of various logic needed
|
||||||
for consuming and providing OAuth 2.0 RFC6749.
|
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 ..parameters import parse_implicit_response, prepare_grant_uri
|
||||||
from .base import Client
|
from .base import Client
|
||||||
|
|
||||||
|
@ -57,7 +55,7 @@ class MobileApplicationClient(Client):
|
||||||
using the "application/x-www-form-urlencoded" format, per `Appendix B`_:
|
using the "application/x-www-form-urlencoded" format, per `Appendix B`_:
|
||||||
|
|
||||||
:param redirect_uri: OPTIONAL. The redirect URI must be an absolute URI
|
: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`_.
|
provider prior to use. As described in `Section 3.1.2`_.
|
||||||
|
|
||||||
:param scope: OPTIONAL. The scope of the access request as described by
|
: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 3.3`: https://tools.ietf.org/html/rfc6749#section-3.3
|
||||||
.. _`Section 10.12`: https://tools.ietf.org/html/rfc6749#section-10.12
|
.. _`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,
|
return prepare_grant_uri(uri, self.client_id, self.response_type,
|
||||||
redirect_uri=redirect_uri, state=state, scope=scope, **kwargs)
|
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 7.1`: https://tools.ietf.org/html/rfc6749#section-7.1
|
||||||
.. _`Section 3.3`: https://tools.ietf.org/html/rfc6749#section-3.3
|
.. _`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.token = parse_implicit_response(uri, state=state, scope=scope)
|
||||||
self.populate_token_attributes(self.token)
|
self.populate_token_attributes(self.token)
|
||||||
return self.token
|
return self.token
|
||||||
|
|
|
@ -6,13 +6,11 @@ oauthlib.oauth2.rfc6749
|
||||||
This module is an implementation of various logic needed
|
This module is an implementation of various logic needed
|
||||||
for consuming and providing OAuth 2.0 RFC6749.
|
for consuming and providing OAuth 2.0 RFC6749.
|
||||||
"""
|
"""
|
||||||
from __future__ import absolute_import, unicode_literals
|
|
||||||
|
|
||||||
import time
|
import time
|
||||||
|
|
||||||
from oauthlib.common import to_unicode
|
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
|
from .base import Client
|
||||||
|
|
||||||
|
|
||||||
|
@ -33,7 +31,7 @@ class ServiceApplicationClient(Client):
|
||||||
|
|
||||||
def __init__(self, client_id, private_key=None, subject=None, issuer=None,
|
def __init__(self, client_id, private_key=None, subject=None, issuer=None,
|
||||||
audience=None, **kwargs):
|
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
|
:param client_id: Client identifier given by the OAuth provider upon
|
||||||
registration.
|
registration.
|
||||||
|
@ -57,7 +55,7 @@ class ServiceApplicationClient(Client):
|
||||||
state and token. See ``Client.__init__.__doc__`` for
|
state and token. See ``Client.__init__.__doc__`` for
|
||||||
details.
|
details.
|
||||||
"""
|
"""
|
||||||
super(ServiceApplicationClient, self).__init__(client_id, **kwargs)
|
super().__init__(client_id, **kwargs)
|
||||||
self.private_key = private_key
|
self.private_key = private_key
|
||||||
self.subject = subject
|
self.subject = subject
|
||||||
self.issuer = issuer
|
self.issuer = issuer
|
||||||
|
@ -101,7 +99,7 @@ class ServiceApplicationClient(Client):
|
||||||
:param extra_claims: A dict of additional claims to include in the JWT.
|
:param extra_claims: A dict of additional claims to include in the JWT.
|
||||||
|
|
||||||
:param body: Existing request body (URL encoded string) to embed parameters
|
: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.
|
:param scope: The scope of the access request.
|
||||||
|
|
||||||
|
@ -183,6 +181,7 @@ class ServiceApplicationClient(Client):
|
||||||
|
|
||||||
kwargs['client_id'] = self.client_id
|
kwargs['client_id'] = self.client_id
|
||||||
kwargs['include_client_id'] = include_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,
|
return prepare_token_request(self.grant_type,
|
||||||
body=body,
|
body=body,
|
||||||
assertion=assertion,
|
assertion=assertion,
|
||||||
|
|
|
@ -6,13 +6,12 @@ oauthlib.oauth2.rfc6749
|
||||||
This module is an implementation of various logic needed
|
This module is an implementation of various logic needed
|
||||||
for consuming and providing OAuth 2.0 RFC6749.
|
for consuming and providing OAuth 2.0 RFC6749.
|
||||||
"""
|
"""
|
||||||
from __future__ import absolute_import, unicode_literals
|
|
||||||
|
|
||||||
import warnings
|
import warnings
|
||||||
|
|
||||||
from ..parameters import (parse_authorization_code_response,
|
from ..parameters import (
|
||||||
parse_token_response, prepare_grant_uri,
|
parse_authorization_code_response, prepare_grant_uri,
|
||||||
prepare_token_request)
|
prepare_token_request,
|
||||||
|
)
|
||||||
from .base import Client
|
from .base import Client
|
||||||
|
|
||||||
|
|
||||||
|
@ -38,11 +37,11 @@ class WebApplicationClient(Client):
|
||||||
grant_type = 'authorization_code'
|
grant_type = 'authorization_code'
|
||||||
|
|
||||||
def __init__(self, client_id, code=None, **kwargs):
|
def __init__(self, client_id, code=None, **kwargs):
|
||||||
super(WebApplicationClient, self).__init__(client_id, **kwargs)
|
super().__init__(client_id, **kwargs)
|
||||||
self.code = code
|
self.code = code
|
||||||
|
|
||||||
def prepare_request_uri(self, uri, redirect_uri=None, scope=None,
|
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
|
"""Prepare the authorization code request URI
|
||||||
|
|
||||||
The client constructs the request URI by adding the following
|
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`_:
|
using the "application/x-www-form-urlencoded" format, per `Appendix B`_:
|
||||||
|
|
||||||
:param redirect_uri: OPTIONAL. The redirect URI must be an absolute URI
|
: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`_.
|
provider prior to use. As described in `Section 3.1.2`_.
|
||||||
|
|
||||||
:param scope: OPTIONAL. The scope of the access request as described by
|
: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
|
to the client. The parameter SHOULD be used for preventing
|
||||||
cross-site request forgery as described in `Section 10.12`_.
|
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.
|
:param kwargs: Extra arguments to include in the request URI.
|
||||||
|
|
||||||
In addition to supplied parameters, OAuthLib will append the ``client_id``
|
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'
|
'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'])
|
>>> client.prepare_request_uri('https://example.com', scope=['profile', 'pictures'])
|
||||||
'https://example.com?client_id=your_id&response_type=code&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')
|
>>> client.prepare_request_uri('https://example.com', foo='bar')
|
||||||
'https://example.com?client_id=your_id&response_type=code&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 3.3`: https://tools.ietf.org/html/rfc6749#section-3.3
|
||||||
.. _`Section 10.12`: https://tools.ietf.org/html/rfc6749#section-10.12
|
.. _`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',
|
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='',
|
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.
|
"""Prepare the access token request body.
|
||||||
|
|
||||||
The client makes a request to the token endpoint by adding the
|
The client makes a request to the token endpoint by adding the
|
||||||
|
@ -105,7 +117,7 @@ class WebApplicationClient(Client):
|
||||||
values MUST be identical.
|
values MUST be identical.
|
||||||
|
|
||||||
:param body: Existing request body (URL encoded string) to embed parameters
|
: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
|
:param include_client_id: `True` (default) to send the `client_id` in the
|
||||||
body of the upstream request. This is required
|
body of the upstream request. This is required
|
||||||
|
@ -113,6 +125,9 @@ class WebApplicationClient(Client):
|
||||||
authorization server as described in `Section 3.2.1`_.
|
authorization server as described in `Section 3.2.1`_.
|
||||||
:type include_client_id: Boolean
|
: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.
|
:param kwargs: Extra parameters to include in the token request.
|
||||||
|
|
||||||
In addition OAuthLib will add the ``grant_type`` parameter set to
|
In addition OAuthLib will add the ``grant_type`` parameter set to
|
||||||
|
@ -127,6 +142,8 @@ class WebApplicationClient(Client):
|
||||||
>>> client = WebApplicationClient('your_id')
|
>>> client = WebApplicationClient('your_id')
|
||||||
>>> client.prepare_request_body(code='sh35ksdf09sf')
|
>>> client.prepare_request_body(code='sh35ksdf09sf')
|
||||||
'grant_type=authorization_code&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')
|
>>> client.prepare_request_body(code='sh35ksdf09sf', foo='bar')
|
||||||
'grant_type=authorization_code&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['client_id'] = self.client_id
|
||||||
kwargs['include_client_id'] = include_client_id
|
kwargs['include_client_id'] = include_client_id
|
||||||
return prepare_token_request(self.grant_type, code=code, body=body,
|
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):
|
def parse_request_uri_response(self, uri, state=None):
|
||||||
"""Parse the URI query for code and state.
|
"""Parse the URI query for code and state.
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
"""
|
"""
|
||||||
oauthlib.oauth2.rfc6749
|
oauthlib.oauth2.rfc6749
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
@ -6,16 +5,13 @@ oauthlib.oauth2.rfc6749
|
||||||
This module is an implementation of various logic needed
|
This module is an implementation of various logic needed
|
||||||
for consuming and providing OAuth 2.0 RFC6749.
|
for consuming and providing OAuth 2.0 RFC6749.
|
||||||
"""
|
"""
|
||||||
from __future__ import absolute_import, unicode_literals
|
|
||||||
|
|
||||||
from .authorization import AuthorizationEndpoint
|
from .authorization import AuthorizationEndpoint
|
||||||
from .introspect import IntrospectEndpoint
|
from .introspect import IntrospectEndpoint
|
||||||
from .metadata import MetadataEndpoint
|
from .metadata import MetadataEndpoint
|
||||||
from .token import TokenEndpoint
|
from .pre_configured import (
|
||||||
|
BackendApplicationServer, LegacyApplicationServer, MobileApplicationServer,
|
||||||
|
Server, WebApplicationServer,
|
||||||
|
)
|
||||||
from .resource import ResourceEndpoint
|
from .resource import ResourceEndpoint
|
||||||
from .revocation import RevocationEndpoint
|
from .revocation import RevocationEndpoint
|
||||||
from .pre_configured import Server
|
from .token import TokenEndpoint
|
||||||
from .pre_configured import WebApplicationServer
|
|
||||||
from .pre_configured import MobileApplicationServer
|
|
||||||
from .pre_configured import LegacyApplicationServer
|
|
||||||
from .pre_configured import BackendApplicationServer
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
"""
|
"""
|
||||||
oauthlib.oauth2.rfc6749
|
oauthlib.oauth2.rfc6749
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
@ -6,8 +5,6 @@ oauthlib.oauth2.rfc6749
|
||||||
This module is an implementation of various logic needed
|
This module is an implementation of various logic needed
|
||||||
for consuming and providing OAuth 2.0 RFC6749.
|
for consuming and providing OAuth 2.0 RFC6749.
|
||||||
"""
|
"""
|
||||||
from __future__ import absolute_import, unicode_literals
|
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from oauthlib.common import Request
|
from oauthlib.common import Request
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
"""
|
"""
|
||||||
oauthlib.oauth2.rfc6749
|
oauthlib.oauth2.rfc6749
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
@ -6,21 +5,18 @@ oauthlib.oauth2.rfc6749
|
||||||
This module is an implementation of various logic needed
|
This module is an implementation of various logic needed
|
||||||
for consuming and providing OAuth 2.0 RFC6749.
|
for consuming and providing OAuth 2.0 RFC6749.
|
||||||
"""
|
"""
|
||||||
from __future__ import absolute_import, unicode_literals
|
|
||||||
|
|
||||||
import functools
|
import functools
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from ..errors import (FatalClientError, OAuth2Error, ServerError,
|
from ..errors import (
|
||||||
TemporarilyUnavailableError, InvalidRequestError,
|
FatalClientError, InvalidClientError, InvalidRequestError, OAuth2Error,
|
||||||
InvalidClientError, UnsupportedTokenTypeError)
|
ServerError, TemporarilyUnavailableError, UnsupportedTokenTypeError,
|
||||||
|
)
|
||||||
from oauthlib.common import CaseInsensitiveDict, urldecode
|
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class BaseEndpoint(object):
|
class BaseEndpoint:
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self._available = True
|
self._available = True
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
"""
|
"""
|
||||||
oauthlib.oauth2.rfc6749.endpoint.introspect
|
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
|
.. _`Token Introspection`: https://tools.ietf.org/html/rfc7662
|
||||||
"""
|
"""
|
||||||
from __future__ import absolute_import, unicode_literals
|
|
||||||
|
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from oauthlib.common import Request
|
from oauthlib.common import Request
|
||||||
|
|
||||||
from ..errors import OAuth2Error, UnsupportedTokenTypeError
|
from ..errors import OAuth2Error
|
||||||
from .base import BaseEndpoint, catch_errors_and_unavailability
|
from .base import BaseEndpoint, catch_errors_and_unavailability
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
@ -89,9 +86,9 @@ class IntrospectEndpoint(BaseEndpoint):
|
||||||
an HTTP POST request with parameters sent as
|
an HTTP POST request with parameters sent as
|
||||||
"application/x-www-form-urlencoded".
|
"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
|
A hint about the type of the token submitted for
|
||||||
introspection. The protected resource MAY pass this parameter to
|
introspection. The protected resource MAY pass this parameter to
|
||||||
help the authorization server optimize the token lookup. If the
|
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
|
extend its search across all of its supported token types. An
|
||||||
authorization server MAY ignore this parameter, particularly if it
|
authorization server MAY ignore this parameter, particularly if it
|
||||||
is able to detect the token type automatically.
|
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`],
|
* access_token: An Access Token as defined in [`RFC6749`], `section 1.4`_
|
||||||
`section 1.5`_
|
* refresh_token: A Refresh Token as defined in [`RFC6749`], `section 1.5`_
|
||||||
|
|
||||||
The introspection endpoint MAY accept other OPTIONAL
|
The introspection endpoint MAY accept other OPTIONAL
|
||||||
parameters to provide further context to the query. For
|
parameters to provide further context to the query. For
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
"""
|
"""
|
||||||
oauthlib.oauth2.rfc6749.endpoint.metadata
|
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
|
.. _`OAuth 2.0 Authorization Server Metadata`: https://tools.ietf.org/html/rfc8414
|
||||||
"""
|
"""
|
||||||
from __future__ import absolute_import, unicode_literals
|
|
||||||
|
|
||||||
import copy
|
import copy
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from ....common import unicode_type
|
from .. import grant_types, utils
|
||||||
from .base import BaseEndpoint, catch_errors_and_unavailability
|
|
||||||
from .authorization import AuthorizationEndpoint
|
from .authorization import AuthorizationEndpoint
|
||||||
|
from .base import BaseEndpoint, catch_errors_and_unavailability
|
||||||
from .introspect import IntrospectEndpoint
|
from .introspect import IntrospectEndpoint
|
||||||
from .token import TokenEndpoint
|
|
||||||
from .revocation import RevocationEndpoint
|
from .revocation import RevocationEndpoint
|
||||||
from .. import grant_types
|
from .token import TokenEndpoint
|
||||||
|
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -59,7 +54,8 @@ class MetadataEndpoint(BaseEndpoint):
|
||||||
"""Create metadata response
|
"""Create metadata response
|
||||||
"""
|
"""
|
||||||
headers = {
|
headers = {
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json',
|
||||||
|
'Access-Control-Allow-Origin': '*',
|
||||||
}
|
}
|
||||||
return headers, json.dumps(self.claims), 200
|
return headers, json.dumps(self.claims), 200
|
||||||
|
|
||||||
|
@ -72,7 +68,7 @@ class MetadataEndpoint(BaseEndpoint):
|
||||||
raise ValueError("key {} is a mandatory metadata.".format(key))
|
raise ValueError("key {} is a mandatory metadata.".format(key))
|
||||||
|
|
||||||
elif is_issuer:
|
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]))
|
raise ValueError("key {}: {} must be an HTTPS URL".format(key, array[key]))
|
||||||
if "?" in array[key] or "&" in array[key] or "#" in 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]))
|
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):
|
if not isinstance(array[key], list):
|
||||||
raise ValueError("key {}: {} must be an Array".format(key, array[key]))
|
raise ValueError("key {}: {} must be an Array".format(key, array[key]))
|
||||||
for elem in 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))
|
raise ValueError("array {}: {} must contains only string (not {})".format(key, array[key], elem))
|
||||||
|
|
||||||
def validate_metadata_token(self, claims, endpoint):
|
def validate_metadata_token(self, claims, endpoint):
|
||||||
|
@ -166,9 +162,9 @@ class MetadataEndpoint(BaseEndpoint):
|
||||||
response_types_supported
|
response_types_supported
|
||||||
REQUIRED.
|
REQUIRED.
|
||||||
|
|
||||||
* Other OPTIONAL fields:
|
Other OPTIONAL fields:
|
||||||
jwks_uri
|
jwks_uri,
|
||||||
registration_endpoint
|
registration_endpoint,
|
||||||
response_modes_supported
|
response_modes_supported
|
||||||
|
|
||||||
grant_types_supported
|
grant_types_supported
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
"""
|
"""
|
||||||
oauthlib.oauth2.rfc6749.endpoints.pre_configured
|
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
|
This module is an implementation of various endpoints needed
|
||||||
for providing OAuth 2.0 RFC6749 servers.
|
for providing OAuth 2.0 RFC6749 servers.
|
||||||
"""
|
"""
|
||||||
from __future__ import absolute_import, unicode_literals
|
from ..grant_types import (
|
||||||
|
AuthorizationCodeGrant, ClientCredentialsGrant, ImplicitGrant,
|
||||||
from ..grant_types import (AuthorizationCodeGrant,
|
RefreshTokenGrant, ResourceOwnerPasswordCredentialsGrant,
|
||||||
ClientCredentialsGrant,
|
)
|
||||||
ImplicitGrant,
|
|
||||||
RefreshTokenGrant,
|
|
||||||
ResourceOwnerPasswordCredentialsGrant)
|
|
||||||
from ..tokens import BearerToken
|
from ..tokens import BearerToken
|
||||||
from .authorization import AuthorizationEndpoint
|
from .authorization import AuthorizationEndpoint
|
||||||
from .introspect import IntrospectEndpoint
|
from .introspect import IntrospectEndpoint
|
||||||
|
@ -42,34 +38,34 @@ class Server(AuthorizationEndpoint, IntrospectEndpoint, TokenEndpoint,
|
||||||
:param kwargs: Extra parameters to pass to authorization-,
|
:param kwargs: Extra parameters to pass to authorization-,
|
||||||
token-, resource-, and revocation-endpoint constructors.
|
token-, resource-, and revocation-endpoint constructors.
|
||||||
"""
|
"""
|
||||||
auth_grant = AuthorizationCodeGrant(request_validator)
|
self.auth_grant = AuthorizationCodeGrant(request_validator)
|
||||||
implicit_grant = ImplicitGrant(request_validator)
|
self.implicit_grant = ImplicitGrant(request_validator)
|
||||||
password_grant = ResourceOwnerPasswordCredentialsGrant(
|
self.password_grant = ResourceOwnerPasswordCredentialsGrant(
|
||||||
request_validator)
|
request_validator)
|
||||||
credentials_grant = ClientCredentialsGrant(request_validator)
|
self.credentials_grant = ClientCredentialsGrant(request_validator)
|
||||||
refresh_grant = RefreshTokenGrant(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)
|
token_expires_in, refresh_token_generator)
|
||||||
|
|
||||||
AuthorizationEndpoint.__init__(self, default_response_type='code',
|
AuthorizationEndpoint.__init__(self, default_response_type='code',
|
||||||
response_types={
|
response_types={
|
||||||
'code': auth_grant,
|
'code': self.auth_grant,
|
||||||
'token': implicit_grant,
|
'token': self.implicit_grant,
|
||||||
'none': auth_grant
|
'none': self.auth_grant
|
||||||
},
|
},
|
||||||
default_token_type=bearer)
|
default_token_type=self.bearer)
|
||||||
|
|
||||||
TokenEndpoint.__init__(self, default_grant_type='authorization_code',
|
TokenEndpoint.__init__(self, default_grant_type='authorization_code',
|
||||||
grant_types={
|
grant_types={
|
||||||
'authorization_code': auth_grant,
|
'authorization_code': self.auth_grant,
|
||||||
'password': password_grant,
|
'password': self.password_grant,
|
||||||
'client_credentials': credentials_grant,
|
'client_credentials': self.credentials_grant,
|
||||||
'refresh_token': refresh_grant,
|
'refresh_token': self.refresh_grant,
|
||||||
},
|
},
|
||||||
default_token_type=bearer)
|
default_token_type=self.bearer)
|
||||||
ResourceEndpoint.__init__(self, default_token='Bearer',
|
ResourceEndpoint.__init__(self, default_token='Bearer',
|
||||||
token_types={'Bearer': bearer})
|
token_types={'Bearer': self.bearer})
|
||||||
RevocationEndpoint.__init__(self, request_validator)
|
RevocationEndpoint.__init__(self, request_validator)
|
||||||
IntrospectEndpoint.__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-,
|
:param kwargs: Extra parameters to pass to authorization-,
|
||||||
token-, resource-, and revocation-endpoint constructors.
|
token-, resource-, and revocation-endpoint constructors.
|
||||||
"""
|
"""
|
||||||
auth_grant = AuthorizationCodeGrant(request_validator)
|
self.auth_grant = AuthorizationCodeGrant(request_validator)
|
||||||
refresh_grant = RefreshTokenGrant(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)
|
token_expires_in, refresh_token_generator)
|
||||||
AuthorizationEndpoint.__init__(self, default_response_type='code',
|
AuthorizationEndpoint.__init__(self, default_response_type='code',
|
||||||
response_types={'code': auth_grant},
|
response_types={'code': self.auth_grant},
|
||||||
default_token_type=bearer)
|
default_token_type=self.bearer)
|
||||||
TokenEndpoint.__init__(self, default_grant_type='authorization_code',
|
TokenEndpoint.__init__(self, default_grant_type='authorization_code',
|
||||||
grant_types={
|
grant_types={
|
||||||
'authorization_code': auth_grant,
|
'authorization_code': self.auth_grant,
|
||||||
'refresh_token': refresh_grant,
|
'refresh_token': self.refresh_grant,
|
||||||
},
|
},
|
||||||
default_token_type=bearer)
|
default_token_type=self.bearer)
|
||||||
ResourceEndpoint.__init__(self, default_token='Bearer',
|
ResourceEndpoint.__init__(self, default_token='Bearer',
|
||||||
token_types={'Bearer': bearer})
|
token_types={'Bearer': self.bearer})
|
||||||
RevocationEndpoint.__init__(self, request_validator)
|
RevocationEndpoint.__init__(self, request_validator)
|
||||||
IntrospectEndpoint.__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-,
|
:param kwargs: Extra parameters to pass to authorization-,
|
||||||
token-, resource-, and revocation-endpoint constructors.
|
token-, resource-, and revocation-endpoint constructors.
|
||||||
"""
|
"""
|
||||||
implicit_grant = ImplicitGrant(request_validator)
|
self.implicit_grant = ImplicitGrant(request_validator)
|
||||||
bearer = BearerToken(request_validator, token_generator,
|
self.bearer = BearerToken(request_validator, token_generator,
|
||||||
token_expires_in, refresh_token_generator)
|
token_expires_in, refresh_token_generator)
|
||||||
AuthorizationEndpoint.__init__(self, default_response_type='token',
|
AuthorizationEndpoint.__init__(self, default_response_type='token',
|
||||||
response_types={
|
response_types={
|
||||||
'token': implicit_grant},
|
'token': self.implicit_grant},
|
||||||
default_token_type=bearer)
|
default_token_type=self.bearer)
|
||||||
ResourceEndpoint.__init__(self, default_token='Bearer',
|
ResourceEndpoint.__init__(self, default_token='Bearer',
|
||||||
token_types={'Bearer': bearer})
|
token_types={'Bearer': self.bearer})
|
||||||
RevocationEndpoint.__init__(self, request_validator,
|
RevocationEndpoint.__init__(self, request_validator,
|
||||||
supported_token_types=['access_token'])
|
supported_token_types=['access_token'])
|
||||||
IntrospectEndpoint.__init__(self, request_validator,
|
IntrospectEndpoint.__init__(self, request_validator,
|
||||||
|
@ -168,19 +164,19 @@ class LegacyApplicationServer(TokenEndpoint, IntrospectEndpoint,
|
||||||
:param kwargs: Extra parameters to pass to authorization-,
|
:param kwargs: Extra parameters to pass to authorization-,
|
||||||
token-, resource-, and revocation-endpoint constructors.
|
token-, resource-, and revocation-endpoint constructors.
|
||||||
"""
|
"""
|
||||||
password_grant = ResourceOwnerPasswordCredentialsGrant(
|
self.password_grant = ResourceOwnerPasswordCredentialsGrant(
|
||||||
request_validator)
|
request_validator)
|
||||||
refresh_grant = RefreshTokenGrant(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)
|
token_expires_in, refresh_token_generator)
|
||||||
TokenEndpoint.__init__(self, default_grant_type='password',
|
TokenEndpoint.__init__(self, default_grant_type='password',
|
||||||
grant_types={
|
grant_types={
|
||||||
'password': password_grant,
|
'password': self.password_grant,
|
||||||
'refresh_token': refresh_grant,
|
'refresh_token': self.refresh_grant,
|
||||||
},
|
},
|
||||||
default_token_type=bearer)
|
default_token_type=self.bearer)
|
||||||
ResourceEndpoint.__init__(self, default_token='Bearer',
|
ResourceEndpoint.__init__(self, default_token='Bearer',
|
||||||
token_types={'Bearer': bearer})
|
token_types={'Bearer': self.bearer})
|
||||||
RevocationEndpoint.__init__(self, request_validator)
|
RevocationEndpoint.__init__(self, request_validator)
|
||||||
IntrospectEndpoint.__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-,
|
:param kwargs: Extra parameters to pass to authorization-,
|
||||||
token-, resource-, and revocation-endpoint constructors.
|
token-, resource-, and revocation-endpoint constructors.
|
||||||
"""
|
"""
|
||||||
credentials_grant = ClientCredentialsGrant(request_validator)
|
self.credentials_grant = ClientCredentialsGrant(request_validator)
|
||||||
bearer = BearerToken(request_validator, token_generator,
|
self.bearer = BearerToken(request_validator, token_generator,
|
||||||
token_expires_in, refresh_token_generator)
|
token_expires_in, refresh_token_generator)
|
||||||
TokenEndpoint.__init__(self, default_grant_type='client_credentials',
|
TokenEndpoint.__init__(self, default_grant_type='client_credentials',
|
||||||
grant_types={
|
grant_types={
|
||||||
'client_credentials': credentials_grant},
|
'client_credentials': self.credentials_grant},
|
||||||
default_token_type=bearer)
|
default_token_type=self.bearer)
|
||||||
ResourceEndpoint.__init__(self, default_token='Bearer',
|
ResourceEndpoint.__init__(self, default_token='Bearer',
|
||||||
token_types={'Bearer': bearer})
|
token_types={'Bearer': self.bearer})
|
||||||
RevocationEndpoint.__init__(self, request_validator,
|
RevocationEndpoint.__init__(self, request_validator,
|
||||||
supported_token_types=['access_token'])
|
supported_token_types=['access_token'])
|
||||||
IntrospectEndpoint.__init__(self, request_validator,
|
IntrospectEndpoint.__init__(self, request_validator,
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
"""
|
"""
|
||||||
oauthlib.oauth2.rfc6749
|
oauthlib.oauth2.rfc6749
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
@ -6,8 +5,6 @@ oauthlib.oauth2.rfc6749
|
||||||
This module is an implementation of various logic needed
|
This module is an implementation of various logic needed
|
||||||
for consuming and providing OAuth 2.0 RFC6749.
|
for consuming and providing OAuth 2.0 RFC6749.
|
||||||
"""
|
"""
|
||||||
from __future__ import absolute_import, unicode_literals
|
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from oauthlib.common import Request
|
from oauthlib.common import Request
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
"""
|
"""
|
||||||
oauthlib.oauth2.rfc6749.endpoint.revocation
|
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
|
.. _`Token Revocation`: https://tools.ietf.org/html/draft-ietf-oauth-revocation-11
|
||||||
"""
|
"""
|
||||||
from __future__ import absolute_import, unicode_literals
|
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from oauthlib.common import Request
|
from oauthlib.common import Request
|
||||||
|
|
||||||
from ..errors import OAuth2Error, UnsupportedTokenTypeError
|
from ..errors import OAuth2Error
|
||||||
from .base import BaseEndpoint, catch_errors_and_unavailability
|
from .base import BaseEndpoint, catch_errors_and_unavailability
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
@ -45,7 +42,7 @@ class RevocationEndpoint(BaseEndpoint):
|
||||||
|
|
||||||
|
|
||||||
The authorization server responds with HTTP status code 200 if the
|
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.
|
invalid token.
|
||||||
|
|
||||||
Note: invalid tokens do not cause an error response since the client
|
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)
|
log.debug('Client error during validation of %r. %r.', request, e)
|
||||||
response_body = e.json
|
response_body = e.json
|
||||||
if self.enable_jsonp and request.callback:
|
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)
|
resp_headers.update(e.headers)
|
||||||
return resp_headers, response_body, e.status_code
|
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
|
submitted for revocation. Clients MAY pass this parameter in order to
|
||||||
help the authorization server to optimize the token lookup. If the
|
help the authorization server to optimize the token lookup. If the
|
||||||
server is unable to locate the token using the given hint, it MUST
|
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
|
authorization server MAY ignore this parameter, particularly if it is
|
||||||
able to detect the token type automatically. This specification
|
able to detect the token type automatically. This specification
|
||||||
defines two such values:
|
defines two such values:
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
"""
|
"""
|
||||||
oauthlib.oauth2.rfc6749
|
oauthlib.oauth2.rfc6749
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
@ -6,8 +5,6 @@ oauthlib.oauth2.rfc6749
|
||||||
This module is an implementation of various logic needed
|
This module is an implementation of various logic needed
|
||||||
for consuming and providing OAuth 2.0 RFC6749.
|
for consuming and providing OAuth 2.0 RFC6749.
|
||||||
"""
|
"""
|
||||||
from __future__ import absolute_import, unicode_literals
|
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from oauthlib.common import Request
|
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 # OK
|
||||||
https://example.com/path?query=component#fragment # Not 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
|
Since requests to the token endpoint result in the transmission of
|
||||||
clear-text credentials (in the HTTP request and response), the
|
clear-text credentials (in the HTTP request and response), the
|
||||||
authorization server MUST require the use of TLS as described in
|
authorization server MUST require the use of TLS as described in
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
# coding=utf-8
|
|
||||||
"""
|
"""
|
||||||
oauthlib.oauth2.rfc6749.errors
|
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
|
Error used both by OAuth 2 clients and providers to represent the spec
|
||||||
defined error responses for all four core grant types.
|
defined error responses for all four core grant types.
|
||||||
"""
|
"""
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
import json
|
import json
|
||||||
|
|
||||||
from oauthlib.common import add_params_to_uri, urlencode
|
from oauthlib.common import add_params_to_uri, urlencode
|
||||||
|
@ -45,10 +42,10 @@ class OAuth2Error(Exception):
|
||||||
if description is not None:
|
if description is not None:
|
||||||
self.description = description
|
self.description = description
|
||||||
|
|
||||||
message = '(%s) %s' % (self.error, self.description)
|
message = '({}) {}'.format(self.error, self.description)
|
||||||
if request:
|
if request:
|
||||||
message += ' ' + repr(request)
|
message += ' ' + repr(request)
|
||||||
super(OAuth2Error, self).__init__(message)
|
super().__init__(message)
|
||||||
|
|
||||||
self.uri = uri
|
self.uri = uri
|
||||||
self.state = state
|
self.state = state
|
||||||
|
@ -106,15 +103,12 @@ class OAuth2Error(Exception):
|
||||||
value "Bearer". This scheme MUST be followed by one or more
|
value "Bearer". This scheme MUST be followed by one or more
|
||||||
auth-param values.
|
auth-param values.
|
||||||
"""
|
"""
|
||||||
authvalues = [
|
authvalues = ['error="{}"'.format(self.error)]
|
||||||
"Bearer",
|
|
||||||
'error="{}"'.format(self.error)
|
|
||||||
]
|
|
||||||
if self.description:
|
if self.description:
|
||||||
authvalues.append('error_description="{}"'.format(self.description))
|
authvalues.append('error_description="{}"'.format(self.description))
|
||||||
if self.uri:
|
if self.uri:
|
||||||
authvalues.append('error_uri="{}"'.format(self.uri))
|
authvalues.append('error_uri="{}"'.format(self.uri))
|
||||||
return {"WWW-Authenticate": ", ".join(authvalues)}
|
return {"WWW-Authenticate": "Bearer " + ", ".join(authvalues)}
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
|
|
||||||
|
@ -389,7 +383,7 @@ class CustomOAuth2Error(OAuth2Error):
|
||||||
"""
|
"""
|
||||||
def __init__(self, error, *args, **kwargs):
|
def __init__(self, error, *args, **kwargs):
|
||||||
self.error = error
|
self.error = error
|
||||||
super(CustomOAuth2Error, self).__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
def raise_from_error(error, params=None):
|
def raise_from_error(error, params=None):
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
"""
|
"""
|
||||||
oauthlib.oauth2.rfc6749.grant_types
|
oauthlib.oauth2.rfc6749.grant_types
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
"""
|
"""
|
||||||
from __future__ import unicode_literals, absolute_import
|
|
||||||
|
|
||||||
from .authorization_code import AuthorizationCodeGrant
|
from .authorization_code import AuthorizationCodeGrant
|
||||||
from .implicit import ImplicitGrant
|
|
||||||
from .resource_owner_password_credentials import ResourceOwnerPasswordCredentialsGrant
|
|
||||||
from .client_credentials import ClientCredentialsGrant
|
from .client_credentials import ClientCredentialsGrant
|
||||||
|
from .implicit import ImplicitGrant
|
||||||
from .refresh_token import RefreshTokenGrant
|
from .refresh_token import RefreshTokenGrant
|
||||||
|
from .resource_owner_password_credentials import (
|
||||||
|
ResourceOwnerPasswordCredentialsGrant,
|
||||||
|
)
|
||||||
|
|
|
@ -1,10 +1,7 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
"""
|
"""
|
||||||
oauthlib.oauth2.rfc6749.grant_types
|
oauthlib.oauth2.rfc6749.grant_types
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
"""
|
"""
|
||||||
from __future__ import absolute_import, unicode_literals
|
|
||||||
|
|
||||||
import base64
|
import base64
|
||||||
import hashlib
|
import hashlib
|
||||||
import json
|
import json
|
||||||
|
@ -275,6 +272,8 @@ class AuthorizationCodeGrant(GrantTypeBase):
|
||||||
grant = self.create_authorization_code(request)
|
grant = self.create_authorization_code(request)
|
||||||
for modifier in self._code_modifiers:
|
for modifier in self._code_modifiers:
|
||||||
grant = modifier(grant, token_handler, request)
|
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)
|
log.debug('Saving grant %r for %r.', grant, request)
|
||||||
self.request_validator.save_authorization_code(
|
self.request_validator.save_authorization_code(
|
||||||
request.client_id, grant, request)
|
request.client_id, grant, request)
|
||||||
|
@ -313,6 +312,7 @@ class AuthorizationCodeGrant(GrantTypeBase):
|
||||||
self.request_validator.save_token(token, request)
|
self.request_validator.save_token(token, request)
|
||||||
self.request_validator.invalidate_authorization_code(
|
self.request_validator.invalidate_authorization_code(
|
||||||
request.client_id, request.code, request)
|
request.client_id, request.code, request)
|
||||||
|
headers.update(self._create_cors_headers(request))
|
||||||
return headers, json.dumps(token), 200
|
return headers, json.dumps(token), 200
|
||||||
|
|
||||||
def validate_authorization_request(self, request):
|
def validate_authorization_request(self, request):
|
||||||
|
|
|
@ -1,23 +1,21 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
"""
|
"""
|
||||||
oauthlib.oauth2.rfc6749.grant_types
|
oauthlib.oauth2.rfc6749.grant_types
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
"""
|
"""
|
||||||
from __future__ import absolute_import, unicode_literals
|
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
from itertools import chain
|
from itertools import chain
|
||||||
|
|
||||||
from oauthlib.common import add_params_to_uri
|
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.oauth2.rfc6749 import errors, utils
|
||||||
|
from oauthlib.uri_validate import is_absolute_uri
|
||||||
|
|
||||||
from ..request_validator import RequestValidator
|
from ..request_validator import RequestValidator
|
||||||
|
from ..utils import is_secure_transport
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class ValidatorsContainer(object):
|
class ValidatorsContainer:
|
||||||
"""
|
"""
|
||||||
Container object for holding custom validator callables to be invoked
|
Container object for holding custom validator callables to be invoked
|
||||||
as part of the grant type `validate_authorization_request()` or
|
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)
|
return chain(self.post_auth, self.post_token)
|
||||||
|
|
||||||
|
|
||||||
class GrantTypeBase(object):
|
class GrantTypeBase:
|
||||||
error_uri = None
|
error_uri = None
|
||||||
request_validator = None
|
request_validator = None
|
||||||
default_response_mode = 'fragment'
|
default_response_mode = 'fragment'
|
||||||
|
@ -251,3 +249,20 @@ class GrantTypeBase(object):
|
||||||
raise errors.MissingRedirectURIError(request=request)
|
raise errors.MissingRedirectURIError(request=request)
|
||||||
if not is_absolute_uri(request.redirect_uri):
|
if not is_absolute_uri(request.redirect_uri):
|
||||||
raise errors.InvalidRedirectURIError(request=request)
|
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}
|
||||||
|
|
|
@ -1,15 +1,11 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
"""
|
"""
|
||||||
oauthlib.oauth2.rfc6749.grant_types
|
oauthlib.oauth2.rfc6749.grant_types
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
"""
|
"""
|
||||||
from __future__ import absolute_import, unicode_literals
|
|
||||||
|
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from .. import errors
|
from .. import errors
|
||||||
from ..request_validator import RequestValidator
|
|
||||||
from .base import GrantTypeBase
|
from .base import GrantTypeBase
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
@ -119,8 +115,8 @@ class ClientCredentialsGrant(GrantTypeBase):
|
||||||
# Ensure client is authorized use of this grant type
|
# Ensure client is authorized use of this grant type
|
||||||
self.validate_grant_type(request)
|
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
|
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)
|
self.validate_scopes(request)
|
||||||
|
|
||||||
for validator in self.custom_validators.post_token:
|
for validator in self.custom_validators.post_token:
|
||||||
|
|
|
@ -1,10 +1,7 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
"""
|
"""
|
||||||
oauthlib.oauth2.rfc6749.grant_types
|
oauthlib.oauth2.rfc6749.grant_types
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
"""
|
"""
|
||||||
from __future__ import absolute_import, unicode_literals
|
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from oauthlib import common
|
from oauthlib import common
|
||||||
|
|
|
@ -1,15 +1,11 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
"""
|
"""
|
||||||
oauthlib.oauth2.rfc6749.grant_types
|
oauthlib.oauth2.rfc6749.grant_types
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
"""
|
"""
|
||||||
from __future__ import absolute_import, unicode_literals
|
|
||||||
|
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from .. import errors, utils
|
from .. import errors, utils
|
||||||
from ..request_validator import RequestValidator
|
|
||||||
from .base import GrantTypeBase
|
from .base import GrantTypeBase
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
@ -25,7 +21,7 @@ class RefreshTokenGrant(GrantTypeBase):
|
||||||
def __init__(self, request_validator=None,
|
def __init__(self, request_validator=None,
|
||||||
issue_new_refresh_tokens=True,
|
issue_new_refresh_tokens=True,
|
||||||
**kwargs):
|
**kwargs):
|
||||||
super(RefreshTokenGrant, self).__init__(
|
super().__init__(
|
||||||
request_validator,
|
request_validator,
|
||||||
issue_new_refresh_tokens=issue_new_refresh_tokens,
|
issue_new_refresh_tokens=issue_new_refresh_tokens,
|
||||||
**kwargs)
|
**kwargs)
|
||||||
|
@ -67,12 +63,13 @@ class RefreshTokenGrant(GrantTypeBase):
|
||||||
refresh_token=self.issue_new_refresh_tokens)
|
refresh_token=self.issue_new_refresh_tokens)
|
||||||
|
|
||||||
for modifier in self._token_modifiers:
|
for modifier in self._token_modifiers:
|
||||||
token = modifier(token)
|
token = modifier(token, token_handler, request)
|
||||||
|
|
||||||
self.request_validator.save_token(token, request)
|
self.request_validator.save_token(token, request)
|
||||||
|
|
||||||
log.debug('Issuing new token to client id %r (%r), %r.',
|
log.debug('Issuing new token to client id %r (%r), %r.',
|
||||||
request.client_id, request.client, token)
|
request.client_id, request.client, token)
|
||||||
|
headers.update(self._create_cors_headers(request))
|
||||||
return headers, json.dumps(token), 200
|
return headers, json.dumps(token), 200
|
||||||
|
|
||||||
def validate_token_request(self, request):
|
def validate_token_request(self, request):
|
||||||
|
@ -126,7 +123,7 @@ class RefreshTokenGrant(GrantTypeBase):
|
||||||
|
|
||||||
if request.scope:
|
if request.scope:
|
||||||
request.scopes = utils.scope_to_list(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(
|
and not self.request_validator.is_within_original_scope(
|
||||||
request.scopes, request.refresh_token, request)):
|
request.scopes, request.refresh_token, request)):
|
||||||
log.debug('Refresh token %s lack requested scopes, %r.',
|
log.debug('Refresh token %s lack requested scopes, %r.',
|
||||||
|
|
|
@ -1,15 +1,11 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
"""
|
"""
|
||||||
oauthlib.oauth2.rfc6749.grant_types
|
oauthlib.oauth2.rfc6749.grant_types
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
"""
|
"""
|
||||||
from __future__ import absolute_import, unicode_literals
|
|
||||||
|
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from .. import errors
|
from .. import errors
|
||||||
from ..request_validator import RequestValidator
|
|
||||||
from .base import GrantTypeBase
|
from .base import GrantTypeBase
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
"""
|
"""
|
||||||
oauthlib.oauth2.rfc6749.parameters
|
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
|
.. _`Section 4`: https://tools.ietf.org/html/rfc6749#section-4
|
||||||
"""
|
"""
|
||||||
from __future__ import absolute_import, unicode_literals
|
|
||||||
|
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
import time
|
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 oauthlib.signals import scope_changed
|
||||||
|
|
||||||
from .errors import (InsecureTransportError, MismatchingStateError,
|
from .errors import (
|
||||||
MissingCodeError, MissingTokenError,
|
InsecureTransportError, MismatchingStateError, MissingCodeError,
|
||||||
MissingTokenTypeError, raise_from_error)
|
MissingTokenError, MissingTokenTypeError, raise_from_error,
|
||||||
|
)
|
||||||
from .tokens import OAuth2Token
|
from .tokens import OAuth2Token
|
||||||
from .utils import is_secure_transport, list_to_scope, scope_to_list
|
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,
|
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.
|
"""Prepare the authorization grant request URI.
|
||||||
|
|
||||||
The client constructs the request URI by adding the following
|
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
|
back to the client. The parameter SHOULD be used for
|
||||||
preventing cross-site request forgery as described in
|
preventing cross-site request forgery as described in
|
||||||
`Section 10.12`_.
|
`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.
|
:param kwargs: Extra arguments to embed in the grant/authorization URL.
|
||||||
|
|
||||||
An example of an authorization code 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
|
.. code-block:: http
|
||||||
|
|
||||||
GET /authorize?response_type=code&client_id=s6BhdRkqt3&state=xyz
|
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
|
&redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb HTTP/1.1
|
||||||
Host: server.example.com
|
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)))
|
params.append(('scope', list_to_scope(scope)))
|
||||||
if state:
|
if state:
|
||||||
params.append(('state', 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:
|
for k in kwargs:
|
||||||
if kwargs[k]:
|
if kwargs[k]:
|
||||||
params.append((unicode_type(k), kwargs[k]))
|
params.append((str(k), kwargs[k]))
|
||||||
|
|
||||||
return add_params_to_uri(uri, params)
|
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.
|
"""Prepare the access token request.
|
||||||
|
|
||||||
The client makes a request to the token endpoint by adding the
|
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
|
authorization request as described in
|
||||||
`Section 4.1.1`_, and their values MUST be identical. *
|
`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.
|
:param kwargs: Extra arguments to embed in the request body.
|
||||||
|
|
||||||
Parameters marked with a `*` above are not explicit arguments in the
|
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)
|
client_id = kwargs.pop('client_id', None)
|
||||||
if include_client_id:
|
if include_client_id:
|
||||||
if client_id is not None:
|
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)
|
# the kwargs iteration below only supports including boolean truth (truthy)
|
||||||
# values, but some servers may require an empty string for `client_secret`
|
# values, but some servers may require an empty string for `client_secret`
|
||||||
client_secret = kwargs.pop('client_secret', None)
|
client_secret = kwargs.pop('client_secret', None)
|
||||||
if client_secret is not 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
|
# this handles: `code`, `redirect_uri`, and other undocumented params
|
||||||
for k in kwargs:
|
for k in kwargs:
|
||||||
if kwargs[k]:
|
if kwargs[k]:
|
||||||
params.append((unicode_type(k), kwargs[k]))
|
params.append((str(k), kwargs[k]))
|
||||||
|
|
||||||
return add_params_to_qs(body, params)
|
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.
|
"""Prepare a token revocation request.
|
||||||
|
|
||||||
The client constructs the request by including the following parameters
|
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:
|
entity-body:
|
||||||
|
|
||||||
:param token: REQUIRED. The token that the client wants to get revoked.
|
: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:
|
for k in kwargs:
|
||||||
if kwargs[k]:
|
if kwargs[k]:
|
||||||
params.append((unicode_type(k), kwargs[k]))
|
params.append((str(k), kwargs[k]))
|
||||||
|
|
||||||
headers = {'Content-Type': 'application/x-www-form-urlencoded'}
|
headers = {'Content-Type': 'application/x-www-form-urlencoded'}
|
||||||
|
|
||||||
|
@ -433,7 +443,7 @@ def parse_token_response(body, scope=None):
|
||||||
|
|
||||||
|
|
||||||
def validate_token_parameters(params):
|
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:
|
if 'error' in params:
|
||||||
raise_from_error(params.get('error'), params)
|
raise_from_error(params.get('error'), params)
|
||||||
|
|
||||||
|
|
|
@ -1,16 +1,13 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
"""
|
"""
|
||||||
oauthlib.oauth2.rfc6749.request_validator
|
oauthlib.oauth2.rfc6749.request_validator
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
"""
|
"""
|
||||||
from __future__ import absolute_import, unicode_literals
|
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class RequestValidator(object):
|
class RequestValidator:
|
||||||
|
|
||||||
def client_authentication_required(self, request, *args, **kwargs):
|
def client_authentication_required(self, request, *args, **kwargs):
|
||||||
"""Determine if client authentication is required for current request.
|
"""Determine if client authentication is required for current request.
|
||||||
|
@ -52,6 +49,17 @@ class RequestValidator(object):
|
||||||
both body and query can be obtained by direct attribute access, i.e.
|
both body and query can be obtained by direct attribute access, i.e.
|
||||||
request.client_id for client_id in the URL query.
|
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.
|
:param request: OAuthlib request.
|
||||||
:type request: oauthlib.common.Request
|
:type request: oauthlib.common.Request
|
||||||
:rtype: True or False
|
:rtype: True or False
|
||||||
|
@ -183,6 +191,7 @@ class RequestValidator(object):
|
||||||
claims associated, or `None` in case the token is unknown.
|
claims associated, or `None` in case the token is unknown.
|
||||||
|
|
||||||
Below the list of registered claims you should be interested in:
|
Below the list of registered claims you should be interested in:
|
||||||
|
|
||||||
- scope : space-separated list of scopes
|
- scope : space-separated list of scopes
|
||||||
- client_id : client identifier
|
- client_id : client identifier
|
||||||
- username : human-readable identifier for the resource owner
|
- username : human-readable identifier for the resource owner
|
||||||
|
@ -196,10 +205,10 @@ class RequestValidator(object):
|
||||||
- jti : string identifier for the token
|
- jti : string identifier for the token
|
||||||
|
|
||||||
Note that most of them are coming directly from JWT RFC. More details
|
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
|
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.
|
The dict of claims is added to request.token after this method.
|
||||||
|
|
||||||
|
@ -435,6 +444,7 @@ class RequestValidator(object):
|
||||||
- request.user
|
- request.user
|
||||||
- request.scopes
|
- request.scopes
|
||||||
- request.claims (if given)
|
- request.claims (if given)
|
||||||
|
|
||||||
OBS! The request.user attribute should be set to the resource owner
|
OBS! The request.user attribute should be set to the resource owner
|
||||||
associated with this authorization code. Similarly request.scopes
|
associated with this authorization code. Similarly request.scopes
|
||||||
must also be set.
|
must also be set.
|
||||||
|
@ -443,6 +453,7 @@ class RequestValidator(object):
|
||||||
|
|
||||||
If PKCE is enabled (see 'is_pkce_required' and 'save_authorization_code')
|
If PKCE is enabled (see 'is_pkce_required' and 'save_authorization_code')
|
||||||
you MUST set the following based on the information stored:
|
you MUST set the following based on the information stored:
|
||||||
|
|
||||||
- request.code_challenge
|
- request.code_challenge
|
||||||
- request.code_challenge_method
|
- request.code_challenge_method
|
||||||
|
|
||||||
|
@ -553,7 +564,7 @@ class RequestValidator(object):
|
||||||
OBS! The validation should also set the user attribute of the request
|
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
|
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
|
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 username: Unicode username.
|
||||||
:param password: Unicode password.
|
:param password: Unicode password.
|
||||||
|
@ -641,3 +652,29 @@ class RequestValidator(object):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError('Subclasses must implement this method.')
|
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
|
||||||
|
|
|
@ -7,28 +7,22 @@ This module contains methods for adding two types of access tokens to requests.
|
||||||
- Bearer https://tools.ietf.org/html/rfc6750
|
- Bearer https://tools.ietf.org/html/rfc6750
|
||||||
- MAC https://tools.ietf.org/html/draft-ietf-oauth-v2-http-mac-01
|
- MAC https://tools.ietf.org/html/draft-ietf-oauth-v2-http-mac-01
|
||||||
"""
|
"""
|
||||||
from __future__ import absolute_import, unicode_literals
|
|
||||||
|
|
||||||
import hashlib
|
import hashlib
|
||||||
import hmac
|
import hmac
|
||||||
from binascii import b2a_base64
|
|
||||||
import warnings
|
import warnings
|
||||||
|
from binascii import b2a_base64
|
||||||
|
from urllib.parse import urlparse
|
||||||
|
|
||||||
from oauthlib import common
|
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
|
from . import utils
|
||||||
|
|
||||||
try:
|
|
||||||
from urlparse import urlparse
|
|
||||||
except ImportError:
|
|
||||||
from urllib.parse import urlparse
|
|
||||||
|
|
||||||
|
|
||||||
class OAuth2Token(dict):
|
class OAuth2Token(dict):
|
||||||
|
|
||||||
def __init__(self, params, old_scope=None):
|
def __init__(self, params, old_scope=None):
|
||||||
super(OAuth2Token, self).__init__(params)
|
super().__init__(params)
|
||||||
self._new_scope = None
|
self._new_scope = None
|
||||||
if 'scope' in params and params['scope']:
|
if 'scope' in params and params['scope']:
|
||||||
self._new_scope = set(utils.scope_to_list(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')
|
raise ValueError('unknown hash algorithm')
|
||||||
|
|
||||||
if draft == 0:
|
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())
|
common.generate_nonce())
|
||||||
else:
|
else:
|
||||||
ts = common.generate_timestamp()
|
ts = common.generate_timestamp()
|
||||||
|
@ -158,7 +152,7 @@ def prepare_mac_header(token, uri, key, http_method,
|
||||||
base_string = '\n'.join(base) + '\n'
|
base_string = '\n'.join(base) + '\n'
|
||||||
|
|
||||||
# hmac struggles with unicode strings - http://bugs.python.org/issue5285
|
# hmac struggles with unicode strings - http://bugs.python.org/issue5285
|
||||||
if isinstance(key, unicode_type):
|
if isinstance(key, str):
|
||||||
key = key.encode('utf-8')
|
key = key.encode('utf-8')
|
||||||
sign = hmac.new(key, base_string.encode('utf-8'), h)
|
sign = hmac.new(key, base_string.encode('utf-8'), h)
|
||||||
sign = b2a_base64(sign.digest())[:-1].decode('utf-8')
|
sign = b2a_base64(sign.digest())[:-1].decode('utf-8')
|
||||||
|
@ -262,7 +256,8 @@ def get_token_from_header(request):
|
||||||
return token
|
return token
|
||||||
|
|
||||||
|
|
||||||
class TokenBase(object):
|
class TokenBase:
|
||||||
|
__slots__ = ()
|
||||||
|
|
||||||
def __call__(self, request, refresh_token=False):
|
def __call__(self, request, refresh_token=False):
|
||||||
raise NotImplementedError('Subclasses must implement this method.')
|
raise NotImplementedError('Subclasses must implement this method.')
|
||||||
|
|
|
@ -1,33 +1,22 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
"""
|
"""
|
||||||
oauthlib.utils
|
oauthlib.utils
|
||||||
~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~
|
||||||
|
|
||||||
This module contains utility methods used by various parts of the OAuth 2 spec.
|
This module contains utility methods used by various parts of the OAuth 2 spec.
|
||||||
"""
|
"""
|
||||||
from __future__ import absolute_import, unicode_literals
|
|
||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
import os
|
import os
|
||||||
|
from urllib.parse import quote, urlparse
|
||||||
|
|
||||||
from oauthlib.common import unicode_type, urldecode
|
from oauthlib.common import 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
|
|
||||||
|
|
||||||
|
|
||||||
def list_to_scope(scope):
|
def list_to_scope(scope):
|
||||||
"""Convert a list of scopes to a space separated string."""
|
"""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
|
return scope
|
||||||
elif isinstance(scope, (set, tuple, list)):
|
elif isinstance(scope, (set, tuple, list)):
|
||||||
return " ".join([unicode_type(s) for s in scope])
|
return " ".join([str(s) for s in scope])
|
||||||
else:
|
else:
|
||||||
raise ValueError("Invalid scope (%s), must be string, tuple, set, or list." % scope)
|
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):
|
def scope_to_list(scope):
|
||||||
"""Convert a space separated string to a list of scopes."""
|
"""Convert a space separated string to a list of scopes."""
|
||||||
if isinstance(scope, (tuple, list, set)):
|
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:
|
elif scope is None:
|
||||||
return None
|
return None
|
||||||
else:
|
else:
|
||||||
|
@ -74,7 +63,7 @@ def escape(u):
|
||||||
TODO: verify whether this can in fact be used for OAuth 2
|
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.')
|
raise ValueError('Only unicode objects are escapable.')
|
||||||
return quote(u.encode('utf-8'), safe=b'~')
|
return quote(u.encode('utf-8'), safe=b'~')
|
||||||
|
|
||||||
|
@ -84,7 +73,7 @@ def generate_age(issue_time):
|
||||||
td = datetime.datetime.now() - issue_time
|
td = datetime.datetime.now() - issue_time
|
||||||
age = (td.microseconds + (td.seconds + td.days * 24 * 3600)
|
age = (td.microseconds + (td.seconds + td.days * 24 * 3600)
|
||||||
* 10 ** 6) / 10 ** 6
|
* 10 ** 6) / 10 ** 6
|
||||||
return unicode_type(age)
|
return str(age)
|
||||||
|
|
||||||
|
|
||||||
def is_secure_transport(uri):
|
def is_secure_transport(uri):
|
||||||
|
|
|
@ -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__)
|
|
@ -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
|
|
@ -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)
|
|
@ -1,11 +1,7 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
"""
|
"""
|
||||||
oauthlib.openid
|
oauthlib.openid
|
||||||
~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from __future__ import absolute_import, unicode_literals
|
from .connect.core.endpoints import Server, UserInfoEndpoint
|
||||||
|
|
||||||
from .connect.core.endpoints import Server
|
|
||||||
from .connect.core.endpoints import UserInfoEndpoint
|
|
||||||
from .connect.core.request_validator import RequestValidator
|
from .connect.core.request_validator import RequestValidator
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
"""
|
"""
|
||||||
oauthlib.oopenid.core
|
oauthlib.oopenid.core
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
@ -6,7 +5,5 @@ oauthlib.oopenid.core
|
||||||
This module is an implementation of various logic needed
|
This module is an implementation of various logic needed
|
||||||
for consuming and providing OpenID Connect
|
for consuming and providing OpenID Connect
|
||||||
"""
|
"""
|
||||||
from __future__ import absolute_import, unicode_literals
|
|
||||||
|
|
||||||
from .pre_configured import Server
|
from .pre_configured import Server
|
||||||
from .userinfo import UserInfoEndpoint
|
from .userinfo import UserInfoEndpoint
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
"""
|
"""
|
||||||
oauthlib.openid.connect.core.endpoints.pre_configured
|
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
|
This module is an implementation of various endpoints needed
|
||||||
for providing OpenID Connect servers.
|
for providing OpenID Connect servers.
|
||||||
"""
|
"""
|
||||||
from __future__ import absolute_import, unicode_literals
|
|
||||||
|
|
||||||
from oauthlib.oauth2.rfc6749.endpoints import (
|
from oauthlib.oauth2.rfc6749.endpoints import (
|
||||||
AuthorizationEndpoint,
|
AuthorizationEndpoint, IntrospectEndpoint, ResourceEndpoint,
|
||||||
IntrospectEndpoint,
|
RevocationEndpoint, TokenEndpoint,
|
||||||
ResourceEndpoint,
|
|
||||||
RevocationEndpoint,
|
|
||||||
TokenEndpoint
|
|
||||||
)
|
)
|
||||||
from oauthlib.oauth2.rfc6749.grant_types import (
|
from oauthlib.oauth2.rfc6749.grant_types import (
|
||||||
AuthorizationCodeGrant as OAuth2AuthorizationCodeGrant,
|
AuthorizationCodeGrant as OAuth2AuthorizationCodeGrant,
|
||||||
ImplicitGrant as OAuth2ImplicitGrant,
|
ClientCredentialsGrant, ImplicitGrant as OAuth2ImplicitGrant,
|
||||||
ClientCredentialsGrant,
|
RefreshTokenGrant, ResourceOwnerPasswordCredentialsGrant,
|
||||||
RefreshTokenGrant,
|
|
||||||
ResourceOwnerPasswordCredentialsGrant
|
|
||||||
)
|
)
|
||||||
from oauthlib.oauth2.rfc6749.tokens import BearerToken
|
from oauthlib.oauth2.rfc6749.tokens import BearerToken
|
||||||
from ..grant_types import (
|
|
||||||
AuthorizationCodeGrant,
|
from ..grant_types import AuthorizationCodeGrant, HybridGrant, ImplicitGrant
|
||||||
ImplicitGrant,
|
|
||||||
HybridGrant,
|
|
||||||
)
|
|
||||||
from ..grant_types.dispatchers import (
|
from ..grant_types.dispatchers import (
|
||||||
AuthorizationCodeGrantDispatcher,
|
AuthorizationCodeGrantDispatcher, AuthorizationTokenGrantDispatcher,
|
||||||
ImplicitTokenGrantDispatcher,
|
ImplicitTokenGrantDispatcher,
|
||||||
AuthorizationTokenGrantDispatcher
|
|
||||||
)
|
)
|
||||||
from ..tokens import JWTToken
|
from ..tokens import JWTToken
|
||||||
from .userinfo import UserInfoEndpoint
|
from .userinfo import UserInfoEndpoint
|
||||||
|
@ -58,52 +46,52 @@ class Server(AuthorizationEndpoint, IntrospectEndpoint, TokenEndpoint,
|
||||||
:param kwargs: Extra parameters to pass to authorization-,
|
:param kwargs: Extra parameters to pass to authorization-,
|
||||||
token-, resource-, and revocation-endpoint constructors.
|
token-, resource-, and revocation-endpoint constructors.
|
||||||
"""
|
"""
|
||||||
auth_grant = OAuth2AuthorizationCodeGrant(request_validator)
|
self.auth_grant = OAuth2AuthorizationCodeGrant(request_validator)
|
||||||
implicit_grant = OAuth2ImplicitGrant(request_validator)
|
self.implicit_grant = OAuth2ImplicitGrant(request_validator)
|
||||||
password_grant = ResourceOwnerPasswordCredentialsGrant(
|
self.password_grant = ResourceOwnerPasswordCredentialsGrant(
|
||||||
request_validator)
|
request_validator)
|
||||||
credentials_grant = ClientCredentialsGrant(request_validator)
|
self.credentials_grant = ClientCredentialsGrant(request_validator)
|
||||||
refresh_grant = RefreshTokenGrant(request_validator)
|
self.refresh_grant = RefreshTokenGrant(request_validator)
|
||||||
openid_connect_auth = AuthorizationCodeGrant(request_validator)
|
self.openid_connect_auth = AuthorizationCodeGrant(request_validator)
|
||||||
openid_connect_implicit = ImplicitGrant(request_validator)
|
self.openid_connect_implicit = ImplicitGrant(request_validator)
|
||||||
openid_connect_hybrid = HybridGrant(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)
|
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)
|
token_expires_in, refresh_token_generator)
|
||||||
|
|
||||||
auth_grant_choice = AuthorizationCodeGrantDispatcher(default_grant=auth_grant, oidc_grant=openid_connect_auth)
|
self.auth_grant_choice = AuthorizationCodeGrantDispatcher(default_grant=self.auth_grant, oidc_grant=self.openid_connect_auth)
|
||||||
implicit_grant_choice = ImplicitTokenGrantDispatcher(default_grant=implicit_grant, oidc_grant=openid_connect_implicit)
|
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
|
# 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
|
# internally our AuthorizationEndpoint will ensure they can appear in any order for any valid combination
|
||||||
AuthorizationEndpoint.__init__(self, default_response_type='code',
|
AuthorizationEndpoint.__init__(self, default_response_type='code',
|
||||||
response_types={
|
response_types={
|
||||||
'code': auth_grant_choice,
|
'code': self.auth_grant_choice,
|
||||||
'token': implicit_grant_choice,
|
'token': self.implicit_grant_choice,
|
||||||
'id_token': openid_connect_implicit,
|
'id_token': self.openid_connect_implicit,
|
||||||
'id_token token': openid_connect_implicit,
|
'id_token token': self.openid_connect_implicit,
|
||||||
'code token': openid_connect_hybrid,
|
'code token': self.openid_connect_hybrid,
|
||||||
'code id_token': openid_connect_hybrid,
|
'code id_token': self.openid_connect_hybrid,
|
||||||
'code id_token token': openid_connect_hybrid,
|
'code id_token token': self.openid_connect_hybrid,
|
||||||
'none': auth_grant
|
'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',
|
TokenEndpoint.__init__(self, default_grant_type='authorization_code',
|
||||||
grant_types={
|
grant_types={
|
||||||
'authorization_code': token_grant_choice,
|
'authorization_code': self.token_grant_choice,
|
||||||
'password': password_grant,
|
'password': self.password_grant,
|
||||||
'client_credentials': credentials_grant,
|
'client_credentials': self.credentials_grant,
|
||||||
'refresh_token': refresh_grant,
|
'refresh_token': self.refresh_grant,
|
||||||
},
|
},
|
||||||
default_token_type=bearer)
|
default_token_type=self.bearer)
|
||||||
ResourceEndpoint.__init__(self, default_token='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)
|
RevocationEndpoint.__init__(self, request_validator)
|
||||||
IntrospectEndpoint.__init__(self, request_validator)
|
IntrospectEndpoint.__init__(self, request_validator)
|
||||||
UserInfoEndpoint.__init__(self, request_validator)
|
UserInfoEndpoint.__init__(self, request_validator)
|
||||||
|
|
|
@ -4,18 +4,15 @@ oauthlib.openid.connect.core.endpoints.userinfo
|
||||||
|
|
||||||
This module is an implementation of userinfo endpoint.
|
This module is an implementation of userinfo endpoint.
|
||||||
"""
|
"""
|
||||||
from __future__ import absolute_import, unicode_literals
|
|
||||||
|
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from oauthlib.common import Request
|
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 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__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -55,7 +52,7 @@ class UserInfoEndpoint(BaseEndpoint):
|
||||||
log.error('Userinfo MUST have "sub" for %r.', request)
|
log.error('Userinfo MUST have "sub" for %r.', request)
|
||||||
raise errors.ServerError(status_code=500)
|
raise errors.ServerError(status_code=500)
|
||||||
body = json.dumps(claims)
|
body = json.dumps(claims)
|
||||||
elif isinstance(claims, unicode_type):
|
elif isinstance(claims, str):
|
||||||
resp_headers = {
|
resp_headers = {
|
||||||
'Content-Type': 'application/jwt'
|
'Content-Type': 'application/jwt'
|
||||||
}
|
}
|
||||||
|
@ -72,7 +69,7 @@ class UserInfoEndpoint(BaseEndpoint):
|
||||||
5.3.1. UserInfo Request
|
5.3.1. UserInfo Request
|
||||||
The Client sends the UserInfo Request using either HTTP GET or HTTP
|
The Client sends the UserInfo Request using either HTTP GET or HTTP
|
||||||
POST. The Access Token obtained from an OpenID Connect Authentication
|
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].
|
Bearer Token Usage [RFC6750].
|
||||||
|
|
||||||
It is RECOMMENDED that the request use the HTTP GET method and the
|
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:
|
The following is a non-normative example of a UserInfo Request:
|
||||||
|
|
||||||
|
.. code-block:: http
|
||||||
|
|
||||||
GET /userinfo HTTP/1.1
|
GET /userinfo HTTP/1.1
|
||||||
Host: server.example.com
|
Host: server.example.com
|
||||||
Authorization: Bearer SlAV32hkKG
|
Authorization: Bearer SlAV32hkKG
|
||||||
|
|
||||||
5.3.3. UserInfo Error Response
|
5.3.3. UserInfo Error Response
|
||||||
When an error condition occurs, the UserInfo Endpoint returns an Error
|
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
|
[RFC6750]. (HTTP errors unrelated to RFC 6750 are returned to the User
|
||||||
Agent using the appropriate HTTP status code.)
|
Agent using the appropriate HTTP status code.)
|
||||||
|
|
||||||
The following is a non-normative example of a UserInfo Error Response:
|
The following is a non-normative example of a UserInfo Error Response:
|
||||||
|
|
||||||
|
.. code-block:: http
|
||||||
|
|
||||||
HTTP/1.1 401 Unauthorized
|
HTTP/1.1 401 Unauthorized
|
||||||
WWW-Authenticate: Bearer error="invalid_token",
|
WWW-Authenticate: Bearer error="invalid_token",
|
||||||
error_description="The Access Token expired"
|
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):
|
if not self.bearer.validate_request(request):
|
||||||
raise errors.InvalidTokenError()
|
raise errors.InvalidTokenError()
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
# coding=utf-8
|
|
||||||
"""
|
"""
|
||||||
oauthlib.oauth2.rfc6749.errors
|
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
|
Error used both by OAuth 2 clients and providers to represent the spec
|
||||||
defined error responses for all four core grant types.
|
defined error responses for all four core grant types.
|
||||||
"""
|
"""
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
from oauthlib.oauth2.rfc6749.errors import FatalClientError, OAuth2Error
|
from oauthlib.oauth2.rfc6749.errors import FatalClientError, OAuth2Error
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,17 +1,13 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
"""
|
"""
|
||||||
oauthlib.openid.connect.core.grant_types
|
oauthlib.openid.connect.core.grant_types
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
"""
|
"""
|
||||||
from __future__ import unicode_literals, absolute_import
|
|
||||||
|
|
||||||
from .authorization_code import AuthorizationCodeGrant
|
from .authorization_code import AuthorizationCodeGrant
|
||||||
from .implicit import ImplicitGrant
|
|
||||||
from .base import GrantTypeBase
|
from .base import GrantTypeBase
|
||||||
from .hybrid import HybridGrant
|
|
||||||
from .exceptions import OIDCNoPrompt
|
|
||||||
from .dispatchers import (
|
from .dispatchers import (
|
||||||
AuthorizationCodeGrantDispatcher,
|
AuthorizationCodeGrantDispatcher, AuthorizationTokenGrantDispatcher,
|
||||||
ImplicitTokenGrantDispatcher,
|
ImplicitTokenGrantDispatcher,
|
||||||
AuthorizationTokenGrantDispatcher
|
|
||||||
)
|
)
|
||||||
|
from .hybrid import HybridGrant
|
||||||
|
from .implicit import ImplicitGrant
|
||||||
|
from .refresh_token import RefreshTokenGrant
|
||||||
|
|
|
@ -1,13 +1,12 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
"""
|
"""
|
||||||
oauthlib.openid.connect.core.grant_types
|
oauthlib.openid.connect.core.grant_types
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
"""
|
"""
|
||||||
from __future__ import absolute_import, unicode_literals
|
|
||||||
|
|
||||||
import logging
|
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
|
from .base import GrantTypeBase
|
||||||
|
|
||||||
|
@ -41,4 +40,4 @@ class AuthorizationCodeGrant(GrantTypeBase):
|
||||||
request.redirect_uri,
|
request.redirect_uri,
|
||||||
request
|
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)
|
||||||
|
|
|
@ -1,17 +1,17 @@
|
||||||
from .exceptions import OIDCNoPrompt
|
|
||||||
|
|
||||||
import base64
|
import base64
|
||||||
import hashlib
|
import hashlib
|
||||||
import logging
|
import logging
|
||||||
import time
|
import time
|
||||||
from json import loads
|
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__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class GrantTypeBase(object):
|
class GrantTypeBase:
|
||||||
|
|
||||||
# Just proxy the majority of method calls through to the
|
# Just proxy the majority of method calls through to the
|
||||||
# proxy_target grant type handler, which will usually be either
|
# proxy_target grant type handler, which will usually be either
|
||||||
|
@ -20,7 +20,7 @@ class GrantTypeBase(object):
|
||||||
return getattr(self.proxy_target, attr)
|
return getattr(self.proxy_target, attr)
|
||||||
|
|
||||||
def __setattr__(self, attr, value):
|
def __setattr__(self, attr, value):
|
||||||
proxied_attrs = set(('refresh_token', 'response_types'))
|
proxied_attrs = {'refresh_token', 'response_types'}
|
||||||
if attr in proxied_attrs:
|
if attr in proxied_attrs:
|
||||||
setattr(self.proxy_target, attr, value)
|
setattr(self.proxy_target, attr, value)
|
||||||
else:
|
else:
|
||||||
|
@ -31,12 +31,6 @@ class GrantTypeBase(object):
|
||||||
|
|
||||||
:returns: (list of scopes, dict of request info)
|
: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):
|
def _inflate_claims(self, request):
|
||||||
|
|
|
@ -1,16 +1,19 @@
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class Dispatcher(object):
|
class Dispatcher:
|
||||||
default_grant = None
|
default_grant = None
|
||||||
oidc_grant = None
|
oidc_grant = None
|
||||||
|
|
||||||
|
|
||||||
class AuthorizationCodeGrantDispatcher(Dispatcher):
|
class AuthorizationCodeGrantDispatcher(Dispatcher):
|
||||||
"""
|
"""
|
||||||
This is an adapter class that will route simple Authorization Code requests, those that have response_type=code and a scope
|
This is an adapter class that will route simple Authorization Code
|
||||||
including 'openid' to either the default_grant or the oidc_grant based on the scopes requested.
|
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):
|
def __init__(self, default_grant=None, oidc_grant=None):
|
||||||
self.default_grant = default_grant
|
self.default_grant = default_grant
|
||||||
|
@ -26,16 +29,20 @@ class AuthorizationCodeGrantDispatcher(Dispatcher):
|
||||||
return handler
|
return handler
|
||||||
|
|
||||||
def create_authorization_response(self, request, token_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)
|
return self._handler_for_request(request).create_authorization_response(request, token_handler)
|
||||||
|
|
||||||
def validate_authorization_request(self, request):
|
def validate_authorization_request(self, request):
|
||||||
|
"""Read scope and route to the designated handler."""
|
||||||
return self._handler_for_request(request).validate_authorization_request(request)
|
return self._handler_for_request(request).validate_authorization_request(request)
|
||||||
|
|
||||||
|
|
||||||
class ImplicitTokenGrantDispatcher(Dispatcher):
|
class ImplicitTokenGrantDispatcher(Dispatcher):
|
||||||
"""
|
"""
|
||||||
This is an adapter class that will route simple Authorization Code requests, those that have response_type=code and a scope
|
This is an adapter class that will route simple Authorization
|
||||||
including 'openid' to either the default_grant or the oidc_grant based on the scopes requested.
|
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):
|
def __init__(self, default_grant=None, oidc_grant=None):
|
||||||
self.default_grant = default_grant
|
self.default_grant = default_grant
|
||||||
|
@ -51,9 +58,11 @@ class ImplicitTokenGrantDispatcher(Dispatcher):
|
||||||
return handler
|
return handler
|
||||||
|
|
||||||
def create_authorization_response(self, request, token_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)
|
return self._handler_for_request(request).create_authorization_response(request, token_handler)
|
||||||
|
|
||||||
def validate_authorization_request(self, request):
|
def validate_authorization_request(self, request):
|
||||||
|
"""Read scope and route to the designated handler."""
|
||||||
return self._handler_for_request(request).validate_authorization_request(request)
|
return self._handler_for_request(request).validate_authorization_request(request)
|
||||||
|
|
||||||
|
|
||||||
|
@ -75,7 +84,7 @@ class AuthorizationTokenGrantDispatcher(Dispatcher):
|
||||||
code = parameters.get('code', None)
|
code = parameters.get('code', None)
|
||||||
redirect_uri = parameters.get('redirect_uri', 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.
|
# raise an error for the missing `code` in `create_token_response` step.
|
||||||
if code:
|
if code:
|
||||||
scopes = self.request_validator.get_authorization_code_scopes(client_id, code, redirect_uri, request)
|
scopes = self.request_validator.get_authorization_code_scopes(client_id, code, redirect_uri, request)
|
||||||
|
@ -87,5 +96,6 @@ class AuthorizationTokenGrantDispatcher(Dispatcher):
|
||||||
return handler
|
return handler
|
||||||
|
|
||||||
def create_token_response(self, request, token_handler):
|
def create_token_response(self, request, token_handler):
|
||||||
|
"""Read scope and route to the designated handler."""
|
||||||
handler = self._handler_for_request(request)
|
handler = self._handler_for_request(request)
|
||||||
return handler.create_token_response(request, token_handler)
|
return handler.create_token_response(request, token_handler)
|
||||||
|
|
|
@ -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)
|
|
|
@ -1,17 +1,16 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
"""
|
"""
|
||||||
oauthlib.openid.connect.core.grant_types
|
oauthlib.openid.connect.core.grant_types
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
"""
|
"""
|
||||||
from __future__ import absolute_import, unicode_literals
|
|
||||||
|
|
||||||
import logging
|
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.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 ..request_validator import RequestValidator
|
||||||
|
from .base import GrantTypeBase
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -36,10 +35,13 @@ class HybridGrant(GrantTypeBase):
|
||||||
self.register_code_modifier(self.add_id_token)
|
self.register_code_modifier(self.add_id_token)
|
||||||
self.register_token_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):
|
def openid_authorization_validator(self, request):
|
||||||
"""Additional validation when following the Authorization Code flow.
|
"""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
|
if not request_info: # returns immediately if OAuth2.0
|
||||||
return request_info
|
return request_info
|
||||||
|
|
||||||
|
|
|
@ -1,16 +1,15 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
"""
|
"""
|
||||||
oauthlib.openid.connect.core.grant_types
|
oauthlib.openid.connect.core.grant_types
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
"""
|
"""
|
||||||
from __future__ import absolute_import, unicode_literals
|
|
||||||
|
|
||||||
import logging
|
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.errors import InvalidRequestError
|
||||||
|
from oauthlib.oauth2.rfc6749.grant_types.implicit import (
|
||||||
|
ImplicitGrant as OAuth2ImplicitGrant,
|
||||||
|
)
|
||||||
|
|
||||||
|
from .base import GrantTypeBase
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -29,12 +28,12 @@ class ImplicitGrant(GrantTypeBase):
|
||||||
def add_id_token(self, token, token_handler, request):
|
def add_id_token(self, token, token_handler, request):
|
||||||
if 'state' not in token and request.state:
|
if 'state' not in token and request.state:
|
||||||
token['state'] = 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):
|
def openid_authorization_validator(self, request):
|
||||||
"""Additional validation when following the implicit flow.
|
"""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
|
if not request_info: # returns immediately if OAuth2.0
|
||||||
return request_info
|
return request_info
|
||||||
|
|
||||||
|
|
|
@ -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)
|
|
@ -1,13 +1,12 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
"""
|
"""
|
||||||
oauthlib.openid.connect.core.request_validator
|
oauthlib.openid.connect.core.request_validator
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
"""
|
"""
|
||||||
from __future__ import absolute_import, unicode_literals
|
|
||||||
|
|
||||||
import logging
|
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__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -307,3 +306,15 @@ class RequestValidator(OAuth2RequestValidator):
|
||||||
Method is used by:
|
Method is used by:
|
||||||
UserInfoEndpoint
|
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
|
||||||
|
|
|
@ -4,10 +4,9 @@ authlib.openid.connect.core.tokens
|
||||||
|
|
||||||
This module contains methods for adding JWT tokens to requests.
|
This module contains methods for adding JWT tokens to requests.
|
||||||
"""
|
"""
|
||||||
from __future__ import absolute_import, unicode_literals
|
from oauthlib.oauth2.rfc6749.tokens import (
|
||||||
|
TokenBase, get_token_from_header, random_token_generator,
|
||||||
|
)
|
||||||
from oauthlib.oauth2.rfc6749.tokens import TokenBase, random_token_generator
|
|
||||||
|
|
||||||
|
|
||||||
class JWTToken(TokenBase):
|
class JWTToken(TokenBase):
|
||||||
|
@ -38,17 +37,12 @@ class JWTToken(TokenBase):
|
||||||
return self.request_validator.get_jwt_bearer_token(None, None, request)
|
return self.request_validator.get_jwt_bearer_token(None, None, request)
|
||||||
|
|
||||||
def validate_request(self, request):
|
def validate_request(self, request):
|
||||||
token = None
|
token = get_token_from_header(request)
|
||||||
if 'Authorization' in request.headers:
|
|
||||||
token = request.headers.get('Authorization')[7:]
|
|
||||||
else:
|
|
||||||
token = request.access_token
|
|
||||||
return self.request_validator.validate_jwt_bearer_token(
|
return self.request_validator.validate_jwt_bearer_token(
|
||||||
token, request.scopes, request)
|
token, request.scopes, request)
|
||||||
|
|
||||||
def estimate_type(self, request):
|
def estimate_type(self, request):
|
||||||
token = request.headers.get('Authorization', '')[7:]
|
token = get_token_from_header(request)
|
||||||
if token.startswith('ey') and token.count('.') in (2, 4):
|
if token and token.startswith('ey') and token.count('.') in (2, 4):
|
||||||
return 10
|
return 10
|
||||||
else:
|
|
||||||
return 0
|
return 0
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
"""
|
"""
|
||||||
Implements signals based on blinker if available, otherwise
|
Implements signals based on blinker if available, otherwise
|
||||||
falls silently back to a noop. Shamelessly stolen from flask.signals:
|
falls silently back to a noop. Shamelessly stolen from flask.signals:
|
||||||
|
@ -9,11 +8,11 @@ try:
|
||||||
from blinker import Namespace
|
from blinker import Namespace
|
||||||
signals_available = True
|
signals_available = True
|
||||||
except ImportError: # noqa
|
except ImportError: # noqa
|
||||||
class Namespace(object):
|
class Namespace:
|
||||||
def signal(self, name, doc=None):
|
def signal(self, name, doc=None):
|
||||||
return _FakeSignal(name, doc)
|
return _FakeSignal(name, doc)
|
||||||
|
|
||||||
class _FakeSignal(object):
|
class _FakeSignal:
|
||||||
"""If blinker is unavailable, create a fake class with the same
|
"""If blinker is unavailable, create a fake class with the same
|
||||||
interface that allows sending of signals but will fail with an
|
interface that allows sending of signals but will fail with an
|
||||||
error on anything else. Instead of doing anything on send, it
|
error on anything else. Instead of doing anything on send, it
|
||||||
|
|
|
@ -8,8 +8,6 @@ They should be processed with re.VERBOSE.
|
||||||
|
|
||||||
Thanks Mark Nottingham for this code - https://gist.github.com/138549
|
Thanks Mark Nottingham for this code - https://gist.github.com/138549
|
||||||
"""
|
"""
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
import re
|
import re
|
||||||
|
|
||||||
# basics
|
# 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(
|
IPv4address = r"%(dec_octet)s \. %(dec_octet)s \. %(dec_octet)s \. %(dec_octet)s" % locals(
|
||||||
)
|
)
|
||||||
|
|
||||||
# h16 = 1*4HEXDIG
|
# IPv6address
|
||||||
h16 = r"(?: %(HEXDIG)s ){1,4}" % locals()
|
IPv6address = r"([A-Fa-f0-9:]+[:$])[A-Fa-f0-9]{1,4}"
|
||||||
|
|
||||||
# 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()
|
|
||||||
|
|
||||||
# IPvFuture = "v" 1*HEXDIG "." 1*( unreserved / sub-delims / ":" )
|
# IPvFuture = "v" 1*HEXDIG "." 1*( unreserved / sub-delims / ":" )
|
||||||
IPvFuture = r"v %(HEXDIG)s+ \. (?: %(unreserved)s | %(sub_delims)s | : )+" % locals()
|
IPvFuture = r"v %(HEXDIG)s+ \. (?: %(unreserved)s | %(sub_delims)s | : )+" % locals()
|
||||||
|
|
13
setup.cfg
13
setup.cfg
|
@ -1,9 +1,16 @@
|
||||||
[bdist_wheel]
|
|
||||||
universal = 1
|
|
||||||
|
|
||||||
[metadata]
|
[metadata]
|
||||||
license_file = LICENSE
|
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]
|
[egg_info]
|
||||||
tag_build =
|
tag_build =
|
||||||
tag_date = 0
|
tag_date = 0
|
||||||
|
|
20
setup.py
20
setup.py
|
@ -1,12 +1,9 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
# Hack because logging + setuptools sucks.
|
# Hack because logging + setuptools sucks.
|
||||||
try:
|
try:
|
||||||
import multiprocessing
|
import multiprocessing
|
||||||
except ImportError:
|
except ImportError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
import sys
|
|
||||||
from os.path import dirname, join
|
from os.path import dirname, join
|
||||||
|
|
||||||
from setuptools import find_packages, setup
|
from setuptools import find_packages, setup
|
||||||
|
@ -19,15 +16,16 @@ def fread(fn):
|
||||||
return f.read()
|
return f.read()
|
||||||
|
|
||||||
|
|
||||||
rsa_require = ['cryptography']
|
rsa_require = ['cryptography>=3.0.0']
|
||||||
signedtoken_require = ['cryptography', 'pyjwt>=1.0.0']
|
signedtoken_require = ['cryptography>=3.0.0', 'pyjwt>=2.0.0,<3']
|
||||||
signals_require = ['blinker']
|
signals_require = ['blinker>=1.4.0']
|
||||||
|
|
||||||
setup(
|
setup(
|
||||||
name='oauthlib',
|
name='oauthlib',
|
||||||
version=oauthlib.__version__,
|
version=oauthlib.__version__,
|
||||||
description='A generic, spec-compliant, thorough implementation of the OAuth request-signing logic',
|
description='A generic, spec-compliant, thorough implementation of the OAuth request-signing logic',
|
||||||
long_description=fread('README.rst'),
|
long_description=fread('README.rst'),
|
||||||
|
long_description_content_type='text/x-rst',
|
||||||
author='The OAuthlib Community',
|
author='The OAuthlib Community',
|
||||||
author_email='idan@gazit.me',
|
author_email='idan@gazit.me',
|
||||||
maintainer='Ib Lundgren',
|
maintainer='Ib Lundgren',
|
||||||
|
@ -36,7 +34,7 @@ setup(
|
||||||
platforms='any',
|
platforms='any',
|
||||||
license='BSD',
|
license='BSD',
|
||||||
packages=find_packages(exclude=('docs', 'tests', 'tests.*')),
|
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={
|
extras_require={
|
||||||
'rsa': rsa_require,
|
'rsa': rsa_require,
|
||||||
'signedtoken': signedtoken_require,
|
'signedtoken': signedtoken_require,
|
||||||
|
@ -52,13 +50,13 @@ setup(
|
||||||
'Operating System :: POSIX',
|
'Operating System :: POSIX',
|
||||||
'Operating System :: POSIX :: Linux',
|
'Operating System :: POSIX :: Linux',
|
||||||
'Programming Language :: Python',
|
'Programming Language :: Python',
|
||||||
'Programming Language :: Python :: 2',
|
|
||||||
'Programming Language :: Python :: 2.7',
|
|
||||||
'Programming Language :: Python :: 3',
|
'Programming Language :: Python :: 3',
|
||||||
'Programming Language :: Python :: 3.4',
|
|
||||||
'Programming Language :: Python :: 3.5',
|
|
||||||
'Programming Language :: Python :: 3.6',
|
'Programming Language :: Python :: 3.6',
|
||||||
'Programming Language :: Python :: 3.7',
|
'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',
|
||||||
'Programming Language :: Python :: Implementation :: CPython',
|
'Programming Language :: Python :: Implementation :: CPython',
|
||||||
'Programming Language :: Python :: Implementation :: PyPy',
|
'Programming Language :: Python :: Implementation :: PyPy',
|
||||||
|
|
|
@ -1,12 +1,10 @@
|
||||||
from __future__ import absolute_import, unicode_literals
|
from unittest.mock import ANY, MagicMock
|
||||||
|
|
||||||
from mock import ANY, MagicMock
|
|
||||||
|
|
||||||
from oauthlib.oauth1 import RequestValidator
|
from oauthlib.oauth1 import RequestValidator
|
||||||
from oauthlib.oauth1.rfc5849 import Client
|
from oauthlib.oauth1.rfc5849 import Client
|
||||||
from oauthlib.oauth1.rfc5849.endpoints import AccessTokenEndpoint
|
from oauthlib.oauth1.rfc5849.endpoints import AccessTokenEndpoint
|
||||||
|
|
||||||
from ....unittest import TestCase
|
from tests.unittest import TestCase
|
||||||
|
|
||||||
|
|
||||||
class AccessTokenEndpointTest(TestCase):
|
class AccessTokenEndpointTest(TestCase):
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
from __future__ import absolute_import, unicode_literals
|
from unittest.mock import MagicMock
|
||||||
|
|
||||||
from mock import MagicMock
|
|
||||||
|
|
||||||
from oauthlib.oauth1 import RequestValidator
|
from oauthlib.oauth1 import RequestValidator
|
||||||
from oauthlib.oauth1.rfc5849 import errors
|
from oauthlib.oauth1.rfc5849 import errors
|
||||||
|
|
|
@ -1,17 +1,16 @@
|
||||||
from __future__ import absolute_import, unicode_literals
|
|
||||||
|
|
||||||
from re import sub
|
from re import sub
|
||||||
|
from unittest.mock import MagicMock
|
||||||
from mock import MagicMock
|
|
||||||
|
|
||||||
from oauthlib.common import CaseInsensitiveDict, safe_string_equals
|
from oauthlib.common import CaseInsensitiveDict, safe_string_equals
|
||||||
from oauthlib.oauth1 import Client, RequestValidator
|
from oauthlib.oauth1 import Client, RequestValidator
|
||||||
from oauthlib.oauth1.rfc5849 import (SIGNATURE_HMAC, SIGNATURE_PLAINTEXT,
|
from oauthlib.oauth1.rfc5849 import (
|
||||||
SIGNATURE_RSA, errors)
|
SIGNATURE_HMAC, SIGNATURE_PLAINTEXT, SIGNATURE_RSA, errors,
|
||||||
from oauthlib.oauth1.rfc5849.endpoints import (BaseEndpoint,
|
)
|
||||||
RequestTokenEndpoint)
|
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"}
|
URLENCODED = {"Content-Type": "application/x-www-form-urlencoded"}
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,10 @@
|
||||||
from __future__ import absolute_import, unicode_literals
|
from unittest.mock import ANY, MagicMock
|
||||||
|
|
||||||
from mock import ANY, MagicMock
|
|
||||||
|
|
||||||
from oauthlib.oauth1 import RequestValidator
|
from oauthlib.oauth1 import RequestValidator
|
||||||
from oauthlib.oauth1.rfc5849 import Client
|
from oauthlib.oauth1.rfc5849 import Client
|
||||||
from oauthlib.oauth1.rfc5849.endpoints import RequestTokenEndpoint
|
from oauthlib.oauth1.rfc5849.endpoints import RequestTokenEndpoint
|
||||||
|
|
||||||
from ....unittest import TestCase
|
from tests.unittest import TestCase
|
||||||
|
|
||||||
|
|
||||||
class RequestTokenEndpointTest(TestCase):
|
class RequestTokenEndpointTest(TestCase):
|
||||||
|
|
|
@ -1,12 +1,10 @@
|
||||||
from __future__ import absolute_import, unicode_literals
|
from unittest.mock import ANY, MagicMock
|
||||||
|
|
||||||
from mock import ANY, MagicMock
|
|
||||||
|
|
||||||
from oauthlib.oauth1 import RequestValidator
|
from oauthlib.oauth1 import RequestValidator
|
||||||
from oauthlib.oauth1.rfc5849 import Client
|
from oauthlib.oauth1.rfc5849 import Client
|
||||||
from oauthlib.oauth1.rfc5849.endpoints import ResourceEndpoint
|
from oauthlib.oauth1.rfc5849.endpoints import ResourceEndpoint
|
||||||
|
|
||||||
from ....unittest import TestCase
|
from tests.unittest import TestCase
|
||||||
|
|
||||||
|
|
||||||
class ResourceEndpointTest(TestCase):
|
class ResourceEndpointTest(TestCase):
|
||||||
|
|
|
@ -1,12 +1,10 @@
|
||||||
from __future__ import absolute_import, unicode_literals
|
from unittest.mock import ANY, MagicMock
|
||||||
|
|
||||||
from mock import ANY, MagicMock
|
|
||||||
|
|
||||||
from oauthlib.oauth1 import RequestValidator
|
from oauthlib.oauth1 import RequestValidator
|
||||||
from oauthlib.oauth1.rfc5849 import Client
|
from oauthlib.oauth1.rfc5849 import Client
|
||||||
from oauthlib.oauth1.rfc5849.endpoints import SignatureOnlyEndpoint
|
from oauthlib.oauth1.rfc5849.endpoints import SignatureOnlyEndpoint
|
||||||
|
|
||||||
from ....unittest import TestCase
|
from tests.unittest import TestCase
|
||||||
|
|
||||||
|
|
||||||
class SignatureOnlyEndpointTest(TestCase):
|
class SignatureOnlyEndpointTest(TestCase):
|
||||||
|
|
|
@ -1,13 +1,12 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from __future__ import absolute_import, unicode_literals
|
|
||||||
|
|
||||||
from oauthlib.common import Request
|
from oauthlib.common import Request
|
||||||
from oauthlib.oauth1 import (SIGNATURE_PLAINTEXT, SIGNATURE_HMAC_SHA1,
|
from oauthlib.oauth1 import (
|
||||||
SIGNATURE_HMAC_SHA256, SIGNATURE_RSA,
|
SIGNATURE_HMAC_SHA1, SIGNATURE_HMAC_SHA256, SIGNATURE_PLAINTEXT,
|
||||||
SIGNATURE_TYPE_BODY, SIGNATURE_TYPE_QUERY)
|
SIGNATURE_RSA, SIGNATURE_TYPE_BODY, SIGNATURE_TYPE_QUERY,
|
||||||
|
)
|
||||||
from oauthlib.oauth1.rfc5849 import Client
|
from oauthlib.oauth1.rfc5849 import Client
|
||||||
|
|
||||||
from ...unittest import TestCase
|
from tests.unittest import TestCase
|
||||||
|
|
||||||
|
|
||||||
class ClientRealmTests(TestCase):
|
class ClientRealmTests(TestCase):
|
||||||
|
@ -163,7 +162,7 @@ class SignatureMethodTest(TestCase):
|
||||||
Client.register_signature_method('PIZZA',
|
Client.register_signature_method('PIZZA',
|
||||||
lambda base_string, client: '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',
|
client = Client('client_key', signature_method='PIZZA',
|
||||||
timestamp='1234567890', nonce='abc')
|
timestamp='1234567890', nonce='abc')
|
||||||
|
|
|
@ -1,13 +1,11 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from __future__ import absolute_import, unicode_literals
|
|
||||||
|
|
||||||
from oauthlib.common import urlencode
|
from oauthlib.common import urlencode
|
||||||
from oauthlib.oauth1.rfc5849.parameters import (_append_params,
|
from oauthlib.oauth1.rfc5849.parameters import (
|
||||||
prepare_form_encoded_body,
|
_append_params, prepare_form_encoded_body, prepare_headers,
|
||||||
prepare_headers,
|
prepare_request_uri_query,
|
||||||
prepare_request_uri_query)
|
)
|
||||||
|
|
||||||
from ...unittest import TestCase
|
from tests.unittest import TestCase
|
||||||
|
|
||||||
|
|
||||||
class ParameterTests(TestCase):
|
class ParameterTests(TestCase):
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from __future__ import absolute_import, unicode_literals
|
|
||||||
|
|
||||||
from oauthlib.oauth1 import RequestValidator
|
from oauthlib.oauth1 import RequestValidator
|
||||||
|
|
||||||
from ...unittest import TestCase
|
from tests.unittest import TestCase
|
||||||
|
|
||||||
|
|
||||||
class RequestValidatorTests(TestCase):
|
class RequestValidatorTests(TestCase):
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,10 +1,7 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from __future__ import absolute_import, unicode_literals
|
|
||||||
|
|
||||||
from oauthlib.common import unicode_type
|
|
||||||
from oauthlib.oauth1.rfc5849.utils import *
|
from oauthlib.oauth1.rfc5849.utils import *
|
||||||
|
|
||||||
from ...unittest import TestCase
|
from tests.unittest import TestCase
|
||||||
|
|
||||||
|
|
||||||
class UtilsTests(TestCase):
|
class UtilsTests(TestCase):
|
||||||
|
@ -102,12 +99,12 @@ class UtilsTests(TestCase):
|
||||||
def test_escape(self):
|
def test_escape(self):
|
||||||
self.assertRaises(ValueError, escape, b"I am a string type. Not a unicode type.")
|
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.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):
|
def test_unescape(self):
|
||||||
self.assertRaises(ValueError, unescape, b"I am a string type. Not a unicode type.")
|
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.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):
|
def test_parse_authorization_header(self):
|
||||||
# make us some headers
|
# make us some headers
|
||||||
|
@ -122,8 +119,8 @@ class UtilsTests(TestCase):
|
||||||
|
|
||||||
# are the internal components of each tuple unicode?
|
# are the internal components of each tuple unicode?
|
||||||
for k, v in authorization_headers:
|
for k, v in authorization_headers:
|
||||||
self.assertIsInstance(k, unicode_type)
|
self.assertIsInstance(k, str)
|
||||||
self.assertIsInstance(v, unicode_type)
|
self.assertIsInstance(v, str)
|
||||||
|
|
||||||
# let's check the parsed headers created
|
# let's check the parsed headers created
|
||||||
correct_headers = [
|
correct_headers = [
|
||||||
|
|
|
@ -1,14 +1,11 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from __future__ import absolute_import, unicode_literals
|
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
from unittest.mock import patch
|
||||||
from mock import patch
|
|
||||||
|
|
||||||
from oauthlib import signals
|
from oauthlib import signals
|
||||||
from oauthlib.oauth2 import BackendApplicationClient
|
from oauthlib.oauth2 import BackendApplicationClient
|
||||||
|
|
||||||
from ....unittest import TestCase
|
from tests.unittest import TestCase
|
||||||
|
|
||||||
|
|
||||||
@patch('time.time', new=lambda: 1000)
|
@patch('time.time', new=lambda: 1000)
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from __future__ import absolute_import, unicode_literals
|
|
||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
from oauthlib import common
|
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 import utils
|
||||||
from oauthlib.oauth2.rfc6749.clients import AUTH_HEADER, BODY, URI_QUERY
|
from oauthlib.oauth2.rfc6749.clients import AUTH_HEADER, BODY, URI_QUERY
|
||||||
|
|
||||||
from ....unittest import TestCase
|
from tests.unittest import TestCase
|
||||||
|
|
||||||
|
|
||||||
class ClientTest(TestCase):
|
class ClientTest(TestCase):
|
||||||
|
@ -295,11 +293,63 @@ class ClientTest(TestCase):
|
||||||
u, h, b = client.prepare_refresh_token_request(url, token, scope=scope)
|
u, h, b = client.prepare_refresh_token_request(url, token, scope=scope)
|
||||||
self.assertEqual(u, url)
|
self.assertEqual(u, url)
|
||||||
self.assertEqual(h, {'Content-Type': 'application/x-www-form-urlencoded'})
|
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
|
# provide scope while init
|
||||||
client = Client(self.client_id, scope=scope)
|
client = Client(self.client_id, scope=scope)
|
||||||
u, h, b = client.prepare_refresh_token_request(url, token, scope=scope)
|
u, h, b = client.prepare_refresh_token_request(url, token, scope=scope)
|
||||||
self.assertEqual(u, url)
|
self.assertEqual(u, url)
|
||||||
self.assertEqual(h, {'Content-Type': 'application/x-www-form-urlencoded'})
|
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)
|
||||||
|
|
|
@ -1,20 +1,12 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from __future__ import absolute_import, unicode_literals
|
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
import urllib.parse as urlparse
|
||||||
from mock import patch
|
from unittest.mock import patch
|
||||||
|
|
||||||
from oauthlib import signals
|
from oauthlib import signals
|
||||||
from oauthlib.oauth2 import LegacyApplicationClient
|
from oauthlib.oauth2 import LegacyApplicationClient
|
||||||
|
|
||||||
from ....unittest import TestCase
|
from tests.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
|
|
||||||
|
|
||||||
|
|
||||||
@patch('time.time', new=lambda: 1000)
|
@patch('time.time', new=lambda: 1000)
|
||||||
|
@ -32,7 +24,7 @@ class LegacyApplicationClientTest(TestCase):
|
||||||
password = "user_password"
|
password = "user_password"
|
||||||
body = "not=empty"
|
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"
|
body_kwargs = body_up + "&some=providers&require=extra+arguments"
|
||||||
|
|
||||||
token_json = ('{ "access_token":"2YotnFZFEjr1zCsicMWpAA",'
|
token_json = ('{ "access_token":"2YotnFZFEjr1zCsicMWpAA",'
|
||||||
|
@ -105,8 +97,8 @@ class LegacyApplicationClientTest(TestCase):
|
||||||
|
|
||||||
# scenario 1, default behavior to not include `client_id`
|
# scenario 1, default behavior to not include `client_id`
|
||||||
r1 = client.prepare_request_body(username=self.username, password=self.password)
|
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, ),
|
self.assertIn(r1, ('grant_type=password&username={}&password={}'.format(self.username, self.password),
|
||||||
'grant_type=password&password=%s&username=%s' % (self.password, self.username, ),
|
'grant_type=password&password={}&username={}'.format(self.password, self.username),
|
||||||
))
|
))
|
||||||
|
|
||||||
# scenario 2, include `client_id` in the body
|
# scenario 2, include `client_id` in the body
|
||||||
|
|
|
@ -1,14 +1,11 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from __future__ import absolute_import, unicode_literals
|
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
from unittest.mock import patch
|
||||||
from mock import patch
|
|
||||||
|
|
||||||
from oauthlib import signals
|
from oauthlib import signals
|
||||||
from oauthlib.oauth2 import MobileApplicationClient
|
from oauthlib.oauth2 import MobileApplicationClient
|
||||||
|
|
||||||
from ....unittest import TestCase
|
from tests.unittest import TestCase
|
||||||
|
|
||||||
|
|
||||||
@patch('time.time', new=lambda: 1000)
|
@patch('time.time', new=lambda: 1000)
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue