Skip to content

feat: auth commands: login, status, logout and token under auth - #46

Merged
alukach merged 6 commits into
mainfrom
feat/auth-commands
Oct 14, 2026
Merged

alukach merged 6 commits into
mainfrom
feat/auth-commands

Conversation

@alukach

@alukach alukach commented Oct 14, 2026

Copy link
Copy Markdown
Contributor

Stacked on #45 (foundation). Supersedes #40 (auth status), #41 (auth logout), the auth login move from #24, and auth status --json from #44.

What

Signing in lives under auth:

  • auth login: the browser login. The top-level login keeps working as a hidden command, so scripts and habits don't break. It records:
    • the account, looked up through /whoami at --api-url / SOURCE_API_URL;
    • when the refresh token was issued.
  • auth status: environment, login state, account and profile link, each token's expiry, and the role's cached credentials.
    • Refresh token: renewed <time> (<ago>); expires <day> (in N days). Ory's refresh tokens are opaque and its token response gives no lifetime (verified: only access_token, expires_in, id_token, refresh_token, scope, token_type).
    • The expiry is an estimate: last renewal plus 30 days, Ory's default, overridable with SOURCE_REFRESH_TOKEN_LIFETIME. That setting only affects the display; nothing sent at login depends on it.
    • --json: the same as data, including profile_url, estimated_lapse_at and assumed_lifetime.
  • auth logout: removes an environment's login session and a role's cached credentials.
  • auth token: prints the API access token, as before.
  • Hints now say source-coop auth login.

Testing

  • cargo test and cargo test --features staging: 64 passed. Covers status rows and JSON, the lifetime estimate and override, relative times, login still working by its old name, the account coming from /whoami, logout's file removal, and refresh tracking through rotation.
  • cargo clippy --all-targets -- -D warnings (both builds): clean.
  • Manual: auth status and --json against a file-cached session in a temporary HOME, so no keychain entry was read.

🤖 Generated with Claude Code

@claude

claude Bot commented Oct 14, 2026 •

Copy link
Copy Markdown

Claude finished @alukach's task in 25s —— View job


✅ No blocking issues — safe to merge.

I read the full diff (HEAD~6..HEAD; origin/main...HEAD has no merge base in this checkout). I didn't build or run the tests.

  • Logout deletes the keyring entry and the file copy under the slot lock, and removes the file even when the keyring errors.
  • write_file now creates the cache file with mode 0600 before any bytes are written.
  • auth status uses peek_credentials, so it writes nothing.
  • Zero or negative refresh lifetimes are dropped in oidc.rs.
  • The login /whoami lookup has a 10s timeout and a failed lookup is non-fatal.

Non-blocking notes:

  • src/main.rs (run_login): the account lookup goes to site(&session), which is the token audience (falling back to defaults::API_URL), not --api-url. LoginArgs has no api_url. The PR description says the lookup uses --api-url / SOURCE_API_URL, and .env.staging now exports SOURCE_API_URL. For a local API (http://localhost:3000) with the default audience, the lookup goes to production. Either fix the description or take --api-url on login.
  • src/main.rs (status_json): logged_in is true for likely_expired, so scripts that read it alone are told you're logged in on a lapsed estimate. This looks deliberate, since the state is exposed in login.state. A line in the README would help scripts.

Simplify (ponytail)

  • src/main.rs login_state returns &'static str that callers re-match on strings ("expired", "likely_expired"). A small enum would drop the stringly-typed matching. The status_json state field can be built from it.
  • src/main.rs status_report and status_json each take 6–7 positional parameters with a repeated (secs, label) tuple. A small struct passed by reference would replace them.
  • src/cache.rs find_credentials(.., migrate: bool) is a bool flag with two wrappers. Fine as is, but peek_credentials could skip the migration check instead if legacy slots are rare.

💰 Estimated review cost: $0.18 · 0m25s · 9 turns

@alukach
alukach force-pushed the feat/auth-commands branch 2 times, most recently from 3d829e9 to f9d47a9 Compare October 14, 2026 21:23
Base automatically changed from feat/cli-foundation to main October 14, 2026 21:32
alukach and others added 2 commits October 14, 2026 14:38
- auth login (the top-level login still works, hidden): records the
  account from /whoami (--api-url) and when the refresh token was issued.
- auth status: environment, login state, account and profile link, when
  the refresh token was renewed and when it expires if unused (Ory's
  refresh tokens are opaque and its token response gives no lifetime, so
  this is estimated from 30 days, or SOURCE_REFRESH_TOKEN_LIFETIME), each
  token's expiry, and the role's credentials; --json gives the same as
  data, with estimated_lapse_at and assumed_lifetime.
- auth logout: removes an environment's session and a role's cached
  credentials.
- Hints now say 'source-coop auth login'.

Built on the shared foundation (api client, failure/exit codes,
output::json, style).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@alukach
alukach force-pushed the feat/auth-commands branch from f9d47a9 to 3ac2976 Compare October 14, 2026 21:38
alukach and others added 3 commits October 14, 2026 14:45
…gout takes --json

- A refresh token past its provider-given expiry makes the login expired.
- auth status no longer copies legacy credentials into the environment's slot.
- An unreadable SOURCE_REFRESH_TOKEN_LIFETIME is warned about, not fatal.
- auth login notes an unusable --api-url instead of silently skipping the account.
- auth logout --json reports what it removed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…account

- auth status counts a login expired once its refresh token's expiry, or
  the estimate where the provider gives none (Ory), has passed. One
  refresh_expiry feeds the state, the human report and --json.
- auth login saves the session and credentials before looking up the
  account, and looks it up at the API its token is for (--audience).
  Its --api-url, new in this branch, is gone.
- auth logout for production leaves another environment's legacy entry
  in production's slot alone.
- auth login is listed among the commands without --json.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…in lookup timeout, logout removes the file copy

- auth status: past only the estimated lapse, the login is likely_expired
  (yellow) and logged_in stays true; a provider-given expiry still ends it.
- A refresh lifetime of 0 (Keycloak's "never") is an unknown expiry.
- auth login gives the account lookup 10s.
- Removing a cached slot deletes its file even when the keyring fails.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@alukach
alukach marked this pull request as ready for review October 14, 2026 22:42
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@alukach
alukach merged commit f31ae44 into main Oct 14, 2026
10 checks passed
@alukach
alukach deleted the feat/auth-commands branch October 14, 2026 22:44
@alukach alukach mentioned this pull request Oct 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant