Skip to content

feat(cli): ship the PKCE login and auth commands - #235

Merged
fnwoke-lightning merged 36 commits into
mainfrom
fnwoke/auth-m4-11-login-commands
Nov 6, 2026
Merged

fnwoke-lightning merged 36 commits into
mainfrom
fnwoke/auth-m4-11-login-commands

Conversation

@fnwoke-lightning

@fnwoke-lightning fnwoke-lightning commented Nov 4, 2026 •

Copy link
Copy Markdown
Contributor

Why

#234 added a per-request credential source and a PKCE login behind LIGHTNING_CLI_OIDC_LOGIN, but the CLI still had no commands to log in with it, inspect it or end it, and several SDK paths still read credentials their own way. This PR adds the login and auth commands, and moves every remaining SDK credential path to the per-request source.

What

  • lightning login [--no-browser] [--no-keychain] on both entrypoints. --no-keychain keeps the login in a file only the user can read instead of the OS keychain. With LIGHTNING_CLI_OIDC_LOGIN unset it runs the interim sess_ login, and both flags fail with a usage error naming the switch. With it set it runs the PKCE login. --org and --project come in a follow-up.
  • Login refuses in a Studio first, with the Studio-specific error. Outside a Studio it refuses when LIGHTNING_AUTH_TOKEN_FILE or LIGHTNING_AUTH_TOKEN is set. A set LIGHTNING_API_KEY gets a stderr warning that it is ignored, then login continues.
  • lightning auth status: source, user, orgs, teamspaces (a whole-org grant shows as all in <org>), permissions, expiry. It never prints a token.
  • lightning auth token: prints what requests send.
  • auth status and auth token turn an unreadable token file or login into a CLI error instead of a traceback.
  • lightning logout: revokes both the new login and a saved sess_, forgets both, names any server revoke that failed, and says when LIGHTNING_AUTH_TOKEN_FILE or LIGHTNING_AUTH_TOKEN still authenticates requests.
  • The interim Auth.save / AuthServer monkeypatch is deleted, and with it the ignored token= and api_key= arguments of Auth.save.
  • Raw requests calls (filesystem, studio, teamspace, billing, _BlobUploader, folder downloads) attach credentials per request through one AuthBase, which refreshes and resends once on a 401 and sends the lightning-sdk/<version> User-Agent. A 401 no refresh fixes (a revoked sess_, a stale token file) raises ConnectionError with the generated client's "Authentication failed. Please run lightning login." message. A 401 from another host after a redirect, such as a storage URL, is returned as it is: the credential is never refreshed for it or sent to that host.
  • Following logs refreshes once on a 401 websocket handshake, then stops with the same message, and stops at once on a 403, instead of reconnecting forever with a refused token. A credential that can't be read (an unreadable token file, a refused login) also stops it, instead of being retried as a dropped connection.
  • LitAI uses the existing credential and, when there is none, asks before opening the browser login with its sign-in params. LitServe never opens a browser and fails with a usage error that points to lightning login, or names an unreadable token file. License validation no longer writes its token over the saved login.
  • CI drops the "Login passthrough" step, because lightning login now refuses when a token is set, and the test step no longer sets the ignored LIGHTNING_API_KEY.
  • Tests for each command in both switch states and for the credential paths.

Notes

  • Edits one vendored file: removing Auth.save's ignored arguments, which main's monkeypatch needed until this PR. Labeled vendoring for that.
  • fix(sdk): preserve modification times when downloading files #238's download tests mock credentials.Auth here, since the auth-header helper they patched is gone.
  • Unit: pytest python/tests 3140 passed at 82ff06f, merged with main, with no saved login and no credential in the environment. The 2 failures (test_job_list_table_shows_tags, test_job_list_table_shows_creator) also fail on main locally (terminal width).

