-
Notifications
You must be signed in to change notification settings - Fork 16.7k
Fix Internal Server Error in API server due to KeycloakPostError when refreshing user in Keycloak provider#59361
Fix Internal Server Error in API server due to KeycloakPostError when refreshing user in Keycloak provider#59361vincbeck merged 3 commits intoapache:mainfrom
Conversation
This PR fixes issue #59359 which fixes an Internal Server Error in the Airflow API server due to a KeycloakPostError when refreshing user in Keycloak provider.
This is because the Airflow API server has a cookie with an expired _token. That token never expires due to an expiration set to Session, thus the cookie is never invalided which at a certain time leads to an invalid token send to Keycloak.
This leads to following exception in API server which makes the API server respond with a HTTP 500 Internal Server Error:
INFO: 172.31.52.95:0 - "GET /favicon.ico HTTP/1.1" 500 Internal Server Error
ERROR: Exception in ASGI application
+ Exception Group Traceback (most recent call last):
| File "/usr/local/lib/python3.13/site-packages/starlette/_utils.py ", line 79, in collapse_excgroups
| yield
| File "/usr/local/lib/python3.13/site-packages/starlette/middlewar e/base.py", line 183, in __call__
| async with anyio.create_task_group() as task_group:
| ~~~~~~~~~~~~~~~~~~~~~~~^^
| File "/usr/local/lib/python3.13/site-packages/anyio/_backends/_as yncio.py", line 783, in __aexit__
| raise BaseExceptionGroup(
| "unhandled errors in a TaskGroup", self._exceptions
| ) from None
| ExceptionGroup: unhandled errors in a TaskGroup (1 sub-exception)
+-+---------------- 1 ----------------
| Traceback (most recent call last):
| File "/usr/local/lib/python3.13/site-packages/uvicorn/protocols/h ttp/httptools_impl.py", line 409, in run_asgi
| result = await app( # type: ignore[func-returns-value]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| self.scope, self.receive, self.send
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| )
| ^
| File "/usr/local/lib/python3.13/site-packages/uvicorn/middleware/ proxy_headers.py", line 60, in __call__
| return await self.app(scope, receive, send)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| File "/usr/local/lib/python3.13/site-packages/fastapi/application s.py", line 1082, in __call__
| await super().__call__(scope, receive, send)
| File "/usr/local/lib/python3.13/site-packages/starlette/applicati ons.py", line 113, in __call__
| await self.middleware_stack(scope, receive, send)
| File "/usr/local/lib/python3.13/site-packages/starlette/middlewar e/errors.py", line 186, in __call__
| raise exc
| File "/usr/local/lib/python3.13/site-packages/starlette/middlewar e/errors.py", line 164, in __call__
| await self.app(scope, receive, _send)
| File "/usr/local/lib/python3.13/site-packages/starlette/middlewar e/gzip.py", line 29, in __call__
| await responder(scope, receive, send)
| File "/usr/local/lib/python3.13/site-packages/starlette/middlewar e/gzip.py", line 130, in __call__
| await super().__call__(scope, receive, send)
| File "/usr/local/lib/python3.13/site-packages/starlette/middlewar e/gzip.py", line 46, in __call__
| await self.app(scope, receive, self.send_with_compression)
| File "/usr/local/lib/python3.13/site-packages/starlette/middlewar e/cors.py", line 85, in __call__
| await self.app(scope, receive, send)
| File "/usr/local/lib/python3.13/site-packages/starlette/middlewar e/base.py", line 182, in __call__
| with recv_stream, send_stream, collapse_excgroups():
| ~~~~~~~~~~~~~~~~~~^^
| File "/usr/lib64/python3.13/contextlib.py", line 162, in __exit__
| self.gen.throw(value)
| ~~~~~~~~~~~~~~^^^^^^^
| File "/usr/local/lib/python3.13/site-packages/starlette/_utils.py ", line 85, in collapse_excgroups
| raise exc
| File "/usr/local/lib/python3.13/site-packages/starlette/middlewar e/base.py", line 184, in __call__
| response = await self.dispatch_func(request, call_next)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| File "/usr/local/lib/python3.13/site-packages/airflow/api_fastapi /auth/middlewares/refresh_token.py", line 45, in dispatch
| new_user = await self._refresh_user(current_token)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| File "/usr/local/lib/python3.13/site-packages/airflow/api_fastapi /auth/middlewares/refresh_token.py", line 68, in _refresh_user
| return get_auth_manager().refresh_user(user=user)
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^
| File "/usr/local/lib/python3.13/site-packages/airflow/providers/k eycloak/auth_manager/keycloak_auth_manager.py", line 121, in refresh_user
| tokens = client.refresh_token(user.refresh_token)
| File "/usr/local/lib/python3.13/site-packages/keycloak/keycloak_o penid.py", line 410, in refresh_token
| return raise_error_from_response(data_raw, KeycloakPostError)
| File "/usr/local/lib/python3.13/site-packages/keycloak/exceptions .py", line 195, in raise_error_from_response
| raise error(
| ...<3 lines>...
| )
| keycloak.exceptions.KeycloakPostError: 400: b'{"error":"invalid_grant","error_description":"Token is not active"}'
+------------------------------------
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/usr/local/lib/python3.13/site-packages/uvicorn/protocols/h ttp/httptools_impl.py", line 409, in run_asgi
result = await app( # type: ignore[func-returns-value]
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
self.scope, self.receive, self.send
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
)
^
File "/usr/local/lib/python3.13/site-packages/uvicorn/middleware/ proxy_headers.py", line 60, in __call__
return await self.app(scope, receive, send)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.13/site-packages/fastapi/application s.py", line 1082, in __call__
await super().__call__(scope, receive, send)
File "/usr/local/lib/python3.13/site-packages/starlette/applicati ons.py", line 113, in __call__
await self.middleware_stack(scope, receive, send)
File "/usr/local/lib/python3.13/site-packages/starlette/middlewar e/errors.py", line 186, in __call__
raise exc
File "/usr/local/lib/python3.13/site-packages/starlette/middlewar e/errors.py", line 164, in __call__
await self.app(scope, receive, _send)
File "/usr/local/lib/python3.13/site-packages/starlette/middlewar e/gzip.py", line 29, in __call__
await responder(scope, receive, send)
File "/usr/local/lib/python3.13/site-packages/starlette/middlewar e/gzip.py", line 130, in __call__
await super().__call__(scope, receive, send)
File "/usr/local/lib/python3.13/site-packages/starlette/middlewar e/gzip.py", line 46, in __call__
await self.app(scope, receive, self.send_with_compression)
File "/usr/local/lib/python3.13/site-packages/starlette/middlewar e/cors.py", line 85, in __call__
await self.app(scope, receive, send)
File "/usr/local/lib/python3.13/site-packages/starlette/middlewar e/base.py", line 182, in __call__
with recv_stream, send_stream, collapse_excgroups():
~~~~~~~~~~~~~~~~~~^^
File "/usr/lib64/python3.13/contextlib.py", line 162, in __exit__
self.gen.throw(value)
~~~~~~~~~~~~~~^^^^^^^
File "/usr/local/lib/python3.13/site-packages/starlette/_utils.py ", line 85, in collapse_excgroups
raise exc
File "/usr/local/lib/python3.13/site-packages/starlette/middlewar e/base.py", line 184, in __call__
response = await self.dispatch_func(request, call_next)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.13/site-packages/airflow/api_fastapi /auth/middlewares/refresh_token.py", line 45, in dispatch
new_user = await self._refresh_user(current_token)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.13/site-packages/airflow/api_fastapi /auth/middlewares/refresh_token.py", line 68, in _refresh_user
return get_auth_manager().refresh_user(user=user)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^
File "/usr/local/lib/python3.13/site-packages/airflow/providers/k eycloak/auth_manager/keycloak_auth_manager.py", line 121, in refresh_user
tokens = client.refresh_token(user.refresh_token)
File "/usr/local/lib/python3.13/site-packages/keycloak/keycloak_o penid.py", line 410, in refresh_token
return raise_error_from_response(data_raw, KeycloakPostError)
File "/usr/local/lib/python3.13/site-packages/keycloak/exceptions .py", line 195, in raise_error_from_response
raise error(
...<3 lines>...
)
keycloak.exceptions.KeycloakPostError: 400: b'{"error":"invalid_grant","error_description":"Token is not active"}'
^ Add meaningful description above
Read the Pull Request Guidelines for more information.
In case of fundamental code changes, an Airflow Improvement Proposal (AIP) is needed.
In case of a new dependency, check compliance with the ASF 3rd Party License Policy.
In case of backwards incompatible changes please leave a note in a newsfragment file, named {pr_number}.significant.rst or {issue_number}.significant.rst, in airflow-core/newsfragments.
vincbeck
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice!
* fix: fix KeycloakPostError: 400: b'{"error":"invalid_grant","error_description":"Token is not active"}'
* refactor: Reformatted test_refresh_user_expired_with_invalid_token
* fix: fix KeycloakPostError: 400: b'{"error":"invalid_grant","error_description":"Token is not active"}'
* refactor: Reformatted test_refresh_user_expired_with_invalid_token
* fix: fix KeycloakPostError: 400: b'{"error":"invalid_grant","error_description":"Token is not active"}'
* refactor: Reformatted test_refresh_user_expired_with_invalid_token