Skip to content

auth: demolish static fallbacks, legacy store, and v1 provider (COR-393, part 2)#1410

Open
toothbrush wants to merge 17 commits into
mainfrom
paul/cor-393-demolish-auth-fallbacks
Open

auth: demolish static fallbacks, legacy store, and v1 provider (COR-393, part 2)#1410
toothbrush wants to merge 17 commits into
mainfrom
paul/cor-393-demolish-auth-fallbacks

Conversation

@toothbrush

@toothbrush toothbrush commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

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

  • No login → control-plane commands (org/repo/project/grant, mirror) error with "run entire login"; auth status prints an informational "Not logged in." (exit 0); logout no-ops. Previously these silently targeted the default core with a legacy keyring token.
  • Data-API commands require the host to advertise /.well-known/entire-api.json; no static fallback remains (entire.io#2277 shipped, so only pre-well-known deployments would notice).
  • Pre-contexts logins are no longer bridged: stranded users see the login hint and re-login once. The legacy entire-cli/<host> keyring slot is neither read nor written anymore.
  • v1 (single-host, non-OIDC) provider support is gone, along with ENTIRE_AUTH_PROVIDER_VERSION and 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 the authfilestore build tag (test keychain-protection now rides the tag-free ENTIRE_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/code and an opaque token. They now serve /device_authorization and a JWT with iss/handle claims, 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 via RecordLoginContext in contexts.json + per-context keyring slots.

Operator-facing behavior

  • ENTIRE_AUTH_BASE_URL is retired: if set (even empty), every built-in command fails with a pointer to entire login --server. Local dev docs use --server + ENTIRE_API_BASE_URL instead.
  • No active login context → control-plane commands return ErrNotLoggedIn / entire login hint; auth status prints informational “Not logged in.”; logout no-ops. No fallback to a default auth host or legacy token.
  • Data API (activity, search, etc.) requires /.well-known/entire-api.json; missing discovery is a hard error (no static TokenForResource fallback).
  • Device login always uses the v2/OIDC surface (/device_authorization, fixed oauthClientID); ENTIRE_AUTH_PROVIDER_VERSION and split-host auto-detect are gone.
  • logout drops legacy keyring reads/deletes; revocation uses the resolved context token only.

Tests / build — integration login tests use JWT iss/handle and /device_authorization; authfilestore build tag removed in favor of ENTIRE_TOKEN_STORE=file for e2e/integration.

Reviewed by Cursor Bugbot for commit 613b91a. Configure here.

Copilot AI review requested due to automatic review settings June 11, 2026 00:09

Copilot AI 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.

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.json discovery (no static fallback).
  • Updates integration/E2E wiring to use ENTIRE_TOKEN_STORE=file (no authfilestore build 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.

Comment thread cmd/entire/cli/login.go
Comment thread cmd/entire/cli/auth/data_api.go

Copilot AI 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.

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-auth is applied after resolveStatusTarget, but resolveStatusTarget may call auth.RefreshedLoginToken, which consults auth.insecureHTTPEnabled() to decide whether a non-loopback http:// core is allowed. This means entire auth status --insecure-http-auth can still fail to refresh/resolve tokens for an insecure-core context because auth.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-auth is applied after resolveStatusTarget, but resolveStatusTarget may call auth.RefreshedLoginToken, which consults auth.insecureHTTPEnabled() to allow a non-loopback http:// core. This makes entire logout --insecure-http-auth fail 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.

Comment thread cmd/entire/cli/auth/context_store.go
@toothbrush toothbrush force-pushed the paul/cor-393-demolish-auth-fallbacks branch from 4f6a269 to 2af3c53 Compare June 11, 2026 04:02
@toothbrush toothbrush force-pushed the paul/cor-393-sunset-entire-auth-base-url branch from f41d01e to 25a0909 Compare June 11, 2026 04:31
@toothbrush toothbrush force-pushed the paul/cor-393-demolish-auth-fallbacks branch from 2af3c53 to b8b0d59 Compare June 11, 2026 05:12
Base automatically changed from paul/cor-393-sunset-entire-auth-base-url to main June 11, 2026 08:11
toothbrush and others added 14 commits June 11, 2026 17:13
…-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
@toothbrush toothbrush force-pushed the paul/cor-393-demolish-auth-fallbacks branch from b8b0d59 to 6c240a0 Compare June 11, 2026 08:18
Comment thread README.md Outdated
Comment thread README.md Outdated
toothbrush and others added 2 commits June 11, 2026 17:20
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
@toothbrush toothbrush marked this pull request as ready for review June 12, 2026 01:19
@toothbrush toothbrush requested a review from a team as a code owner June 12, 2026 01:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants