Skip to content

CORS

Middleware CORS (Cross-Origin Resource Sharing) prêt pour la production pour les applications YasuiJS. Gère les requêtes preflight, la validation des origines, les credentials et les fonctionnalités de sécurité modernes.

Installation

bash
npm install @yasui/cors
bash
pnpm add @yasui/cors
bash
bun add @yasui/cors
bash
deno add jsr:@yasui/cors

Vue d'ensemble

Le package @yasui/cors fournit un middleware CORS conforme aux standards avec des fonctionnalités avancées incluant :

  • Validation des origines - Correspondance exacte, wildcard ou patterns regex
  • Gestion preflight - Traitement automatique des requêtes OPTIONS
  • Support credentials - Gestion des cookies et en-têtes d'autorisation
  • Optimisation cache - Gestion appropriée de l'en-tête Vary
  • Private Network Access - Support de la spécification CORS-RFC1918
  • Sécurité en premier - Valeurs par défaut et validation conformes aux standards de l'industrie

Important : Il s'agit d'un middleware fonctionnel (non basé sur les classes). Il fonctionne aux côtés des middlewares de classe YasuiJS et doit être enregistré dans le tableau global middlewares.

Démarrage rapide

Utilisation de base

typescript
import yasui from 'yasui';
import { cors } from '@yasui/cors';

yasui.createServer({
  middlewares: [
    cors({
      origins: ['https://app.example.com', 'https://admin.example.com']
    })
  ],
  controllers: [UserController]
});

Wildcard (Développement uniquement)

typescript
import { cors } from '@yasui/cors';

yasui.createServer({
  middlewares: [
    cors({
      origins: '*'  // ⚠️ Non recommandé en production
    })
  ],
  controllers: [UserController]
});

Attention : L'utilisation de origins: '*' n'est pas recommandée en production. Spécifiez toujours des origines exactes ou utilisez des patterns regex pour une meilleure sécurité.

Configuration

La fonction cors() accepte un objet de configuration avec les options suivantes :

origins (requis)

Origines autorisées pour les requêtes cross-origin. Peut être un wildcard, un tableau d'origines exactes ou un tableau incluant des patterns regex.

  • Type : string[] | RegExp[] | (string | RegExp)[] | '*'
  • Requis : Oui
  • Exemples :
typescript
// Origines exactes
cors({
  origins: ['https://app.example.com', 'https://admin.example.com']
})

// Wildcard (développement uniquement)
cors({
  origins: '*'
})

// Patterns regex pour sous-domaines dynamiques
cors({
  origins: [
    'https://app.example.com',
    /^https:\/\/.*\.example\.com$/  // Correspond à n'importe quel sous-domaine
  ]
})

methods

Méthodes HTTP autorisées dans les requêtes cross-origin.

  • Type : string
  • Par défaut : 'GET,POST,PUT,DELETE,PATCH,OPTIONS'
  • Exemple :
typescript
cors({
  origins: ['https://app.example.com'],
  methods: 'GET,POST,DELETE'
})

headers

En-têtes de requête autorisés dans les requêtes cross-origin.

  • Type : string
  • Par défaut : 'Content-Type,Authorization'
  • Exemple :
typescript
cors({
  origins: ['https://app.example.com'],
  headers: 'Content-Type,Authorization,X-API-Key'
})

credentials

