Skip to content

fix(auth): handle Astro v6 removal of locals.runtime.env in OAuth routes#763

Open
kamine81 wants to merge 10 commits into
emdash-cms:mainfrom
kamine81:fix/oauth-astro-v6-locals-runtime-env
Open

fix(auth): handle Astro v6 removal of locals.runtime.env in OAuth routes#763
kamine81 wants to merge 10 commits into
emdash-cms:mainfrom
kamine81:fix/oauth-astro-v6-locals-runtime-env

Conversation

@kamine81
Copy link
Copy Markdown
Contributor

@kamine81 kamine81 commented Apr 25, 2026

What does this PR do?

Fixes GitHub/Google OAuth login when using @astrojs/cloudflare v13+ (Astro v6).

The @astrojs/cloudflare adapter removed Astro.locals.runtime.env in Astro v6, replacing it with import { env } from "cloudflare:workers". The OAuth initiation ([provider].ts) and callback ([provider]/callback.ts) routes were still reading from locals.runtime.env, which now throws at runtime — causing every OAuth login attempt to silently redirect to a generic oauth_error page.

Resolution order after this fix:

  1. locals.runtime.env — Astro v5 + @astrojs/cloudflare (unchanged behaviour)
  2. cloudflare:workers — Astro v6 + @astrojs/cloudflare. New fallback via a dynamic import() whose module specifier is built at runtime (["cloudflare", "workers"].join(":")), so it is not statically analyzable.
  3. import.meta.env — Node.js / Vite dev server (unchanged fallback)

Why the specifier is computed: A literal import("cloudflare:workers") is statically resolved by Rollup at build time. On Node-adapter templates (templates/marketing, templates/blog, …) there is no plugin to externalize it, so the template build fails with "Rollup failed to resolve import cloudflare:workers". Computing the specifier at runtime keeps it out of static analysis:

  • On Cloudflare, @astrojs/cloudflare externalizes via a resolveId filter { id: /^cloudflare:/ } (a build-time regex on the specifier string). A computed specifier never hits that filter, so Rollup leaves the import() as a runtime expression and workerd resolves cloudflare:workers natively. (Verified: the marketing-cloudflare template build keeps the runtime import intact in dist/server/chunks/.)
  • On Node, the runtime import throws and is caught, falling through to import.meta.env.

Related issues: #464, #249

Type of change

  • Bug fix
  • Feature (requires maintainer-approved Discussion)
  • Refactor (no behavior change)
  • Translation
  • Documentation
  • Performance improvement
  • Tests
  • Chore (dependencies, CI, tooling)

Checklist

  • I have read CONTRIBUTING.md
  • pnpm typecheck passes
  • pnpm lint passes
  • pnpm test passes (or targeted tests for my change)
  • pnpm format has been run
  • I have added/updated tests for my changes (if applicable)
  • User-visible strings in the admin UI are wrapped for translation and pnpm locale:extract has been run (if applicable)
  • I have added a changeset (if this PR changes a published package)
  • New features link to an approved Discussion: https://github.com/emdash-cms/emdash/discussions/...

AI-generated code disclosure

  • This PR includes AI-generated code

Screenshots / test output

✓ tests/unit/auth/oauth-provider-route.test.ts > redirects to GitHub authorization URL when env vars are in import.meta.env
✓ tests/unit/auth/oauth-provider-route.test.ts > redirects to error page when provider is not configured
✓ tests/unit/auth/oauth-provider-route.test.ts > falls back gracefully when locals.runtime.env throws (Astro v6 Cloudflare)
✓ tests/unit/auth/oauth-provider-route.test.ts > redirects to error page for an unknown provider
✓ tests/unit/auth/oauth-provider-route.test.ts > redirects to error page when db is not available
✓ tests/unit/auth/oauth-callback-route.test.ts > resolves env from import.meta.env and reaches OAuth state validation
✓ tests/unit/auth/oauth-callback-route.test.ts > redirects to provider_not_configured when env vars are absent
✓ tests/unit/auth/oauth-callback-route.test.ts > falls back gracefully when locals.runtime.env throws (Astro v6 Cloudflare)
✓ tests/unit/auth/oauth-callback-route.test.ts > redirects to invalid_callback when code or state is missing

Test Files  2 passed (2)
Tests       9 passed (9)

@astrojs/cloudflare v13+ (Astro v6) removed locals.runtime.env,
causing OAuth login to fail silently with a generic error redirect.

Resolution order for env vars:
  1. locals.runtime.env  — Astro v5 + @astrojs/cloudflare
  2. cloudflare:workers  — Astro v6 + @astrojs/cloudflare
  3. import.meta.env     — Node.js / Vite dev server fallback

Adds unit tests for all three resolution paths and the error cases.
@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented Apr 25, 2026

🦋 Changeset detected

Latest commit: 7dcab01

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

