auth: demolish static fallbacks, legacy store, and v1 provider (COR-393, part 2)#1410
auth: demolish static fallbacks, legacy store, and v1 provider (COR-393, part 2)#1410toothbrush wants to merge 17 commits into
Conversation
There was a problem hiding this comment.
Pull request overview
This PR completes COR-393 by removing legacy authentication fallbacks and enforcing a single auth model across the CLI: contexts.json + per-context token managers + host discovery (no static fallbacks, no legacy keyring slot reads/writes, and no v1 provider routing). This materially changes login/logout/status and data-API authentication behavior, and deletes a large amount of transitional machinery.
Changes:
- Removes legacy auth store/migration/singleton-token-manager plumbing and hardcodes the OIDC/v2 OAuth endpoint wiring.
- Makes control-plane commands require an active context (no default-core fallback); data API now requires
/.well-known/entire-api.jsondiscovery (no static fallback). - Updates integration/E2E wiring to use
ENTIRE_TOKEN_STORE=file(noauthfilestorebuild tag) and adjusts login fixtures to the OIDC device authorization endpoint.
Reviewed changes
Copilot reviewed 41 out of 41 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| README.md | Updates local device-auth dev instructions to use login --server instead of retired env override. |
| mise.toml | Removes authfilestore build tags; documents ENTIRE_TOKEN_STORE=file for e2e/integration. |
| internal/entireclient/clusterdiscovery/api_discovery_test.go | Updates comment to reflect removal of operator-side auth override. |
| internal/coreapi/client.go | Updates Core API client docs to reflect contexts-only auth targeting/erroring when logged out. |
| internal/coreapi/client_test.go | Updates expected “not logged in” guidance to entire login --server …. |
| e2e/tests/main_test.go | Switches e2e token isolation to ENTIRE_TOKEN_STORE=file only. |
| docs/architecture/upstream-host-resolution.md | Updates architecture doc to remove static fallback and default-host behavior. |
| cmd/git-remote-entire/main.go | Removes legacy login migration bridge on git-remote cold start. |
| cmd/entire/cli/logout.go | Reworks logout to operate purely on contexts/tokenstore; no legacy token delete. |
| cmd/entire/cli/logout_test.go | Updates logout unit tests to match new contexts-only behavior. |
| cmd/entire/cli/login.go | Makes login persist only via contexts/tokenstore (RecordLoginContext) and fail on save error. |
| cmd/entire/cli/integration_test/setup_test.go | Builds integration-test subprocess without authfilestore tag. |
| cmd/entire/cli/integration_test/login_test.go | Updates integration login fixture to /device_authorization + JWT-shaped token; uses tokenstore file backend. |
| cmd/entire/cli/auth/store.go | Deletes legacy keyring/file-backed store implementation. |
| cmd/entire/cli/auth/store_test.go | Deletes tests for removed legacy store. |
| cmd/entire/cli/auth/store_invariants_test.go | Deletes build-tag invariant tests tied to removed store backend. |
| cmd/entire/cli/auth/store_filebackend.go | Deletes authfilestore-tagged file backend. |
| cmd/entire/cli/auth/store_filebackend_test.go | Deletes tests for removed file backend. |
| cmd/entire/cli/auth/repo_token.go | Removes legacy login migration from repo-token source. |
| cmd/entire/cli/auth/refresh.go | Hardcodes OAuth wiring constants; keeps per-context token manager behavior. |
| cmd/entire/cli/auth/provider.go | Replaces provider/version routing with fixed OIDC endpoint constants. |
| cmd/entire/cli/auth/provider_test.go | Deletes tests for removed provider/version routing logic. |
| cmd/entire/cli/auth/keyring_timeout.go | Deletes legacy keyring timeout shim. |
| cmd/entire/cli/auth/keyring_timeout_test.go | Deletes tests for removed timeout shim. |
| cmd/entire/cli/auth/exchange.go | Removes singleton token manager + TokenForResource; keeps insecure-http opt-in flag. |
| cmd/entire/cli/auth/data_api.go | Removes static fallback; discovery is mandatory and failures are hard errors. |
| cmd/entire/cli/auth/data_api_test.go | Updates tests for “no fallback” discovery error behavior. |
| cmd/entire/cli/auth/control_plane.go | Removes default-host fallback; no active context now returns ErrNotLoggedIn-wrapping error. |
| cmd/entire/cli/auth/control_plane_test.go | Updates tests to expect not-logged-in error instead of fallback target selection. |
| cmd/entire/cli/auth/contexts.go | Removes legacy login migration function and related env/legacy-store coupling. |
| cmd/entire/cli/auth/contexts_test.go | Deletes tests for legacy migration and legacy-fallback context store behavior; adjusts current-context assertions. |
| cmd/entire/cli/auth/context_store.go | Removes legacy “prefer context then legacy store” wrapper types/helpers. |
| cmd/entire/cli/auth/client.go | Uses fixed OIDC device/token endpoint constants instead of provider routing. |
| cmd/entire/cli/auth.go | Changes status/logout to target active context only; refactors insecure-http handling. |
| cmd/entire/cli/auth_test.go | Removes helper scaffolding for singleton token manager fallback testing. |
| cmd/entire/cli/auth_context_test.go | Updates status target resolution tests for contexts-only model. |
| cmd/entire/cli/api/client.go | Updates docs to describe login-server targeting without split-host env override. |
| cmd/entire/cli/api/base_url.go | Removes AuthBaseURL()/IsSplitHost(); keeps “reject retired env” gate and updates comments. |
| cmd/entire/cli/api/base_url_test.go | Deletes tests for removed AuthBaseURL()/IsSplitHost(). |
| cmd/entire/cli/api_client.go | Updates data API client auth commentary to contexts+discovery-only exchange flow. |
| cmd/entire/cli/activity_cmd_test.go | Reworks tests to stub discovery/cancellation paths without relying on removed singleton-manager fallback. |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 43 out of 43 changed files in this pull request and generated 1 comment.
Comments suppressed due to low confidence (2)
cmd/entire/cli/auth.go:142
--insecure-http-authis applied afterresolveStatusTarget, butresolveStatusTargetmay callauth.RefreshedLoginToken, which consultsauth.insecureHTTPEnabled()to decide whether a non-loopbackhttp://core is allowed. This meansentire auth status --insecure-http-authcan still fail to refresh/resolve tokens for an insecure-core context becauseauth.EnableInsecureHTTP()hasn’t been called yet.
Also, the TLS check currently runs whenever target.coreURL != "", even when target.token == "" (no request will be made and status just prints "Not logged in").
RunE: func(cmd *cobra.Command, _ []string) error {
target, err := resolveStatusTarget(cmd.Context(), auth.Contexts, auth.RefreshedLoginToken)
if err != nil {
return err
}
// We send the session token to target.coreURL; enforce TLS on it.
if !applyInsecureHTTPAuth(insecureHTTPAuth) && target.coreURL != "" {
if err := api.RequireSecureURL(target.coreURL); err != nil {
return fmt.Errorf("context login server URL check: %w", err)
}
}
cmd/entire/cli/logout.go:80
--insecure-http-authis applied afterresolveStatusTarget, butresolveStatusTargetmay callauth.RefreshedLoginToken, which consultsauth.insecureHTTPEnabled()to allow a non-loopbackhttp://core. This makesentire logout --insecure-http-authfail to refresh/resolve tokens against an insecure core because the opt-in isn’t enabled yet.
Additionally, the TLS check currently runs even when target.token is empty (no revocation request will be sent), which can unnecessarily block logout of a context on http:// when the keyring read failed and we’re only trying to clear local state.
4f6a269 to
2af3c53
Compare
f41d01e to
25a0909
Compare
2af3c53 to
b8b0d59
Compare
…-393) ResolveControlPlaneTarget loses its no-context fallback (static AuthBaseURL target + TokenForResource): a control-plane command without a login has no identity to act as, so it now errors wrapping ErrNotLoggedIn and callers render the `entire login` hint. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Entire-Checkpoint: 1cbc7d4b5116
resolveStatusTarget loses its legacy-keyring fallback and probes only the active context. With no login at all, `auth status` prints an informational "Not logged in. Run 'entire login'." (exit 0) and `logout` no-ops — neither ever targeted a default host with a stale identity. logout's revoke now takes the already-resolved bearer instead of re-reading a legacy store slot, and the legacy-slot cleanup in --all-contexts goes with it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Entire-Checkpoint: 987ef1ca3188
…R-393) ResolveDataAPIToken loses both static fallbacks: a host that doesn't advertise /.well-known/entire-api.json (or has no parseable host) is now an error naming the host — without discovery we can't know which login servers it trusts, and guessing risks exchanging a token at a core the host doesn't accept. entire.io#2277 shipped the well-known, so the old-deployment case the fallback existed for is gone. With its last callers removed, TokenForResource and the process-wide singleton manager go too, along with the SetManagerForTest / DiscoveryUnavailableForTest seams and the test scaffolding that only existed to pin the fallback path. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Entire-Checkpoint: 7056101a1d83
The v1 provider described the retired single-host world where entire.io was its own login server (homegrown device-code path, no STS). Everyone has been on the v2 OIDC surface since split-host became the default, so the version routing table, the ENTIRE_AUTH_PROVIDER_VERSION override, the split-host auto-detect (api.IsSplitHost), the process-wide sync.Once singleton, and the SetProviderForTest seam all reduce to four package constants. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Entire-Checkpoint: 4899a74ed268
…-393) contexts.json + the shared tokenstore are now the only credential store. Login records the context fatally instead of dual-writing a legacy entire-cli/<authBaseURL> keyring slot; the context-preferring ContextStore wrapper, CurrentContextToken, LookupCurrentToken, and MigrateLegacyLoginContext (with its callers in the git remote helper and the repo-token path) are gone. Pre-contexts logins now surface the existing "run `entire login`" hints instead of being bridged. The authfilestore build tag existed only to keep the legacy store off developer keychains in tests; the surviving store honors ENTIRE_TOKEN_STORE=file unconditionally, so the tag, the file backend, and the keyring-timeout shim go too. Integration login tests move to `--server` + the v2 device-authorization path and sandbox via ENTIRE_CONFIG_DIR / ENTIRE_TOKEN_STORE. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Entire-Checkpoint: e1810eaf446f
No reader of ENTIRE_AUTH_BASE_URL remains: every token path resolves its core from contexts.json or /.well-known discovery, and login takes --server. The constant DefaultAuthBaseURL stays as the --server default, and AuthBaseURLEnvVar stays only for RejectRemovedAuthEnv. NewAuthenticatedAPIClient now TLS-checks just the data origin it actually dials; the exchange leg is guarded by the per-context token manager. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Entire-Checkpoint: c981a8816bc0
…k auth Local-dev instructions switch from exporting ENTIRE_AUTH_BASE_URL to `entire login --server`; upstream-host-resolution.md drops the fallback steps that no longer exist; stray comment references to the deleted TokenForResource path and env var are reworded. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Entire-Checkpoint: 829766ddb442
Review finding: RemoveCurrentContext/RemoveContext deleted the contexts.json entry first and swallowed keyring-delete failures, so a locked or failing keychain still printed "Logged out." while the long-lived refresh token survived, mintable by any keyring-capable process. Deletion now runs credentials-first (refresh slot before access, so a partial failure strands at worst the short-lived token) and any failure aborts with the entry intact — the benign direction: the context reads as not logged in and a retry no-ops the deletes. Also rewords the login-token validation comments: opaque tokens pass the trust check but can no longer complete a login — RecordLoginContext is the sole persistence path and requires iss/handle claims; legacy opaque-token servers are intentionally unsupported. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Entire-Checkpoint: ac4fd24010aa
Sweep of every comment touched by the COR-393/COR-395 stack for transition framing: RecordLoginContext no longer claims a dual-write or a best-effort contract, the git-remote-entire package doc drops the read-time migration, newContextTokenManager and the coreapi bearer source stop referencing the deleted singleton/static paths, and the NormalizeOriginURL doc loses its dangling AuthBaseURL pointer. Each now states the current behaviour and why it holds. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Entire-Checkpoint: fe2b80edc270
The sentinel covers 404/503/malformed too, so calling a reachable host's missing well-known "unreachable" misled; the wrapped error still carries the precise cause. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Entire-Checkpoint: 1edf76e5cd82
An opaque or claims-free token could never complete a login — RecordLoginContext keys the context and keychain slot on iss and handle/sub — but it only failed at the save step with a bare parse error. validateReceivedToken now requires parseable claims, iss, and handle-or-sub up front, with errors naming the requirement. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Entire-Checkpoint: 6c73d489af9e
Review finding: the logout-failsafe rework split context removal into a Load and a separate Modify, reopening the read/write lock gap the contexts package warns about — a concurrent `auth use` in the window could retarget which context "current" means mid-logout. Selection, keyring deletion, and entry removal now all run inside one locked Modify; a failed credential delete aborts the callback, which discards the entry change and preserves the retry-friendly failure mode. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Entire-Checkpoint: 02cd9554697c
The rebase onto the updated part-1 branch resolved login.go to the demolition side wholesale, dropping the base's redaction of rejected --server values — a userinfo-bearing URL must not land in CI logs. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Entire-Checkpoint: 0dd9fc48dac4
With the legacy dual-write's warn-and-continue gone, persistLogin never writes to stderr. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Entire-Checkpoint: b7c0ca0b79e1
b8b0d59 to
6c240a0
Compare
With the authfilestore tag gone, build:e2e compiled exactly what mise-tasks/build already builds — the same two binaries with no special configuration. The e2e drivers (test:e2e, canary, roger-roger) now call `mise run build`; the keychain protection note lives with the harness env vars that actually provide it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Entire-Checkpoint: 45f3b2cbee73
https://entire.io/gh/entireio/cli/trails/554
Part 2 of COR-393, stacked on #1404. Closes COR-393.
Auth resolution is now contexts + discovery, full stop: every bearer comes from a per-context token manager keyed on that context's core. Net −1,600 lines.
What changes
entire login";auth statusprints an informational "Not logged in." (exit 0);logoutno-ops. Previously these silently targeted the default core with a legacy keyring token./.well-known/entire-api.json; no static fallback remains (entire.io#2277 shipped, so only pre-well-known deployments would notice).entire-cli/<host>keyring slot is neither read nor written anymore.ENTIRE_AUTH_PROVIDER_VERSIONand the split-host auto-detect.Deleted machinery:
TokenForResource+ the process-wide singleton manager,MigrateLegacyLoginContext,LookupCurrentToken,ContextStore, the legacy keyring store with its keyring-timeout shim,api.AuthBaseURL()/IsSplitHost, and theauthfilestorebuild tag (test keychain-protection now rides the tag-freeENTIRE_TOKEN_STORE=file).Review note: the integration login tests were silently coupled to v1 — same-host env pinning made the old heuristic pick the v1 provider, so the fixtures served
/oauth/device/codeand an opaque token. They now serve/device_authorizationand a JWT withiss/handleclaims, which the contexts-only model genuinely requires.🤖 Generated with Claude Code
Note
High Risk
Breaking auth for users without migrated contexts or APIs without well-known discovery; login and all authenticated commands depend on the new model exclusively.
Overview
Auth is contexts + discovery only — the legacy keyring store (
entire-cli/<host>),TokenForResource/ singleton token manager,MigrateLegacyLoginContext,ContextStore, and v1 OAuth provider routing are removed (~1,600 lines). Logins persist only viaRecordLoginContextincontexts.json+ per-context keyring slots.Operator-facing behavior
ENTIRE_AUTH_BASE_URLis retired: if set (even empty), every built-in command fails with a pointer toentire login --server. Local dev docs use--server+ENTIRE_API_BASE_URLinstead.ErrNotLoggedIn/entire loginhint;auth statusprints informational “Not logged in.”;logoutno-ops. No fallback to a default auth host or legacy token.activity,search, etc.) requires/.well-known/entire-api.json; missing discovery is a hard error (no staticTokenForResourcefallback)./device_authorization, fixedoauthClientID);ENTIRE_AUTH_PROVIDER_VERSIONand split-host auto-detect are gone.logoutdrops legacy keyring reads/deletes; revocation uses the resolved context token only.Tests / build — integration login tests use JWT
iss/handleand/device_authorization;authfilestorebuild tag removed in favor ofENTIRE_TOKEN_STORE=filefor e2e/integration.Reviewed by Cursor Bugbot for commit 613b91a. Configure here.