CORS with Credentials

Sending cookies and authentication across origins

By default, cross-origin requests don't include cookies or HTTP authentication. To send credentials, you must explicitly enable them on both client and server.

This is more restrictive than regular CORS — you can't use wildcards, and the server must explicitly allow credentials.

Security Consideration

Sending credentials cross-origin has security implications. Only enable this when you trust both the client origin and the server, and understand the CSRF risks involved.

Client: Sending Credentials

// Include cookies in cross-origin request const response = await fetch('https://api.example.com/user', { credentials: 'include' // Send cookies! }); // credentials options: // 'omit' - Never send cookies (default for cross-origin) // 'same-origin' - Only send cookies for same-origin requests // 'include' - Always send cookies, even cross-origin // Example: Authenticated API request async function fetchUserProfile() { const response = await fetch('https://api.example.com/profile', { method: 'GET', credentials: 'include', headers: { 'Accept': 'application/json' } }); if (response.status === 401) { // Session expired or not logged in window.location.href = '/login'; return; } return response.json(); }

Server: Allowing Credentials

import { createServer } from 'node:http'; const allowedOrigins = ['https://myapp.com', 'https://admin.myapp.com']; const server = createServer((req, res) => { const origin = req.headers.origin; // Must specify exact origin (no wildcard with credentials) if (allowedOrigins.includes(origin)) { res.setHeader('Access-Control-Allow-Origin', origin); } // Required for credentials res.setHeader('Access-Control-Allow-Credentials', 'true'); // Other CORS headers res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE'); res.setHeader('Access-Control-Allow-Headers', 'Content-Type'); // Handle preflight if (req.method === 'OPTIONS') { res.writeHead(204); res.end(); return; } // Now cookies are available in req.headers.cookie const sessionId = parseCookies(req.headers.cookie).sessionId; // ... authenticate user }); function parseCookies(cookieHeader) { if (!cookieHeader) return {}; return Object.fromEntries( cookieHeader.split('; ').map(c => c.split('=')) ); }

Key restrictions when using credentials:

  • Access-Control-Allow-Origin cannot be *
  • Access-Control-Allow-Credentials must be true
  • Both client and server must opt-in

Credentials Mode Comparison

Mode Same-Origin Cross-Origin Use Case
omit No cookies No cookies Public APIs, no auth needed
same-origin Sends cookies No cookies Default behavior (most apps)
include Sends cookies Sends cookies Cross-origin auth (SSO, APIs)

Setting Cookies Cross-Origin

// Server: Set cookie that works cross-origin res.setHeader('Set-Cookie', [ 'sessionId=abc123', 'SameSite=None', // Required for cross-origin 'Secure', // Required when SameSite=None 'HttpOnly', // Can't be accessed by JavaScript 'Path=/', 'Max-Age=86400' ].join('; ')); // Full example response res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': 'https://myapp.com', 'Access-Control-Allow-Credentials': 'true', 'Set-Cookie': 'sessionId=abc123; SameSite=None; Secure; HttpOnly' });

For cross-origin cookies, SameSite=None and Secure are required. This means cookies only work over HTTPS.

Common Issues

// ❌ Error: Wildcard not allowed with credentials res.setHeader('Access-Control-Allow-Origin', '*'); res.setHeader('Access-Control-Allow-Credentials', 'true'); // Browser error: Cannot use wildcard with credentials // ✓ Fix: Use specific origin res.setHeader('Access-Control-Allow-Origin', req.headers.origin); res.setHeader('Access-Control-Allow-Credentials', 'true'); // ❌ Error: Cookie not sent (missing SameSite) res.setHeader('Set-Cookie', 'sessionId=abc123'); // Cookie won't be included in cross-origin requests // ✓ Fix: Add SameSite=None and Secure res.setHeader('Set-Cookie', 'sessionId=abc123; SameSite=None; Secure'); // ❌ Error: Credentials not sent (client forgot) fetch('https://api.example.com/user'); // No cookies! // ✓ Fix: Add credentials option fetch('https://api.example.com/user', { credentials: 'include' });