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]

Reply via email to