oauthlib 3.3.0__tar.gz → 4.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {oauthlib-3.3.0 → oauthlib-4.0.0}/CHANGELOG.rst +36 -2
- {oauthlib-3.3.0/oauthlib.egg-info → oauthlib-4.0.0}/PKG-INFO +13 -3
- {oauthlib-3.3.0 → oauthlib-4.0.0}/README.rst +3 -0
- {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/__init__.py +1 -1
- {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/common.py +1 -0
- {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/oauth1/rfc5849/endpoints/access_token.py +2 -2
- {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/oauth1/rfc5849/endpoints/base.py +1 -1
- {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/oauth1/rfc5849/endpoints/request_token.py +1 -1
- {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/oauth1/rfc5849/errors.py +1 -1
- {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/oauth2/__init__.py +3 -2
- {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/oauth2/rfc6749/clients/base.py +5 -22
- {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/oauth2/rfc6749/clients/service_application.py +1 -1
- {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/oauth2/rfc6749/endpoints/revocation.py +2 -9
- {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/oauth2/rfc6749/grant_types/authorization_code.py +8 -27
- {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/oauth2/rfc6749/grant_types/base.py +51 -0
- {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/oauth2/rfc6749/grant_types/client_credentials.py +2 -9
- {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/oauth2/rfc6749/grant_types/refresh_token.py +2 -16
- {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/oauth2/rfc6749/grant_types/resource_owner_password_credentials.py +2 -15
- {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/oauth2/rfc6749/parameters.py +12 -11
- {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/oauth2/rfc6749/request_validator.py +17 -6
- {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/oauth2/rfc6749/tokens.py +1 -1
- {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/oauth2/rfc6749/utils.py +1 -1
- {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/oauth2/rfc8628/grant_types/device_code.py +1 -15
- {oauthlib-3.3.0 → oauthlib-4.0.0/oauthlib.egg-info}/PKG-INFO +13 -3
- {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib.egg-info/SOURCES.txt +0 -3
- {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib.egg-info/top_level.txt +0 -1
- {oauthlib-3.3.0 → oauthlib-4.0.0}/setup.py +12 -4
- {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/oauth1/rfc5849/endpoints/test_access_token.py +9 -9
- {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/oauth1/rfc5849/endpoints/test_authorization.py +2 -2
- {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/oauth1/rfc5849/endpoints/test_base.py +7 -7
- {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/oauth1/rfc5849/endpoints/test_request_token.py +8 -8
- {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/oauth1/rfc5849/endpoints/test_resource.py +2 -2
- {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/oauth1/rfc5849/endpoints/test_signature_only.py +4 -4
- {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/oauth1/rfc5849/test_client.py +9 -9
- {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/oauth2/rfc6749/clients/test_base.py +2 -9
- {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/oauth2/rfc6749/clients/test_web_application.py +22 -1
- {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/oauth2/rfc6749/endpoints/test_base_endpoint.py +4 -4
- {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/oauth2/rfc6749/endpoints/test_credentials_preservation.py +1 -1
- {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/oauth2/rfc6749/endpoints/test_error_responses.py +7 -7
- {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/oauth2/rfc6749/endpoints/test_metadata.py +1 -1
- {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/oauth2/rfc6749/endpoints/test_revocation_endpoint.py +0 -13
- {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/oauth2/rfc6749/endpoints/test_scope_handling.py +2 -2
- {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/oauth2/rfc6749/grant_types/test_authorization_code.py +37 -7
- {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/oauth2/rfc6749/grant_types/test_client_credentials.py +2 -2
- {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/oauth2/rfc6749/grant_types/test_implicit.py +2 -2
- {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/oauth2/rfc6749/grant_types/test_refresh_token.py +17 -6
- {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/oauth2/rfc6749/grant_types/test_resource_owner_password.py +2 -2
- {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/oauth2/rfc6749/test_parameters.py +13 -0
- {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/oauth2/rfc6749/test_server.py +17 -17
- {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/oauth2/rfc8628/grant_types/test_device_code.py +32 -1
- {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/openid/connect/core/endpoints/test_claims_handling.py +5 -5
- {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/openid/connect/core/endpoints/test_openid_connect_params_handling.py +1 -1
- {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/openid/connect/core/endpoints/test_refresh_token.py +9 -2
- {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/openid/connect/core/grant_types/test_authorization_code.py +9 -9
- {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/openid/connect/core/grant_types/test_hybrid.py +1 -1
- {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/openid/connect/core/grant_types/test_implicit.py +5 -5
- {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/openid/connect/core/grant_types/test_refresh_token.py +3 -3
- {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/openid/connect/core/test_server.py +7 -7
- {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/test_common.py +16 -0
- oauthlib-3.3.0/examples/device_code_flow.py +0 -260
- oauthlib-3.3.0/examples/skeleton_oauth2_web_application_server.py +0 -114
- oauthlib-3.3.0/tests/openid/connect/core/grant_types/__init__.py +0 -0
- {oauthlib-3.3.0 → oauthlib-4.0.0}/LICENSE +0 -0
- {oauthlib-3.3.0 → oauthlib-4.0.0}/MANIFEST.in +0 -0
- {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/oauth1/__init__.py +0 -0
- {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/oauth1/rfc5849/__init__.py +0 -0
- {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/oauth1/rfc5849/endpoints/__init__.py +0 -0
- {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/oauth1/rfc5849/endpoints/authorization.py +0 -0
- {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/oauth1/rfc5849/endpoints/pre_configured.py +0 -0
- {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/oauth1/rfc5849/endpoints/resource.py +0 -0
- {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/oauth1/rfc5849/endpoints/signature_only.py +0 -0
- {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/oauth1/rfc5849/parameters.py +0 -0
- {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/oauth1/rfc5849/request_validator.py +0 -0
- {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/oauth1/rfc5849/signature.py +0 -0
- {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/oauth1/rfc5849/utils.py +0 -0
- {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/oauth2/rfc6749/__init__.py +0 -0
- {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/oauth2/rfc6749/clients/__init__.py +0 -0
- {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/oauth2/rfc6749/clients/backend_application.py +0 -0
- {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/oauth2/rfc6749/clients/legacy_application.py +0 -0
- {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/oauth2/rfc6749/clients/mobile_application.py +0 -0
- {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/oauth2/rfc6749/clients/web_application.py +0 -0
- {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/oauth2/rfc6749/endpoints/__init__.py +0 -0
- {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/oauth2/rfc6749/endpoints/authorization.py +0 -0
- {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/oauth2/rfc6749/endpoints/base.py +0 -0
- {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/oauth2/rfc6749/endpoints/introspect.py +0 -0
- {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/oauth2/rfc6749/endpoints/metadata.py +0 -0
- {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/oauth2/rfc6749/endpoints/pre_configured.py +0 -0
- {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/oauth2/rfc6749/endpoints/resource.py +0 -0
- {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/oauth2/rfc6749/endpoints/token.py +0 -0
- {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/oauth2/rfc6749/errors.py +0 -0
- {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/oauth2/rfc6749/grant_types/__init__.py +0 -0
- {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/oauth2/rfc6749/grant_types/implicit.py +0 -0
- {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/oauth2/rfc8628/__init__.py +0 -0
- {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/oauth2/rfc8628/clients/__init__.py +0 -0
- {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/oauth2/rfc8628/clients/device.py +0 -0
- {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/oauth2/rfc8628/endpoints/__init__.py +0 -0
- {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/oauth2/rfc8628/endpoints/device_authorization.py +0 -0
- {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/oauth2/rfc8628/endpoints/pre_configured.py +0 -0
- {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/oauth2/rfc8628/errors.py +0 -0
- {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/oauth2/rfc8628/grant_types/__init__.py +0 -0
- {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/oauth2/rfc8628/request_validator.py +0 -0
- {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/openid/__init__.py +0 -0
- {oauthlib-3.3.0/examples → oauthlib-4.0.0/oauthlib/openid/connect}/__init__.py +0 -0
- {oauthlib-3.3.0/oauthlib/openid/connect → oauthlib-4.0.0/oauthlib/openid/connect/core}/__init__.py +0 -0
- {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/openid/connect/core/endpoints/__init__.py +0 -0
- {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/openid/connect/core/endpoints/pre_configured.py +0 -0
- {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/openid/connect/core/endpoints/userinfo.py +0 -0
- {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/openid/connect/core/exceptions.py +0 -0
- {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/openid/connect/core/grant_types/__init__.py +0 -0
- {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/openid/connect/core/grant_types/authorization_code.py +0 -0
- {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/openid/connect/core/grant_types/base.py +0 -0
- {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/openid/connect/core/grant_types/dispatchers.py +0 -0
- {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/openid/connect/core/grant_types/hybrid.py +0 -0
- {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/openid/connect/core/grant_types/implicit.py +0 -0
- {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/openid/connect/core/grant_types/refresh_token.py +0 -0
- {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/openid/connect/core/request_validator.py +0 -0
- {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/openid/connect/core/tokens.py +0 -0
- {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/signals.py +0 -0
- {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/uri_validate.py +0 -0
- {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib.egg-info/dependency_links.txt +0 -0
- {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib.egg-info/requires.txt +0 -0
- {oauthlib-3.3.0 → oauthlib-4.0.0}/setup.cfg +0 -0
- {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/__init__.py +0 -0
- {oauthlib-3.3.0/oauthlib/openid/connect/core → oauthlib-4.0.0/tests/oauth1}/__init__.py +0 -0
- {oauthlib-3.3.0/tests/oauth1 → oauthlib-4.0.0/tests/oauth1/rfc5849}/__init__.py +0 -0
- {oauthlib-3.3.0/tests/oauth1/rfc5849 → oauthlib-4.0.0/tests/oauth1/rfc5849/endpoints}/__init__.py +0 -0
- {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/oauth1/rfc5849/test_parameters.py +0 -0
- {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/oauth1/rfc5849/test_request_validator.py +0 -0
- {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/oauth1/rfc5849/test_signatures.py +0 -0
- {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/oauth1/rfc5849/test_utils.py +0 -0
- {oauthlib-3.3.0/tests/oauth1/rfc5849/endpoints → oauthlib-4.0.0/tests/oauth2}/__init__.py +0 -0
- {oauthlib-3.3.0/tests/oauth2 → oauthlib-4.0.0/tests/oauth2/rfc6749}/__init__.py +0 -0
- {oauthlib-3.3.0/tests/oauth2/rfc6749 → oauthlib-4.0.0/tests/oauth2/rfc6749/clients}/__init__.py +0 -0
- {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/oauth2/rfc6749/clients/test_backend_application.py +0 -0
- {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/oauth2/rfc6749/clients/test_legacy_application.py +0 -0
- {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/oauth2/rfc6749/clients/test_mobile_application.py +0 -0
- {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/oauth2/rfc6749/clients/test_service_application.py +0 -0
- {oauthlib-3.3.0/tests/oauth2/rfc6749/clients → oauthlib-4.0.0/tests/oauth2/rfc6749/endpoints}/__init__.py +0 -0
- {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/oauth2/rfc6749/endpoints/test_client_authentication.py +0 -0
- {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/oauth2/rfc6749/endpoints/test_extra_credentials.py +0 -0
- {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/oauth2/rfc6749/endpoints/test_introspect_endpoint.py +0 -0
- {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/oauth2/rfc6749/endpoints/test_resource_owner_association.py +0 -0
- {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/oauth2/rfc6749/endpoints/test_utils.py +0 -0
- {oauthlib-3.3.0/tests/oauth2/rfc6749/endpoints → oauthlib-4.0.0/tests/oauth2/rfc6749/grant_types}/__init__.py +0 -0
- {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/oauth2/rfc6749/test_request_validator.py +0 -0
- {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/oauth2/rfc6749/test_tokens.py +0 -0
- {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/oauth2/rfc6749/test_utils.py +0 -0
- {oauthlib-3.3.0/tests/oauth2/rfc6749/grant_types → oauthlib-4.0.0/tests/oauth2/rfc8628}/__init__.py +0 -0
- {oauthlib-3.3.0/tests/oauth2/rfc8628 → oauthlib-4.0.0/tests/oauth2/rfc8628/clients}/__init__.py +0 -0
- {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/oauth2/rfc8628/clients/test_device.py +0 -0
- {oauthlib-3.3.0/tests/oauth2/rfc8628/clients → oauthlib-4.0.0/tests/oauth2/rfc8628/endpoints}/__init__.py +0 -0
- {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/oauth2/rfc8628/endpoints/test_device_application_server.py +0 -0
- {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/oauth2/rfc8628/endpoints/test_error_responses.py +0 -0
- {oauthlib-3.3.0/tests/oauth2/rfc8628/endpoints → oauthlib-4.0.0/tests/oauth2/rfc8628/grant_types}/__init__.py +0 -0
- {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/oauth2/rfc8628/test_server.py +0 -0
- {oauthlib-3.3.0/tests/oauth2/rfc8628/grant_types → oauthlib-4.0.0/tests/openid}/__init__.py +0 -0
- {oauthlib-3.3.0/tests/openid → oauthlib-4.0.0/tests/openid/connect}/__init__.py +0 -0
- {oauthlib-3.3.0/tests/openid/connect → oauthlib-4.0.0/tests/openid/connect/core}/__init__.py +0 -0
- {oauthlib-3.3.0/tests/openid/connect/core → oauthlib-4.0.0/tests/openid/connect/core/endpoints}/__init__.py +0 -0
- {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/openid/connect/core/endpoints/test_userinfo_endpoint.py +0 -0
- {oauthlib-3.3.0/tests/openid/connect/core/endpoints → oauthlib-4.0.0/tests/openid/connect/core/grant_types}/__init__.py +0 -0
- {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/openid/connect/core/grant_types/test_base.py +0 -0
- {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/openid/connect/core/grant_types/test_dispatchers.py +0 -0
- {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/openid/connect/core/test_request_validator.py +0 -0
- {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/openid/connect/core/test_tokens.py +0 -0
- {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/test_uri_validate.py +0 -0
- {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/unittest/__init__.py +0 -0
|
@@ -1,6 +1,40 @@
|
|
|
1
1
|
Changelog
|
|
2
2
|
=========
|
|
3
3
|
|
|
4
|
+
4.0.0 (2026-09-28):
|
|
5
|
+
------------------
|
|
6
|
+
OAuth2.0 Provider:
|
|
7
|
+
* **Breaking**: #951: Removed JSONP support from token revocation endpoint.
|
|
8
|
+
JSONP has been superseded by CORS for cross-origin requests.
|
|
9
|
+
The ``enable_jsonp`` parameter has been removed from ``RevocationEndpoint``
|
|
10
|
+
and the ``callback`` parameter has been removed from
|
|
11
|
+
``prepare_token_revocation_request``.
|
|
12
|
+
* **Breaking**: #919, #920: Fixed ``DeviceCodeGrant.validate_token_request``
|
|
13
|
+
trying to authenticate public clients.
|
|
14
|
+
Client authentication validation has been reorganized and is now shared
|
|
15
|
+
across ``AuthorizationCodeGrant``, ``DeviceCodeGrant``, ``RefreshTokenGrant``
|
|
16
|
+
and ``ResourceOwnerPasswordCredentialsGrant``: the ``grant_type`` parameter
|
|
17
|
+
is validated before client authentication, so requests missing
|
|
18
|
+
``grant_type`` now return ``400 invalid_request`` instead of
|
|
19
|
+
``401 invalid_client``.
|
|
20
|
+
* #963: Improved PKCE code comparison
|
|
21
|
+
|
|
22
|
+
Misc:
|
|
23
|
+
* #904: Stop installing ``examples`` into ``site-packages``.
|
|
24
|
+
* #930: Add devcontainer, Add Python3.14, Python3.14t.
|
|
25
|
+
* #931: Fix ruff checks about unused variables.
|
|
26
|
+
* #932: Dropped EOL Python 3.8 from CI.
|
|
27
|
+
* #934: Pre-commit hooks autoupdate.
|
|
28
|
+
* #938: Fix typos discovered by typos.
|
|
29
|
+
* Add OAuthLib Maintainer agent for automated issue/PR triage and release
|
|
30
|
+
management.
|
|
31
|
+
|
|
32
|
+
3.3.1 (2025-06-19):
|
|
33
|
+
------------------
|
|
34
|
+
OAuth2.0 Client:
|
|
35
|
+
* #906: fix regression of expires_in parsing when float in string.
|
|
36
|
+
|
|
37
|
+
|
|
4
38
|
3.3.0 (2025-06-17):
|
|
5
39
|
------------------
|
|
6
40
|
OAuth2.0 Provider:
|
|
@@ -95,7 +129,7 @@ General
|
|
|
95
129
|
* #727: build isort integration
|
|
96
130
|
* #734: python2 code removal
|
|
97
131
|
* #735, #750: add python3.8 support
|
|
98
|
-
* #749: bump minimum versions of pyjwt and cryptography
|
|
132
|
+
* #749: bump minimum versions of pyjwt and cryptography
|
|
99
133
|
|
|
100
134
|
3.1.0 (2019-08-06)
|
|
101
135
|
------------------
|
|
@@ -407,7 +441,7 @@ Draft revocation endpoint features and numerous fixes including:
|
|
|
407
441
|
|
|
408
442
|
* (OAuth 2 Provider) returned JSON headers no longer include charset.
|
|
409
443
|
|
|
410
|
-
* (OAuth 2 Provider)
|
|
444
|
+
* (OAuth 2 Provider) validate_authorization_request now also includes the
|
|
411
445
|
internal request object in the returned dictionary. Note that this is
|
|
412
446
|
not meant to be relied upon heavily and its interface might change.
|
|
413
447
|
|
|
@@ -1,12 +1,18 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: oauthlib
|
|
3
|
-
Version:
|
|
3
|
+
Version: 4.0.0
|
|
4
4
|
Summary: A generic, spec-compliant, thorough implementation of the OAuth request-signing logic
|
|
5
5
|
Home-page: https://github.com/oauthlib/oauthlib
|
|
6
6
|
Author: The OAuthlib Community
|
|
7
7
|
Maintainer: Jonathan Huot
|
|
8
8
|
Maintainer-email: jonathan.huot@gmail.com
|
|
9
9
|
License: BSD-3-Clause
|
|
10
|
+
Project-URL: Changelog, https://github.com/oauthlib/oauthlib/blob/master/CHANGELOG.rst
|
|
11
|
+
Project-URL: Documentation, https://oauthlib.readthedocs.io/
|
|
12
|
+
Project-URL: Gitter, https://gitter.im/oauthlib/Lobby
|
|
13
|
+
Project-URL: Issues, https://github.com/oauthlib/oauthlib/issues
|
|
14
|
+
Project-URL: Source, https://github.com/oauthlib/oauthlib
|
|
15
|
+
Project-URL: Sponsor, https://github.com/sponsors/JonathanHuot
|
|
10
16
|
Platform: any
|
|
11
17
|
Classifier: Development Status :: 5 - Production/Stable
|
|
12
18
|
Classifier: Environment :: Web Environment
|
|
@@ -16,18 +22,18 @@ Classifier: Operating System :: POSIX
|
|
|
16
22
|
Classifier: Operating System :: POSIX :: Linux
|
|
17
23
|
Classifier: Programming Language :: Python
|
|
18
24
|
Classifier: Programming Language :: Python :: 3
|
|
19
|
-
Classifier: Programming Language :: Python :: 3.8
|
|
20
25
|
Classifier: Programming Language :: Python :: 3.9
|
|
21
26
|
Classifier: Programming Language :: Python :: 3.10
|
|
22
27
|
Classifier: Programming Language :: Python :: 3.11
|
|
23
28
|
Classifier: Programming Language :: Python :: 3.12
|
|
24
29
|
Classifier: Programming Language :: Python :: 3.13
|
|
30
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
25
31
|
Classifier: Programming Language :: Python :: 3 :: Only
|
|
26
32
|
Classifier: Programming Language :: Python :: Implementation
|
|
27
33
|
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
28
34
|
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
29
35
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
30
|
-
Requires-Python: >=3.
|
|
36
|
+
Requires-Python: >=3.9
|
|
31
37
|
Description-Content-Type: text/x-rst
|
|
32
38
|
License-File: LICENSE
|
|
33
39
|
Provides-Extra: rsa
|
|
@@ -47,6 +53,7 @@ Dynamic: license-file
|
|
|
47
53
|
Dynamic: maintainer
|
|
48
54
|
Dynamic: maintainer-email
|
|
49
55
|
Dynamic: platform
|
|
56
|
+
Dynamic: project-url
|
|
50
57
|
Dynamic: provides-extra
|
|
51
58
|
Dynamic: requires-python
|
|
52
59
|
Dynamic: summary
|
|
@@ -57,6 +64,9 @@ OAuthLib - Python Framework for OAuth1 & OAuth2
|
|
|
57
64
|
*A generic, spec-compliant, thorough implementation of the OAuth request-signing
|
|
58
65
|
logic for Python 3.8+*
|
|
59
66
|
|
|
67
|
+
.. image:: https://img.shields.io/static/v1?label=Dev%20Containers&message=Open&color=blue
|
|
68
|
+
:target: https://vscode.dev/redirect?url=vscode://ms-vscode-remote.remote-containers/cloneInVolume?url=https://github.com/oauthlib/oauthlib
|
|
69
|
+
:alt: Open DevContainer in VSCode
|
|
60
70
|
.. image:: https://github.com/oauthlib/oauthlib/actions/workflows/python-build.yml/badge.svg
|
|
61
71
|
:target: https://github.com/oauthlib/oauthlib/actions
|
|
62
72
|
:alt: GitHub Actions
|
|
@@ -4,6 +4,9 @@ OAuthLib - Python Framework for OAuth1 & OAuth2
|
|
|
4
4
|
*A generic, spec-compliant, thorough implementation of the OAuth request-signing
|
|
5
5
|
logic for Python 3.8+*
|
|
6
6
|
|
|
7
|
+
.. image:: https://img.shields.io/static/v1?label=Dev%20Containers&message=Open&color=blue
|
|
8
|
+
:target: https://vscode.dev/redirect?url=vscode://ms-vscode-remote.remote-containers/cloneInVolume?url=https://github.com/oauthlib/oauthlib
|
|
9
|
+
:alt: Open DevContainer in VSCode
|
|
7
10
|
.. image:: https://github.com/oauthlib/oauthlib/actions/workflows/python-build.yml/badge.svg
|
|
8
11
|
:target: https://github.com/oauthlib/oauthlib/actions
|
|
9
12
|
:alt: GitHub Actions
|
|
@@ -102,7 +102,7 @@ class AccessTokenEndpoint(BaseEndpoint):
|
|
|
102
102
|
resp_headers = {'Content-Type': 'application/x-www-form-urlencoded'}
|
|
103
103
|
try:
|
|
104
104
|
request = self._create_request(uri, http_method, body, headers)
|
|
105
|
-
valid,
|
|
105
|
+
valid, _processed_request = self.validate_access_token_request(
|
|
106
106
|
request)
|
|
107
107
|
if valid:
|
|
108
108
|
token = self.create_access_token(request, credentials or {})
|
|
@@ -183,7 +183,7 @@ class AccessTokenEndpoint(BaseEndpoint):
|
|
|
183
183
|
# .. _`Section 3.2`: https://tools.ietf.org/html/rfc5849#section-3.2
|
|
184
184
|
#
|
|
185
185
|
# Note that early exit would enable resource owner authorization
|
|
186
|
-
# verifier
|
|
186
|
+
# verifier enumeration.
|
|
187
187
|
valid_verifier = self.request_validator.validate_verifier(
|
|
188
188
|
request.client_key,
|
|
189
189
|
request.resource_owner_key,
|
|
@@ -73,7 +73,7 @@ class BaseEndpoint:
|
|
|
73
73
|
request = Request(uri, http_method, body, headers)
|
|
74
74
|
else:
|
|
75
75
|
request = Request(uri, http_method, '', headers)
|
|
76
|
-
|
|
76
|
+
_signature_type, params, oauth_params = (
|
|
77
77
|
self._get_signature_type_and_params(request))
|
|
78
78
|
|
|
79
79
|
# The server SHOULD return a 400 (Bad Request) status code when
|
|
@@ -97,7 +97,7 @@ class RequestTokenEndpoint(BaseEndpoint):
|
|
|
97
97
|
resp_headers = {'Content-Type': 'application/x-www-form-urlencoded'}
|
|
98
98
|
try:
|
|
99
99
|
request = self._create_request(uri, http_method, body, headers)
|
|
100
|
-
valid,
|
|
100
|
+
valid, _processed_request = self.validate_request_token_request(
|
|
101
101
|
request)
|
|
102
102
|
if valid:
|
|
103
103
|
token = self.create_request_token(request, credentials or {})
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
oauthlib.oauth1.rfc5849.errors
|
|
3
3
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
4
4
|
|
|
5
|
-
Error used both by OAuth 1 clients and
|
|
5
|
+
Error used both by OAuth 1 clients and providers to represent the spec
|
|
6
6
|
defined error responses for all four core grant types.
|
|
7
7
|
"""
|
|
8
8
|
from oauthlib.common import add_params_to_uri, urlencode
|
|
@@ -66,5 +66,6 @@ from .rfc6749.request_validator import RequestValidator
|
|
|
66
66
|
from .rfc6749.tokens import BearerToken, OAuth2Token
|
|
67
67
|
from .rfc6749.utils import is_secure_transport
|
|
68
68
|
from .rfc8628.clients import DeviceClient
|
|
69
|
-
from
|
|
70
|
-
from
|
|
69
|
+
from .rfc8628.endpoints import DeviceApplicationServer, DeviceAuthorizationEndpoint
|
|
70
|
+
from .rfc8628.errors import AccessDenied, AuthorizationPendingError, ExpiredTokenError, SlowDownError
|
|
71
|
+
from .rfc8628.grant_types import DeviceCodeGrant
|
|
@@ -320,7 +320,7 @@ class Client:
|
|
|
320
320
|
return token_url, FORM_ENC_HEADERS, body
|
|
321
321
|
|
|
322
322
|
def prepare_token_revocation_request(self, revocation_url, token,
|
|
323
|
-
token_type_hint="access_token", body='',
|
|
323
|
+
token_type_hint="access_token", body='', **kwargs):
|
|
324
324
|
"""Prepare a token revocation request.
|
|
325
325
|
|
|
326
326
|
:param revocation_url: Provider token revocation endpoint URL.
|
|
@@ -329,14 +329,9 @@ class Client:
|
|
|
329
329
|
``"refresh_token"``. This is optional and if you wish to not pass it you
|
|
330
330
|
must provide ``token_type_hint=None``.
|
|
331
331
|
:param body:
|
|
332
|
-
:param callback: A jsonp callback such as ``package.callback`` to be invoked
|
|
333
|
-
upon receiving the response. Not that it should not include a () suffix.
|
|
334
332
|
:param kwargs: Additional parameters to included in the request.
|
|
335
333
|
:returns: The prepared request tuple with (url, headers, body).
|
|
336
334
|
|
|
337
|
-
Note that JSONP request may use GET requests as the parameters will
|
|
338
|
-
be added to the request URL query as opposed to the request body.
|
|
339
|
-
|
|
340
335
|
An example of a revocation request
|
|
341
336
|
|
|
342
337
|
.. code-block:: http
|
|
@@ -348,21 +343,6 @@ class Client:
|
|
|
348
343
|
|
|
349
344
|
token=45ghiukldjahdnhzdauz&token_type_hint=refresh_token
|
|
350
345
|
|
|
351
|
-
An example of a jsonp revocation request
|
|
352
|
-
|
|
353
|
-
.. code-block:: http
|
|
354
|
-
|
|
355
|
-
GET /revoke?token=agabcdefddddafdd&callback=package.myCallback HTTP/1.1
|
|
356
|
-
Host: server.example.com
|
|
357
|
-
Content-Type: application/x-www-form-urlencoded
|
|
358
|
-
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
|
|
359
|
-
|
|
360
|
-
and an error response
|
|
361
|
-
|
|
362
|
-
.. code-block:: javascript
|
|
363
|
-
|
|
364
|
-
package.myCallback({"error":"unsupported_token_type"});
|
|
365
|
-
|
|
366
346
|
Note that these requests usually require client credentials, client_id in
|
|
367
347
|
the case for public clients and provider specific authentication
|
|
368
348
|
credentials for confidential clients.
|
|
@@ -370,8 +350,11 @@ class Client:
|
|
|
370
350
|
if not is_secure_transport(revocation_url):
|
|
371
351
|
raise InsecureTransportError()
|
|
372
352
|
|
|
353
|
+
if 'callback' in kwargs:
|
|
354
|
+
raise TypeError("prepare_token_revocation_request() got an unexpected keyword argument 'callback'")
|
|
355
|
+
|
|
373
356
|
return prepare_token_revocation_request(revocation_url, token,
|
|
374
|
-
token_type_hint=token_type_hint, body=body,
|
|
357
|
+
token_type_hint=token_type_hint, body=body,
|
|
375
358
|
**kwargs)
|
|
376
359
|
|
|
377
360
|
def parse_request_body_response(self, body, scope=None, **kwargs):
|
|
@@ -17,7 +17,7 @@ from .base import Client
|
|
|
17
17
|
class ServiceApplicationClient(Client):
|
|
18
18
|
"""A public client utilizing the JWT bearer grant.
|
|
19
19
|
|
|
20
|
-
JWT bearer
|
|
20
|
+
JWT bearer tokens can be used to request an access token when a client
|
|
21
21
|
wishes to utilize an existing trust relationship, expressed through the
|
|
22
22
|
semantics of (and digital signature or keyed message digest calculated
|
|
23
23
|
over) the JWT, without a direct user approval step at the authorization
|
|
@@ -27,13 +27,11 @@ class RevocationEndpoint(BaseEndpoint):
|
|
|
27
27
|
valid_token_types = ('access_token', 'refresh_token')
|
|
28
28
|
valid_request_methods = ('POST',)
|
|
29
29
|
|
|
30
|
-
def __init__(self, request_validator, supported_token_types=None
|
|
31
|
-
enable_jsonp=False):
|
|
30
|
+
def __init__(self, request_validator, supported_token_types=None):
|
|
32
31
|
BaseEndpoint.__init__(self)
|
|
33
32
|
self.request_validator = request_validator
|
|
34
33
|
self.supported_token_types = (
|
|
35
34
|
supported_token_types or self.valid_token_types)
|
|
36
|
-
self.enable_jsonp = enable_jsonp
|
|
37
35
|
|
|
38
36
|
@catch_errors_and_unavailability
|
|
39
37
|
def create_revocation_response(self, uri, http_method='POST', body=None,
|
|
@@ -69,18 +67,13 @@ class RevocationEndpoint(BaseEndpoint):
|
|
|
69
67
|
except OAuth2Error as e:
|
|
70
68
|
log.debug('Client error during validation of %r. %r.', request, e)
|
|
71
69
|
response_body = e.json
|
|
72
|
-
if self.enable_jsonp and request.callback:
|
|
73
|
-
response_body = '{}({});'.format(request.callback, response_body)
|
|
74
70
|
resp_headers.update(e.headers)
|
|
75
71
|
return resp_headers, response_body, e.status_code
|
|
76
72
|
|
|
77
73
|
self.request_validator.revoke_token(request.token,
|
|
78
74
|
request.token_type_hint, request)
|
|
79
75
|
|
|
80
|
-
|
|
81
|
-
if self.enable_jsonp and request.callback:
|
|
82
|
-
response_body = request.callback + '();'
|
|
83
|
-
return {}, response_body, 200
|
|
76
|
+
return {}, '', 200
|
|
84
77
|
|
|
85
78
|
def validate_revocation_request(self, request):
|
|
86
79
|
"""Ensure the request is valid.
|
|
@@ -4,6 +4,7 @@ oauthlib.oauth2.rfc6749.grant_types
|
|
|
4
4
|
"""
|
|
5
5
|
import base64
|
|
6
6
|
import hashlib
|
|
7
|
+
import hmac
|
|
7
8
|
import json
|
|
8
9
|
import logging
|
|
9
10
|
|
|
@@ -39,14 +40,15 @@ def code_challenge_method_s256(verifier, challenge):
|
|
|
39
40
|
return s;
|
|
40
41
|
}
|
|
41
42
|
|
|
42
|
-
|
|
43
|
-
|
|
43
|
+
Python's urlsafe_b64encode() replaces '+' and '/', but preserves the
|
|
44
|
+
trailing '=', so we have to remove it.
|
|
44
45
|
|
|
45
46
|
.. _`Section 4.3`: https://tools.ietf.org/html/rfc7636#section-4.3
|
|
46
47
|
"""
|
|
47
|
-
|
|
48
|
+
expected = base64.urlsafe_b64encode(
|
|
48
49
|
hashlib.sha256(verifier.encode()).digest()
|
|
49
|
-
).decode().rstrip('=')
|
|
50
|
+
).decode().rstrip('=')
|
|
51
|
+
return hmac.compare_digest(expected, challenge)
|
|
50
52
|
|
|
51
53
|
|
|
52
54
|
def code_challenge_method_plain(verifier, challenge):
|
|
@@ -58,7 +60,7 @@ def code_challenge_method_plain(verifier, challenge):
|
|
|
58
60
|
|
|
59
61
|
.. _`Section 4.3`: https://tools.ietf.org/html/rfc7636#section-4.3
|
|
60
62
|
"""
|
|
61
|
-
return verifier
|
|
63
|
+
return hmac.compare_digest(verifier, challenge)
|
|
62
64
|
|
|
63
65
|
|
|
64
66
|
class AuthorizationCodeGrant(GrantTypeBase):
|
|
@@ -452,28 +454,7 @@ class AuthorizationCodeGrant(GrantTypeBase):
|
|
|
452
454
|
raise errors.InvalidRequestError(description='Duplicate %s parameter.' % param,
|
|
453
455
|
request=request)
|
|
454
456
|
|
|
455
|
-
|
|
456
|
-
# If the client type is confidential or the client was issued client
|
|
457
|
-
# credentials (or assigned other authentication requirements), the
|
|
458
|
-
# client MUST authenticate with the authorization server as described
|
|
459
|
-
# in Section 3.2.1.
|
|
460
|
-
# https://tools.ietf.org/html/rfc6749#section-3.2.1
|
|
461
|
-
if not self.request_validator.authenticate_client(request):
|
|
462
|
-
log.debug('Client authentication failed, %r.', request)
|
|
463
|
-
raise errors.InvalidClientError(request=request)
|
|
464
|
-
elif not self.request_validator.authenticate_client_id(request.client_id, request):
|
|
465
|
-
# REQUIRED, if the client is not authenticating with the
|
|
466
|
-
# authorization server as described in Section 3.2.1.
|
|
467
|
-
# https://tools.ietf.org/html/rfc6749#section-3.2.1
|
|
468
|
-
log.debug('Client authentication failed, %r.', request)
|
|
469
|
-
raise errors.InvalidClientError(request=request)
|
|
470
|
-
|
|
471
|
-
if not hasattr(request.client, 'client_id'):
|
|
472
|
-
raise NotImplementedError('Authenticate client must set the '
|
|
473
|
-
'request.client.client_id attribute '
|
|
474
|
-
'in authenticate_client.')
|
|
475
|
-
|
|
476
|
-
request.client_id = request.client_id or request.client.client_id
|
|
457
|
+
self.validate_client_authentication(request)
|
|
477
458
|
|
|
478
459
|
# Ensure client is authorized use of this grant type
|
|
479
460
|
self.validate_grant_type(request)
|
|
@@ -175,6 +175,57 @@ class GrantTypeBase:
|
|
|
175
175
|
request.scopes, request.client, request):
|
|
176
176
|
raise errors.InvalidScopeError(request=request)
|
|
177
177
|
|
|
178
|
+
def validate_client_confidential(self, request):
|
|
179
|
+
# If the client type is confidential or the client was issued client
|
|
180
|
+
# credentials (or assigned other authentication requirements), the
|
|
181
|
+
# client MUST authenticate with the authorization server as described
|
|
182
|
+
# in Section 3.2.1.
|
|
183
|
+
# https://tools.ietf.org/html/rfc6749#section-3.2.1
|
|
184
|
+
log.debug('Authenticating confidential client, %r.', request)
|
|
185
|
+
if not self.request_validator.authenticate_client(request):
|
|
186
|
+
log.debug('Client authentication failed, %r.', request)
|
|
187
|
+
raise errors.InvalidClientError(request=request)
|
|
188
|
+
|
|
189
|
+
if request.client is None or \
|
|
190
|
+
not hasattr(request.client, 'client_id'):
|
|
191
|
+
raise NotImplementedError('Authenticate client must set the '
|
|
192
|
+
'request.client.client_id attribute '
|
|
193
|
+
'in authenticate_client.')
|
|
194
|
+
|
|
195
|
+
if not request.client_id:
|
|
196
|
+
request.client_id = request.client.client_id
|
|
197
|
+
elif request.client_id != request.client.client_id:
|
|
198
|
+
raise errors.ServerError('Discrepency found between client identifier')
|
|
199
|
+
|
|
200
|
+
def validate_client_public(self, request):
|
|
201
|
+
# REQUIRED, if the client is not authenticating with the
|
|
202
|
+
# authorization server as described in Section 3.2.1.
|
|
203
|
+
# https://tools.ietf.org/html/rfc6749#section-3.2.1
|
|
204
|
+
log.debug('Authenticating public client, %r.', request)
|
|
205
|
+
if not self.request_validator.authenticate_client_id(request.client_id, request):
|
|
206
|
+
log.debug('Client authentication failed, %r.', request)
|
|
207
|
+
raise errors.InvalidClientError(request=request)
|
|
208
|
+
|
|
209
|
+
if request.client is None or \
|
|
210
|
+
not hasattr(request.client, 'client_id'):
|
|
211
|
+
raise NotImplementedError('Authenticate client must set the '
|
|
212
|
+
'request.client.client_id attribute '
|
|
213
|
+
'in authenticate_client.')
|
|
214
|
+
|
|
215
|
+
if not request.client_id:
|
|
216
|
+
request.client_id = request.client.client_id
|
|
217
|
+
elif request.client_id != request.client.client_id:
|
|
218
|
+
raise errors.ServerError('Discrepency found between client identifier')
|
|
219
|
+
|
|
220
|
+
def validate_client_authentication(self, request):
|
|
221
|
+
"""Raise on failed client authentication."""
|
|
222
|
+
# Handles confidential clients
|
|
223
|
+
if self.request_validator.client_authentication_required(request):
|
|
224
|
+
self.validate_client_confidential(request)
|
|
225
|
+
# Handles public clients
|
|
226
|
+
else:
|
|
227
|
+
self.validate_client_public(request)
|
|
228
|
+
|
|
178
229
|
def prepare_authorization_response(self, request, token, headers, body, status):
|
|
179
230
|
"""Place token according to response mode.
|
|
180
231
|
|
|
@@ -103,18 +103,11 @@ class ClientCredentialsGrant(GrantTypeBase):
|
|
|
103
103
|
raise errors.InvalidRequestError(description='Duplicate %s parameter.' % param,
|
|
104
104
|
request=request)
|
|
105
105
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
log.debug('Client authentication failed, %r.', request)
|
|
109
|
-
raise errors.InvalidClientError(request=request)
|
|
110
|
-
elif not hasattr(request.client, 'client_id'):
|
|
111
|
-
raise NotImplementedError('Authenticate client must set the '
|
|
112
|
-
'request.client.client_id attribute '
|
|
113
|
-
'in authenticate_client.')
|
|
106
|
+
self.validate_client_confidential(request)
|
|
107
|
+
|
|
114
108
|
# Ensure client is authorized use of this grant type
|
|
115
109
|
self.validate_grant_type(request)
|
|
116
110
|
|
|
117
|
-
request.client_id = request.client_id or request.client.client_id
|
|
118
111
|
log.debug('Authorizing access to client %r.', request.client_id)
|
|
119
112
|
self.validate_scopes(request)
|
|
120
113
|
|
|
@@ -91,22 +91,8 @@ class RefreshTokenGrant(GrantTypeBase):
|
|
|
91
91
|
|
|
92
92
|
# Because refresh tokens are typically long-lasting credentials used to
|
|
93
93
|
# request additional access tokens, the refresh token is bound to the
|
|
94
|
-
# client to which it was issued
|
|
95
|
-
|
|
96
|
-
# authentication requirements), the client MUST authenticate with the
|
|
97
|
-
# authorization server as described in Section 3.2.1.
|
|
98
|
-
# https://tools.ietf.org/html/rfc6749#section-3.2.1
|
|
99
|
-
if self.request_validator.client_authentication_required(request):
|
|
100
|
-
log.debug('Authenticating client, %r.', request)
|
|
101
|
-
if not self.request_validator.authenticate_client(request):
|
|
102
|
-
log.debug('Invalid client (%r), denying access.', request)
|
|
103
|
-
raise errors.InvalidClientError(request=request)
|
|
104
|
-
# Ensure that request.client_id is set.
|
|
105
|
-
if request.client_id is None and request.client is not None:
|
|
106
|
-
request.client_id = request.client.client_id
|
|
107
|
-
elif not self.request_validator.authenticate_client_id(request.client_id, request):
|
|
108
|
-
log.debug('Client authentication failed, %r.', request)
|
|
109
|
-
raise errors.InvalidClientError(request=request)
|
|
94
|
+
# client to which it was issued
|
|
95
|
+
self.validate_client_authentication(request)
|
|
110
96
|
|
|
111
97
|
# Ensure client is authorized use of this grant type
|
|
112
98
|
self.validate_grant_type(request)
|
|
@@ -85,14 +85,6 @@ class ResourceOwnerPasswordCredentialsGrant(GrantTypeBase):
|
|
|
85
85
|
"""
|
|
86
86
|
headers = self._get_default_headers()
|
|
87
87
|
try:
|
|
88
|
-
if self.request_validator.client_authentication_required(request):
|
|
89
|
-
log.debug('Authenticating client, %r.', request)
|
|
90
|
-
if not self.request_validator.authenticate_client(request):
|
|
91
|
-
log.debug('Client authentication failed, %r.', request)
|
|
92
|
-
raise errors.InvalidClientError(request=request)
|
|
93
|
-
elif not self.request_validator.authenticate_client_id(request.client_id, request):
|
|
94
|
-
log.debug('Client authentication failed, %r.', request)
|
|
95
|
-
raise errors.InvalidClientError(request=request)
|
|
96
88
|
log.debug('Validating access token request, %r.', request)
|
|
97
89
|
self.validate_token_request(request)
|
|
98
90
|
except errors.OAuth2Error as e:
|
|
@@ -175,23 +167,18 @@ class ResourceOwnerPasswordCredentialsGrant(GrantTypeBase):
|
|
|
175
167
|
if not request.grant_type == 'password':
|
|
176
168
|
raise errors.UnsupportedGrantTypeError(request=request)
|
|
177
169
|
|
|
170
|
+
self.validate_client_authentication(request)
|
|
171
|
+
|
|
178
172
|
log.debug('Validating username %s.', request.username)
|
|
179
173
|
if not self.request_validator.validate_user(request.username,
|
|
180
174
|
request.password, request.client, request):
|
|
181
175
|
raise errors.InvalidGrantError(
|
|
182
176
|
'Invalid credentials given.', request=request)
|
|
183
|
-
elif not hasattr(request.client, 'client_id'):
|
|
184
|
-
raise NotImplementedError(
|
|
185
|
-
'Validate user must set the '
|
|
186
|
-
'request.client.client_id attribute '
|
|
187
|
-
'in authenticate_client.')
|
|
188
177
|
log.debug('Authorizing access to user %r.', request.user)
|
|
189
178
|
|
|
190
179
|
# Ensure client is authorized use of this grant type
|
|
191
180
|
self.validate_grant_type(request)
|
|
192
181
|
|
|
193
|
-
if request.client:
|
|
194
|
-
request.client_id = request.client_id or request.client.client_id
|
|
195
182
|
self.validate_scopes(request)
|
|
196
183
|
|
|
197
184
|
for validator in self.custom_validators.post_token:
|
|
@@ -172,7 +172,7 @@ def prepare_token_request(grant_type, body='', include_client_id=True, code_veri
|
|
|
172
172
|
|
|
173
173
|
|
|
174
174
|
def prepare_token_revocation_request(url, token, token_type_hint="access_token",
|
|
175
|
-
|
|
175
|
+
body='', **kwargs):
|
|
176
176
|
"""Prepare a token revocation request.
|
|
177
177
|
|
|
178
178
|
The client constructs the request by including the following parameters
|
|
@@ -216,17 +216,16 @@ def prepare_token_revocation_request(url, token, token_type_hint="access_token",
|
|
|
216
216
|
if token_type_hint:
|
|
217
217
|
params.append(('token_type_hint', token_type_hint))
|
|
218
218
|
|
|
219
|
+
if 'callback' in kwargs:
|
|
220
|
+
raise TypeError("prepare_token_revocation_request() got an unexpected keyword argument 'callback'")
|
|
221
|
+
|
|
219
222
|
for k in kwargs:
|
|
220
223
|
if kwargs[k]:
|
|
221
224
|
params.append((str(k), kwargs[k]))
|
|
222
225
|
|
|
223
226
|
headers = {'Content-Type': 'application/x-www-form-urlencoded'}
|
|
224
227
|
|
|
225
|
-
|
|
226
|
-
params.append(('callback', callback))
|
|
227
|
-
return add_params_to_uri(url, params), headers, body
|
|
228
|
-
else:
|
|
229
|
-
return url, headers, add_params_to_qs(body, params)
|
|
228
|
+
return url, headers, add_params_to_qs(body, params)
|
|
230
229
|
|
|
231
230
|
|
|
232
231
|
def parse_authorization_code_response(uri, state=None):
|
|
@@ -339,7 +338,7 @@ def parse_implicit_response(uri, state=None, scope=None):
|
|
|
339
338
|
if 'scope' in params:
|
|
340
339
|
params['scope'] = scope_to_list(params['scope'])
|
|
341
340
|
|
|
342
|
-
vin, vat,
|
|
341
|
+
vin, vat, _v_at = parse_expires(params)
|
|
343
342
|
if vin:
|
|
344
343
|
params['expires_in'] = vin
|
|
345
344
|
elif 'expires_in' in params:
|
|
@@ -430,7 +429,7 @@ def parse_token_response(body, scope=None):
|
|
|
430
429
|
if 'scope' in params:
|
|
431
430
|
params['scope'] = scope_to_list(params['scope'])
|
|
432
431
|
|
|
433
|
-
vin, vat,
|
|
432
|
+
vin, vat, _v_at = parse_expires(params)
|
|
434
433
|
if vin:
|
|
435
434
|
params['expires_in'] = vin
|
|
436
435
|
elif 'expires_in' in params:
|
|
@@ -476,7 +475,7 @@ def parse_expires(params):
|
|
|
476
475
|
"""Parse `expires_in`, `expires_at` fields from params
|
|
477
476
|
|
|
478
477
|
Parse following these rules:
|
|
479
|
-
- `expires_in` must be either
|
|
478
|
+
- `expires_in` must be either integer, float or None. If a float, it is converted into an integer.
|
|
480
479
|
- `expires_at` is not in specification so it does its best to:
|
|
481
480
|
- convert into a int, else
|
|
482
481
|
- convert into a float, else
|
|
@@ -495,14 +494,16 @@ def parse_expires(params):
|
|
|
495
494
|
if 'expires_in' in params:
|
|
496
495
|
if isinstance(params.get('expires_in'), int):
|
|
497
496
|
expires_in = params.get('expires_in')
|
|
497
|
+
elif isinstance(params.get('expires_in'), float):
|
|
498
|
+
expires_in = int(params.get('expires_in'))
|
|
498
499
|
elif isinstance(params.get('expires_in'), str):
|
|
499
500
|
try:
|
|
500
501
|
# Attempt to convert to int
|
|
501
502
|
expires_in = int(params.get('expires_in'))
|
|
502
503
|
except ValueError:
|
|
503
|
-
raise ValueError("
|
|
504
|
+
raise ValueError("expires_in must be an int")
|
|
504
505
|
elif params.get('expires_in') is not None:
|
|
505
|
-
raise ValueError("
|
|
506
|
+
raise ValueError("expires_in must be an int")
|
|
506
507
|
|
|
507
508
|
if 'expires_at' in params:
|
|
508
509
|
if isinstance(params.get('expires_at'), (float, int)):
|