# auth: make `auth status` context-aware; hit /me on the active core

`97207ef`·
toothbrush·1mo ago·4 files·+205 added/-150 removed

`auth status` queried /me against the static api.AuthBaseURL(), so with an active context on a different core (e.g. `auth use eu.auth.entire.io` while AuthBaseURL defaults to us.*) it sent the context's token to the wrong core and got a 401 — surfaced as a raw ogen decode dump because the 401 body was text/plain.

- Resolve the active contexts.json context first (resolveStatusTarget): use its CoreURL + session token, falling back to AuthBaseURL + the legacy keyring entry only when no context is active. `auth use` now retargets status. "Logged in to <core>" reflects the active context.
- Add coreapi.NewWithBearer(coreURL, token) to hit a specific login server with a fixed bearer (no STS), used by status's /me.
- Harden isKeychainTokenRejected: a non-JSON 401 (ogen "decode response: ... (code 401)") now maps to the friendly re-login hint, not a raw dump.
- TLS-guard the resolved context core URL before sending the token.

## Sessions

### Transcript: 1385b003c7a8

#### Changes

4

- cmd/entire/cli

- Mauth.go+75/-35

- Mauth_context_test.go+26

- Mauth_test.go+78/-115

- internal/coreapi

- Mclient.go+26

```
118 unmodified lines
```

if errors.Is(err, auth.ErrNotLoggedIn) {
    return true
}
// A 401 whose body isn't JSON (e.g. a gateway returning text/plain) fails
// the ogen typed decode, so it never becomes an ErrorModelStatusCode — it
// arrives as a decode error whose message carries "(code 401)". Match that
// so the user still gets the re-login hint, not a raw decode dump.
if strings.Contains(err.Error(), "code 401") {
    return true
}
return strings.Contains(err.Error(), "token exchange: status 4")
}

```  
// statusTarget is the resolved core `entire auth status` should query: the active context's CoreURL + its session token, or (no active context) the configured AuthBaseURL + legacy keyring entry.
type statusTarget struct {
    coreURL       string
    token         string
    activeContext string // "" when falling back to the legacy entry
    totalContexts int
}

// resolveStatusTarget picks the core + token for `entire auth status`. The active contexts.json context wins (so `auth use` retargets status onto that login server); otherwise it falls back to the legacy keyring entry keyed by the configured auth host.
func resolveStatusTarget(store tokenStore, listContexts contextsProvider, fallbackBaseURL string) statusTarget {
    all, current, err := listContexts()
    total := 0
    if err == nil {
        total = len(all)
        for _, c := range all {
            if c.Name != current || c.CoreURL == "" {
                continue
            }
            if tok, terr := auth.LoginTokenForContext(c); terr == nil && tok != "" {
                return statusTarget{coreURL: c.CoreURL, token: tok, activeContext: c.Name, totalContexts: total}
            }
        }
    }
    tok, gerr := store.GetToken(fallbackBaseURL)
    if gerr != nil {
        tok = "" // best-effort: a keyring read failure just reads as "no token"
    }
    return statusTarget{coreURL: fallbackBaseURL, token: tok, totalContexts: total}
}

// runAuthStatus reports auth state without listing server-side sessions: GET /me validates the token and supplies the profile header, and the active login context is read locally. (Session listing/revocation lives on entire-core and is reached only by logout — see newSessionsClient.)
func runAuthStatus(ctx context.Context, w io.Writer, store tokenStore, fetchProfile profileFetcher, listContexts contextsProvider, baseURL string) error {
    token, err := store.GetToken(baseURL)
    if err != nil {
        return fmt.Errorf("read keychain: %w", err)
    }
    if token == "" {
        fmt.Fprintf(w, "Not logged in to %s\n", baseURL)
        return nil
    }
    profile, err := fetchProfile(ctx)
    profile, err := fetchProfile(ctx, t.coreURL, t.token)
    if err != nil {
        if isKeychainTokenRejected(err) {
            fmt.Fprintf(w, "Token in keychain for %s is no longer valid.\n", baseURL)
            fmt.Fprintf(w, "Login for %s is no longer valid.\n", t.coreURL)
            fmt.Fprintln(w, "Run 'entire login' to re-authenticate.")
            return nil
        }
        return fmt.Errorf("validate token: %w", err)
    }
    fmt.Fprintf(w, "Logged in to %s\n", baseURL)
    fmt.Fprintf(w, "Logged in to %s\n", t.coreURL)
    writeProfileLines(w, profile)

// Local context info is informational; a read failure shouldn't fail the command, so on error we just skip the context lines.
    all, current, ctxErr := listContexts()
    if ctxErr == nil && current != "" {
        fmt.Fprintf(w, "  %-9s %s\n", "Context:", current)
        if t.activeContext != "" {
            fmt.Fprintf(w, "  %-9s %s\n", "Context:", t.activeContext)
        }
        fmt.Fprintf(w, "  %-9s %s\n", "Token:", "stored in OS keychain")

if ctxErr == nil && len(all) > 1 {
            if t.totalContexts > 1 {
                fmt.Fprintln(w)
                fmt.Fprintf(w, "%d login contexts saved; run 'entire auth contexts' to list or 'entire auth use <name>' to switch.\n", len(all))
            }
        }
    }
}
```