Copy the per-request credential source and PKCE login from grid's lightning-cloud package into the SDK's vendored copy, and switch the CLI to the vendored login.
Add lightning login options, auth status, auth token and logout, and move the remaining SDK credential paths to the per-request source.
@fnwoke-lightning
fnwoke-lightning force-pushed the fnwoke/auth-m4-11-login-commands branch from 640b707 to 678347c Compare November 4, 2026 19:03
fnwoke-lightning and others added 9 commits November 4, 2026 18:01
A release from main must keep logging in on production, which serves only
the interim login until relaunch. A new login is the interim one unless
LIGHTNING_CLI_OIDC_LOGIN is set, read when the login starts.

- Restore AuthServer with get_auth_url(port) and login_with_browser(auth).
  It sends state on the /sign-in redirectTo, adds caller sign-in params,
  refuses a callback whose state doesn't match, saves the sess_ as
  auth_token with Auth.save, and redirects with no credential in the URL.
- Auth.save accepts and ignores token again, writes a 0600 file.
- credentials: CredentialKind, Credential, resolve(), refresh(token) for
  the token a request sent, start_login() dispatching on the switch with
  LoginUnavailable for options the interim login can't honour, and
  logout() that revokes each saved kind and reports which revokes failed.
- A login that saves revokes the login it replaced, after the save:
  /oidc/revoke for a new login, /v1/auth/logout for a sess_.
- Both browser logins stop after five minutes with no callback.
- Reuse is detected only from the exact error_description.
- The source's own login goes through Auth.authenticate, so main's
  browser_authentication(False) still keeps resource commands from
  opening a browser.
- Restore llm_api.py, cli/entrypoint.py, cli/legacy/entrypoint.py and
  cli/utils/auth.py to main. Their changes belong to sdk#235.

Co-authored-by: Cursor <cursoragent@cursor.com>
Port grid's fake platform, fake browser, keyrings and login worker under
tests.lightning_cloud, and extend the fake platform with the interim
/sign-in to /login-complete flow and POST /v1/auth/logout.

The conftest overrides the SDK's autouse threading.Thread mock, clears the
SDK's dummy LIGHTNING_API_KEY and LIGHTNING_AUTH_TOKEN, and puts back the
vendored interim login that main's CLI replaces when imported.

New cases cover the interim login, the switch at every entry point, the
options only the new login has, replacing either kind of login with
either kind, logout of both kinds, the five minute stop, and the exact
reuse text.

Co-authored-by: Cursor <cursoragent@cursor.com>
sdk#235 rewrites the CLI and credential readers against the
lightning_cloud API from sdk#234, including the interim sess_ login
as the default.
A 401 retries once after the source refreshes, and a folder download
hands its workers that handler instead of a header captured once.

Co-authored-by: Cursor <cursoragent@cursor.com>
The interim sess_ login stays the default. The new login runs only when
LIGHTNING_CLI_OIDC_LOGIN is set, and the SDK no longer patches Auth.

Co-authored-by: Cursor <cursoragent@cursor.com>
…n is switched on

Co-authored-by: Cursor <cursoragent@cursor.com>
…n-commands

Co-authored-by: Cursor <cursoragent@cursor.com>
…switch

Co-authored-by: Cursor <cursoragent@cursor.com>
… key

With the switch unset, a saved sess_ is sent ahead of LIGHTNING_API_KEY. When it went stale, requests said to run lightning login and login refused because the key was set. Login now returns early only when the key is what requests send.

Co-authored-by: Cursor <cursoragent@cursor.com>
fnwoke-lightning and others added 13 commits November 4, 2026 22:16
Co-authored-by: Cursor <cursoragent@cursor.com>
… failed revokes on login

Co-authored-by: Cursor <cursoragent@cursor.com>
…n-commands

Co-authored-by: Cursor <cursoragent@cursor.com>

# Conflicts:
#	python/lightning_sdk/cli/entrypoint.py
#	python/lightning_sdk/cli/resource_completion.py
#	python/lightning_sdk/cli/utils/lockdown_status.py
#	python/lightning_sdk/cli/utils/logging.py
#	python/tests/cli/test_entrypoint.py
#	python/tests/cli/utils/test_auth.py
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
…g OSError

