fix(auth): resolve active account from external token in token/gh-cli mode#25
Open
DutchmanNL wants to merge 1 commit into
Open
fix(auth): resolve active account from external token in token/gh-cli mode#25DutchmanNL wants to merge 1 commit into
DutchmanNL wants to merge 1 commit into
Conversation
… mode
In `token` and `gh-cli` auth modes the credential lives outside the on-disk
account store (env var / gh CLI). `/api/auth/status` authenticated fine via
`getProviderStatus()`, but the data layer resolves the active account through
`accountStore.getActive()`, which was only ever populated from `accounts.json`
or a legacy `auth.json`. With neither present the store stayed empty, so every
data endpoint (`/api/repos`, `/api/issues`, `/api/pulls`, notifications, …)
returned `401 {"needsAuth":true}` and the UI showed "Connect an account",
despite the status endpoint reporting the token as authenticated.
Seed an ephemeral account synthesised from the external credential:
- authProvider: add `loadExternalAccountDraft()` exposing the env / gh-cli
token (with login + scope) for the current auth mode.
- accountStore: seed/refresh that as an ephemeral `external` account inside
`getActive()`/`list()`, reusing the pre-existing ephemeral-account support.
It is never written to disk and is dropped when the credential goes away.
Covers both `token` and `gh-cli` modes at the single choke point every data
consumer already goes through, so no call sites change.
Verified: `GH_AUTH_MODE=token GITHUB_TOKEN=<PAT>` with an empty store now
returns 200 from `/api/repos` (was 401). Added accountStore regression tests.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
In
GH_AUTH_MODE=tokenandgh-climodes,/api/auth/statusreports the token as authenticated, but every data endpoint (/api/repos,/api/issues,/api/pulls, notifications, CI health) returns401 {"ok":false,"error":"authentication required","needsAuth":true}, and the UI shows "Connect an account". Fixes #24.Root cause
The status endpoint and the data layer resolve auth through two different paths:
/api/auth/status→getProviderStatus()understands env / gh-cli tokens (loadEnvToken()/loadGhCliToken()), so it authenticates. ✅dashboardData.resolveActive()→accountStore.getActive(), which only returns accounts from the on-disk store (accounts.json/ legacyauth.json). Nothing ever seeds that store fromGITHUB_TOKENor the gh CLI, so in pure token/gh-cli mode it stays empty →getActive()returnsnull→authFail()→401 needsAuth. ❌accountStorealready prefers an ephemeral env-based account andadd()already supportsephemeral:true, but no code path ever created that account.Fix
Seed an ephemeral account from the external credential, at the single choke point every data consumer already uses:
authProvider.ts— addloadExternalAccountDraft(), exposing the env / gh-cli token (withlogin+scope) for the current auth mode, ornullindevicemode / when no token is available.accountStore.ts—ensureExternalAccount()seeds/refreshes that as an ephemeralexternalaccount insidegetActive()/list(). It is never persisted to disk and is dropped if the credential disappears at runtime.Because the seed happens in
accountStore.getActive()/list(), no call sites change —dashboardData,notifications, the accounts list and status endpoints all pick it up. Bothtokenandgh-climodes are covered.Testing
npm test— 80 pass (added 2accountStoreregression tests: the ephemeral external account is seeded and resolved as active, is not persisted, and is dropped when the credential goes away).Manual, empty account store:
/api/auth/status/api/repos200 authenticated401 needsAuth200 authenticated200, repos listedNotes
accounts.json).[]from/user/orgs, so a classicread:orgPAT is needed to list org repos. Not addressed here.