Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .changeset/temporary-account-multi-auth.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
"@cloudflare/workers-auth": patch
"wrangler": patch
---

Fix the `--temporary` error on commands that authenticate more than one time

`wrangler d1 migrations apply --remote --temporary` failed with this error: `You're already authenticated with Cloudflare, so --temporary can't be used`. The failure occurred with no login and with no `CLOUDFLARE_API_TOKEN`. This command authenticates one time for each statement that it runs. The first authentication makes a temporary preview account. The second authentication read the token of this new account as an earlier login.

Wrangler now uses again the temporary account from the same command run. Commands that authenticate more than one time now work as `wrangler deploy --temporary` works. If real credentials are available, `--temporary` is still an error.
9 changes: 9 additions & 0 deletions packages/workers-auth/src/core/factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -570,6 +570,15 @@ ${accounts
);
}

// This command run made this temporary account. It is not an earlier
// login, so use it again. Some commands call `requireAuth` more than
// one time, for example `d1 migrations apply --remote`. Without this
// check, the second call fails at the test below.
const latchedTemporaryAccount = oauthFlow.getActiveTemporaryAccount();
if (latchedTemporaryAccount) {
return latchedTemporaryAccount.account.id;
}

// `--temporary` is only for unauthenticated use. If any credentials are
// already available (env, global key, or a stored OAuth token), refuse
// rather than silently provisioning a throwaway account alongside them.
Expand Down
121 changes: 121 additions & 0 deletions packages/wrangler/src/__tests__/d1/migrate.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -519,6 +519,127 @@ Your database may not be available to serve requests during the migration, conti
]
`);
});

describe("with a temporary preview account", () => {
mockAccountId({ accountId: null });
mockApiToken({ apiToken: null });

const temporaryPreviewAccountUrl =
"https://api.cloudflare.com/client/v4/provisioning/previews";

function mockTemporaryPreviewAccount() {
let mintRequests = 0;
msw.use(
http.post(`${temporaryPreviewAccountUrl}/challenge`, () =>
HttpResponse.json({
success: true,
result: {
challengeToken: "challenge-token",
seed: Buffer.alloc(32, 1).toString("base64url"),
k: 2,
g: 2,
s: 16,
expiresAt: 9999999999,
},
errors: [],
messages: [],
})
),
http.post(temporaryPreviewAccountUrl, () => {
mintRequests += 1;
return HttpResponse.json({
success: true,
result: {
account: {
id: "preview-account-id",
name: "Preview Account Alpha",
type: "standard",
apiToken: "preview-account-token",
tokenId: "preview-token-id",
expiresAt: "2027-01-01T00:00:00.000Z",
},
claim: {
token: "claim-token",
url: "https://dash.cloudflare.com/claim-preview?claimToken=claim-token",
expiresAt: "2027-01-02T00:00:00.000Z",
},
},
errors: [],
messages: [],
});
})
);
return () => mintRequests;
}

// `d1 migrations apply --remote` calls `requireAuth` one time for
// each statement. The account from the first call must also satisfy
// the later calls.
it("should apply migrations against the account it minted", async ({
expect,
}) => {
setIsTTY(false);
const std = mockConsoleMethods();
const migrationsDir = path.join(process.cwd(), "migrations");
writeWranglerConfig({
d1_databases: [
{
binding: "DATABASE",
database_name: "db",
database_id: "xxxx",
migrations_dir: migrationsDir,
},
],
});
let queryRequests = 0;
msw.use(
http.get("*/accounts/:accountId/d1/database", () =>
HttpResponse.json({
success: true,
result: [
{ uuid: "xxxx", name: "db", created_at: "", version: "alpha" },
],
errors: [],
messages: [],
})
),
http.post(
"*/accounts/:accountId/d1/database/:databaseId/query",
() => {
queryRequests += 1;
return HttpResponse.json({
success: true,
result: [{ results: [], success: true, meta: {} }],
errors: [],
messages: [],
});
}
)
);
const mintRequests = mockTemporaryPreviewAccount();

mockConfirm({
text: `No migrations folder found.
Ok to create ${migrationsDir}?`,
result: true,
});
await runWrangler("d1 migrations create db test");
mockConfirm({
text: `About to apply 1 migration(s)
Your database may not be available to serve requests during the migration, continue?`,
result: true,
});

await runWrangler("d1 migrations apply db --remote --temporary");

expect(std.err).toBe("");
expect(std.out).toContain("0001_test.sql");
// More than one query proves that the command authenticated more
// than one time and did not stop at the first authentication.
expect(queryRequests).toBeGreaterThan(1);
expect(mintRequests()).toBe(1);
});
});
});

describe("list", () => {
Expand Down
Loading