Skip to content

Fix admin extension CORS preflight in validateAuthenticatedSession#3237

Open
ryandiginomad wants to merge 1 commit into
Shopify:mainfrom
ryandiginomad:fix/1420-admin-extension-cors-preflight
Open

Fix admin extension CORS preflight in validateAuthenticatedSession#3237
ryandiginomad wants to merge 1 commit into
Shopify:mainfrom
ryandiginomad:fix/1420-admin-extension-cors-preflight

Conversation

@ryandiginomad
Copy link
Copy Markdown

What

Admin UI extension fetch calls to an app's backend (Express template) fail because the CORS preflight OPTIONS request is run through validateAuthenticatedSession. The preflight carries no Authorization header, so the middleware redirects/403s it, and the browser then blocks the real (authenticated) request on CORS.

A maintainer confirmed the fix direction in #1420:

we're not handling OPTIONS requests properly in that package - we should be just setting the CORS headers and responding in that case, instead of trying to authenticate it.

How

validateAuthenticatedSession now short-circuits OPTIONS requests before the auth logic runs: it responds 204 with the CORS headers for https://extensions.shopifycdn.com — the origin that Shopify's admin-extensions security docs require apps to allow. The short-circuit is config-agnostic, so it applies to both embedded and non-embedded apps.

Before / after

Before: OPTIONS /api/...302 redirect to /api/auth (or 403) → browser blocks the real request on CORS.
After: OPTIONS /api/...204 with Access-Control-Allow-Origin: https://extensions.shopifycdn.com (+ Allow-Methods / Allow-Headers) → the real request proceeds and is authenticated exactly as before.

Tests

Added 3 tests to validate-authenticated-session.test.ts: preflight returns 204 without calling getCurrentId (no auth attempt); allows the Authorization header; allows the request methods. Full shopify-app-express suite: 88 passing, no regressions. tsc + lint clean. Changeset added (patch).

Open to feedback

  • Allowed origin — hardcoded to https://extensions.shopifycdn.com per the docs. Happy to make it configurable or echo a validated Origin if you'd prefer.
  • Allow-Methods / Allow-Headers — currently GET, POST, PUT, PATCH, DELETE, OPTIONS and Authorization, Content-Type. Can narrow/widen to match what extensions actually send.
  • Allow-Credentials — not set (admin extension auth uses the Authorization header, not cookies). Let me know if a case needs it.

AI disclosure

This change was developed with AI assistance (Claude). The approach, code, and tests were reviewed and verified locally by me.

Fixes #1420

…ticatedSession

Admin UI extension fetch calls send a CORS preflight (OPTIONS) request
without an Authorization header. The middleware would try to authenticate
it, redirect/403, and the browser would then block the real request on
CORS. Short-circuit OPTIONS requests with a 204 and the CORS headers for
https://extensions.shopifycdn.com instead.

Fixes Shopify#1420
@github-actions github-actions Bot added the devtools-gardener Post the issue or PR to Slack for the gardener label May 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

devtools-gardener Post the issue or PR to Slack for the gardener

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Admin extensions preflight authentication

1 participant