This PR includes changesets to release 14 packages
Name Type
emdash Patch
@emdash-cms/cloudflare Patch
@emdash-cms/sandbox-workerd Patch
@emdash-cms/fixture-perf-site Patch
@emdash-cms/perf-demo-site Patch
@emdash-cms/cache-demo-site Patch
@emdash-cms/admin Patch
@emdash-cms/auth Patch
@emdash-cms/blocks Patch
@emdash-cms/gutenberg-to-portable-text Patch
@emdash-cms/x402 Patch
create-emdash Patch
@emdash-cms/auth-atproto Patch
@emdash-cms/plugin-embeds 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

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented Apr 25, 2026

Open in StackBlitz

@emdash-cms/admin

npm i https://pkg.pr.new/@emdash-cms/admin@763

@emdash-cms/auth

npm i https://pkg.pr.new/@emdash-cms/auth@763

@emdash-cms/blocks

npm i https://pkg.pr.new/@emdash-cms/blocks@763

@emdash-cms/cloudflare

npm i https://pkg.pr.new/@emdash-cms/cloudflare@763

emdash

npm i https://pkg.pr.new/emdash@763

create-emdash

npm i https://pkg.pr.new/create-emdash@763

@emdash-cms/gutenberg-to-portable-text

npm i https://pkg.pr.new/@emdash-cms/gutenberg-to-portable-text@763

@emdash-cms/x402

npm i https://pkg.pr.new/@emdash-cms/x402@763

@emdash-cms/plugin-ai-moderation

npm i https://pkg.pr.new/@emdash-cms/plugin-ai-moderation@763

@emdash-cms/plugin-atproto

npm i https://pkg.pr.new/@emdash-cms/plugin-atproto@763

@emdash-cms/plugin-audit-log

npm i https://pkg.pr.new/@emdash-cms/plugin-audit-log@763

@emdash-cms/plugin-color

npm i https://pkg.pr.new/@emdash-cms/plugin-color@763

@emdash-cms/plugin-embeds

npm i https://pkg.pr.new/@emdash-cms/plugin-embeds@763

@emdash-cms/plugin-forms

npm i https://pkg.pr.new/@emdash-cms/plugin-forms@763

@emdash-cms/plugin-webhook-notifier

npm i https://pkg.pr.new/@emdash-cms/plugin-webhook-notifier@763

commit: 594c7c6

Rollup statically resolves string-literal dynamic imports at build time.
Using a variable (`const cfWorkersModId = "cloudflare:workers"`) defers
resolution to runtime, avoiding build failures in Node.js fixture builds.
@github-actions github-actions Bot added size/L and removed size/M labels Apr 25, 2026
@github-actions
Copy link
Copy Markdown
Contributor

This PR has been inactive for 14 days. It will be closed automatically in 7 days if there is no further activity.

If you're still working on this, please push an update or leave a comment.

@github-actions github-actions Bot added the stale label May 16, 2026
kamine81 added 3 commits May 30, 2026 06:42
Bring the branch up to date with upstream/main. The only conflicts were
in the OAuth routes ([provider].ts, [provider]/callback.ts); resolve them
by taking the upstream version so this commit is a clean main integration.
The Astro v6 locals.runtime.env fix is re-applied in the following commit.
@astrojs/cloudflare v13+ (Astro v6) removed locals.runtime.env, so the
OAuth initiation and callback routes threw when reading env. Resolve env
through a fallback chain: locals.runtime.env (Astro v5) -> cloudflare:workers
(Astro v6) -> import.meta.env (Node). The cloudflare:workers import is a
literal dynamic import; the core build preserves it as a runtime-only
import, so no static-analysis workaround is needed.

Matches the production-validated patch in kamine81/my-emdash-site#100.
The callback route ([provider]/callback.ts) received the same
locals.runtime.env -> cloudflare:workers -> import.meta.env fallback as
the initiation route, but had no test. Add a unit test mirroring the
provider-route suite, including the regression case where the Astro v6
locals.runtime.env getter throws and must be caught (error=invalid_state,
not error=oauth_error).
@github-actions github-actions Bot added review/needs-review No maintainer or bot review yet and removed needs-rebase labels May 29, 2026
…solve

The literal `import("cloudflare:workers")` in the OAuth routes broke
non-Cloudflare (Node adapter) template builds: Rollup statically
resolves the specifier at build time and fails because the module only
exists on workerd. Build the module id at runtime
(`["cloudflare", "workers"].join(":")` + @vite-ignore) so neither this
package's bundler nor the downstream Astro/Rollup template build
resolves it statically.

On Cloudflare the dynamic import is left as a runtime expression and
resolves natively on workerd (verified: the adapter externalizes via a
`/^cloudflare:/` resolveId filter, and the marketing-cloudflare build
keeps the runtime import intact). On Node it throws and is caught,
falling through to import.meta.env.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant