# CORS

CORS is a Node.js middleware package for providing Cross-Origin Resource Sharing (CORS) functionality to Connect and Express applications. It enables web applications to handle cross-origin requests securely with comprehensive configuration options including origin validation, HTTP methods configuration, credential handling, and custom header management.

## Package Information

- **Package Name**: cors
- **Package Type**: npm
- **Language**: JavaScript
- **Installation**: `npm install cors`

## Core Imports

```javascript
const cors = require('cors');
```

ES6 modules:

```javascript
import cors from 'cors';
```

## Basic Usage

```javascript
const express = require('express');
const cors = require('cors');
const app = express();

// Enable CORS for all routes with default options
app.use(cors());

// Enable CORS for a single route
app.get('/api/data', cors(), (req, res) => {
  res.json({ message: 'This is CORS-enabled' });
});

// Configure CORS with custom options
const corsOptions = {
  origin: 'https://example.com',
  methods: ['GET', 'POST'],
  credentials: true
};

app.use(cors(corsOptions));
```

## Architecture

CORS is built around several key components that handle different aspects of cross-origin request processing:

- **Middleware Factory**: The main export creates Express/Connect middleware with static or dynamic configuration
- **Origin Validation Engine**: Handles complex origin validation logic including arrays, RegExp patterns, and custom functions
- **Header Configuration System**: Manages CORS headers for both preflight and actual requests with automatic reflection
- **Request Type Detection**: Differentiates between simple requests and preflight OPTIONS requests
- **Automatic Vary Management**: Adds appropriate Vary headers when origin or header reflection is used

## Capabilities

### CORS Middleware Factory

Creates Express/Connect middleware that handles CORS headers for both preflight and actual requests.

```javascript { .api }
/**
 * Creates CORS middleware with optional configuration
 * @param {Object|Function} [options] - Static configuration object or dynamic options callback
 * @returns {Function} Express/Connect middleware function (req, res, next) => void
 */
function cors(options);
```

### Static Configuration Options

Configure CORS behavior with a static options object.

```javascript { .api }
interface CorsOptions {
  /** Configures Access-Control-Allow-Origin header */
  origin?: string | boolean | RegExp | Array<string | RegExp> | OriginCallback;
  /** Configures Access-Control-Allow-Methods header (arrays are joined with commas) */
  methods?: string | string[];
  /** Configures Access-Control-Allow-Headers header (arrays are joined with commas) */
  allowedHeaders?: string | string[];
  /** Alias for allowedHeaders */
  headers?: string | string[];
  /** Configures Access-Control-Expose-Headers header (arrays are joined with commas) */
  exposedHeaders?: string | string[];
  /** Configures Access-Control-Allow-Credentials header */
  credentials?: boolean;
  /** Configures Access-Control-Max-Age header (converted to string if number) */
  maxAge?: number | string;
  /** Pass preflight response to next handler instead of ending */
  preflightContinue?: boolean;
  /** Status code for successful OPTIONS requests */
  optionsSuccessStatus?: number;
}
```

**Usage Examples:**

```javascript
// Allow specific origin
const corsOptions = {
  origin: 'https://example.com'
};
app.use(cors(corsOptions));

// Allow multiple origins using array
const corsOptions = {
  origin: ['https://example1.com', 'https://example2.com']
};
app.use(cors(corsOptions));

// Allow origins matching regex pattern
const corsOptions = {
  origin: /\.example\.com$/
};
app.use(cors(corsOptions));

// Configure methods and headers (arrays converted to comma-separated strings)
const corsOptions = {
  origin: true,
  methods: ['GET', 'POST', 'PUT', 'DELETE'], // Becomes 'GET,POST,PUT,DELETE'
  allowedHeaders: ['Content-Type', 'Authorization'], // Becomes 'Content-Type,Authorization'
  credentials: true,
  maxAge: 86400 // Converted to string '86400'
};
app.use(cors(corsOptions));

// Using headers alias instead of allowedHeaders
const corsOptions = {
  origin: true,
  headers: ['Content-Type', 'X-Custom-Header'] // Same as allowedHeaders
};
app.use(cors(corsOptions));

// Legacy browser compatibility
const corsOptions = {
  optionsSuccessStatus: 200 // Some legacy browsers choke on 204
};
app.use(cors(corsOptions));
```