Co-authored-by: Cursor <cursoragent@cursor.com>
…sign-in calls

Also applies ruff-format to test_replaced_login.py, which failed pre-commit.

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
…necting forever

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
@fnwoke-lightning fnwoke-lightning added the vendoring Intentionally updates vendored files and bypasses the vendored Cloud guard label Nov 5, 2026
@fnwoke-lightning
fnwoke-lightning marked this pull request as ready for review November 5, 2026 15:13
fnwoke-lightning and others added 3 commits November 5, 2026 11:25
…he API key is ignored

The interim login's headless error now points at LIGHTNING_AUTH_TOKEN too.

Co-authored-by: Cursor <cursoragent@cursor.com>
…n-commands

Co-authored-by: Cursor <cursoragent@cursor.com>

# Conflicts:
#	python/lightning_sdk/cli/utils/auth.py
…oken is set

The API key in the test env is ignored now, so it goes too.

Co-authored-by: Cursor <cursoragent@cursor.com>
@fnwoke-lightning fnwoke-lightning self-assigned this Nov 5, 2026
Comment thread python/lightning_sdk/cli/utils/auth.py Resolved
fnwoke-lightning and others added 3 commits November 5, 2026 14:35
The flag keeps the login in a file only the user can read instead of the OS
keychain; the new name says that without alarming users.

Co-authored-by: Cursor <cursoragent@cursor.com>
…hain

Co-authored-by: Cursor <cursoragent@cursor.com>
Comment thread python/lightning_sdk/api/logs_api.py Resolved
A Studio sets LIGHTNING_AUTH_TOKEN_FILE, so lightning login there reported the token-file refusal instead of the Studio-specific error. Check in_studio() first; the env guards still apply outside a Studio.
Auth().auth_header raises LoginError, a ConnectionError and therefore an OSError, which the reconnect handler caught. With reconnect on and no idle timeout, following logs retried forever on an unreadable token file or refused login. Read the header before the try so the error reaches the caller, and pin it with an unreadable-token-file test.
Base automatically changed from fnwoke/auth-m4-9-10-vendor to main November 6, 2026 14:31
Co-authored-by: Cursor <cursoragent@cursor.com>

# Conflicts:
#	.github/workflows/test-cli.yaml
#	python/lightning_sdk/cli/entrypoint.py
#	python/lightning_sdk/cli/resource_completion.py
#	python/lightning_sdk/cli/utils/auth.py
#	python/lightning_sdk/cli/utils/lockdown_status.py
#	python/lightning_sdk/cli/utils/logging.py
#	python/lightning_sdk/lightning_cloud/login.py
#	python/tests/api/test_billing_api.py
#	python/tests/api/test_filesystem_api.py
#	python/tests/api/test_studio_api.py
#	python/tests/api/test_teamspace_api.py
#	python/tests/cli/test_entrypoint.py
#	python/tests/cli/utils/test_auth.py
#	python/tests/lightning_cloud/test_credentials.py
#	python/tests/lightning_cloud/utils/fake_platform.py
Comment thread python/lightning_sdk/api/utils.py Resolved

@Danidapena Danidapena left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

small Claude issue :D

…ss-host redirect

requests follows redirects with a copy of the request, response hooks
included, and drops Authorization when the host changes. The 401 hook then
ran on the redirect target's response, refreshed, and resent the fresh token
to that host, then told the user to log in. Retry only a 401 to a request
that carried the credential.

Co-authored-by: Cursor <cursoragent@cursor.com>
@fnwoke-lightning
fnwoke-lightning merged commit 6d6b71c into main Nov 6, 2026
26 checks passed
@fnwoke-lightning
fnwoke-lightning deleted the fnwoke/auth-m4-11-login-commands branch November 6, 2026 18:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

vendoring Intentionally updates vendored files and bypasses the vendored Cloud guard

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants