SIGN IN SIGN UP

Keycloak auth manager: keep API tokens usable with the Authorization (#72381)

On Airflow 3.3+, a token from `POST /auth/token` no longer authorized anything.
`serialize_user()` omits the Keycloak JWTs from the claims, and the only code
that supplies them again, `KeycloakJWTMiddleware`, reads them from cookies. A
client authenticating with the `Authorization` header sends no cookies, so
`get_user_from_token()` fell through to `return None` and the request failed --
in practice with a 500, because the `None` reaches the authorization layer and
raises `AttributeError: 'NoneType' object has no attribute 'get_id'`.

The tokens were moved into cookies to keep the browser session cookie under the
4096 byte limit. That constraint does not apply to a token handed to an API
client, which is never stored in a cookie, so the two paths can differ:

- `generate_api_jwt()` mints tokens that keep the Keycloak JWTs in their claims,
  and `POST /auth/token` uses it for both the password and client-credentials
  grants. It builds on `serialize_user()` so an API token cannot silently miss a
  claim the browser flow gains later.
- The browser paths, `routes/login.py` and the middleware, are unchanged and
  still mint claim-free tokens backed by the cookies.
- `get_user_from_token()` falls back to the claims when no cookie-supplied
  tokens are present.

The fallback does not weaken the subject binding added for the cookie flow.
That check exists because cookies are not covered by the Airflow JWT signature,
so a caller could pair their own session with somebody else's Keycloak token.
In the claims both values come from the same signed payload, and both minting
sites derive them from one Keycloak response, so a mismatched pair cannot be
constructed.

A browser token still resolves to `None` without its cookies: it carries no
claims to fall back on. Tested alongside the fix so the two paths stay distinct.

Fixes #72352
E
Ed Summers committed
dc529a12f1bd54650523beecfa931bf31fc3f03d
Parent: 760ee9a
Committed by GitHub <noreply@github.com> on 9/4/2026, 12:39:11 PM