Description
Describe the bug
When an identity provider has multiple keys (for example one for signature and one for encryption), and when the signature key and encryption key are identical, the authorize_access_token call can fail depending on the order in which the identity provider returns the keys.
The issue comes from the create_load_key method, which selects the key via a find_by_kid
call. If the server returns keys with identical kid
's, which can be the case if the certificates themselves are identical (e.g. keycloak will return identical kid
's and this is not configurable), then the find_by_kid
will select the first key matching the kid
. If this first key's use is set to encryption (because it's the 2nd identical key whose use is set to signature), this causes subsequent validation to raise an exception.
I am linking here to the flask integration for authorize_access_token
, but I believe the issue is relevant to other integrations and possibly other use cases as well.
Error Stacks
Traceback (most recent call last):
File "test-case/.venv/lib64/python3.12/site-packages/flask/app.py", line 1536, in __call__
return self.wsgi_app(environ, start_response)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "test-case/.venv/lib64/python3.12/site-packages/flask/app.py", line 1514, in wsgi_app
response = self.handle_exception(e)
^^^^^^^^^^^^^^^^^^^^^^^^
File "test-case/.venv/lib64/python3.12/site-packages/flask/app.py", line 1511, in wsgi_app
response = self.full_dispatch_request()
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "test-case/.venv/lib64/python3.12/site-packages/flask/app.py", line 919, in full_dispatch_request
rv = self.handle_user_exception(e)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "test-case/.venv/lib64/python3.12/site-packages/flask/app.py", line 917, in full_dispatch_request
rv = self.dispatch_request()
^^^^^^^^^^^^^^^^^^^^^^^
File "test-case/.venv/lib64/python3.12/site-packages/flask/app.py", line 902, in dispatch_request
return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args) # type: ignore[no-any-return]
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "test-case/.venv/lib64/python3.12/site-packages/flask_oidc/views.py", line 60, in authorize_view
token = g._oidc_auth.authorize_access_token()
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "test-case/.venv/lib64/python3.12/site-packages/authlib/integrations/flask_client/apps.py", line 114, in authorize_access_token
userinfo = self.parse_id_token(
^^^^^^^^^^^^^^^^^^^^
File "test-case/.venv/lib64/python3.12/site-packages/authlib/integrations/base_client/sync_openid.py", line 65, in parse_id_token
claims = _jwt.decode(
^^^^^^^^^^^^
File "test-case/.venv/lib64/python3.12/site-packages/authlib/jose/rfc7519/jwt.py", line 104, in decode
data = self._jws.deserialize_compact(s, load_key, decode_payload)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "test-case/.venv/lib64/python3.12/site-packages/authlib/jose/rfc7515/jws.py", line 107, in deserialize_compact
if algorithm.verify(signing_input, signature, key):
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "test-case/.venv/lib64/python3.12/site-packages/authlib/jose/rfc7518/jws_algs.py", line 98, in verify
op_key = key.get_op_key("verify")
^^^^^^^^^^^^^^^^^^^^^^^^
File "test-case/.venv/lib64/python3.12/site-packages/authlib/jose/rfc7517/asymmetric_key.py", line 42, in get_op_key
self.check_key_op(operation)
File "test-case/.venv/lib64/python3.12/site-packages/authlib/jose/rfc7517/base_key.py", line 87, in check_key_op
raise InvalidUseError()
authlib.jose.errors.InvalidUseError: invalid_use: Key 'use' is not valid for your usage"""
To Reproduce
Any minimal example that uses flask integration, as long as the identity provider is configured to return 2 or more identical certificates with different uses.
Expected behavior
AuthLib should select the right key (i.e. try to find the signature-use key), no exception should be raised, and authentication should continue.
Environment:
- OS: Linux
- Python Version: 3.12
- Authlib Version: 1.5.2
Additional context
Add any other context about the problem here.