Skip to content

Add browser login to airflowctl - #75938

Open
baptistegh wants to merge 3 commits into
apache:mainfrom
baptistegh:airflowctl-keycloak-sso-preview
Open

baptistegh wants to merge 3 commits into
apache:mainfrom
baptistegh:airflowctl-keycloak-sso-preview

Conversation

@baptistegh

Copy link
Copy Markdown
Contributor

Users who sign in to Airflow through SSO or with a second factor have no password to give airflowctl. This adds airflowctl auth login --sso (and auth token --sso), which logs in through the browser when the API server's auth manager offers it through the CLI login discovery route from #75672.

airflowctl only follows the contract documented in core, so it doesn't know or care which auth manager serves it. The Keycloak auth manager is the first to offer browser login, in #75913. The two PRs are independent and can be merged in either order.

related: #75560

Changes

  • auth login --sso / auth token --sso read GET /api/v2/auth/cli/metadata. They need authorization_code with S256 and both endpoints, otherwise they fail at once with "does not offer browser login (--sso)". A 404 (an API server without discovery) counts as offering nothing.
  • The browser flow is authorization code + PKCE (S256, RFC 7636) over a loopback redirect (RFC 8252): a one-shot HTTP server on 127.0.0.1 with a random port, a random state checked on return, and no request logging, so the code never reaches stderr.
  • --no-browser only prints the login URL (for example over ssh -L). It is not the device code flow, which comes in a later PR.
  • --timeout (default 300 seconds) bounds the wait for the browser.
  • --sso refuses --api-token, --username, --password and --skip-keyring.
  • Docs: start.rst describes the new login, and the auth login help image is regenerated.

Not included: refreshing the CLI token with the refresh_token grant, and the device code flow. Both follow in separate PRs.

Trying it

With #75913 and [keycloak_auth_manager] cli_authorization_code_enabled = True:

airflowctl auth login --sso --api-url http://localhost:8080 --env sso
airflowctl dags list --env sso

Was generative AI tooling used to co-author this PR?
  • Yes (please specify the tool below)

Generated-by: Claude Code (Opus 5.5) following the guidelines

Users who sign in to Airflow through SSO or with a second factor have no
password to give airflowctl, so they could only log in by copying a
token from somewhere else. Auth managers can now advertise a browser
login through the CLI login discovery route, and airflowctl can use it
without knowing which auth manager serves it.

Servers without discovery, or whose auth manager does not offer browser
login, fail with a clear message instead of a silent wait for a browser
redirect that never comes.

Generated-by: Claude Code (Opus 5.5)
Signed-off-by: Baptiste Gouhoury <gouhoury.baptiste@gmail.com>
The login URL was printed to stdout, so capturing the output of
`auth token --sso` also captured the URL and the user never saw it. It
was also wrapped at the terminal width, which breaks copying it with
--no-browser.

Generated-by: Claude Code (Opus 5.5)
Signed-off-by: Baptiste Gouhoury <gouhoury.baptiste@gmail.com>
@baptistegh

Copy link
Copy Markdown
Contributor Author

Tested end to end in breeze with Keycloak and #75913: auth token --sso and auth login --sso (browser login, loopback callback, then dags list with the stored credentials), the refresh grant, and the error cases (browser login disabled on the server, timeout, incompatible arguments).

Comment thread airflow-ctl/docs/start.rst Outdated
Comment thread airflow-ctl/src/airflowctl/ctl/utils/browser_login.py
Comment thread airflow-ctl/tests/airflow_ctl/ctl/commands/test_auth_command.py
Comment thread airflow-ctl/src/airflowctl/ctl/utils/browser_login.py
The login metadata comes from the network, so an endpoint outside the API
server would have received the code, the PKCE verifier and any stored
token. Authorization codes are single use and identity providers can treat
a replay as an attack, so the exchange must not be retried.

Reviewers also pointed out that the browser reported success on failed
logins and that an idle connection defeated --timeout. Error text from the
server could crash the output, and auth token ignored the arguments that
--sso is documented to refuse.
@baptistegh

Copy link
Copy Markdown
Contributor Author

Pushed a follow-up commit with the review fixes. While in there I also fixed a few things that weren't raised in review:

  • Endpoints from the login metadata must now be on the API server's origin. Before, an absolute URL would have received the code, the PKCE verifier and any stored token.
  • The code exchange is no longer retried, because codes are single use and a replay can be treated as an attack.
  • Error text from the server is escaped, so it can't crash the rich output.
  • auth token --sso now refuses --username and --password.

@baptistegh
baptistegh requested a review from vincbeck October 30, 2026 11:52

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants