Adding better auth token management from the CLI#1050
Merged
gtrrz-victor merged 7 commits intomainfrom Apr 28, 2026
Merged
Conversation
Previously `entire logout` only removed the bearer token from the OS keyring, leaving any exfiltrated copy valid until natural expiry. Now the command first calls DELETE /api/v1/auth/tokens/current to revoke the token server-side, then deletes the local entry. Revocation is best-effort: a network or 401 error logs a warning to stderr but still completes the local logout, so users can sign out while offline or after a token has already expired. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Entire-Checkpoint: 11c163296a0d
Introduces `entire auth` as the home for authentication subcommands, backed by the `/api/v1/auth/tokens` endpoints in the entire.io API. entire auth status show login state, validate token entire auth list [--json] list active API tokens for the user entire auth revoke <id> revoke a specific token by id entire auth revoke --current revoke + remove the local token (logout) entire auth login same handler as `entire login` entire auth logout same handler as `entire logout` Top-level `entire login` and `entire logout` remain registered as shortcuts; the auth subcommands are independent cobra.Command instances that delegate to the existing handlers, so muscle memory keeps working. `auth revoke` with no args prints help instead of erroring, matching how the user requested it. To support 401 detection cleanly, refactors `api.CheckResponse` to return a typed `*HTTPError` carrying StatusCode + Message. Existing callers that wrap with %w keep working unchanged. `entire logout` now swallows 401 silently on revoke (token is already invalid server-side, so no warning is needed). `auth status` validates the bearer token against `GET /api/v1/auth/tokens` because `/api/v1/auth/me` is currently session-cookie only on the backend. A backend change to `requireAuthOrBearer` would let us show the github_login as well. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Entire-Checkpoint: a3815809a5c3
Replaces the plain tabwriter table with a lipgloss-styled, color-aware
table that mirrors the visual idiom of `entire activity` (bold names,
muted IDs/dates, expiry warnings).
- Headers rendered as bold + dim, uppercased.
- Token IDs styled like commit hashes (muted gray).
- Names rendered bold; missing names ("-") shown faint.
- "LAST USED" uses friendly relative format: "just now", "Xm ago",
"Xh ago", "yesterday", "Xd ago", absolute date for older. nil
shows as faint "never".
- "EXPIRES" colored by urgency: red if already expired, yellow if
within 7 days, muted otherwise — easy to spot tokens to rotate.
Column padding is computed from plain text via lipgloss.Width because
tabwriter doesn't strip ANSI escapes. Color is gated by shouldUseColor
so non-tty output (tests, pipes, NO_COLOR) stays plain — existing
substring assertions in TestRunAuthList_TablePrintsRows continue to
pass without changes.
Adds focused tests for formatAuthLastUsed (relative-time buckets) and
expiresState (normal/soon/expired classification).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: b2b5a2926335
Aggregated cleanups from a parallel reuse/quality/efficiency review:
- Drop double-wrapped error messages in defaultListTokens /
defaultRevokeTokenByID / defaultRevokeCurrentToken — the api.Client
methods already prefix every error with the action name, so the
outer fmt.Errorf produced "list tokens: list tokens: …".
- Unify token-store interface: drop authStatusStore (a strict subset
of logoutTokenStore) and use logoutTokenStore everywhere.
- Extract formatRelativeDuration in status.go for the shared
"just now / Xm ago / Xh ago / Xd ago" buckets, then call it from
formatAuthLastUsed for the recent cases. timeAgo() is now a
one-line wrapper. Buckets stay consistent across status and auth.
- Drop the cell{styled, plain} struct in renderAuthListTable.
lipgloss.Width strips ANSI escapes itself, so the duplicate plain
field was carrying weight for no reason. The renderer is now a
single pass over `[]string`, with a tiny writeRow helper that the
header and body rows share (no more "// Header row." /
"// Body rows." special-cases).
- Promote magic strings (placeholderDash, lastUsedNever,
lastUsedJustNow) to constants so tests and production share one
source of truth.
- Rename the awkward expiresStateValue type to expiresState and
rename the colliding function to classifyExpiresAt. Constants
follow: expiresNormal / expiresSoon / expiresExpired.
- Extract styleName / styleLastUsed / styleExpires helpers; the
inline if/else chains in the row loop were doing more than the
loop body should.
Behavior unchanged. Full unit + integration suite still passes;
existing tests work without modification because they assert on
plain-text substrings.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: e7b60b189032
Two correctness fixes from review feedback. (1) The new authenticated commands (auth status / list / revoke and the modified logout) bypassed the insecure-HTTP guard that login.go already enforces — anyone with ENTIRE_API_BASE_URL=http://... would have transmitted their bearer token in cleartext. Extracts requireSecureBaseURL + addInsecureHTTPAuthFlag helpers in auth.go and applies them everywhere a bearer crosses the wire: auth status, auth list, auth revoke, logout, and (refactored to share the helpers) login. Each command now exposes the same hidden --insecure-http-auth flag for local development. (2) `entire auth revoke <id>` left a stale keychain entry behind when <id> happened to be this CLI's own bearer. The --current path delegates to runLogout (which deletes locally), but the by-id path only revoked server-side. After RevokeToken succeeds, runAuthRevoke now probes ListTokens with the same bearer. ListTokens requires bearer auth, so a 401 means the id we just killed was our own — when that happens we delete the keychain entry and tell the user, avoiding the "logged in but every command 401s" failure mode. Tests: - TestRunAuthRevoke_ByIDCallsRevoker now also verifies no local cleanup when the post-revoke list call returns 200. - New TestRunAuthRevoke_ByIDSelfRevokeCleansLocal covers the 401 branch. - TestRunAuthRevoke_CurrentDelegatesToLogout updated for the new signature. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Entire-Checkpoint: cc1ec5902e3e
Collaborator
|
@BugBot review |
Bugbot couldn't runBugbot is not enabled for your user on this team. Ask your team administrator to increase your team's hard limit for Bugbot seats or add you to the allowlist in the Cursor dashboard. |
Contributor
There was a problem hiding this comment.
Pull request overview
Adds a new entire auth command group to improve token management (status, list, revoke) while keeping existing top-level login/logout behavior, and extends the API client to support token metadata and revocation.
Changes:
- Introduces
entire authwithstatus,list, andrevokesubcommands (and mirrorslogin/logoutunderauth). - Enhances logout to attempt best-effort server-side revocation before deleting the local keychain token.
- Adds API endpoints and error typing to support token listing/revocation and consistent HTTP status inspection.
Reviewed changes
Copilot reviewed 12 out of 12 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| cmd/entire/cli/status.go | Refactors relative-time formatting into a shared helper for consistency with auth list. |
| cmd/entire/cli/root.go | Registers the new auth command on the root CLI. |
| cmd/entire/cli/logout.go | Adds server-side revoke + warning behavior and secure-base-URL gating to logout. |
| cmd/entire/cli/logout_test.go | Expands logout unit tests to cover revoke + warning + error paths. |
| cmd/entire/cli/login.go | Centralizes secure-base-URL guard and shared hidden flag wiring. |
| cmd/entire/cli/auth.go | Implements auth command and subcommands (status/list/revoke) plus token table rendering. |
| cmd/entire/cli/auth_test.go | Adds unit tests for auth subcommands and formatting helpers. |
| cmd/entire/cli/api/client.go | Introduces HTTPError + IsHTTPErrorStatus for status-aware error handling. |
| cmd/entire/cli/api/client_test.go | Adds shared bearer-header test constant. |
| cmd/entire/cli/api/repositories_test.go | Reuses the shared bearer-header test constant. |
| cmd/entire/cli/api/auth_tokens.go | Adds ListTokens, RevokeToken, and RevokeCurrentToken API methods. |
| cmd/entire/cli/api/auth_tokens_test.go | Adds API tests for token list/revoke endpoints and HTTPError behavior. |
Two inline review findings from Copilot on #1050. (1) The interface formerly named logoutTokenStore is used by the auth status / list / revoke subcommands as well as logout, so its name was actively misleading. Renamed to tokenStore (and logoutRevokeFunc to revokeCurrentFunc to follow suit). (2) The token-list sort had a "Stable order" comment but called sort.Slice (not sort.SliceStable) and the composite sort key (last_used, then created_at) left ties undefined. Added id as a final deterministic tie-breaker and rephrased the comment so it matches what the code actually does. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Entire-Checkpoint: 54689b7f6421
gtrrz-victor
approved these changes
Apr 28, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
https://entire.io/gh/entireio/cli/trails/239
This adds a new
authsubcommand with:login/logoutworking as the root commandslistlists all active tokensrevokeallows revoking an existing tokenstatuscheck if there current credentials in the keychain are still valid