-
Notifications
You must be signed in to change notification settings - Fork 1k
fix: read OAuth env from cloudflare workers runtime #1482
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
5e87ce7
2ca4c54
a763d48
53bf1a2
9756538
2c8812a
6e01da9
eae7c77
6303272
a52df8c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| "emdash": patch | ||
| --- | ||
|
|
||
| Fixes GitHub and Google OAuth login on Cloudflare Workers deployments using Astro v6. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,48 @@ | ||
| /** | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [needs fixing] The PR changes a published package ( |
||
| * Resolve runtime environment for OAuth providers. | ||
| * | ||
| * Astro v6 removed `locals.runtime.env`. On Cloudflare Workers, bindings are | ||
| * now read from `cloudflare:workers`. On Node-based adapters that module does | ||
| * not exist, so fall back to `import.meta.env`. | ||
| */ | ||
|
|
||
| type OAuthEnvLoader = () => Promise<Record<string, unknown>>; | ||
|
|
||
| function hasEnv(value: unknown): value is { env: Record<string, unknown> } { | ||
| if (typeof value !== "object" || value === null || !("env" in value)) return false; | ||
| return typeof value.env === "object" && value.env !== null; | ||
| } | ||
|
|
||
| async function loadCloudflareOAuthEnv(): Promise<Record<string, unknown>> { | ||
| // Keep the Cloudflare virtual module out of Node-target bundles. Otherwise | ||
| // non-Cloudflare builds try to resolve it before the runtime fallback runs. | ||
| const moduleUrl = `data:text/javascript,${encodeURIComponent( | ||
| 'export { env } from "cloudflare:workers";', | ||
| )}`; | ||
| const module = await import(/* @vite-ignore */ moduleUrl); | ||
| return hasEnv(module) ? module.env : {}; | ||
| } | ||
|
|
||
| function isMissingCloudflareWorkersModule(error: unknown): boolean { | ||
| if (!(error instanceof Error)) return false; | ||
| const message = error.message; | ||
| return ( | ||
| message.includes("cloudflare:workers") && | ||
| (message.includes("Cannot find package") || | ||
| message.includes("ERR_MODULE_NOT_FOUND") || | ||
| message.includes("Failed to resolve") || | ||
| message.includes("Could not resolve") || | ||
| message.includes("No such module")) | ||
| ); | ||
| } | ||
|
|
||
| export async function getOAuthEnv( | ||
| loadEnv: OAuthEnvLoader = loadCloudflareOAuthEnv, | ||
| ): Promise<Record<string, unknown>> { | ||
| try { | ||
| return await loadEnv(); | ||
| } catch (error) { | ||
| if (!isMissingCloudflareWorkersModule(error)) throw error; | ||
| return import.meta.env; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| import { describe, expect, it } from "vitest"; | ||
|
|
||
| import { getOAuthEnv } from "../../../src/auth/oauth-env.js"; | ||
|
|
||
| describe("getOAuthEnv", () => { | ||
| it("returns env from the provided loader", async () => { | ||
| const env = { EMDASH_OAUTH_GITHUB_CLIENT_ID: "abc" }; | ||
| await expect(getOAuthEnv(async () => env)).resolves.toBe(env); | ||
| }); | ||
|
|
||
| it("falls back to import.meta.env when cloudflare:workers is unavailable", async () => { | ||
| const env = await getOAuthEnv(async () => { | ||
| throw new Error("Cannot find package 'cloudflare:workers' imported from test"); | ||
| }); | ||
|
|
||
| expect(env).toBe(import.meta.env); | ||
| }); | ||
|
|
||
| it("rethrows unexpected loader errors", async () => { | ||
| await expect( | ||
| getOAuthEnv(async () => { | ||
| throw new Error("Unexpected runtime failure"); | ||
| }), | ||
| ).rejects.toThrow("Unexpected runtime failure"); | ||
| }); | ||
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[needs fixing] This file adds a new helper used by OAuth routes, but the PR includes no test for it. AGENTS.md requires TDD for bugs: "Failing test -> fix -> verify. A bug without a reproducing test is not fixed." Add at least a minimal unit test that verifies
getOAuthEnvfalls back toimport.meta.envwhen thecloudflare:workersdynamic import throws. Vitest supports module-level mocking viavi.mockor temporalimportinterception for this.