Repository navigation
Add Keycloak browser login and token refresh for CLI clients - #75913
baptistegh wants to merge 4 commits into
Conversation
24616a7 to
f7a8eb5
Compare
|
The CLI never talks to Keycloak's token endpoint and never gets Keycloak tokens. Keycloak sends the authorization code to the CLI's loopback callback. The CLI passes the code and its PKCE So the existing confidential client is enough. Airflow enforces PKCE (S256) and loopback-only redirect URIs itself. An intercepted code is useless without the client secret and the A separate public client would need its own config option, an audience mapper so its tokens still pass authorization against the |
| "apache-airflow>=3.0.0", | ||
| "apache-airflow-providers-common-compat>=1.18.0", | ||
| "python-keycloak>=5.0.0", | ||
| "python-keycloak>=6.0.0", |
There was a problem hiding this comment.
Is that needed? It might break existing users no?
There was a problem hiding this comment.
Yes, PKCE flow was added by marcospereirampj/python-keycloak#670 in v5.12.0
We can try with >=5.12.0 this would not be a major update.
There was a problem hiding this comment.
I would do that, @stegololz played a bit with Keycloak 6 I think, so he might have some insights here?
c97ba08 to
8409ab9
Compare
Command-line clients could only log in to the Keycloak auth manager with a username and password, which does not work for users who sign in through SSO or with a second factor. The CLI login discovery route lets an auth manager advertise other login methods, and Keycloak is the first to offer one. The CLI token embeds a Keycloak access token that usually expires within minutes, so a CLI session also needs a way to get fresh Keycloak tokens without ever outliving the original CLI token. The airflowctl side follows in a separate change. Generated-by: Claude Code (Opus 5.5) Signed-off-by: Baptiste Gouhoury <gouhoury.baptiste@gmail.com>
Refreshing a CLI token assumed Keycloak always returns a new refresh token. Clients with "Use Refresh Tokens" turned off return none at all, and RFC 6749 section 6 lets any refresh response omit one and keep the previous token valid, so those deployments could not refresh. Where a token genuinely cannot be refreshed, a dedicated response tells the user to log in again instead of leaving a valid token looking invalid. Token validation allows [api_auth] jwt_leeway, so a token that had just expired was still accepted and refreshed into one with a lifetime in the past. A request body that is valid JSON but not an object reached a validator that assumed a mapping, so clients got 500 where the request was simply malformed. The same validator guards the pre-existing /auth/token route. The floor on python-keycloak was raised further than the PKCE support this feature needs, which landed in 5.12.0, and would have forced a major upgrade on existing users for no gain. Generated-by: Claude Code (Opus 5) Signed-off-by: BAPTISTE GOUHOURY <baptiste-externe.gouhoury@edf.fr>
Logging out revokes only the token the CLI currently holds. Tokens it had refreshed away from stayed valid and refreshable until they expired, so a logged-out CLI session could be resumed with any earlier token. Generated-by: Claude Code (Opus 5.5) Signed-off-by: Baptiste Gouhoury <gouhoury.baptiste@gmail.com>
07f27ff to
153ccf1
Compare
|
Hello @vincbeck, thanks for your review. I tried to fix as much as I can. I still have 3 things that came up :
|
The /login/cli docstring is published as the endpoint description in the OpenAPI spec, so implementation details about PKCE did not belong there. Several comments restated the code or explained a refactor that is no longer visible, and the browser login flag was read in two places. Generated-by: Claude Code (Opus 5.5) Signed-off-by: Baptiste Gouhoury <gouhoury.baptiste@gmail.com>
Command-line clients can only log in to the Keycloak auth manager with a username and password, which doesn't work for users who sign in through SSO or with a second factor. This lets them log in through Keycloak in their browser, and refresh the CLI token's short-lived Keycloak tokens. Keycloak advertises both through the CLI login discovery route from #75672.
This is the provider side only. The airflowctl side (
auth login --sso) follows in #75804, which also shows the whole flow end to end.related: #75560
Changes
get_cli_login_methods()always advertisespasswordandrefresh_token. It addsauthorization_codewithS256only when[keycloak_auth_manager] cli_authorization_code_enabledis on (defaultFalse)./auth/token/cligets its own body:password(the default, so existing clients are unaffected),refresh_tokenorauthorization_code.client_credentialsandjwt-bearerstay on/auth/tokenonly.GET /auth/login/clirequires a loopbackredirect_uri(http://127.0.0.1or[::1], any port, path/callback) and anS256challenge, then forwards both to Keycloak. Airflow enforces PKCE here rather than on the Keycloak client, because the web login doesn't use it.get_user_from_token.python-keycloak>=6.0.0for PKCE support.Trying it without airflowctl
With
cli_authorization_code_enabled = Trueandhttp://127.0.0.1/callbackin the Keycloak client's valid redirect URIs:Was generative AI tooling used to co-author this PR?
Generated-by: Claude Code (Opus 5.5) following the guidelines