Skip to content

add auth profiles to wrangler #14200

Draft
emily-shen wants to merge 2 commits into
mainfrom
emily/profiles-2
Draft

add auth profiles to wrangler #14200
emily-shen wants to merge 2 commits into
mainfrom
emily/profiles-2

Conversation

@emily-shen

@emily-shen emily-shen commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

See changeset and discussion #14161 for design.

(i did also delete a bunch of unnecessary snapshots, since adding a new global flag caused snapshot mismatches in way too many places...)


  • Tests
    • Tests included/updated
    • Automated tests not possible - manual testing has been completed as follows:
    • Additional testing not necessary because:
  • Public documentation
    • Cloudflare docs PR(s): TODO
    • Documentation not necessary because:

A picture of a cute animal (not mandatory, but encouraged)


Open in Devin Review

@changeset-bot

changeset-bot Bot commented Jun 5, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 79fb374

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 3 packages
Name Type
wrangler Minor
@cloudflare/vite-plugin Patch
@cloudflare/vitest-pool-workers Patch

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

@github-project-automation github-project-automation Bot moved this to Untriaged in workers-sdk Jun 5, 2026
@workers-devprod workers-devprod requested review from a team and penalosa and removed request for a team June 5, 2026 14:21
@workers-devprod

workers-devprod commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

Codeowners approval required for this PR:

  • @cloudflare/cloudchamber
  • @cloudflare/d1
  • @cloudflare/workers-kv
  • @cloudflare/wrangler
Show detailed file reviewers
  • .changeset/auth-profiles.md: [@cloudflare/wrangler]
  • packages/workers-auth/src/auth-config-file.ts: [@cloudflare/wrangler]
  • packages/workers-auth/src/flow.ts: [@cloudflare/wrangler]
  • packages/workers-auth/src/index.ts: [@cloudflare/wrangler]
  • packages/workers-auth/src/profiles.ts: [@cloudflare/wrangler]
  • packages/workers-auth/src/state.ts: [@cloudflare/wrangler]
  • packages/workers-auth/src/token-exchange.ts: [@cloudflare/wrangler]
  • packages/workers-utils/src/test-helpers/normalize.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/tests/agent-memory.test.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/tests/ai.test.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/tests/cert.test.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/tests/cloudchamber/create.test.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/tests/cloudchamber/curl.test.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/tests/cloudchamber/delete.test.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/tests/cloudchamber/images.test.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/tests/cloudchamber/list.test.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/tests/cloudchamber/modify.test.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/tests/containers/delete.test.ts: [@cloudflare/cloudchamber @cloudflare/wrangler]
  • packages/wrangler/src/tests/containers/images.test.ts: [@cloudflare/cloudchamber @cloudflare/wrangler]
  • packages/wrangler/src/tests/containers/info.test.ts: [@cloudflare/cloudchamber @cloudflare/wrangler]
  • packages/wrangler/src/tests/containers/instances.test.ts: [@cloudflare/cloudchamber @cloudflare/wrangler]
  • packages/wrangler/src/tests/containers/list.test.ts: [@cloudflare/cloudchamber @cloudflare/wrangler]
  • packages/wrangler/src/tests/containers/push.test.ts: [@cloudflare/cloudchamber @cloudflare/wrangler]
  • packages/wrangler/src/tests/containers/registries.test.ts: [@cloudflare/cloudchamber @cloudflare/wrangler]
  • packages/wrangler/src/tests/containers/ssh.test.ts: [@cloudflare/cloudchamber @cloudflare/wrangler]
  • packages/wrangler/src/tests/d1/d1.test.ts: [@cloudflare/d1 @cloudflare/wrangler]
  • packages/wrangler/src/tests/deployments.test.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/tests/docs.test.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/tests/experimental-commands-api.test.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/tests/hyperdrive.test.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/tests/index.test.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/tests/kv/help.test.ts: [@cloudflare/workers-kv @cloudflare/wrangler]
  • packages/wrangler/src/tests/kv/key.test.ts: [@cloudflare/workers-kv @cloudflare/wrangler]
  • packages/wrangler/src/tests/kv/namespace.test.ts: [@cloudflare/workers-kv @cloudflare/wrangler]
  • packages/wrangler/src/tests/mtls-certificates.test.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/tests/pages/deploy.test.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/tests/pages/pages.test.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/tests/profiles.test.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/tests/queues/queues-subscription.test.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/tests/queues/queues.test.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/tests/r2/bucket.test.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/tests/r2/bulk.test.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/tests/r2/help.test.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/tests/r2/local-uploads.test.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/tests/r2/object.test.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/tests/secrets-store.test.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/tests/setup.test.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/tests/tunnel/tunnel.test.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/tests/vectorize/vectorize.test.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/tests/versions/versions.help.test.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/tests/vpc.test.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/tests/worker-namespace.test.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/tests/workflows.test.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/api/dev.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/core/register-yargs-command.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/core/types.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/dev.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/experimental-flags.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/index.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/pages/dev.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/user/commands.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/user/profiles.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/user/user.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/wrangler-banner.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/yargs-types.ts: [@cloudflare/wrangler]

