Credential Sync Across Subdomains: CORS Preflight & Cookie Isolation Workflows

Browser-enforced mechanics for sharing authentication credentials across subdomains require strict adherence to CORS preflight lifecycles and cookie domain scoping. This guide details secure header orchestration for credentialed cross-origin requests.

Preflight Mechanics for Credentialed Subdomain Requests

When credentials: 'include' is set, browsers automatically trigger an OPTIONS preflight for non-simple cross-subdomain requests. The server must validate headers before the actual payload proceeds.

Strict origin matching is mandatory per WHATWG Fetch standards. The Access-Control-Allow-Origin response header cannot use a wildcard. It must reflect the exact requesting origin string, including scheme, host, and port.

Caching credentialed responses requires the Vary: Origin header. Omitting it causes shared proxies to serve incorrect origin values to subsequent subdomain requests.

Programmatic allowlisting scales better than static configuration. Implementing Dynamic Origin Validation Patterns ensures only authorized subdomains receive credential headers.

const ALLOWED_SUBDOMAIN_PATTERN = /^https:\/\/[a-z0-9-]+\.example\.com$/;

app.use((req, res, next) => {
  const origin = req.headers.origin;
  if (origin && ALLOWED_SUBDOMAIN_PATTERN.test(origin)) {
    res.setHeader('Access-Control-Allow-Origin', origin);
    res.setHeader('Access-Control-Allow-Credentials', 'true');
    res.setHeader('Vary', 'Origin');
  }
  next();
});

This middleware safely reflects validated subdomain origins while enforcing credential flags and cache variation headers. The regex is anchored with ^ and $ to prevent substring bypass attacks.

Cross-subdomain session propagation relies on precise HTTP cookie attributes. The Domain attribute must be explicitly set to .example.com (with the leading dot). This allows propagation across app.example.com and api.example.com.

Modern browsers enforce strict SameSite policies. Credentialed cross-origin API calls require SameSite=None; Secure. Without this combination, cookies are silently dropped on cross-origin requests, even when CORS headers are correct.

Ensure TLS termination handles the Secure flag correctly. Mixed-content deployments (http API from an https page) reject SameSite=None cookies entirely.

Set-Cookie: session_id=abc123; Domain=.example.com; Path=/; Secure; SameSite=None; HttpOnly

This header guarantees the cookie attaches to credentialed cross-subdomain requests while resisting XSS extraction via HttpOnly.

Debugging Workflows & Network Trace Analysis

Credential sync failures typically manifest as opaque network errors or explicit 401/403 responses. Distinguish between CORS preflight blocks and application-level authentication failures.

Use browser DevTools Network tab with “Disable Cache” enabled. Filter by XHR/Fetch and inspect the OPTIONS request headers. Verify the presence of Origin and confirm that Access-Control-Request-Credentials is not a real header (it doesn’t exist — credentials mode is conveyed through the Fetch API options, not a request header).

Trace Set-Cookie mismatches by checking the Domain and Path attributes in the response headers. Cookies scoped to api.example.com will not sync to app.example.com — the Domain attribute must be .example.com to cover both.

Implementation Patterns for Cross-Subdomain Auth

Server-side origin reflection provides flexibility but requires strict validation. Static allowlists offer higher security guarantees for fixed infrastructure deployments.

The credentials flag in fetch() or XMLHttpRequest dictates browser behavior. Setting it to include attaches cookies and HTTP authentication headers automatically.

Wildcard origins combined with credential flags violate WHATWG Fetch standards. Browsers will block the response and log a console error immediately.

Aligning client and server configurations with Server-Side CORS Configuration & Header Management principles ensures predictable session synchronization across distributed endpoints.

fetch('https://api.example.com/data', {
  method: 'POST',
  credentials: 'include',
  headers: { 'Content-Type': 'application/json' }
});

This configuration triggers the credentialed preflight (because Content-Type: application/json is non-safelisted) and ensures session cookies attach to the cross-subdomain payload.

Common Mistakes

Issue Explanation
Using Access-Control-Allow-Origin: * with credentials: true Browsers explicitly reject wildcard origins when credentials are included, causing immediate preflight failure.
Omitting Vary: Origin on credentialed responses Caches may serve incorrect origin headers to subsequent requests, breaking credential sync for other subdomains.
Setting SameSite=Lax for cross-subdomain API calls Lax restricts cookies to top-level navigations, blocking credentialed API requests and causing silent auth failures.
Cookie Domain scoped to a specific subdomain Domain=api.example.com restricts the cookie to that subdomain only; use Domain=.example.com for cross-subdomain sharing.

FAQ

Does Access-Control-Allow-Credentials work with wildcard origins?

No. Browsers strictly forbid combining credentials: true with Access-Control-Allow-Origin: * due to credential exposure risks.

How does SameSite=Lax affect cross-subdomain preflight requests?

Lax blocks cookies on cross-origin API requests that are not top-level navigations. Credentialed preflights will fail because the session cookie is not sent, causing auth middleware to reject the request before CORS headers are applied.

Why do credentialed OPTIONS requests fail with 403?

Typically caused by missing Access-Control-Allow-Credentials: true, incorrect origin matching, or server-side auth middleware intercepting the preflight before CORS headers are applied. Auth middleware must be bypassed for OPTIONS requests.

Can I sync JWTs via localStorage instead of cookies for CORS?

Yes. Storing JWTs in localStorage and sending them as Authorization: Bearer <token> bypasses cookie mechanics. However, custom headers trigger preflight, so the server must allowlist Authorization in Access-Control-Allow-Headers.