### Dynamic Origin Validation

Use a callback function for custom origin validation logic.

```javascript { .api }
/**
 * Callback function for custom origin validation
 * @param {string} origin - Request origin from Origin header
 * @param {Function} callback - Callback function (err, allowedOrigin) => void
 *   - err: Error object or null
 *   - allowedOrigin: The origin value to use (string, boolean, etc.)
 */
type OriginCallback = (origin: string, callback: (err: Error | null, allowedOrigin: any) => void) => void;
```

**Usage Examples:**

```javascript
// Whitelist specific origins
const whitelist = ['https://example1.com', 'https://example2.com'];
const corsOptions = {
  origin: function (origin, callback) {
    if (whitelist.indexOf(origin) !== -1 || !origin) {
      callback(null, origin); // Return the origin value, not boolean
    } else {
      callback(new Error('Not allowed by CORS'));
    }
  }
};
app.use(cors(corsOptions));

// Dynamic origin validation allowing/blocking
const corsOptions = {
  origin: function (origin, callback) {
    if (isAllowedOrigin(origin)) {
      callback(null, true); // true reflects the request origin
    } else {
      callback(null, false); // false disables CORS
    }
  }
};
app.use(cors(corsOptions));

// Custom origin transformation
const corsOptions = {
  origin: function (origin, callback) {
    // Transform origin or use custom logic
    const allowedOrigin = transformOrigin(origin);
    callback(null, allowedOrigin); // Can return string, boolean, etc.
  }
};
app.use(cors(corsOptions));
```

### Dynamic Options Configuration

Use a callback function to determine CORS options dynamically per request.

```javascript { .api }
/**
 * Callback function for dynamic CORS options per request
 * @param {Object} req - Express/Connect request object
 * @param {Function} callback - Callback function (err, options) => void
 */
type OptionsCallback = (req: any, callback: (err: Error | null, options: CorsOptions) => void) => void;
```

**Usage Examples:**

```javascript
// Dynamic options based on request properties
const corsOptionsDelegate = function (req, callback) {
  let corsOptions;
  
  if (req.header('User-Agent') && req.header('User-Agent').includes('MyTrustedApp')) {
    corsOptions = { origin: true }; // Enable CORS for trusted apps
  } else {
    corsOptions = { origin: false }; // Disable CORS for others
  }
  
  callback(null, corsOptions);
};

app.use(cors(corsOptionsDelegate));

// Route-specific dynamic options
const corsOptionsDelegate = function (req, callback) {
  const corsOptions = {
    origin: req.path.startsWith('/api/public') ? true : 'https://trusted-domain.com',
    credentials: req.path.startsWith('/api/auth')
  };
  
  callback(null, corsOptions);
};

app.use(cors(corsOptionsDelegate));
```

### Pre-flight Request Handling

Enable CORS pre-flight for complex requests that require an initial OPTIONS request.

**Usage Examples:**

```javascript
// Enable pre-flight for a specific route
app.options('/api/data', cors()); // Enable pre-flight
app.delete('/api/data', cors(), (req, res) => {
  // DELETE request handler
});

// Enable pre-flight for all routes
app.options('*', cors()); // Include before other routes

// Continue processing after pre-flight
const corsOptions = {
  preflightContinue: true,
  optionsSuccessStatus: 200
};

app.options('/api/data', cors(corsOptions), (req, res) => {
  // Additional pre-flight logic
  res.sendStatus(200);
});
```

## Configuration Defaults

```javascript { .api }
const defaultOptions = {
  origin: '*',
  methods: 'GET,HEAD,PUT,PATCH,POST,DELETE',
  preflightContinue: false,
  optionsSuccessStatus: 204
};
```

## Error Handling

CORS errors are passed to Express error handling middleware. Understanding error flow is important for proper CORS configuration:

```javascript
// Origin callback errors - passed to Express error handler
const corsOptions = {
  origin: function (origin, callback) {
    if (!isValidOrigin(origin)) {
      callback(new Error('Not allowed by CORS')); // Triggers error middleware
    } else {
      callback(null, origin); // Return allowed origin
    }
  }
};

// Options callback errors - also passed to Express error handler
const corsOptionsDelegate = function (req, callback) {
  validateRequest(req)
    .then(options => callback(null, options))
    .catch(err => callback(err)); // Error passed to Express error handler
};

// Error handling middleware
app.use((err, req, res, next) => {
  if (err.message === 'Not allowed by CORS') {
    res.status(403).json({ error: 'CORS policy violation' });
  } else {
    next(err);
  }
});

// Important: Returning false vs throwing error
const corsOptions = {
  origin: function (origin, callback) {
    if (!isValidOrigin(origin)) {
      // Option 1: Return false to disable CORS (no error thrown)
      callback(null, false);
      
      // Option 2: Throw error to trigger error middleware
      // callback(new Error('Not allowed by CORS'));
    } else {
      callback(null, true);
    }
  }
};
```

## Implementation Details

### Automatic Vary Header Management

CORS automatically adds `Vary` headers when needed to ensure proper caching:

- **`Vary: Origin`**: Added when origin is dynamic (function, array, or RegExp)
- **`Vary: Access-Control-Request-Headers`**: Added when allowedHeaders/headers is not specified and headers are reflected from the request

### Header Reflection Behavior

When `allowedHeaders` (or `headers`) is not specified:
- CORS automatically reflects the request's `Access-Control-Request-Headers`
- This allows maximum flexibility but may be less secure
- A `Vary: Access-Control-Request-Headers` header is added

### Array to String Conversion

Array values are automatically converted to comma-separated strings:
- `methods: ['GET', 'POST']` → `'GET,POST'`
- `allowedHeaders: ['Content-Type', 'Authorization']` → `'Content-Type,Authorization'`
- `exposedHeaders: ['X-Total-Count']` → `'X-Total-Count'`

### Type Coercion

- **maxAge**: Numbers are converted to strings (`86400` → `'86400'`)
- **methods, allowedHeaders, exposedHeaders**: Arrays are joined with commas

### Request Type Handling

**Preflight Requests (OPTIONS)**:
- Include all CORS headers: Origin, Methods, Headers, Max-Age, Credentials, Exposed-Headers
- Response ends immediately unless `preflightContinue: true`

**Simple Requests**:
- Include only: Origin, Credentials, Exposed-Headers
- Processing continues to next middleware

## Types

```javascript { .api }
/**
 * CORS configuration options interface
 */
interface CorsOptions {
  /** 
   * Configures Access-Control-Allow-Origin CORS header
   * - string: Fixed origin
   * - boolean: true reflects request origin, false disables CORS
   * - RegExp: Pattern to test request origin
   * - Array: Array of valid origins (strings or regexps)
   * - Function: Custom origin validation logic
   */
  origin?: string | boolean | RegExp | Array<string | RegExp> | OriginCallback;
  
  /** 
   * Configures Access-Control-Allow-Methods CORS header
   * String: Comma-delimited methods
   * Array: Array of method strings
   */
  methods?: string | string[];
  
  /** 
   * Configures Access-Control-Allow-Headers CORS header
   * If not specified, reflects request's Access-Control-Request-Headers
   */
  allowedHeaders?: string | string[];
  
  /** Alias for allowedHeaders */
  headers?: string | string[];
  
  /** 
   * Configures Access-Control-Expose-Headers CORS header
   * Headers that clients can access
   */
  exposedHeaders?: string | string[];
  
  /** 
   * Configures Access-Control-Allow-Credentials CORS header
   * Set to true to pass the header
   */
  credentials?: boolean;
  
  /** 
   * Configures Access-Control-Max-Age CORS header
   * Cache duration for preflight requests in seconds
   */
  maxAge?: number | string;
  
  /** 
   * Pass CORS preflight response to next handler
   * Default: false (ends response immediately)
   */
  preflightContinue?: boolean;
  
  /** 
   * Status code for successful OPTIONS requests
   * Default: 204, set to 200 for legacy browser compatibility
   */
  optionsSuccessStatus?: number;
}

/**
 * Origin validation callback function
 */
type OriginCallback = (
  origin: string,
  callback: (err: Error | null, allowedOrigin: any) => void
) => void;

/**
 * Options callback function for dynamic configuration
 */
type OptionsCallback = (
  req: any,
  callback: (err: Error | null, options: CorsOptions) => void
) => void;

/**
 * Express/Connect middleware function signature
 */
type CorsMiddleware = (req: any, res: any, next: Function) => void;
```