@ask-bonk

ask-bonk Bot commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

I'm Bonk, and I've done a quick review of your PR.

This PR adds named auth profiles to wrangler (per-directory bindings, a global --profile flag, and wrangler auth create/delete/activate/deactivate/list).

Two issues, ranked by severity:

1. (medium) wrangler auth token ignores the active profile

getOAuthTokenFromLocalState in packages/workers-auth/src/flow.ts (the function starting at line 327) always reads the default profile's auth state:

let stored = readStoredAuthState({ warningLogger: ctx.logger });   // no profile
...
stored = readStoredAuthState({ warningLogger: ctx.logger });        // re-read, also no profile

Every other read path you touched (getAPIToken, getScopes, loginOrRefreshIfRequired, logout) threads the resolved profile through — but this one was left unchanged.

Its only consumer is wrangler auth token (packages/wrangler/src/user/commands.ts:230), which does not hide the global --profile flag and does not call getProfile(). As a result, wrangler auth token --profile foo (or running it from a directory bound to foo) silently returns the default profile's token instead of foo's — a correctness bug for scripting/CI use cases. There's no test covering auth token with a non-default profile.

Suggested fix: give getOAuthTokenFromLocalState an optional profile?: string parameter, pass it to both readStoredAuthState calls and to the internal refreshToken() call, update the OAuthFlowAPI interface (line 108) and the wrapper in packages/wrangler/src/user/user.ts:265-269, and have commands.ts pass getProfile() (mapping default -> undefined as the other call sites do).

2. (low) listProfilePaths surfaces the default file as a reserved-name profile on non-prod environments

In packages/workers-auth/src/profiles.ts, listProfilePaths() lists every *.toml in the config dir and strips the extension. But getAuthConfigFilePath names the default profile's file <environment>.toml (e.g. staging.toml) on non-production Cloudflare API environments. So on a staging environment the default profile shows up in wrangler auth list as a profile literally named staging — which is one of RESERVED_PROFILE_NAMES and can never be created by a user. Consider mapping the environment-suffixed default file back to default and/or filtering reserved names out of the listing.

@ask-bonk

ask-bonk Bot commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

Posted my review on PR #14200. Summary of what I flagged:

  1. (medium) wrangler auth token ignores the active profilegetOAuthTokenFromLocalState in flow.ts always reads the default profile, unlike every other read path in the PR (getAPIToken, getScopes, logout, etc.). Since auth token doesn't hide the global --profile flag, wrangler auth token --profile foo silently returns the default profile's token. Untested.

  2. (low) listProfilePaths surfaces the default file as staging on non-prod environments — the default profile's file is <environment>.toml, so on staging it lists a profile named staging, which is itself a reserved name that users can't create.

Note: the inline batch review failed with a 422 because the relevant flow.ts function lines fall outside this PR's diff hunks (the function itself was unchanged — the PR added profile support to its siblings but not to it), so I fell back to a single top-level PR comment as instructed.

github run

@github-actions

