cli: stored-auth injection should honor refresh_token, not just access_token (#1665)#1673
cli: stored-auth injection should honor refresh_token, not just access_token (#1665)#1673cliffhall wants to merge 2 commits into
Conversation
#1665) Closes #1665 `--use-stored-auth` previously injected the stored access token blindly, so an expired token failed even with a valid refresh_token on disk. Now, when a `refresh_token` is stored, the CLI runs the OAuth refresh grant and injects the fresh access token, persisting the rotation. - New exported `refreshStoredAuthToken(serverUrl, statePath, deps?)`: reuses the SDK's `refreshAuthorization` (not a hand-rolled token request) with the stored `clientInformation` + `serverMetadata` (discovered via core `getAuthorizationServerUrl` + SDK `discoverAuthorizationServerMetadata` when the metadata wasn't persisted). Rotated tokens are written back through `serializeOAuthPersistBlob` under the same `{servers,idpSessions}` key, so web and CLI stay consistent. Missing refresh_token/clientInformation or a failed grant throw `CliExitCodeError(AUTH_REQUIRED)` with a clear message + envelope code (`no_stored_token` / `refresh_failed`). - `--use-stored-auth` prefers refresh when a refresh_token is present (covers both absent and expired access tokens — the stored blob has no expiry, so the refresh token is the durable credential); falls back to injecting the stored access token otherwise (unchanged). - `deps` are injectable so the refresh/discovery are unit-tested without a live token endpoint; an e2e test drives the real SDK path against a mock token endpoint and asserts the refreshed token is injected + the rotation persisted. - CLI README updated (removed the "blind injection" caveat). CLI coverage gate green (cli.ts 100/92/100/100). Part of the #1579 decomposition (Wave 4, CLI lane). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BrihGWcrM9JGRyu41nzZYw
|
@claude review |
|
Claude finished @cliffhall's task in 2m 19s —— View job Review — stored-auth
|
- Refresh failure now falls back to injecting a still-present stored access token instead of hard-exiting 3, so a transient auth-server hiccup doesn't regress a previously-working invocation (review #1). Only a failure with no access token to fall back on surfaces as exit 3. - Persist rotated tokens via the shared writeStoreFile (0o600 + mkdir -p) instead of a raw writeFile, so the secrets file keeps owner-only perms identical to the web backend (review #2). - Missing clientInformation now throws the distinct no_client_information envelope code rather than reusing no_stored_token (review #5a). - Docs: --wait-for-auth no longer claims parity with --use-stored-auth (it injects the freshly-landed token without refreshing); noted the always-refresh rotation/concurrency caveat (review #3, #4). - Tests: discovery-returns-undefined metadata path, no_client_information code, and an e2e refresh-failure → access-token fallback. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BrihGWcrM9JGRyu41nzZYw
|
Thanks — addressed the substantive findings in
@claude review |
|
Claude finished @cliffhall's task in 1m 6s —— View job Re-review — follow-up commit
|
|
Superseded by the Wave 4 rollup #1676, which contains this change (reviewed to LGTM here) merged base→tip. Closing in favor of the rollup. |
Closes #1665
Summary
--use-stored-authpreviously injected the stored access token blindly, so an expired token failed even with a validrefresh_tokenon disk. Now, when arefresh_tokenis stored, the CLI runs the OAuth refresh grant to mint a fresh access token before connecting, and persists the rotation.refreshStoredAuthToken(serverUrl, statePath, deps?):refreshAuthorization(not a hand-rolled token request) with the storedclientInformation+serverMetadata— discovered from--server-urlvia coregetAuthorizationServerUrl+ SDKdiscoverAuthorizationServerMetadatawhen the metadata wasn't persisted.serializeOAuthPersistBlobunder the same{servers,idpSessions}key, so web and CLI stay consistent.refresh_token/clientInformationor a failed grant throwCliExitCodeError(AUTH_REQUIRED)with a clear message + envelope code (no_stored_token/refresh_failed).--use-stored-authprefers refresh when arefresh_tokenis present (covers both absent and expired access tokens — the persisted blob carries no expiry, so the refresh token is the durable credential); falls back to injecting the stored access token otherwise (unchanged behavior).Tests
depsare injectable, so refresh/discovery branches are unit-tested without a live endpoint: happy path + persist, discovery-when-metadata-absent, no-refresh-token, no-client-information, and refresh-grant-failure.node:http) and asserts the refreshed token is injected on the MCP request + the rotation is persisted.CLI coverage gate green (
npm run test:coverage, EXIT 0;cli.ts100/92/100/100).Related: #665, #1160. Part of the #1579 decomposition (Wave 4, CLI lane). Per AGENTS.md the
Closesline won't auto-fire onv2/main; the issue is closed and its board card moved on the Wave 4 rollup merge.🤖 Generated with Claude Code