greekera1000 commented on issue #72351:
URL: https://github.com/apache/airflow/issues/72351#issuecomment-5536068409
I hit the same thing. The analysis here is correct about the immediate
cause, but I think the suggested minimal fix will only turn the 500 into a 403.
On 3.3+, `serialize_user` deliberately drops the Keycloak tokens from the
Airflow JWT claims:
```python
def serialize_user(self, user):
if AIRFLOW_V_3_3_PLUS:
# Omit Keycloak JWTs from claims, they are stored in separate cookies
return {"user_id": user.get_id(), "name": user.get_name()}
...
```
and `deserialize_user` falls back to `access_token=token.get("access_token",
"")`. So returning the user instead of `None` gives a bearer caller a user
whose `access_token` is `""`; `_is_authorized` POSTs `Authorization: Bearer `
to the UMA endpoint, Keycloak answers 401, and the request is denied.
The root cause looks like the cookie-splitting from #70800 being applied to
token minting in general rather than only to the browser cookie path. The 4 KB
limit that motivated it (#61771) is a browser constraint and doesn't apply to
`Authorization: Bearer`, but `POST /auth/token` with
`grant_type=client_credentials` goes through `generate_jwt()` →
`serialize_user()`, so a non-browser client has no way to get a token carrying
the Keycloak access token.
Happy to put up a PR, but I'd like a steer on the first half before I do,
since it's a design call:
1. Keep the Keycloak tokens in the claims for tokens that aren't destined
for cookies — an `include_tokens` flag on `serialize_user` (default `True`)
that the login/cookie path passes `False` for, or have the token route build
its claims directly. Is there a preference between those, or a reason the
client-credentials token shouldn't carry the Keycloak JWT at all?
2. Stop returning `None` from `get_user_from_token`; apply cookie tokens
when present, otherwise keep what came from the claims:
```python
user = cast("KeycloakAuthManagerUser", await
super().get_user_from_token(token))
if not AIRFLOW_V_3_3_PLUS:
return user
if access_token:
user.access_token = access_token
user.refresh_token = refresh_token
return user
```
`refresh_user` already returns `None` when there's no refresh token, so the
service-account path is unaffected.
I'd add a regression test that mints a token through
`serialize_user`/`generate_jwt`, resolves it via `get_user_from_token(token)`
with no cookies, and asserts the user has a usable `access_token`.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]