github-actions Bot commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

✅ All changesets look good

devin-ai-integration[bot]

This comment was marked as resolved.

@pkg-pr-new

pkg-pr-new Bot commented Jun 5, 2026

Copy link
Copy Markdown
create-cloudflare

npm i https://pkg.pr.new/create-cloudflare@14200

@cloudflare/deploy-helpers

npm i https://pkg.pr.new/@cloudflare/deploy-helpers@14200

@cloudflare/kv-asset-handler

npm i https://pkg.pr.new/@cloudflare/kv-asset-handler@14200

miniflare

npm i https://pkg.pr.new/miniflare@14200

@cloudflare/pages-shared

npm i https://pkg.pr.new/@cloudflare/pages-shared@14200

@cloudflare/unenv-preset

npm i https://pkg.pr.new/@cloudflare/unenv-preset@14200

@cloudflare/vite-plugin

npm i https://pkg.pr.new/@cloudflare/vite-plugin@14200

@cloudflare/vitest-pool-workers

npm i https://pkg.pr.new/@cloudflare/vitest-pool-workers@14200

@cloudflare/workers-auth

npm i https://pkg.pr.new/@cloudflare/workers-auth@14200

@cloudflare/workers-editor-shared

npm i https://pkg.pr.new/@cloudflare/workers-editor-shared@14200

@cloudflare/workers-utils

npm i https://pkg.pr.new/@cloudflare/workers-utils@14200

wrangler

npm i https://pkg.pr.new/wrangler@14200

commit: 79fb374

devin-ai-integration[bot]

This comment was marked as resolved.

@emily-shen emily-shen mentioned this pull request Jun 8, 2026
5 tasks
devin-ai-integration[bot]

This comment was marked as resolved.

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Devin Review found 4 new potential issues.

View 9 additional findings in Devin Review.

Open in Devin Review

Comment thread packages/workers-auth/src/profiles.ts Outdated
Comment thread packages/workers-auth/src/profiles.ts Outdated
Comment on lines +40 to +50
export function listProfilePaths(): string[] {
const configDir = path.join(getGlobalWranglerConfigPath(), "config");
if (!existsSync(configDir)) {
return [];
}

const files = readdirSync(configDir);
return files
.filter((f) => f.endsWith(".toml"))
.map((f) => f.replace(/\.toml$/, ""));
}

@devin-ai-integration devin-ai-integration Bot Jun 8, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 listProfilePaths includes staging-environment default config as a user-visible profile

The listProfilePaths function lists all .toml files in the config directory and strips the extension to produce profile names. In staging environments, getAuthConfigFilePath() (without a profile) creates staging.toml for the default profile. This means wrangler auth list will show "staging" as a listed profile even though it's just the default profile's staging-environment variant, not a user-created profile. This is confusing because validateProfileName rejects "staging" as a reserved name, so the user would see a profile they cannot manage with wrangler auth delete or other profile commands.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment thread packages/wrangler/src/api/dev.ts Outdated
MULTIWORKER: false,
RESOURCES_PROVISION: false,
AUTOCREATE_RESOURCES: false,
profile: "default",

@devin-ai-integration devin-ai-integration Bot Jun 8, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🚩 Programmatic API (unstable_dev) hardcodes profile to 'default'

In api/dev.ts:233, the programmatic API entry point sets profile: "default" unconditionally. This means the unstable_dev() / programmatic API cannot use named auth profiles. This is likely intentional — the programmatic API doesn't expose a --profile flag and should use the default credential chain. But if a user sets up directory bindings and then uses the programmatic API from that directory, the profile binding will be ignored. Worth documenting if not already covered.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment thread packages/wrangler/src/pages/dev.ts Outdated
MULTIWORKER: Array.isArray(args.config),
RESOURCES_PROVISION: false,
AUTOCREATE_RESOURCES: false,
profile: "default",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🚩 pages/dev.ts also hardcodes profile to 'default'

Similar to the programmatic API, pages/dev.ts:960 sets profile: "default" when calling run(). This means wrangler pages dev does not participate in the profile resolution system. Since pages dev is always a local-only dev command that doesn't make authenticated API calls in the same way as other commands, this is likely fine. But it means profile bindings will be ignored during pages development.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Devin Review found 3 new potential issues.

View 10 additional findings in Devin Review.

Open in Devin Review

}
}

await logout(args.name);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔴 wrangler auth delete silently fails to remove profile file when CLOUDFLARE_API_TOKEN is set

The authDeleteCommand handler calls await logout(args.name) at packages/wrangler/src/user/profiles.ts:126, which delegates to oauthFlow.logout(profile). However, in packages/workers-auth/src/flow.ts:290-297, the logout function short-circuits when ctx.hasEnvCredentials() returns true — it prints "You are logged in with an API Token" and returns without revoking the token or deleting the file. The authDeleteCommand then proceeds to log Profile "<name>" deleted. even though the profile's TOML file was never removed from disk. Users who have CLOUDFLARE_API_TOKEN set (common in CI or multi-account setups — exactly the audience for profiles) will believe the profile was deleted when it wasn't.

Prompt for agents
In authDeleteCommand handler (packages/wrangler/src/user/profiles.ts), the call to `await logout(args.name)` delegates to `oauthFlow.logout()` which short-circuits (no-op) when CLOUDFLARE_API_TOKEN is set in the environment. This means the profile file on disk is never deleted, yet the handler logs 'Profile deleted'.

The fix should bypass the OAuth flow's logout entirely when deleting a named profile. Instead of calling `logout(args.name)`, the handler should:
1. Directly call `deleteProfileFile(args.name)` (already exported from @cloudflare/workers-auth) to remove the TOML file.
2. Optionally attempt to revoke the stored refresh token (best-effort), but not let the env-credentials guard prevent file deletion.

Alternatively, the `oauthFlow.logout` could be modified to accept an option like `{ force: true }` that skips the env-credentials check, but the simpler approach is to use `deleteProfileFile` directly in the delete command handler since the profile management commands should always operate on profile files regardless of env credentials.
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines +212 to +214
if (args.profile) {
validateProfileName(args.profile);
return args.profile;

@devin-ai-integration devin-ai-integration Bot Jun 8, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 --profile default throws misleading 'reserved profile name' error on any command

resolveProfile is called for every command at packages/wrangler/src/core/register-yargs-command.ts:134. When a user passes --profile default (e.g., wrangler deploy --profile default), resolveProfile at packages/workers-auth/src/profiles.ts:212-213 calls validateProfileName("default") which throws a UserError: "default" is a reserved profile name. Use wrangler login.... This error message is designed for wrangler auth create default but is confusing when a user simply wants to explicitly use the default profile for a regular command. The --profile flag is globally available, so users may reasonably try --profile default to override a directory binding.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines +148 to +171
function getProfileForDirectoryFromBindings(
startDir: string,
bindings: Record<string, string>
): { profile: string; dir: string } | undefined {
const normalizedDir = path.resolve(startDir);

const sortedEntries = Object.entries(bindings).sort(
([a], [b]) => b.length - a.length
);

for (const [boundDir, profile] of sortedEntries) {
if (normalizedDir === boundDir) {
return { profile, dir: boundDir };
}
if (
normalizedDir.startsWith(boundDir) &&
normalizedDir[boundDir.length] === path.sep
) {
return { profile, dir: boundDir };
}
}

return undefined;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🚩 Directory binding prefix matching uses string comparison — correct on POSIX, needs Windows case-insensitivity consideration

The getProfileForDirectoryFromBindings function at profiles.ts:148-171 uses path.resolve() and exact string comparison (normalizedDir === boundDir) plus startsWith for prefix matching. On Windows, file paths are case-insensitive, so C:\Projects and c:\projects should match. path.resolve() normalizes separators but does NOT normalize case on Windows. If a user activates a profile for C:\Projects but their cwd resolves to c:\projects, the match would fail. This is a pre-existing pattern (many tools have this issue on Windows), and since path.resolve() typically preserves the case from the OS, it may rarely trigger in practice.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

@workers-devprod

Copy link
Copy Markdown
Contributor

Codeowners approval required for this PR:

  • @cloudflare/cloudchamber
  • @cloudflare/d1
  • @cloudflare/workers-kv
  • @cloudflare/wrangler
Show detailed file reviewers
  • .changeset/auth-profiles.md: [@cloudflare/wrangler]
  • packages/workers-auth/src/context.ts: [@cloudflare/wrangler]
  • packages/workers-auth/src/flow.ts: [@cloudflare/wrangler]
  • packages/workers-auth/src/index.ts: [@cloudflare/wrangler]
  • packages/workers-auth/src/profiles.ts: [@cloudflare/wrangler]
  • packages/workers-utils/src/test-helpers/normalize.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/tests/agent-memory.test.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/tests/ai.test.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/tests/cert.test.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/tests/cloudchamber/create.test.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/tests/cloudchamber/curl.test.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/tests/cloudchamber/delete.test.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/tests/cloudchamber/images.test.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/tests/cloudchamber/list.test.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/tests/cloudchamber/modify.test.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/tests/containers/delete.test.ts: [@cloudflare/cloudchamber @cloudflare/wrangler]
  • packages/wrangler/src/tests/containers/images.test.ts: [@cloudflare/cloudchamber @cloudflare/wrangler]
  • packages/wrangler/src/tests/containers/info.test.ts: [@cloudflare/cloudchamber @cloudflare/wrangler]
  • packages/wrangler/src/tests/containers/instances.test.ts: [@cloudflare/cloudchamber @cloudflare/wrangler]
  • packages/wrangler/src/tests/containers/list.test.ts: [@cloudflare/cloudchamber @cloudflare/wrangler]
  • packages/wrangler/src/tests/containers/push.test.ts: [@cloudflare/cloudchamber @cloudflare/wrangler]
  • packages/wrangler/src/tests/containers/registries.test.ts: [@cloudflare/cloudchamber @cloudflare/wrangler]
  • packages/wrangler/src/tests/containers/ssh.test.ts: [@cloudflare/cloudchamber @cloudflare/wrangler]
  • packages/wrangler/src/tests/d1/d1.test.ts: [@cloudflare/d1 @cloudflare/wrangler]
  • packages/wrangler/src/tests/deployments.test.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/tests/docs.test.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/tests/experimental-commands-api.test.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/tests/hyperdrive.test.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/tests/index.test.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/tests/kv/help.test.ts: [@cloudflare/workers-kv @cloudflare/wrangler]
  • packages/wrangler/src/tests/kv/key.test.ts: [@cloudflare/workers-kv @cloudflare/wrangler]
  • packages/wrangler/src/tests/kv/namespace.test.ts: [@cloudflare/workers-kv @cloudflare/wrangler]
  • packages/wrangler/src/tests/mtls-certificates.test.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/tests/pages/deploy.test.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/tests/pages/pages.test.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/tests/profiles.test.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/tests/queues/queues-subscription.test.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/tests/queues/queues.test.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/tests/r2/bucket.test.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/tests/r2/bulk.test.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/tests/r2/help.test.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/tests/r2/local-uploads.test.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/tests/r2/object.test.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/tests/secrets-store.test.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/tests/setup.test.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/tests/tunnel/tunnel.test.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/tests/vectorize/vectorize.test.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/tests/versions/versions.help.test.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/tests/vpc.test.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/tests/worker-namespace.test.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/tests/workflows.test.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/api/dev.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/core/register-yargs-command.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/core/types.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/dev.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/experimental-flags.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/index.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/pages/dev.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/user/auth-config-file.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/user/commands.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/user/profiles.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/user/user.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/wrangler-banner.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/yargs-types.ts: [@cloudflare/wrangler]

@emily-shen emily-shen marked this pull request as draft June 16, 2026 13:48

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Devin Review found 3 new potential issues.

Open in Devin Review

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔴 getAPIToken() and requireApiToken() always read from default storage, ignoring the active profile

The getAPITokenInternal and requireApiTokenInternal functions in flow.ts always use the static storage (the default/no-profile backend) rather than profile-aware getStorage(profile). This means that while loginOrRefreshIfRequired correctly refreshes the token for the active profile (e.g. writing to work.toml), subsequent credential resolution via requireApiToken() reads from the default storage (default.toml). Every API call in wrangler goes through requireApiToken() in packages/wrangler/src/cfetch/internal.ts:69 and :157, so commands like wrangler deploy will authenticate with the default profile's credentials even when a non-default profile is active.

Affected code in flow.ts

At packages/workers-auth/src/flow.ts:452-462, getAPITokenInternal passes storage (the default) rather than getStorage(profile). Same issue at lines 464-474 for requireApiTokenInternal. Meanwhile, all the login/refresh/logout functions correctly use getStorage(props.profile) or getStorage(profile).

(Refers to lines 452-462)

Prompt for agents
The getAPITokenInternal and requireApiTokenInternal functions in flow.ts (lines 452-474) always use the default `storage` rather than the profile-aware `getStorage(profile)`. These are the functions that actually resolve API credentials for all wrangler API calls via packages/wrangler/src/cfetch/internal.ts.

The fix requires threading the active profile into these functions. Since these are synchronous and called without a profile argument currently, the simplest approach would be to either:
1. Accept an optional profile parameter on getAPIToken/requireApiToken (matching the pattern of other profile-aware functions in the flow), OR
2. Have the wrangler-side wrapper (packages/wrangler/src/user/user.ts getAPIToken/requireApiToken) read getProfile() from the experimental-flags store and construct a profile-specific storage to pass.

The current OAuthFlowAPI interface for getAPIToken() and requireApiToken() doesn't accept a profile parameter, so the interface would need updating too. The wrangler consumer in packages/wrangler/src/user/user.ts at lines 245-253 would also need to pass the profile.
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines 17 to 19
normalizeCwd(
normalizeSlashes(
normalizeTempDirs(stripTimings(replaceThinSpaces(input)))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔴 normalizeCwd fails on Windows after normalizeSlashes strips drive letters and converts backslashes

The reordering of normalizeCwd and normalizeSlashes in the normalization pipeline breaks the cwd replacement on Windows. Previously, normalizeCwd ran before normalizeSlashes, so it could match process.cwd() (e.g. C:\Users\runner\tmp) against the raw string before backslash conversion. Now normalizeSlashes runs first, converting C:\Users\runner\tmp to /Users/runner/tmp and stripping the drive letter. Then normalizeCwd tries to replaceAll(process.cwd(), "<cwd>") — but process.cwd() on Windows still returns C:\Users\runner\tmp, which no longer exists in the already-normalized string. This means test snapshots on Windows would contain leaked absolute paths instead of <cwd>.

Suggested change
normalizeCwd(
normalizeSlashes(
normalizeTempDirs(stripTimings(replaceThinSpaces(input)))
normalizeCwd(
normalizeSlashes(
normalizeTempDirs(stripTimings(replaceThinSpaces(input)))
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment thread packages/workers-auth/src/profiles.ts Outdated
Comment on lines +28 to +36
const resolved = profile ?? "default";
let fileName: string;
if (resolved === "default") {
const environment = getCloudflareApiEnvironmentFromEnv();
fileName =
environment === "production" ? "default.toml" : `${environment}.toml`;
} else {
fileName = `${resolved}.toml`;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🚩 Non-default profile names bypass the staging/production environment file-name logic

In both packages/workers-auth/src/profiles.ts:29-36 and packages/wrangler/src/user/auth-config-file.ts:91-99, the getAuthConfigFilePath function only consults getCloudflareApiEnvironmentFromEnv() for the "default" profile. Named profiles like "work" always map to work.toml regardless of whether the user is targeting staging or production. This means a user with a "work" profile would use the same token file for both staging and production environments. This appears intentional — named profiles represent a single identity — but could surprise users who switch environments.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

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

Labels

None yet

Projects

Status: Untriaged

Development

Successfully merging this pull request may close these issues.

2 participants