Skip to content

feat: auth status - #40

Closed
alukach wants to merge 5 commits into
feat/product-commandsfrom
feat/status-command
Closed

alukach wants to merge 5 commits into
feat/product-commandsfrom
feat/status-command

Conversation

@alukach

@alukach alukach commented Oct 13, 2026 •

Copy link
Copy Markdown
Contributor

Stacked on #24 (feat/product-commands). Retarget to main once #24 merges.

Rebuilt on #24, under auth (#24 moved login to auth login). Replaces this PR's earlier top-level source-coop status.

What

source-coop auth status reports, for one environment and role, whether you're logged in, as whom, and when things expire:

Environment    production (https://data.source.coop)
Login          logged in
Account        Anthony Lukach (alukach)
Refresh token  yes
ID token       expires 2026-10-13T18:00:00Z
Access token   for https://source.coop, expires 2026-10-13T18:00:00Z
Credentials    _default: expire 2026-10-13T18:30:00Z
  • Who's logged in: auth login now asks the API's /whoami who the new access token acts as (new --api-url / SOURCE_API_URL) and keeps the account in the session (Session.account, optional, so old sessions still load). auth status names it without going to the network. A failed lookup is noted at login, not an error. A session from before this says "unknown until your next auth login".

  • Layout: one row per item, like product view. On a terminal (and without NO_COLOR) labels are dim, the login state is green or red, the account ID is gray, and a past expiry is yellow.

  • Reads the cache only: nothing is refreshed and nothing goes over the network. It always exits 0.

  • --proxy-url / SOURCE_PROXY_URL and --role-arn / SOURCE_ROLE_ARN pick the environment and role, as for creds. The keyring can't list its entries, so it reports on one of each.

  • The report is a pure function (status_report), so it's unit-tested without the keyring.

  • README has a "Checking login status" section. every_command_is_in_the_readme requires it.

Testing

  • status_names_who_is_logged_in, status_says_when_the_account_is_unknown_or_nobody_is_logged_in, status_colours_labels_and_the_login_state, the_account_comes_from_whoami, and login_looks_up_the_account_only_with_an_access_token (wiremock).
  • cargo fmt, cargo clippy --all-targets -- -D warnings, cargo test and cargo test --features staging: 93 passed.
  • Ran auth status with HOME in a scratch folder, so the real keychain was never read: "not logged in" / "no cached credentials".

🤖 Generated with Claude Code

@claude

claude Bot commented Oct 13, 2026 •

Copy link
Copy Markdown

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


✅ No blocking issues — safe to merge.

I read the diff against origin/feat/product-commands but didn't build or run the tests.

  • Compatibility: The new session fields use #[serde(default)], so old stored sessions still load.
  • Failure handling: A failed /whoami lookup is only noted at login and doesn't fail it. auth status makes no network calls and always exits 0.
  • Docs: The README documents SOURCE_REFRESH_TOKEN_LIFETIME and --api-url / SOURCE_API_URL.
  • Expiry estimate: The refresh-token expiry is a client-side estimate. After the estimated date the row turns yellow "expired" while Login still shows green "logged in". That is acceptable because the README says it is an estimate. Wording such as "estimated" in the row would avoid a false alarm.

Simplify (ponytail)

  • src/main.rs, status_report: eight positional args plus #[allow(clippy::too_many_arguments)]. Pass &StatusArgs and the Option<Env> instead of separate proxy_url, env and role_arn.
  • src/main.rs, refresh_lifetime(): returns (i64, String), and the String is never used (status_report ignores .1). Return just the i64. This also drops the tuple type on the refresh_lifetime parameter.
  • src/main.rs, relative() / expiry(): the manual DateTime::from_timestamp round-trips can share one helper. expiry and rfc3339 both format the same timestamp.
  • src/oidc.rs, refresh_expires_in and Session.refresh_token_expires_at: no code path here sets it from the current provider (Ory sends none). Cut both until a provider needs them, along with the test branch that covers them.

💰 Estimated review cost: $0.16 · 0m18s · 5 turns

Base automatically changed from feat/per-environment-cache to main October 13, 2026 22:36
`source-coop auth status` reports, for one environment and role, whether
you're logged in (refresh token, ID and access token expiry) and when the
role's cached credentials expire. It reads the cache only: nothing is
refreshed and nothing goes over the network. --proxy-url and --role-arn
pick the environment and role, as for creds.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@alukach
alukach force-pushed the feat/status-command branch from bf7a1f3 to b834701 Compare October 14, 2026 16:29
@alukach alukach changed the title feat: add status command feat: auth status Oct 14, 2026
@alukach
alukach changed the base branch from main to feat/product-commands October 14, 2026 16:29
@alukach alukach mentioned this pull request Oct 14, 2026
@alukach
alukach added this pull request to stack #25 October 14, 2026 16:39
auth login now asks the API's /whoami who the new access token acts as
(--api-url, SOURCE_API_URL) and keeps the account in the session, so
auth status can name it without going to the network. A failed lookup is
noted, not an error, and a session from before says it's unknown until
the next login.

auth status shows one row per item, laid out like product view: dim
labels, the login state green or red, the account ID gray, and a past
expiry yellow, on a terminal and without NO_COLOR.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
alukach and others added 3 commits October 14, 2026 10:41
… was renewed

auth status adds a Profile row (the account on the site the login was
for) and says when the refresh token was last renewed, with its expiry
where the provider gives one. Ory's refresh tokens are opaque and their
lifespan is its own setting, restarted at each renewal, so the renewal
time is what can be known. The session records it, and the expiry when
given (refresh_token_expires_in / refresh_expires_in), as optional
fields that older sessions read without.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Ory's token response gives no refresh-token lifetime (only the access
token's expires_in) and its refresh tokens are opaque, so auth status
estimates: the last renewal plus a lifetime, 30 days by default (Ory's
default oauth2.ttl.refresh_token) or SOURCE_REFRESH_TOKEN_LIFETIME, worded
as an estimate ("if unused, lapses around <day> (assuming a 30d
lifetime)"). An expiry the provider gives is shown instead. The lifetime
only feeds this estimate; nothing sent at login depends on it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
"renewed <time> (<ago>); expires <when> (in <n> days)", or "expired
<when> (<n> ago)" in yellow, instead of "yes, renewed …; if unused,
lapses around … (assuming a 30d lifetime)". An estimated expiry is
given to the day; one the provider gave, to the second. The README still
explains that the estimate assumes a lifetime.

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

alukach commented Oct 14, 2026

Copy link
Copy Markdown
Contributor Author

Superseded by #46 (auth commands: login, status, logout, token), built on #45 instead of #24. The branch is kept.

@alukach alukach closed this Oct 14, 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