Skip to content

fix(console): stale browser token must not override the gateway; a dead deep link must fall back, not dead-end - #20

Open
damonleelcx wants to merge 1 commit into
develop-v1.0.5from
fix/console-gateway-browser-auth
Open

fix(console): stale browser token must not override the gateway; a dead deep link must fall back, not dead-end#20
damonleelcx wants to merge 1 commit into
develop-v1.0.5from
fix/console-gateway-browser-auth

Conversation

@damonleelcx

Copy link
Copy Markdown
Contributor

Two independent ways this page reported "session expired" when the session was fine.

1 · A stale browser token overrode the gateway's authoritative one

Personal's composing gateway owns team authentication server-side: it reads the current CLI JWT from the local vault and injects it into forwarded requests. A token in localStorage is stale by construction in that quadrant — aikey login refreshes the vault, not the browser.

shouldAttachBrowserToken (new shared/api/browser-auth.ts) suppresses the browser Authorization header for exactly two quadrants:

quadrant attach browser token?
local_bypass + Personal ❌ gateway owns auth
local_bypass + Team via gateway ❌ gateway owns auth
local_bypass + standalone Trial ✅ historical behaviour — its one server may use it to resolve a real identity

2 · A stale deep link rendered as an expired session

A vault deep link can outlive the group or account it targeted. Master rejects the stale credential with 403, which the page rendered as an expired team session.

  • fetchMyPoolAccounts: a scoped read that returns unauth retries the caller's ordinary projection once. A genuinely expired JWT still fails the unscoped retry and stays unauth.
  • isKnownPoolFilter: a successful groups response is authoritative, so the page drops both coupled deep-link fields (group, expand) from the URL and shows the caller's current projection instead of an empty dead end.

Test

npx tsc --noEmit                                                  # clean
npx vitest run src/shared/api src/pages/user/oauth-contribute     # 14 files, 71 tests

Companion PRs: aikey-trial-server#10 (the gateway side of the same failure) and the matching aikey-control-master change.

🤖 Generated with Claude Code

… and let a dead deep link fall back instead of dead-ending

Two ways this page reported "session expired" when the session was fine.

1. Personal's composing gateway owns team authentication SERVER-side: it reads
   the current CLI JWT from the local vault and injects it into forwarded
   requests. A token in localStorage is stale by construction in that quadrant
   — `aikey login` refreshes the vault, not the browser — and was overriding
   the authoritative one. `shouldAttachBrowserToken` suppresses it for exactly
   two quadrants: Personal, and a gateway-forwarded Team page. Standalone
   Trial keeps the historical local_bypass behaviour, because its one server
   may use the browser token to resolve a real identity.

2. A vault deep link can outlive the group or account it targeted. Master
   rejects the stale credential with 403, which the page rendered as an
   expired team session. Now a scoped read that comes back `unauth` retries
   the caller's ordinary projection once; a genuinely expired JWT still fails
   the unscoped retry and stays `unauth`. On the page side,
   `isKnownPoolFilter` treats a SUCCESSFUL groups response as authoritative
   and drops both coupled deep-link fields (`group`, `expand`) from the URL,
   so a bad bookmark shows the caller's current projection instead of an
   empty dead end.

Test: `tsc --noEmit` clean; `vitest run src/shared/api src/pages/user/oauth-contribute`
→ 14 files, 71 tests green.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant