Authlib 1.7.2__tar.gz → 1.8.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.
- {authlib-1.7.2 → authlib-1.8.0}/Authlib.egg-info/PKG-INFO +3 -3
- {authlib-1.7.2 → authlib-1.8.0}/Authlib.egg-info/SOURCES.txt +1 -0
- authlib-1.8.0/Authlib.egg-info/requires.txt +2 -0
- {authlib-1.7.2 → authlib-1.8.0}/PKG-INFO +3 -3
- authlib-1.8.0/authlib/common/security.py +48 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/common/urls.py +11 -2
- {authlib-1.7.2 → authlib-1.8.0}/authlib/consts.py +1 -1
- {authlib-1.7.2 → authlib-1.8.0}/authlib/integrations/base_client/async_openid.py +3 -2
- {authlib-1.7.2 → authlib-1.8.0}/authlib/integrations/base_client/sync_openid.py +3 -2
- authlib-1.8.0/authlib/integrations/httpx_client/_compat.py +18 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/integrations/httpx_client/assertion_client.py +12 -13
- {authlib-1.7.2 → authlib-1.8.0}/authlib/integrations/httpx_client/oauth1_client.py +10 -15
- {authlib-1.7.2 → authlib-1.8.0}/authlib/integrations/httpx_client/oauth2_client.py +16 -17
- {authlib-1.7.2 → authlib-1.8.0}/authlib/integrations/httpx_client/utils.py +7 -2
- {authlib-1.7.2 → authlib-1.8.0}/authlib/integrations/requests_client/assertion_session.py +2 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oauth1/rfc5849/errors.py +1 -1
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oauth2/rfc7521/client.py +4 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oauth2/rfc7523/assertion.py +6 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oauth2/rfc7523/client.py +18 -1
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oauth2/rfc7523/jwt_bearer.py +26 -3
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oauth2/rfc7523/token.py +1 -1
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oauth2/rfc7523/validator.py +3 -2
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oauth2/rfc8628/endpoint.py +1 -1
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oauth2/rfc9068/token.py +1 -1
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oauth2/rfc9068/token_validator.py +1 -2
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oidc/core/grants/_legacy.py +12 -8
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oidc/core/grants/code.py +6 -4
- {authlib-1.7.2 → authlib-1.8.0}/pyproject.toml +21 -22
- authlib-1.7.2/Authlib.egg-info/requires.txt +0 -2
- authlib-1.7.2/authlib/common/security.py +0 -21
- {authlib-1.7.2 → authlib-1.8.0}/Authlib.egg-info/dependency_links.txt +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/Authlib.egg-info/top_level.txt +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/LICENSE +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/MANIFEST.in +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/README.md +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/__init__.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/_joserfc_helpers.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/common/__init__.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/common/encoding.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/common/errors.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/common/language.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/deprecate.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/integrations/__init__.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/integrations/base_client/__init__.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/integrations/base_client/async_app.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/integrations/base_client/errors.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/integrations/base_client/framework_integration.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/integrations/base_client/registry.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/integrations/base_client/sync_app.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/integrations/django_client/__init__.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/integrations/django_client/apps.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/integrations/django_client/integration.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/integrations/django_oauth1/__init__.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/integrations/django_oauth1/authorization_server.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/integrations/django_oauth1/nonce.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/integrations/django_oauth1/resource_protector.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/integrations/django_oauth2/__init__.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/integrations/django_oauth2/authorization_server.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/integrations/django_oauth2/endpoints.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/integrations/django_oauth2/requests.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/integrations/django_oauth2/resource_protector.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/integrations/django_oauth2/signals.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/integrations/flask_client/__init__.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/integrations/flask_client/apps.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/integrations/flask_client/integration.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/integrations/flask_oauth1/__init__.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/integrations/flask_oauth1/authorization_server.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/integrations/flask_oauth1/cache.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/integrations/flask_oauth1/resource_protector.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/integrations/flask_oauth2/__init__.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/integrations/flask_oauth2/authorization_server.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/integrations/flask_oauth2/errors.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/integrations/flask_oauth2/requests.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/integrations/flask_oauth2/resource_protector.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/integrations/flask_oauth2/signals.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/integrations/httpx_client/__init__.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/integrations/requests_client/__init__.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/integrations/requests_client/oauth1_session.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/integrations/requests_client/oauth2_session.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/integrations/requests_client/utils.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/integrations/sqla_oauth2/__init__.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/integrations/sqla_oauth2/client_mixin.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/integrations/sqla_oauth2/functions.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/integrations/sqla_oauth2/tokens_mixins.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/integrations/starlette_client/__init__.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/integrations/starlette_client/apps.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/integrations/starlette_client/integration.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/jose/__init__.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/jose/drafts/__init__.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/jose/drafts/_jwe_algorithms.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/jose/drafts/_jwe_enc_cryptodome.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/jose/drafts/_jwe_enc_cryptography.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/jose/errors.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/jose/jwk.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/jose/rfc7515/__init__.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/jose/rfc7515/jws.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/jose/rfc7515/models.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/jose/rfc7516/__init__.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/jose/rfc7516/jwe.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/jose/rfc7516/models.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/jose/rfc7517/__init__.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/jose/rfc7517/_cryptography_key.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/jose/rfc7517/asymmetric_key.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/jose/rfc7517/base_key.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/jose/rfc7517/jwk.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/jose/rfc7517/key_set.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/jose/rfc7518/__init__.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/jose/rfc7518/ec_key.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/jose/rfc7518/jwe_algs.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/jose/rfc7518/jwe_encs.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/jose/rfc7518/jwe_zips.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/jose/rfc7518/jws_algs.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/jose/rfc7518/oct_key.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/jose/rfc7518/rsa_key.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/jose/rfc7518/util.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/jose/rfc7519/__init__.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/jose/rfc7519/claims.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/jose/rfc7519/jwt.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/jose/rfc8037/__init__.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/jose/rfc8037/jws_eddsa.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/jose/rfc8037/okp_key.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/jose/util.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oauth1/__init__.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oauth1/client.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oauth1/errors.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oauth1/rfc5849/__init__.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oauth1/rfc5849/authorization_server.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oauth1/rfc5849/base_server.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oauth1/rfc5849/client_auth.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oauth1/rfc5849/models.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oauth1/rfc5849/parameters.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oauth1/rfc5849/resource_protector.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oauth1/rfc5849/rsa.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oauth1/rfc5849/signature.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oauth1/rfc5849/util.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oauth1/rfc5849/wrapper.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oauth2/__init__.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oauth2/auth.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oauth2/base.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oauth2/claims.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oauth2/client.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oauth2/rfc6749/__init__.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oauth2/rfc6749/authenticate_client.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oauth2/rfc6749/authorization_server.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oauth2/rfc6749/endpoint.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oauth2/rfc6749/errors.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oauth2/rfc6749/grants/__init__.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oauth2/rfc6749/grants/authorization_code.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oauth2/rfc6749/grants/base.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oauth2/rfc6749/grants/client_credentials.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oauth2/rfc6749/grants/implicit.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oauth2/rfc6749/grants/refresh_token.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oauth2/rfc6749/grants/resource_owner_password_credentials.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oauth2/rfc6749/hooks.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oauth2/rfc6749/models.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oauth2/rfc6749/parameters.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oauth2/rfc6749/requests.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oauth2/rfc6749/resource_protector.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oauth2/rfc6749/token_endpoint.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oauth2/rfc6749/util.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oauth2/rfc6749/wrappers.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oauth2/rfc6750/__init__.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oauth2/rfc6750/errors.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oauth2/rfc6750/parameters.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oauth2/rfc6750/token.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oauth2/rfc6750/validator.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oauth2/rfc7009/__init__.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oauth2/rfc7009/parameters.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oauth2/rfc7009/revocation.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oauth2/rfc7521/__init__.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oauth2/rfc7523/__init__.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oauth2/rfc7523/auth.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oauth2/rfc7591/__init__.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oauth2/rfc7591/claims.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oauth2/rfc7591/endpoint.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oauth2/rfc7591/errors.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oauth2/rfc7592/__init__.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oauth2/rfc7592/endpoint.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oauth2/rfc7636/__init__.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oauth2/rfc7636/challenge.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oauth2/rfc7662/__init__.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oauth2/rfc7662/introspection.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oauth2/rfc7662/models.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oauth2/rfc7662/token_validator.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oauth2/rfc8414/__init__.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oauth2/rfc8414/models.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oauth2/rfc8414/well_known.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oauth2/rfc8628/__init__.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oauth2/rfc8628/device_code.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oauth2/rfc8628/errors.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oauth2/rfc8628/models.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oauth2/rfc8693/__init__.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oauth2/rfc9068/__init__.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oauth2/rfc9068/claims.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oauth2/rfc9068/introspection.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oauth2/rfc9068/revocation.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oauth2/rfc9101/__init__.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oauth2/rfc9101/authorization_server.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oauth2/rfc9101/discovery.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oauth2/rfc9101/errors.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oauth2/rfc9101/registration.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oauth2/rfc9207/__init__.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oauth2/rfc9207/discovery.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oauth2/rfc9207/parameter.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oidc/__init__.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oidc/core/__init__.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oidc/core/claims.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oidc/core/errors.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oidc/core/grants/__init__.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oidc/core/grants/hybrid.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oidc/core/grants/implicit.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oidc/core/grants/util.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oidc/core/models.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oidc/core/userinfo.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oidc/core/util.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oidc/discovery/__init__.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oidc/discovery/models.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oidc/discovery/well_known.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oidc/registration/__init__.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oidc/registration/claims.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oidc/rpinitiated/__init__.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oidc/rpinitiated/discovery.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oidc/rpinitiated/end_session.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/authlib/oidc/rpinitiated/registration.py +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/setup.cfg +0 -0
- {authlib-1.7.2 → authlib-1.8.0}/setup.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: Authlib
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.8.0
|
|
4
4
|
Summary: The ultimate Python library in building OAuth and OpenID Connect servers and clients.
|
|
5
5
|
Author-email: Hsiaoming Yang <me@lepture.com>
|
|
6
6
|
License: BSD-3-Clause
|
|
@@ -32,8 +32,8 @@ Classifier: Topic :: Internet :: WWW/HTTP :: WSGI :: Application
|
|
|
32
32
|
Requires-Python: >=3.10
|
|
33
33
|
Description-Content-Type: text/markdown
|
|
34
34
|
License-File: LICENSE
|
|
35
|
-
Requires-Dist: cryptography
|
|
36
|
-
Requires-Dist: joserfc>=1.6.
|
|
35
|
+
Requires-Dist: cryptography>=45.0.1
|
|
36
|
+
Requires-Dist: joserfc>=1.6.1
|
|
37
37
|
Dynamic: license-file
|
|
38
38
|
|
|
39
39
|
<div align="center">
|
|
@@ -54,6 +54,7 @@ authlib/integrations/flask_oauth2/requests.py
|
|
|
54
54
|
authlib/integrations/flask_oauth2/resource_protector.py
|
|
55
55
|
authlib/integrations/flask_oauth2/signals.py
|
|
56
56
|
authlib/integrations/httpx_client/__init__.py
|
|
57
|
+
authlib/integrations/httpx_client/_compat.py
|
|
57
58
|
authlib/integrations/httpx_client/assertion_client.py
|
|
58
59
|
authlib/integrations/httpx_client/oauth1_client.py
|
|
59
60
|
authlib/integrations/httpx_client/oauth2_client.py
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: Authlib
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.8.0
|
|
4
4
|
Summary: The ultimate Python library in building OAuth and OpenID Connect servers and clients.
|
|
5
5
|
Author-email: Hsiaoming Yang <me@lepture.com>
|
|
6
6
|
License: BSD-3-Clause
|
|
@@ -32,8 +32,8 @@ Classifier: Topic :: Internet :: WWW/HTTP :: WSGI :: Application
|
|
|
32
32
|
Requires-Python: >=3.10
|
|
33
33
|
Description-Content-Type: text/markdown
|
|
34
34
|
License-File: LICENSE
|
|
35
|
-
Requires-Dist: cryptography
|
|
36
|
-
Requires-Dist: joserfc>=1.6.
|
|
35
|
+
Requires-Dist: cryptography>=45.0.1
|
|
36
|
+
Requires-Dist: joserfc>=1.6.1
|
|
37
37
|
Dynamic: license-file
|
|
38
38
|
|
|
39
39
|
<div align="center">
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import ipaddress
|
|
2
|
+
import os
|
|
3
|
+
import random
|
|
4
|
+
import string
|
|
5
|
+
from urllib.parse import urlsplit
|
|
6
|
+
|
|
7
|
+
UNICODE_ASCII_CHARACTER_SET = string.ascii_letters + string.digits
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def generate_token(length=30, chars=UNICODE_ASCII_CHARACTER_SET):
|
|
11
|
+
rand = random.SystemRandom()
|
|
12
|
+
return "".join(rand.choice(chars) for _ in range(length))
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def is_secure_transport(uri):
|
|
16
|
+
"""Check if the uri is over ssl."""
|
|
17
|
+
if os.getenv("AUTHLIB_INSECURE_TRANSPORT"):
|
|
18
|
+
return True
|
|
19
|
+
|
|
20
|
+
try:
|
|
21
|
+
parts = urlsplit(uri)
|
|
22
|
+
except ValueError:
|
|
23
|
+
return False
|
|
24
|
+
|
|
25
|
+
if not parts.hostname:
|
|
26
|
+
return False
|
|
27
|
+
|
|
28
|
+
if parts.scheme == "https":
|
|
29
|
+
return True
|
|
30
|
+
|
|
31
|
+
if parts.scheme != "http":
|
|
32
|
+
return False
|
|
33
|
+
|
|
34
|
+
# rfc8252 §7.3: native apps may use http for loopback redirection URIs.
|
|
35
|
+
if parts.hostname == "localhost":
|
|
36
|
+
return True
|
|
37
|
+
|
|
38
|
+
try:
|
|
39
|
+
address = ipaddress.ip_address(parts.hostname)
|
|
40
|
+
except ValueError:
|
|
41
|
+
return False
|
|
42
|
+
|
|
43
|
+
# IPv6Address.is_loopback only accounts for the mapped IPv4 address since
|
|
44
|
+
# CPython 3.10.16, 3.11.11 and 3.12.4.
|
|
45
|
+
if isinstance(address, ipaddress.IPv6Address) and address.ipv4_mapped:
|
|
46
|
+
address = address.ipv4_mapped
|
|
47
|
+
|
|
48
|
+
return address.is_loopback
|
|
@@ -140,7 +140,16 @@ def extract_params(raw):
|
|
|
140
140
|
|
|
141
141
|
|
|
142
142
|
def is_valid_url(url: str, fragments_allowed=True):
|
|
143
|
-
|
|
144
|
-
|
|
143
|
+
try:
|
|
144
|
+
parsed = urlparse.urlparse(url)
|
|
145
|
+
except ValueError:
|
|
146
|
+
return False
|
|
147
|
+
|
|
148
|
+
# The userinfo component can disguise the actual host,
|
|
149
|
+
# as in "https://provider.test@attacker.test/".
|
|
150
|
+
if "@" in parsed.netloc:
|
|
151
|
+
return False
|
|
152
|
+
|
|
153
|
+
return bool(
|
|
145
154
|
parsed.scheme and parsed.hostname and (fragments_allowed or not parsed.fragment)
|
|
146
155
|
)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from joserfc import jwt
|
|
2
2
|
from joserfc.errors import InvalidKeyIdError
|
|
3
3
|
from joserfc.jwk import KeySet
|
|
4
|
+
from joserfc.jws import JWSRegistry
|
|
4
5
|
|
|
5
6
|
from authlib.common.security import generate_token
|
|
6
7
|
from authlib.common.urls import add_params_to_uri
|
|
@@ -67,7 +68,7 @@ class AsyncOpenIDMixin:
|
|
|
67
68
|
token = jwt.decode(
|
|
68
69
|
token["id_token"],
|
|
69
70
|
key=key_set,
|
|
70
|
-
algorithms=alg_values,
|
|
71
|
+
registry=JWSRegistry(algorithms=alg_values, strict_check_header=False),
|
|
71
72
|
)
|
|
72
73
|
except InvalidKeyIdError:
|
|
73
74
|
jwks = await self.fetch_jwk_set(force=True)
|
|
@@ -75,7 +76,7 @@ class AsyncOpenIDMixin:
|
|
|
75
76
|
token = jwt.decode(
|
|
76
77
|
token["id_token"],
|
|
77
78
|
key=key_set,
|
|
78
|
-
algorithms=alg_values,
|
|
79
|
+
registry=JWSRegistry(algorithms=alg_values, strict_check_header=False),
|
|
79
80
|
)
|
|
80
81
|
|
|
81
82
|
claims = claims_cls(token.claims, token.header, claims_options, claims_params)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from joserfc import jwt
|
|
2
2
|
from joserfc.errors import InvalidKeyIdError
|
|
3
3
|
from joserfc.jwk import KeySet
|
|
4
|
+
from joserfc.jws import JWSRegistry
|
|
4
5
|
|
|
5
6
|
from authlib.common.security import generate_token
|
|
6
7
|
from authlib.common.urls import add_params_to_uri
|
|
@@ -66,14 +67,14 @@ class OpenIDMixin:
|
|
|
66
67
|
token = jwt.decode(
|
|
67
68
|
token["id_token"],
|
|
68
69
|
key=key_set,
|
|
69
|
-
algorithms=alg_values,
|
|
70
|
+
registry=JWSRegistry(algorithms=alg_values, strict_check_header=False),
|
|
70
71
|
)
|
|
71
72
|
except InvalidKeyIdError:
|
|
72
73
|
key_set = KeySet.import_key_set(self.fetch_jwk_set(force=True))
|
|
73
74
|
token = jwt.decode(
|
|
74
75
|
token["id_token"],
|
|
75
76
|
key=key_set,
|
|
76
|
-
algorithms=alg_values,
|
|
77
|
+
registry=JWSRegistry(algorithms=alg_values, strict_check_header=False),
|
|
77
78
|
)
|
|
78
79
|
|
|
79
80
|
claims = claims_cls(token.claims, token.header, claims_options, claims_params)
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"""Compatibility shim for httpx2 with legacy httpx fallback.
|
|
2
|
+
|
|
3
|
+
Falls back to httpx when httpx2 is unavailable. The fallback is deprecated
|
|
4
|
+
and will be removed in a future release.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from authlib.deprecate import deprecate
|
|
8
|
+
|
|
9
|
+
try:
|
|
10
|
+
import httpx2
|
|
11
|
+
except ImportError:
|
|
12
|
+
import httpx as httpx2
|
|
13
|
+
|
|
14
|
+
deprecate(
|
|
15
|
+
"The httpx module is deprecated; please use httpx2 instead."
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
__all__ = ["httpx2"]
|
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
import httpx
|
|
2
|
-
from httpx import USE_CLIENT_DEFAULT
|
|
3
|
-
from httpx import Response
|
|
4
|
-
|
|
5
1
|
from authlib.oauth2.rfc7521 import AssertionClient as _AssertionClient
|
|
6
2
|
from authlib.oauth2.rfc7523 import JWTBearerGrant
|
|
7
3
|
|
|
8
4
|
from ..base_client import OAuthError
|
|
5
|
+
from ._compat import httpx2
|
|
9
6
|
from .oauth2_client import OAuth2Auth
|
|
10
7
|
from .utils import extract_client_kwargs
|
|
11
8
|
|
|
9
|
+
USE_CLIENT_DEFAULT = httpx2.USE_CLIENT_DEFAULT
|
|
10
|
+
Response = httpx2.Response
|
|
11
|
+
|
|
12
12
|
__all__ = ["AsyncAssertionClient"]
|
|
13
13
|
|
|
14
14
|
|
|
15
|
-
class AsyncAssertionClient(_AssertionClient,
|
|
15
|
+
class AsyncAssertionClient(_AssertionClient, httpx2.AsyncClient):
|
|
16
16
|
token_auth_class = OAuth2Auth
|
|
17
17
|
oauth_error_class = OAuthError
|
|
18
18
|
JWT_BEARER_GRANT_TYPE = JWTBearerGrant.GRANT_TYPE
|
|
@@ -31,10 +31,11 @@ class AsyncAssertionClient(_AssertionClient, httpx.AsyncClient):
|
|
|
31
31
|
claims=None,
|
|
32
32
|
token_placement="header",
|
|
33
33
|
scope=None,
|
|
34
|
+
client_id=None,
|
|
34
35
|
**kwargs,
|
|
35
36
|
):
|
|
36
37
|
client_kwargs = extract_client_kwargs(kwargs)
|
|
37
|
-
|
|
38
|
+
httpx2.AsyncClient.__init__(self, **client_kwargs)
|
|
38
39
|
|
|
39
40
|
_AssertionClient.__init__(
|
|
40
41
|
self,
|
|
@@ -47,6 +48,7 @@ class AsyncAssertionClient(_AssertionClient, httpx.AsyncClient):
|
|
|
47
48
|
claims=claims,
|
|
48
49
|
token_placement=token_placement,
|
|
49
50
|
scope=scope,
|
|
51
|
+
client_id=client_id,
|
|
50
52
|
**kwargs,
|
|
51
53
|
)
|
|
52
54
|
|
|
@@ -69,7 +71,7 @@ class AsyncAssertionClient(_AssertionClient, httpx.AsyncClient):
|
|
|
69
71
|
return self.parse_response_token(resp)
|
|
70
72
|
|
|
71
73
|
|
|
72
|
-
class AssertionClient(_AssertionClient,
|
|
74
|
+
class AssertionClient(_AssertionClient, httpx2.Client):
|
|
73
75
|
token_auth_class = OAuth2Auth
|
|
74
76
|
oauth_error_class = OAuthError
|
|
75
77
|
JWT_BEARER_GRANT_TYPE = JWTBearerGrant.GRANT_TYPE
|
|
@@ -88,15 +90,11 @@ class AssertionClient(_AssertionClient, httpx.Client):
|
|
|
88
90
|
claims=None,
|
|
89
91
|
token_placement="header",
|
|
90
92
|
scope=None,
|
|
93
|
+
client_id=None,
|
|
91
94
|
**kwargs,
|
|
92
95
|
):
|
|
93
96
|
client_kwargs = extract_client_kwargs(kwargs)
|
|
94
|
-
|
|
95
|
-
app_value = client_kwargs.pop("app", None)
|
|
96
|
-
if app_value is not None:
|
|
97
|
-
client_kwargs["transport"] = httpx.WSGITransport(app=app_value)
|
|
98
|
-
|
|
99
|
-
httpx.Client.__init__(self, **client_kwargs)
|
|
97
|
+
httpx2.Client.__init__(self, **client_kwargs)
|
|
100
98
|
|
|
101
99
|
_AssertionClient.__init__(
|
|
102
100
|
self,
|
|
@@ -109,6 +107,7 @@ class AssertionClient(_AssertionClient, httpx.Client):
|
|
|
109
107
|
claims=claims,
|
|
110
108
|
token_placement=token_placement,
|
|
111
109
|
scope=scope,
|
|
110
|
+
client_id=client_id,
|
|
112
111
|
**kwargs,
|
|
113
112
|
)
|
|
114
113
|
|
|
@@ -1,10 +1,5 @@
|
|
|
1
1
|
import typing
|
|
2
2
|
|
|
3
|
-
import httpx
|
|
4
|
-
from httpx import Auth
|
|
5
|
-
from httpx import Request
|
|
6
|
-
from httpx import Response
|
|
7
|
-
|
|
8
3
|
from authlib.common.encoding import to_unicode
|
|
9
4
|
from authlib.oauth1 import SIGNATURE_HMAC_SHA1
|
|
10
5
|
from authlib.oauth1 import SIGNATURE_TYPE_HEADER
|
|
@@ -12,12 +7,17 @@ from authlib.oauth1 import ClientAuth
|
|
|
12
7
|
from authlib.oauth1.client import OAuth1Client as _OAuth1Client
|
|
13
8
|
|
|
14
9
|
from ..base_client import OAuthError
|
|
10
|
+
from ._compat import httpx2
|
|
15
11
|
from .utils import build_request
|
|
16
12
|
from .utils import extract_client_kwargs
|
|
17
13
|
|
|
14
|
+
Auth = httpx2.Auth
|
|
15
|
+
Request = httpx2.Request
|
|
16
|
+
Response = httpx2.Response
|
|
17
|
+
|
|
18
18
|
|
|
19
19
|
class OAuth1Auth(Auth, ClientAuth):
|
|
20
|
-
"""Signs the
|
|
20
|
+
"""Signs the httpx2 request using OAuth 1 (RFC5849)."""
|
|
21
21
|
|
|
22
22
|
requires_request_body = True
|
|
23
23
|
|
|
@@ -31,7 +31,7 @@ class OAuth1Auth(Auth, ClientAuth):
|
|
|
31
31
|
)
|
|
32
32
|
|
|
33
33
|
|
|
34
|
-
class AsyncOAuth1Client(_OAuth1Client,
|
|
34
|
+
class AsyncOAuth1Client(_OAuth1Client, httpx2.AsyncClient):
|
|
35
35
|
auth_class = OAuth1Auth
|
|
36
36
|
|
|
37
37
|
def __init__(
|
|
@@ -49,7 +49,7 @@ class AsyncOAuth1Client(_OAuth1Client, httpx.AsyncClient):
|
|
|
49
49
|
**kwargs,
|
|
50
50
|
):
|
|
51
51
|
_client_kwargs = extract_client_kwargs(kwargs)
|
|
52
|
-
|
|
52
|
+
httpx2.AsyncClient.__init__(self, **_client_kwargs)
|
|
53
53
|
|
|
54
54
|
_OAuth1Client.__init__(
|
|
55
55
|
self,
|
|
@@ -99,7 +99,7 @@ class AsyncOAuth1Client(_OAuth1Client, httpx.AsyncClient):
|
|
|
99
99
|
raise OAuthError(error_type, error_description)
|
|
100
100
|
|
|
101
101
|
|
|
102
|
-
class OAuth1Client(_OAuth1Client,
|
|
102
|
+
class OAuth1Client(_OAuth1Client, httpx2.Client):
|
|
103
103
|
auth_class = OAuth1Auth
|
|
104
104
|
|
|
105
105
|
def __init__(
|
|
@@ -117,12 +117,7 @@ class OAuth1Client(_OAuth1Client, httpx.Client):
|
|
|
117
117
|
**kwargs,
|
|
118
118
|
):
|
|
119
119
|
_client_kwargs = extract_client_kwargs(kwargs)
|
|
120
|
-
|
|
121
|
-
app_value = _client_kwargs.pop("app", None)
|
|
122
|
-
if app_value is not None:
|
|
123
|
-
_client_kwargs["transport"] = httpx.WSGITransport(app=app_value)
|
|
124
|
-
|
|
125
|
-
httpx.Client.__init__(self, **_client_kwargs)
|
|
120
|
+
httpx2.Client.__init__(self, **_client_kwargs)
|
|
126
121
|
|
|
127
122
|
_OAuth1Client.__init__(
|
|
128
123
|
self,
|
|
@@ -1,12 +1,11 @@
|
|
|
1
|
+
# ruff: noqa: I001
|
|
2
|
+
# The import order below is intentional: httpx compatibility must be loaded
|
|
3
|
+
# before anyio so import errors refer to httpx.
|
|
1
4
|
import typing
|
|
2
5
|
from contextlib import asynccontextmanager
|
|
3
6
|
|
|
4
|
-
import
|
|
5
|
-
from anyio import Lock
|
|
6
|
-
from httpx import USE_CLIENT_DEFAULT
|
|
7
|
-
from httpx import Auth
|
|
8
|
-
from httpx import Request
|
|
9
|
-
from httpx import Response
|
|
7
|
+
from ._compat import httpx2
|
|
8
|
+
from anyio import Lock # Import after httpx so import errors refer to httpx
|
|
10
9
|
|
|
11
10
|
from authlib.common.urls import url_decode
|
|
12
11
|
from authlib.oauth2.auth import ClientAuth
|
|
@@ -20,6 +19,11 @@ from ..base_client import UnsupportedTokenTypeError
|
|
|
20
19
|
from .utils import HTTPX_CLIENT_KWARGS
|
|
21
20
|
from .utils import build_request
|
|
22
21
|
|
|
22
|
+
USE_CLIENT_DEFAULT = httpx2.USE_CLIENT_DEFAULT
|
|
23
|
+
Auth = httpx2.Auth
|
|
24
|
+
Request = httpx2.Request
|
|
25
|
+
Response = httpx2.Response
|
|
26
|
+
|
|
23
27
|
__all__ = [
|
|
24
28
|
"OAuth2Auth",
|
|
25
29
|
"OAuth2ClientAuth",
|
|
@@ -60,7 +64,7 @@ class OAuth2ClientAuth(Auth, ClientAuth):
|
|
|
60
64
|
)
|
|
61
65
|
|
|
62
66
|
|
|
63
|
-
class AsyncOAuth2Client(_OAuth2Client,
|
|
67
|
+
class AsyncOAuth2Client(_OAuth2Client, httpx2.AsyncClient):
|
|
64
68
|
SESSION_REQUEST_PARAMS = HTTPX_CLIENT_KWARGS
|
|
65
69
|
|
|
66
70
|
client_auth_class = OAuth2ClientAuth
|
|
@@ -81,9 +85,9 @@ class AsyncOAuth2Client(_OAuth2Client, httpx.AsyncClient):
|
|
|
81
85
|
leeway=60,
|
|
82
86
|
**kwargs,
|
|
83
87
|
):
|
|
84
|
-
# extract
|
|
88
|
+
# extract httpx2.Client kwargs
|
|
85
89
|
client_kwargs = self._extract_session_request_params(kwargs)
|
|
86
|
-
|
|
90
|
+
httpx2.AsyncClient.__init__(self, **client_kwargs)
|
|
87
91
|
|
|
88
92
|
# We use a Lock to synchronize coroutines to prevent
|
|
89
93
|
# multiple concurrent attempts to refresh the same token
|
|
@@ -208,7 +212,7 @@ class AsyncOAuth2Client(_OAuth2Client, httpx.AsyncClient):
|
|
|
208
212
|
)
|
|
209
213
|
|
|
210
214
|
|
|
211
|
-
class OAuth2Client(_OAuth2Client,
|
|
215
|
+
class OAuth2Client(_OAuth2Client, httpx2.Client):
|
|
212
216
|
SESSION_REQUEST_PARAMS = HTTPX_CLIENT_KWARGS
|
|
213
217
|
|
|
214
218
|
client_auth_class = OAuth2ClientAuth
|
|
@@ -228,14 +232,9 @@ class OAuth2Client(_OAuth2Client, httpx.Client):
|
|
|
228
232
|
update_token=None,
|
|
229
233
|
**kwargs,
|
|
230
234
|
):
|
|
231
|
-
# extract
|
|
235
|
+
# extract httpx2.Client kwargs
|
|
232
236
|
client_kwargs = self._extract_session_request_params(kwargs)
|
|
233
|
-
|
|
234
|
-
app_value = client_kwargs.pop("app", None)
|
|
235
|
-
if app_value is not None:
|
|
236
|
-
client_kwargs["transport"] = httpx.WSGITransport(app=app_value)
|
|
237
|
-
|
|
238
|
-
httpx.Client.__init__(self, **client_kwargs)
|
|
237
|
+
httpx2.Client.__init__(self, **client_kwargs)
|
|
239
238
|
|
|
240
239
|
_OAuth2Client.__init__(
|
|
241
240
|
self,
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
from
|
|
1
|
+
from ._compat import httpx2
|
|
2
|
+
|
|
3
|
+
Request = httpx2.Request
|
|
2
4
|
|
|
3
5
|
HTTPX_CLIENT_KWARGS = [
|
|
4
6
|
"headers",
|
|
@@ -32,7 +34,10 @@ def extract_client_kwargs(kwargs):
|
|
|
32
34
|
def build_request(url, headers, body, initial_request: Request) -> Request:
|
|
33
35
|
"""Make sure that all the data from initial request is passed to the updated object."""
|
|
34
36
|
updated_request = Request(
|
|
35
|
-
method=initial_request.method,
|
|
37
|
+
method=initial_request.method,
|
|
38
|
+
url=url,
|
|
39
|
+
headers=headers,
|
|
40
|
+
content=body,
|
|
36
41
|
)
|
|
37
42
|
|
|
38
43
|
if hasattr(initial_request, "extensions"):
|
|
@@ -39,6 +39,7 @@ class AssertionSession(AssertionClient, Session):
|
|
|
39
39
|
claims=None,
|
|
40
40
|
token_placement="header",
|
|
41
41
|
scope=None,
|
|
42
|
+
client_id=None,
|
|
42
43
|
default_timeout=None,
|
|
43
44
|
leeway=60,
|
|
44
45
|
**kwargs,
|
|
@@ -57,6 +58,7 @@ class AssertionSession(AssertionClient, Session):
|
|
|
57
58
|
claims=claims,
|
|
58
59
|
token_placement=token_placement,
|
|
59
60
|
scope=scope,
|
|
61
|
+
client_id=client_id,
|
|
60
62
|
leeway=leeway,
|
|
61
63
|
**kwargs,
|
|
62
64
|
)
|
|
@@ -25,6 +25,7 @@ class AssertionClient:
|
|
|
25
25
|
claims=None,
|
|
26
26
|
token_placement="header",
|
|
27
27
|
scope=None,
|
|
28
|
+
client_id=None,
|
|
28
29
|
leeway=60,
|
|
29
30
|
**kwargs,
|
|
30
31
|
):
|
|
@@ -46,6 +47,7 @@ class AssertionClient:
|
|
|
46
47
|
self.audience = audience
|
|
47
48
|
self.claims = claims
|
|
48
49
|
self.scope = scope
|
|
50
|
+
self.client_id = client_id
|
|
49
51
|
if self.token_auth_class is not None:
|
|
50
52
|
self.token_auth = self.token_auth_class(None, token_placement, self)
|
|
51
53
|
self._kwargs = kwargs
|
|
@@ -79,6 +81,8 @@ class AssertionClient:
|
|
|
79
81
|
}
|
|
80
82
|
if self.scope:
|
|
81
83
|
data["scope"] = self.scope
|
|
84
|
+
if self.client_id:
|
|
85
|
+
data["client_id"] = self.client_id
|
|
82
86
|
|
|
83
87
|
return self._refresh_token(data)
|
|
84
88
|
|
|
@@ -41,6 +41,12 @@ def sign_jwt_bearer_assertion(
|
|
|
41
41
|
payload["iat"] = issued_at
|
|
42
42
|
payload["exp"] = expires_at
|
|
43
43
|
|
|
44
|
+
if claims is None:
|
|
45
|
+
claims = {}
|
|
46
|
+
# jti uniquely identifies the JWT and prevents replay attacks (RFC 7523 §3)
|
|
47
|
+
if "jti" not in claims:
|
|
48
|
+
claims["jti"] = generate_token(36)
|
|
49
|
+
|
|
44
50
|
if claims:
|
|
45
51
|
payload.update(claims)
|
|
46
52
|
|
|
@@ -46,7 +46,15 @@ class JWTBearerClientAssertion:
|
|
|
46
46
|
assertion = data.get("client_assertion")
|
|
47
47
|
if assertion_type == ASSERTION_TYPE and assertion:
|
|
48
48
|
headers, claims = self.extract_assertion(assertion)
|
|
49
|
+
# "sub" is read before the signature and the claims are verified,
|
|
50
|
+
# so it cannot be assumed to be present nor to be a string.
|
|
51
|
+
if "sub" not in claims:
|
|
52
|
+
raise InvalidClientError(description="Missing claim: 'sub'")
|
|
53
|
+
|
|
49
54
|
client_id = claims["sub"]
|
|
55
|
+
if not isinstance(client_id, str):
|
|
56
|
+
raise InvalidClientError(description="Invalid claim: 'sub'")
|
|
57
|
+
|
|
50
58
|
client = query_client(client_id)
|
|
51
59
|
if not client:
|
|
52
60
|
raise InvalidClientError(
|
|
@@ -137,11 +145,20 @@ class JWTBearerClientAssertion:
|
|
|
137
145
|
)
|
|
138
146
|
|
|
139
147
|
def extract_assertion(self, assertion: str):
|
|
140
|
-
|
|
148
|
+
try:
|
|
149
|
+
obj = jws.extract_compact(to_bytes(assertion))
|
|
150
|
+
except JoseError as e:
|
|
151
|
+
log.debug("Assertion Error: %r", e)
|
|
152
|
+
raise InvalidClientError(description="Invalid JWT assertion.") from e
|
|
153
|
+
|
|
141
154
|
try:
|
|
142
155
|
claims = json_loads(obj.payload)
|
|
143
156
|
except ValueError:
|
|
144
157
|
raise InvalidClientError(description="Invalid JWT payload.") from None
|
|
158
|
+
|
|
159
|
+
if not isinstance(claims, dict):
|
|
160
|
+
raise InvalidClientError(description="Invalid JWT payload.")
|
|
161
|
+
|
|
145
162
|
return obj.headers(), claims
|
|
146
163
|
|
|
147
164
|
def validate_jti(self, claims, jti):
|
|
@@ -82,7 +82,20 @@ class JWTBearerGrant(BaseGrant, TokenEndpointMixin):
|
|
|
82
82
|
.. _`Section 3.1`: https://tools.ietf.org/html/rfc7523#section-3.1
|
|
83
83
|
"""
|
|
84
84
|
headers, claims = self.extract_assertion(assertion)
|
|
85
|
-
|
|
85
|
+
# "iss" is read before the signature and the claims are verified, so it
|
|
86
|
+
# cannot be assumed to be present nor to be a string.
|
|
87
|
+
if "iss" not in claims:
|
|
88
|
+
raise InvalidGrantError(description="Missing claim: 'iss'")
|
|
89
|
+
|
|
90
|
+
issuer = claims["iss"]
|
|
91
|
+
if not isinstance(issuer, str):
|
|
92
|
+
raise InvalidGrantError(description="Invalid claim: 'iss'")
|
|
93
|
+
|
|
94
|
+
client = self.resolve_issuer_client(issuer)
|
|
95
|
+
if not client:
|
|
96
|
+
raise InvalidGrantError(
|
|
97
|
+
description="The client does not exist on this server."
|
|
98
|
+
)
|
|
86
99
|
|
|
87
100
|
if hasattr(self, "resolve_client_key"): # pragma: no cover
|
|
88
101
|
key = import_any_key(self.resolve_client_key(client, headers, claims))
|
|
@@ -106,11 +119,20 @@ class JWTBearerGrant(BaseGrant, TokenEndpointMixin):
|
|
|
106
119
|
return token.claims
|
|
107
120
|
|
|
108
121
|
def extract_assertion(self, assertion: str):
|
|
109
|
-
|
|
122
|
+
try:
|
|
123
|
+
obj = jws.extract_compact(to_bytes(assertion))
|
|
124
|
+
except JoseError as e:
|
|
125
|
+
log.debug("Assertion Error: %r", e)
|
|
126
|
+
raise InvalidGrantError(description="Invalid JWT assertion.") from e
|
|
127
|
+
|
|
110
128
|
try:
|
|
111
129
|
claims = json_loads(obj.payload)
|
|
112
130
|
except ValueError:
|
|
113
131
|
raise InvalidGrantError(description="Invalid JWT payload.") from None
|
|
132
|
+
|
|
133
|
+
if not isinstance(claims, dict):
|
|
134
|
+
raise InvalidGrantError(description="Invalid JWT payload.")
|
|
135
|
+
|
|
114
136
|
return obj.headers(), claims
|
|
115
137
|
|
|
116
138
|
def validate_token_request(self):
|
|
@@ -194,7 +216,8 @@ class JWTBearerGrant(BaseGrant, TokenEndpointMixin):
|
|
|
194
216
|
return Client.query_by_iss(issuer)
|
|
195
217
|
|
|
196
218
|
:param issuer: "iss" value in assertion
|
|
197
|
-
:return: Client instance
|
|
219
|
+
:return: Client instance, or ``None`` when no client matches the
|
|
220
|
+
issuer. In that case an ``invalid_grant`` error is returned.
|
|
198
221
|
"""
|
|
199
222
|
raise NotImplementedError()
|
|
200
223
|
|
|
@@ -48,7 +48,7 @@ class JWTBearerTokenGenerator:
|
|
|
48
48
|
def get_sub_value(user):
|
|
49
49
|
return str(user.id)
|
|
50
50
|
"""
|
|
51
|
-
return user.get_user_id()
|
|
51
|
+
return str(user.get_user_id())
|
|
52
52
|
|
|
53
53
|
def get_token_data(self, grant_type, client, expires_in, user=None, scope=None):
|
|
54
54
|
scope = self.get_allowed_scope(client, scope)
|
|
@@ -33,9 +33,10 @@ class JWTBearerTokenValidator(BearerTokenValidator):
|
|
|
33
33
|
TOKEN_TYPE = "bearer"
|
|
34
34
|
token_cls = JWTBearerToken
|
|
35
35
|
|
|
36
|
-
def __init__(self, public_key, issuer=None, realm=None, **extra_attributes):
|
|
36
|
+
def __init__(self, public_key, issuer=None, realm=None, leeway=60, **extra_attributes):
|
|
37
37
|
super().__init__(realm, **extra_attributes)
|
|
38
38
|
self.public_key = import_any_key(public_key)
|
|
39
|
+
self.leeway = leeway
|
|
39
40
|
claims_options = {
|
|
40
41
|
"exp": {"essential": True},
|
|
41
42
|
"client_id": {"essential": True},
|
|
@@ -52,7 +53,7 @@ class JWTBearerTokenValidator(BearerTokenValidator):
|
|
|
52
53
|
logger.debug("Authenticate token failed. %r", error)
|
|
53
54
|
return None
|
|
54
55
|
|
|
55
|
-
claims_requests = jwt.JWTClaimsRegistry(leeway=
|
|
56
|
+
claims_requests = jwt.JWTClaimsRegistry(leeway=self.leeway, **self.claims_options)
|
|
56
57
|
try:
|
|
57
58
|
claims_requests.validate(token.claims)
|
|
58
59
|
except JoseError as error:
|
|
@@ -115,7 +115,7 @@ class DeviceAuthorizationEndpoint:
|
|
|
115
115
|
}
|
|
116
116
|
|
|
117
117
|
self.save_device_credential(
|
|
118
|
-
request.
|
|
118
|
+
request.client.get_client_id(), request.payload.scope, data
|
|
119
119
|
)
|
|
120
120
|
return 200, data, default_json_headers
|
|
121
121
|
|
|
@@ -144,7 +144,7 @@ class JWTBearerTokenGenerator(BearerTokenGenerator):
|
|
|
144
144
|
# correspond to the subject identifier of the resource owner.
|
|
145
145
|
|
|
146
146
|
if user:
|
|
147
|
-
token_data["sub"] = user.get_user_id()
|
|
147
|
+
token_data["sub"] = str(user.get_user_id())
|
|
148
148
|
|
|
149
149
|
# In cases of access tokens obtained through grants where no resource owner is
|
|
150
150
|
# involved, such as the client credentials grant, the value of 'sub' SHOULD
|
|
@@ -7,7 +7,6 @@ Implementation of Validating JWT Access Tokens per `Section 4`_.
|
|
|
7
7
|
"""
|
|
8
8
|
|
|
9
9
|
from joserfc import jwt
|
|
10
|
-
from joserfc.errors import DecodeError
|
|
11
10
|
from joserfc.errors import JoseError
|
|
12
11
|
|
|
13
12
|
from authlib._joserfc_helpers import import_any_key
|
|
@@ -115,7 +114,7 @@ class JWTBearerTokenValidator(BearerTokenValidator):
|
|
|
115
114
|
try:
|
|
116
115
|
token = jwt.decode(token_string, key=key)
|
|
117
116
|
return JWTAccessTokenClaims(token.claims, token.header, claims_options)
|
|
118
|
-
except
|
|
117
|
+
except JoseError as exc:
|
|
119
118
|
raise InvalidTokenError(
|
|
120
119
|
realm=self.realm, extra_attributes=self.extra_attributes
|
|
121
120
|
) from exc
|