Skip to content

Fix: respond to CORS preflight OPTIONS requests without authenticating (shopify-app-express)#3238

Open
morgan-coded wants to merge 2 commits into
Shopify:mainfrom
morgan-coded:fix/1420-options-preflight-cors
Open

Fix: respond to CORS preflight OPTIONS requests without authenticating (shopify-app-express)#3238
morgan-coded wants to merge 2 commits into
Shopify:mainfrom
morgan-coded:fix/1420-options-preflight-cors

Conversation

@morgan-coded
Copy link
Copy Markdown

WHY are these changes introduced?

Fixes #1420

The validateAuthenticatedSession middleware in @shopify/shopify-app-express
runs every request — including CORS preflight OPTIONS requests — through the
full authentication flow. A preflight request never carries a session cookie or
Authorization: Bearer header, so it always fails authentication and is
redirected to /auth (HTTP 302). The browser's CORS preflight then fails on the
redirect, so the real authenticated request from an admin extension to the app's
own backend never fires.

This is the behavior the maintainer confirmed in #1420: for OPTIONS we should
just set the CORS headers and respond, instead of trying to authenticate.

WHAT is this pull request doing?

  • Adds a small respondToOptionsRequest helper to the Express package that, for
    OPTIONS requests, sets the CORS headers (Access-Control-Allow-Origin,
    Access-Control-Allow-Headers, Access-Control-Expose-Headers,
    Access-Control-Max-Age) and responds 204 without authenticating. This
    adapts the preflight handling already used by shopify-app-remix and
    shopify-app-react-router to the Express middleware path.
  • Calls it at the top of validateAuthenticatedSession, before any session
    lookup, returning early for preflight requests.
  • Adds unit tests covering the OPTIONS path: the preflight now returns 204
    with CORS headers, never invokes session lookup, never redirects, and still
    handles preflights without an Origin header.

No behavior change for any non-OPTIONS request.

How was this verified?

  • Targeted jest for validate-authenticated-session.test.ts: 18/18 tests
    pass, including the hardened preflight coverage.
  • Red→green check: with the fix removed, the new tests fail with
    expected 204, got 302 — reproducing the reported redirect — and pass once
    restored.
  • eslint . and tsc --noEmit for the package: both clean.

Type of change

  • Patch: Bug (non-breaking change which fixes an issue)
  • Minor: New feature (non-breaking change which adds functionality)
  • Major: Breaking change (fix or feature that would cause existing functionality to not work as expected)

Checklist

  • I have used pnpm changeset to create a draft changelog entry (do NOT update the CHANGELOG.md files manually)
  • I have added/updated tests for this change
  • I have documented new APIs/updated the documentation for modified APIs (for public APIs) — n/a, no public API change

…y-app-express)

The validateAuthenticatedSession middleware ran every request, including
CORS preflight OPTIONS requests, through the full authentication flow.
Preflight requests never carry a session token or Authorization header,
so they always failed authentication and were redirected to /auth, which
then failed CORS. This broke authenticated fetches from admin extensions
to an app's own backend.

Detect OPTIONS preflight requests at the top of the middleware, set the
appropriate CORS headers, and respond with 204 instead of authenticating.
This mirrors the existing respondToOptionsRequest helper already used by
the shopify-app-remix and shopify-app-react-router packages.

Adds unit tests covering the OPTIONS path: the preflight now returns 204
with CORS headers and never invokes session lookup or redirects.

Fixes Shopify#1420
Copilot AI review requested due to automatic review settings May 29, 2026 15:14
@github-actions github-actions Bot added cla-needed devtools-gardener Post the issue or PR to Slack for the gardener labels May 29, 2026
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

Adds explicit handling for CORS preflight OPTIONS requests so validateAuthenticatedSession doesn’t attempt session authentication/redirects for preflights (notably for admin extensions).

Changes:

  • Short-circuit validateAuthenticatedSession for OPTIONS requests by responding with CORS headers and 204.
  • Introduce respondToOptionsRequest middleware helper to send the preflight response.
  • Add Jest/Supertest coverage for the new preflight behavior and publish a patch changeset.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.

File Description
packages/apps/shopify-app-express/src/middlewares/validate-authenticated-session.ts Calls a new helper to intercept OPTIONS requests before auth logic runs
packages/apps/shopify-app-express/src/middlewares/respond-to-options-request.ts New helper that returns a 204 and sets CORS-related headers
packages/apps/shopify-app-express/src/middlewares/tests/validate-authenticated-session.test.ts Adds tests ensuring preflights don’t authenticate and return expected CORS headers
.changeset/express-options-preflight-cors.md Patch note describing the preflight behavior change

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +20 to +22
if (req.method !== 'OPTIONS') {
return false;
}
Comment on lines +29 to +30
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Headers', 'Authorization, Content-Type');
'X-Shopify-Api-Request-Failure-Reauthorize',
'X-Shopify-Api-Request-Failure-Reauthorize-Url',
]);
res.header('Access-Control-Max-Age', '7200');
@morgan-coded
Copy link
Copy Markdown
Author

I have signed the CLA!

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

2 participants