[wrangler] Read OAuth state from disk lazily so env auth takes priority#13954
Merged
Conversation
🦋 Changeset detectedLatest commit: fec8a84 The changes in this PR will be included in the next version bump. This PR includes changesets to release 3 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
Contributor
|
✅ All changesets look good |
Contributor
|
Codeowners approval required for this PR:
Show detailed file reviewers
|
Contributor
|
Pure function, no side effects - fine to call multiple times. Now I've done a thorough review. Let me assess the overall quality: Summary of what this PR does:
Analysis of correctness:
I don't see any logic bugs, security issues, backward compatibility violations, or incorrect API behavior. The change is well-structured and the approach is correct. LGTM |
create-cloudflare
@cloudflare/kv-asset-handler
miniflare
@cloudflare/pages-shared
@cloudflare/unenv-preset
@cloudflare/vite-plugin
@cloudflare/vitest-pool-workers
@cloudflare/workers-editor-shared
@cloudflare/workers-utils
wrangler
commit: |
NuroDev
approved these changes
May 18, 2026
james-elicx
approved these changes
May 18, 2026
dario-piotrowicz
approved these changes
May 18, 2026
…riority Wrangler previously read its OAuth state from the user auth config file eagerly at module-import time, before `.env` files have been loaded. The in-memory state therefore always held the OAuth tokens even when the user only wanted to authenticate via `CLOUDFLARE_API_TOKEN`. If that stored OAuth token happened to be expired, Wrangler would try to refresh it (and fail), aborting the command with 'Failed to fetch auth token: 400 Bad Request' / 'Not logged in.' — even though a valid API token was in scope. Read the auth config file on demand from every site that needs it. `isAccessTokenExpired()` short-circuits to `false` when env-based credentials are present, so the OAuth refresh endpoint is no longer called when env auth is going to be used. Sibling-process refresh-token rotation is also handled naturally because every check reads the current file contents. Drive-by cleanups: - Drop the dead in-memory mutations in `exchangeRefreshTokenForAccessToken` and `exchangeAuthCodeForAccessToken` — they were redundant with the `writeAuthConfigFile` call that immediately follows. - Drop the dead inner `!localState.accessToken && localState.refreshToken` branch in `logout()` plus its accidental double-revoke. - Remove the now-redundant `reinitialiseAuthTokens()` call from the top of `refreshToken()` (introduced in #13910 for sibling rotation; now naturally subsumed). - Drop the in-process selected-account cache in favour of the existing file-based `wrangler-account.json` cache, which is naturally per-cwd. - Remove the exported `reinitialiseAuthTokens()` function — there is no module-level cache left to invalidate. Fixes #13744.
The local `packages/wrangler/src/__tests__/helpers/run-in-tmp.ts` helper only existed to wrap `runInTempDir` from `@cloudflare/workers-utils/test-helpers` with a `beforeEach(reinitialiseAuthTokens)` hook. Now that auth state is read from disk on demand and `reinitialiseAuthTokens` has been removed, the wrapper has no purpose. Delete the wrapper and update every caller to import `runInTempDir` directly from `@cloudflare/workers-utils/test-helpers`.
Co-authored-by: Dario Piotrowicz <dario@cloudflare.com>
50ae152 to
4777aeb
Compare
workers-devprod
approved these changes
May 19, 2026
workers-devprod
left a comment
Contributor
There was a problem hiding this comment.
Codeowners reviews satisfied
… one on refresh RFC 6749 §6 allows the authorization server to return a successful refresh response without a new refresh_token, in which case the previously issued refresh token remains valid. The previous code wiped the stored value in that case, which caused the next refresh attempt to fail with 'No refresh token is present' and forced the user to re-run wrangler login. exchangeRefreshTokenForAccessToken now falls back to the storedRefreshToken already read from disk when the response omits a new refresh_token. Adds a regression test alongside the existing sibling-rotation test.
8633154 to
fec8a84
Compare
Merged
This was referenced May 19, 2026
Merged
petebacondarwin
added a commit
that referenced
this pull request
May 20, 2026
The local helpers/run-in-tmp.ts wrapper was removed in #13954. New test files must import runInTempDir directly from @cloudflare/workers-utils/test-helpers.
petebacondarwin
added a commit
that referenced
this pull request
May 20, 2026
The local helpers/run-in-tmp.ts wrapper was removed in #13954. New test files must import runInTempDir directly from @cloudflare/workers-utils/test-helpers.
petebacondarwin
added a commit
to bimsonz/workers-sdk
that referenced
this pull request
May 20, 2026
The local helpers/run-in-tmp.ts wrapper was removed in cloudflare#13954. New test files must import runInTempDir directly from @cloudflare/workers-utils/test-helpers.
petebacondarwin
added a commit
that referenced
this pull request
May 20, 2026
The local helpers/run-in-tmp.ts wrapper was removed in #13954. New test files must import runInTempDir directly from @cloudflare/workers-utils/test-helpers.
penalosa
pushed a commit
that referenced
this pull request
May 28, 2026
…ty (#13954) Co-authored-by: Dario Piotrowicz <dario@cloudflare.com>
emily-shen
pushed a commit
that referenced
this pull request
Jun 11, 2026
The local helpers/run-in-tmp.ts wrapper was removed in #13954. New test files must import runInTempDir directly from @cloudflare/workers-utils/test-helpers.
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.
Fixes #13744.
What
Wrangler previously read its OAuth state from the user auth config file (e.g.
~/.config/.wrangler/config/default.toml) eagerly at module-import time. That happens before.envfiles are loaded, so the in-memory state would always hold the OAuth tokens even when the user only wanted to authenticate viaCLOUDFLARE_API_TOKEN. If that stored OAuth token happened to be expired, Wrangler would try to refresh it (and fail), aborting the command with:— even though a valid API token was in scope.
How
Read the auth config file on demand from every site that needs it, rather than caching it at module scope. The key fix is in
isAccessTokenExpired(): it now short-circuits tofalsewhen env-based credentials are present, so the OAuth refresh endpoint is no longer called when env auth is going to be used.Sibling-process refresh-token rotation (the case PR #13910 partially addressed) is also handled naturally because every check reads the current file contents.
Two commits:
[wrangler] fix: read OAuth state from disk lazily so env auth takes priority— the actual fix inpackages/wrangler/src/user/user.ts, plus three regression tests inuser.test.ts. Includes several drive-by cleanups (deadlocalStatemutations, deadlogout()branch with accidental double-revoke, redundantreinitialiseAuthTokensinrefreshToken, in-process selected-account cache).[wrangler] refactor: import runInTempDir from workers-utils directly— pure mechanical cleanup: the localpackages/wrangler/src/__tests__/helpers/run-in-tmp.tswrapper only existed to callreinitialiseAuthTokens()between tests. With that function gone, every caller can importrunInTempDirdirectly from@cloudflare/workers-utils/test-helpers. 163 test files updated by a one-shot script, thenoxfmt-ed.Test plan
user.test.tscover the issue scenario directly:getAPITokenreturns the env token even when a stored OAuth token also existsloginOrRefreshIfRequireddoes not attempt OAuth refresh when env auth is setwrangler whoamisucceeds via env token when a stale OAuth token also exists on disk (asserting nooauth2/tokenrequest is fired)main, plus the 3 new tests).getAuthFromEnv()documentation in user.ts.