Skip to content

feat(auth): refresh an expiring OAuth token before token view / status - #123

Merged
scottlovegrove merged 2 commits into
mainfrom
feat/refresh-before-token-view
Sep 16, 2026
Merged

scottlovegrove merged 2 commits into
mainfrom
feat/refresh-before-token-view

Conversation

@scottlovegrove

Copy link
Copy Markdown
Contributor

Problem

ol auth token view read the access token straight from the store and never triggered a refresh. Outline access tokens last about an hour, so once one expired the command kept printing the same dead token until some other API command happened to rotate it, which defeats the point of export OUTLINE_API_TOKEN="$(ol auth token view)" for scripts. ol auth status worked around the same gap with a hand-rolled refreshedTokenForStatus inside fetchLive.

Change

Bumps @doist/cli-core to 1.6.0 (Doist/cli-core#135) and wires its new refresh option on both attachers via a shared getTokenRefreshOptions() in src/lib/auth.ts. The handshake is resolved per account through the function form ({ baseUrl, clientId } from the account cli-core selected), so --user <other> refreshes the right account at the right instance without a second store read.

  • token view: rotates an expiring OAuth token before printing. Env, legacy and manual-token accounts carry no refresh token and are served from the single bundle read, so their behaviour is unchanged. The env-var guard still runs before any refresh.
  • status: refreshedTokenForStatus is gone. cli-core rotates through the attacher's own --user-aware store and hands fetchLive the live token. One behavioural difference worth knowing: the old helper swallowed every refresh error and probed with the stale token, so a rejected refresh token surfaced as a NO_TOKEN from the 401. It now surfaces as cli-core's AUTH_REFRESH_EXPIRED (re-login required) before the probe, which is the same outcome with a clearer cause. Transient refresh failures still fall back to the stored token while it is valid.

Tests

  • auth-command.test.ts: the cli-core attachers are now pass-through spies, and a wiring test asserts both status and token view receive the same refresh options. The default mockOutlineAuthModule fixture stubs getTokenRefreshOptions to undefined so the existing command-surface tests keep exercising the stored-token path.
  • lib/auth.test.ts: getTokenRefreshOptions returns the config-scoped lock path and a handshake resolver that pins to the given account's base URL and client id.

Skill content and README updated for the token view behaviour; SKILL.md regenerated. The lockfile diff beyond the cli-core bump is npm 10.8 metadata drift (libc / dev flags), not dependency changes.

…atus`

`ol auth token view` printed whatever access token was stored, so once an
OAuth token expired (they last about an hour) it kept handing scripts a
dead token until some other command happened to refresh it. Wire cli-core
1.6.0's `refresh` option on both `token view` and `status` so an expiring
token is rotated first.

`status` previously did this by hand via `refreshedTokenForStatus`, which
re-read the store through the module singleton and swallowed every refresh
error. cli-core now rotates the selected account through the attacher's
own `--user`-aware store and hands `fetchLive` the live token; a rejected
refresh token surfaces as `AUTH_REFRESH_EXPIRED` (re-login) instead of
silently probing with the stale one.
@scottlovegrove scottlovegrove self-assigned this Sep 16, 2026

@doistbot doistbot left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR wires cli-core's new refresh option into both auth attachers via a shared getTokenRefreshOptions() helper, removing the hand-rolled refreshedTokenForStatus from auth status and making auth token view rotate an expiring OAuth token before printing it.

Few things worth tightening:

  • auth-token.test.ts's store mock lacks activeBundle, so the token view tests now throw storeMocks.activeBundle is not a function — add the mock or stub getTokenRefreshOptions to undefined there like auth-command.test.ts does.
  • In auth-command.test.ts, the sentinel mockReturnValue for getTokenRefreshOptions leaks into every later test in the file (vi.clearAllMocks() only clears calls); switch to mockReturnValueOnce or restore with mockReturnValue(undefined) so subsequent tests exercise the stored-token path as the fixture promises.

Share FeedbackReview Logs

Comment thread src/commands/auth.ts
Comment thread src/commands/auth-command.test.ts
… default explicit

`auth-token.test.ts` only stayed on the stored-token path because its
store stub happened to lack `setBundle`. Stub `getTokenRefreshOptions`
there like the other command-surface suite does, and add a test that
drives cli-core's real refresh through `ol auth token view` with a fake
provider. Both suites now restore the fixture default after registering
with a real refresh, since each registration calls the builder twice.
@scottlovegrove
scottlovegrove requested review from a team and rfgamaral and removed request for a team September 16, 2026 17:11
@scottlovegrove scottlovegrove added the 👀 Show PR PR must be reviewed before or after merging label Sep 16, 2026
@scottlovegrove
scottlovegrove merged commit 2584eaf into main Sep 16, 2026
5 checks passed
@scottlovegrove
scottlovegrove deleted the feat/refresh-before-token-view branch September 16, 2026 17:12
doist-release-bot Bot added a commit that referenced this pull request Sep 16, 2026
## [2.1.0](v2.0.9...v2.1.0) (2026-09-16)

### Features

* **auth:** refresh an expiring OAuth token before `token view` / `status` ([#123](#123)) ([2584eaf](2584eaf))
@doist-release-bot

Copy link
Copy Markdown
Contributor

🎉 This PR is included in version 2.1.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

@rfgamaral rfgamaral left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ LGTM!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

released 👀 Show PR PR must be reviewed before or after merging

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants