auth: active context wins over ENTIRE_AUTH_BASE_URL for control plane · Entire
auth: active context wins over ENTIRE_AUTH_BASE_URL for control plane
81e341d→main·
toothbrush·1mo ago·4 files·+67 added/-57 removed
Make ResolveControlPlaneTarget match auth status: the active context always drives host+token; ENTIRE_AUTH_BASE_URL is only the fallback when no context is active, not an override. A token minted by the context's core can't authenticate against a different host, so "override host + context identity" can't succeed — the active context is authoritative.
Drops the now-unused api.AuthBaseURLOverridden() helper.
Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com
Sessions
dbdac822b936View transcript
Changes
4
cmd/entire/cli
api
- Mbase_url.go-9
auth
- Mcontrol_plane.go+14/-18
- Mcontrol_plane_test.go+38/-20
docs/architecture
- Mupstream-host-resolution.md+15/-10
63 unmodified lines
return NormalizeOriginURL(raw)
}
// AuthBaseURLOverridden reports whether ENTIRE_AUTH_BASE_URL is explicitly
// set (non-empty after trimming). Control-plane host resolution treats an
// explicit override as unconditional: it pins the core to that origin and
// skips the active-context lookup, so split-host / local-dev invocations that
// already set this var keep their exact behaviour.
func AuthBaseURLOverridden() bool {
return strings.TrimSpace(os.Getenv(AuthBaseURLEnvVar)) != ""
}
// IsSplitHost reports whether the CLI is configured for split-host —
// i.e. ENTIRE_AUTH_BASE_URL points at a different origin than the data
// API. Both sides are canonicalised via NormalizeOriginURL before
Mcmd/entire/cli/api/base_url.go-9
22 unmodified lines
// ResolveControlPlaneTarget chooses which core the control-plane commands talk
// to and how their bearer is obtained. The control-plane host *is* a core, so
// there is no /.well-known discovery here — the active context already names
// the core. Precedence:
//
// The core. Precedence (matching `auth status`):
//
// 1. ENTIRE_AUTH_BASE_URL explicitly set -> that origin verbatim, bearer via
// the singleton manager (TokenForResource), exactly as before. The env
// var stays an unconditional override so split-host / local-dev
// invocations are untouched.
// 2. otherwise the active contexts.json login -> its CoreURL, with a
// per-context refreshing bearer (silent JWT re-mint). This is what makes
// 1. the active contexts.json login -> its CoreURL, with a per-context
// refreshing bearer (silent JWT re-mint). This is what makes
// `entire auth use <ctx>` retarget the control plane onto that core.
// 3. no active context -> the configured default origin + TokenForResource,
// the pre-contexts behaviour for users who never ran `entire auth use`.
// 2. no active context -> the configured auth origin (ENTIRE_AUTH_BASE_URL or
// the default) + TokenForResource, the pre-contexts fallback.
//
// ENTIRE_AUTH_BASE_URL is the fallback host, not an override: a token minted
// by the active context's core can't authenticate against a different host, so
// "use the override host but the context's identity" can't succeed. The active
// context always wins when present.
func ResolveControlPlaneTarget() (ControlPlaneTarget, error) {
if api.AuthBaseURLOverridden() {
return staticControlPlaneTarget(), nil
}
c, ok, err := activeContext()
if err != nil {
return ControlPlaneTarget{}, err
}
return ControlPlaneTarget{CoreURL: strings.TrimRight(c.CoreURL, "/"), TokenSource: src}, nil
}
// staticControlPlaneTarget is the pre-contexts path: dial the configured auth
// origin and resolve the bearer through the singleton manager, which performs
// an RFC 8693 exchange when the stored token's audience doesn't cover the
// core. Used for an explicit ENTIRE_AUTH_BASE_URL override and as the
// no-active-context fallback.
// staticControlPlaneTarget is the no-active-context fallback: dial the
// configured auth origin (ENTIRE_AUTH_BASE_URL or the default) and resolve the
// bearer through the singleton manager, which performs an RFC 8693 exchange
// when the stored token's audience doesn't cover the core.
func staticControlPlaneTarget() ControlPlaneTarget {
base := strings.TrimRight(api.AuthBaseURL(), "/")
// The exchange's resource must be the bare origin; OriginOnly strips any
Mcmd/entire/cli/auth/control_plane.go+14/-18
24 unmodified lines
// An explicit ENTIRE_AUTH_BASE_URL pins the core to that origin and skips the
// active-context lookup entirely — the override is unconditional.
func TestResolveControlPlaneTarget_EnvOverrideWins(t *testing.T) {
// The active context wins even when ENTIRE_AUTH_BASE_URL is set to a different
// origin: the env var is a fallback host, not an override (a token from the
// context's core can't authenticate against the env host anyway).
func TestResolveControlPlaneTarget_ActiveContextBeatsEnv(t *testing.T) {
configDir := t.TempDir()
t.Setenv("ENTIRE_CONFIG_DIR", configDir)
t.Setenv(api.AuthBaseURLEnvVar, "https://override.example")
// An active context on a *different* core must be ignored under override.
writeActiveContext(t, configDir, "ctx", "https://other-core.example", "alice", "svc")
const coreURL = "https://other-core.example"
writeActiveContext(t, configDir, "ctx", coreURL, "alice", "svc")
target, err := ResolveControlPlaneTarget()
if err != nil {
t.Fatalf("ResolveControlPlaneTarget: %v", err)
}
if want := api.AuthBaseURL(); target.CoreURL != want {
t.Fatalf("CoreURL = %q, want the override origin %q (not the context core)", target.CoreURL, want)
}
if target.CoreURL != coreURL {
t.Fatalf("CoreURL = %q, want the active context's core %q (env is only a fallback)", target.CoreURL, coreURL)
}
}
// With no override and no active context, the target falls back to the
// configured default auth origin (pre-contexts behaviour).
func TestResolveControlPlaneTarget_NoContextFallsBackToDefault(t *testing.T) {
configDir := t.TempDir() // empty: no contexts.json
t.Setenv("ENTIRE_CONFIG_DIR", configDir)
t.Setenv(api.AuthBaseURLEnvVar, "")
// With no active context, the target falls back to the configured auth origin
// — the default when ENTIRE_AUTH_BASE_URL is unset, or the env value when set
// (the env var is the fallback host).
func TestResolveControlPlaneTarget_NoContextFallsBackToAuthBaseURL(t *testing.T) {
t.Run("default when env unset", func(t *testing.T) {
configDir := t.TempDir() // empty: no contexts.json
t.Setenv("ENTIRE_CONFIG_DIR", configDir)
t.Setenv(api.AuthBaseURLEnvVar, "")
target, err := ResolveControlPlaneTarget()
if err != nil {
t.Fatalf("ResolveControlPlaneTarget: %v", err)
}
if want := api.AuthBaseURL(); target.CoreURL != want {
t.Fatalf("CoreURL = %q, want the default auth origin %q", target.CoreURL, want)
}
target, err := ResolveControlPlaneTarget()
if err != nil {
t.Fatalf("ResolveControlPlaneTarget: %v", err)
}
if want := api.AuthBaseURL(); target.CoreURL != want {
t.Fatalf("CoreURL = %q, want the default auth origin %q", target.CoreURL, want)
}
})
t.Run("env value when set", func(t *testing.T) {
configDir := t.TempDir()
t.Setenv("ENTIRE_CONFIG_DIR", configDir)
t.Setenv(api.AuthBaseURLEnvVar, "https://fallback.example")
target, err := ResolveControlPlaneTarget()
if err != nil {
t.Fatalf("ResolveControlPlaneTarget: %v", err)
}
if want := api.AuthBaseURL(); target.CoreURL != want {
t.Fatalf("CoreURL = %q, want the env fallback origin %q", target.CoreURL, want)
}
})
}
Mcmd/entire/cli/auth/control_plane_test.go+38/-20
Control plane (done — this slice)
The host is a core, so there is no discovery. coreapi.New() consults
auth.ResolveControlPlaneTarget(), which mirrors auth status:
ENTIRE_AUTH_BASE_URLset → that origin, verbatim; bearer via the singleton token manager (TokenForResource). The env var is an unconditional override, so split-host / local-dev invocations are untouched.- else active context → its
CoreURL, with a per-context refreshing bearer (auth.NewRefreshingLoginProvider): the token manager is keyed onc.CoreURLas issuer, so store reads and refresh/STS hit the right core, and an expired access token is silently re-minted from the stored refresh token. This is what makesentire auth use <ctx>actually retargetorg/repo/project/grant. - else (no active context) → the configured default origin +
TokenForResource— the pre-contexts behaviour. - else (no active context) → the configured auth origin
(
ENTIRE_AUTH_BASE_URLor the default) +TokenForResource— the pre-contexts fallback.
ENTIRE_AUTH_BASE_URL is the fallback host, not an override: a token
minted by the active context's core can't authenticate against a different
host, so the active context always wins when present. (At login time the env
var still chooses where to authenticate, and the resulting context's CoreURL
is that host — so local-dev / split-host setups keep working.)
Key files: cmd/entire/cli/auth/control_plane.go (resolver),
cmd/entire/cli/auth/refresh.go (per-context refreshing provider),
the activity / search / trail / dispatch constructors
(NewAuthenticatedAPIClient, dispatch.NewCloudClient, search.Search).
ENTIRE_AUTH_BASE_URL / ENTIRE_API_BASE_URL remain explicit overrides: when
set they short-circuit discovery; discovery only runs when they are unset.
ENTIRE_API_BASE_URL names the resource host to dial (the discovery target);
the context is then chosen from the issuers that host advertises. It does not
need to be paired with ENTIRE_AUTH_BASE_URL once discovery exists — that is
the whole point of the well-known.