Autoriser les credentials (cookies, en-têtes d'autorisation) dans les requêtes cross-origin.

  • Type : boolean
  • Par défaut : false
  • Important : Ne peut pas être utilisé avec origins: '*' (génèrera une erreur au démarrage)
typescript
cors({
  origins: ['https://app.example.com'],  // Doit spécifier des origines exactes
  credentials: true
})

Note de sécurité : Quand credentials: true, les navigateurs exigent une origine exacte dans l'en-tête Access-Control-Allow-Origin. Le middleware applique cela au démarrage et générera une erreur si vous essayez d'utiliser des wildcards avec credentials.

maxAge

Durée de mise en cache de la réponse preflight en secondes. Détermine combien de temps les navigateurs mettent en cache la réponse preflight.

  • Type : number
  • Par défaut : 86400 (24 heures)
  • Exemple :
typescript
cors({
  origins: ['https://app.example.com'],
  maxAge: 3600  // 1 heure
})

exposeHeaders

En-têtes de réponse exposés au client (accessibles via JavaScript).

  • Type : string
  • Par défaut : undefined
  • Exemple :
typescript
cors({
  origins: ['https://app.example.com'],
  exposeHeaders: 'X-Total-Count,X-Page-Number'
})

Utilisation : Par défaut, les navigateurs n'exposent que les en-têtes sûrs (comme Content-Type). Utilisez cette option pour exposer des en-têtes personnalisés au JavaScript côté client.

allowNullOrigin

Autoriser les requêtes avec origine null (file://, iframes sandboxés, contextes préservant la confidentialité).

  • Type : boolean
  • Par défaut : false
  • Exemple :
typescript
cors({
  origins: ['https://app.example.com'],
  allowNullOrigin: true  // Autoriser file:// et contextes sandboxés
})

Cas d'usage :

  • Tests depuis des fichiers HTML locaux (protocole file://)
  • Iframes sandboxés (<iframe sandbox>)
  • Fonctionnalités de navigateur préservant la confidentialité

allowPrivateNetwork

Activer le support Private Network Access (CORS-RFC1918) pour les requêtes des réseaux publics vers les réseaux privés/locaux.

  • Type : boolean
  • Par défaut : false
  • Exemple :
typescript
cors({
  origins: ['https://app.example.com'],
  allowPrivateNetwork: true
})

Cas d'usage : Permet aux applications web d'accéder aux ressources du réseau local (ex : http://192.168.1.100) lorsque le navigateur le demande via l'en-tête preflight Access-Control-Request-Private-Network.

Note de sécurité : Le middleware n'envoie Access-Control-Allow-Private-Network: true que si la requête preflight inclut explicitement Access-Control-Request-Private-Network: true, suivant la spécification CORS-RFC1918.

Fonctionnement

Requêtes preflight

Lorsqu'un navigateur effectue une requête cross-origin avec des en-têtes ou méthodes personnalisés, il envoie d'abord une requête preflight OPTIONS.

Le middleware CORS intercepte cela et répond avec les en-têtes appropriés.

Requêtes réelles

Pour les requêtes réelles, le middleware ajoute les en-têtes CORS à la réponse.

Rejet d'origine

Lorsqu'une origine n'est pas autorisée, le middleware :

  1. Preflight (OPTIONS) : Retourne 204 No Content sans en-têtes CORS (standard de l'industrie)
  2. Requêtes réelles : Passe sans ajouter d'en-têtes CORS

Le navigateur bloque ensuite la réponse, empêchant l'accès JavaScript.

Pourquoi 204 au lieu de 403 ? Retourner 204 sans en-têtes CORS est le standard de l'industrie (utilisé par Express, Fastify, etc.) car cela évite de divulguer des informations sur l'existence d'un endpoint.

Gestion du cache

Le middleware gère automatiquement l'en-tête Vary: Origin :

  • Quand origins: '*' sans credentials : Pas d'en-tête Vary (réponse identique pour toutes les origines)
  • Quand liste d'origines ou credentials : Ajoute l'en-tête Vary: Origin
  • Quand en-tête Vary existant : Fusionne Origin avec les valeurs existantes

Cela garantit que les CDN et navigateurs mettent correctement en cache les réponses CORS.

Bonnes pratiques de sécurité

1. Ne jamais utiliser de wildcards avec credentials

typescript
// ❌ MAUVAIS - Générera une erreur au démarrage
cors({
  origins: '*',
  credentials: true  // Erreur: impossible d'utiliser credentials avec wildcard
})

// ✅ BON
cors({
  origins: ['https://app.example.com'],
  credentials: true
})

2. Valider les origines strictement

typescript
// ❌ RISQUÉ - Trop permissif
cors({
  origins: '*'
})

// ✅ MIEUX - Origines explicites
cors({
  origins: ['https://app.example.com']
})

// ✅ BON - Regex pour wildcards contrôlés
cors({
  origins: [/^https:\/\/[a-z0-9-]+\.example\.com$/]
})

3. Minimiser les en-têtes exposés

typescript
// ❌ RISQUÉ - Expose tous les en-têtes
cors({
  origins: ['https://app.example.com'],
  exposeHeaders: '*'  // Non recommandé
})

// ✅ BON - N'exposer que les en-têtes nécessaires
cors({
  origins: ['https://app.example.com'],
  exposeHeaders: 'X-Total-Count,X-Page-Number'
})

4. Utiliser une configuration basée sur l'environnement

typescript
// ✅ BON - Configs différentes pour dev/prod
const corsConfig = {
  origins: process.env.NODE_ENV === 'production'
    ? [process.env.FRONTEND_URL]
    : ['http://localhost:3000'],
  credentials: true
};

yasui.createServer({
  middlewares: [cors(corsConfig)],
  controllers: [UserController]
});

Détails techniques

Important : Le middleware CORS doit être enregistré dans le tableau global middlewares pour intercepter les requêtes OPTIONS :

typescript
yasui.createServer({
  middlewares: [cors({ origins: [...] })],  // ✅ Enregistré globalement
  controllers: [UserController]
});

Si vous obtenez une 404 sur les requêtes preflight, vérifiez que le middleware est enregistré au niveau de l'application, pas au niveau du contrôleur ou de la route.

Validation au démarrage

Le middleware valide la configuration au démarrage de l'application (pas par requête) :

  • ❌ Génère une erreur si credentials: true avec origins: '*'

Fusion d'en-têtes

Lors de l'injection des en-têtes CORS dans les réponses :

  • Préserve les en-têtes de réponse existants
  • Fusionne l'en-tête Vary intelligemment (n'écrase pas Vary: Accept-Encoding)
  • Utilise Headers.set() pour les en-têtes CORS (insensible à la casse)

Optimisations de performance

  • Validation d'origine utilise Array.some() (s'arrête à la première correspondance)
  • Patterns regex compilés une fois à la création du middleware
  • Pas de parsing du body pour les requêtes OPTIONS (réponse immédiate)

Conformité

  • Spécification CORS : Conformité complète avec la spécification CORS du W3C
  • RFC1918 : Support Private Network Access
  • Standards de l'industrie : Suit les patterns Express/Fastify (204 pour preflight rejeté)

Voir aussi

Publié sous la licence AGPL v3.