Chapitre 4.4 — OAuth2 / OIDC & sessions
⏱️ TL;DR — OAuth2 délègue l’autorisation (« cette app peut-elle accéder à telle ressource en mon nom ? ») ; OpenID Connect (OIDC) ajoute par-dessus l’authentification (« qui es-tu ? ») — c’est ce qui alimente le « se connecter avec Google/GitHub ». Bien utilisé, ça externalise la gestion des mots de passe (moins de risque). Mais le flux a des paramètres critiques : le
state(anti-CSRF du flux), le PKCE (protège le code d’autorisation), la validation stricte desredirect_uriet du jeton d’identité. Enfin, une fois connecté, il faut gérer la session : rotation de l’id à la connexion (anti-fixation), expiration (absolue + inactivité), et invalidation propre à la déconnexion.
🎯 Objectifs
- Distinguer OAuth2 (autorisation déléguée) et OIDC (authentification) et savoir lequel s’applique au login.
- Comprendre le flux Authorization Code et le rôle de
state, PKCE,redirect_uri. - Éviter les pièges :
redirect_uritrop permissif,stateabsent, jeton non validé. - Gérer le cycle de vie d’une session : rotation (anti-fixation), expiration, invalidation.
OAuth2 vs OIDC : deux couches
Ces termes sont souvent confondus. La distinction est simple :
- OAuth2 est un protocole d’autorisation déléguée : il permet à une application d’obtenir un accès limité à des ressources d’un utilisateur hébergées ailleurs, sans connaître son mot de passe. Exemple : autoriser une app à lire ton agenda Google. Le résultat est un access token (jeton d’accès), pas une identité.
- OIDC (OpenID Connect) est une couche d’identité au-dessus d’OAuth2. Il ajoute un ID Token (un JWT signé décrivant l’utilisateur :
sub, email…) qui répond à « qui es-tu ? ». C’est OIDC — pas OAuth2 seul — qui convient pour se connecter (authentification).
⚠️ Piège — Utiliser OAuth2 seul pour authentifier est une erreur classique et dangereuse (« se connecter avec… » bricolé). OAuth2 donne un access token d’autorisation ; le présenter comme une preuve d’identité ouvre des failles (jeton émis pour une autre app, confusion de destinataire). Pour le login, utilise OIDC et valide l’ID Token (signature,
iss,aud,exp,nonce). L’access token n’est pas une carte d’identité.
Le flux Authorization Code (le bon)
Le flux recommandé pour une app web est l’Authorization Code (avec PKCE). Vue d’ensemble :
Le point clé : le code revient au navigateur, mais il est échangé contre les jetons via un appel serveur-à-serveur (le navigateur ne voit jamais les jetons finaux), et cet échange est protégé par PKCE.
Les trois paramètres critiques
state — l’anti-CSRF du flux. L’app génère une valeur aléatoire (CSPRNG), l’envoie au fournisseur, et vérifie qu’elle revient identique. Sans state, un attaquant peut injecter son propre code d’autorisation dans la session de la victime (login CSRF), la connectant au compte de l’attaquant (ou capturant son flux). Le state lie la réponse à la requête initiale du bon utilisateur.
PKCE (Proof Key for Code Exchange) — protège le code d’autorisation contre l’interception. L’app génère un secret éphémère (code verifier), en envoie le hash (code challenge) au fournisseur, et ne peut échanger le code qu’en présentant le verifier original. Ainsi, un code intercepté est inutilisable sans le verifier. Autrefois réservé au mobile, PKCE est aujourd’hui recommandé pour tous les clients, y compris web.
redirect_uri — l’adresse de retour, à verrouiller. Le fournisseur ne doit renvoyer le code que vers des URI exactement pré-enregistrées. Une redirect_uri trop permissive (wildcards, chemins ouverts, http) permet de détourner le code vers un site attaquant.
⚠️ Piège — Une
redirect_uritrop laxiste est l’une des failles OAuth les plus exploitées. Autoriserhttps://app.formacampus.fr/*, ou pire un domaine ouvert, permet à un attaquant de faire rediriger le code d’autorisation vers une page qu’il contrôle et de s’emparer de la session. Enregistre des URI complètes et exactes, en HTTPS, sans wildcard. Valide-les strictement côté fournisseur et côté app.
💡 Réflexe — En pratique, tu n’implémentes pas OAuth/OIDC à la main : tu utilises une bibliothèque éprouvée (Auth.js/NextAuth côté Next.js — Partie 10 —, le composant/bundle OIDC côté Symfony — Partie 9) qui gère
state, PKCE, la validation des jetons. Ton job : la configurer strictement (URI exactes, scopes minimaux, validation activée) et ne pas désactiver ses garde-fous. Réimplémenter le flux soi-même est une source classique de failles.
Gérer le cycle de vie de la session
Une fois l’utilisateur authentifié (par mot de passe, MFA ou OIDC), l’app crée sa propre session (chapitre 4.1). Sa gestion dans le temps est une partie souvent négligée de la sécurité d’auth.
Rotation à la connexion (anti session fixation)
À chaque élévation de privilège de session — surtout au login — il faut régénérer l’identifiant de session. Sinon, une attaque de session fixation est possible : l’attaquant fixe un identifiant de session connu de lui dans le navigateur de la victime avant qu’elle se connecte ; si l’id ne change pas à la connexion, l’attaquant se retrouve avec un id authentifié.
// A la connexion reussie : REGENERER l'id de session
session_regenerate_id(true); // PHP : nouvel id, ancien invalide
// (Symfony le fait via la migration de session du composant Security)Expiration : absolue + inactivité
- Expiration par inactivité (idle timeout) : la session expire après une période sans activité (ex. 30 min sur une appli sensible) — limite l’exposition d’un poste laissé ouvert.
- Expiration absolue : une durée maximale de vie quelle que soit l’activité (ex. quelques heures/jours), après quoi il faut se ré-authentifier — borne la durée qu’un attaquant peut exploiter avec une session volée.
Invalidation propre
- Déconnexion : supprimer la session côté serveur (pas seulement effacer le cookie côté client — un cookie effacé mais une session encore valide reste exploitable si le cookie a été copié).
- Changement de mot de passe / compromission : invalider toutes les autres sessions de l’utilisateur (« déconnecter partout »).
- Déconnexion OIDC : penser au single logout si le fournisseur le supporte, sinon au moins couper la session locale.
🎯 Côté attaquant — Sur les sessions, l’attaquant guette : une session qui ne change pas à la connexion (fixation), une session qui n’expire jamais (un vol reste exploitable indéfiniment), une déconnexion qui n’invalide que le cookie client (il rejoue le cookie copié), un
state/PKCE absent (login CSRF, code volé). Chacun de ces oublis transforme une session en cible durable. La parade : régénérer, expirer, invalider côté serveur — systématiquement.
🧭 Sur FormaCampus — FormaCampus propose « se connecter avec » via OIDC (bibliothèque configurée strictement :
redirect_uriexactes en HTTPS,stateet PKCE activés, ID Token validé — signature,iss,aud,exp,nonce) et crée sa propre session serveur derrière. Cette session est régénérée à la connexion (anti-fixation), a une expiration par inactivité (30 min) et absolue (quelques heures), et se coupe côté serveur à la déconnexion. Un changement de mot de passe déconnecte tous les appareils. La gestion des mots de passe étant déléguée au fournisseur d’identité, FormaCampus réduit d’autant sa propre surface.
✏️ Exercices
Exercice 1 — À quoi sert le state ? Un dev implémente « se connecter avec GitHub » mais omet le paramètre state. Décris l’attaque que cela permet et comment state la bloque.
✅ Solution
Sans state, on est exposé au login CSRF : un attaquant démarre lui-même le flux OAuth, obtient un code d’autorisation lié à son compte, puis piège la victime pour qu’elle termine le flux avec ce code (via un lien de callback). Résultat : la victime se retrouve connectée au compte de l’attaquant sur l’app (et y saisit peut-être des données personnelles), ou l’attaquant lie son identité au compte visé. Le state — valeur aléatoire (CSPRNG) générée par l’app, stockée côté session, et vérifiée au retour — lie la réponse à la requête initiée par le bon utilisateur : un code injecté par un tiers arrive avec un state invalide et est rejeté.
Exercice 2 — La session immortelle. Après connexion, une appli garde la même session sans jamais la régénérer ni l’expirer, et la déconnexion se contente de supprimer le cookie côté navigateur. Cite les trois problèmes et leur correctif.
✅ Solution
(1) Pas de régénération à la connexion → session fixation possible : corriger par session_regenerate_id(true) (ou la migration de session du framework) au login. (2) Pas d’expiration → une session volée reste exploitable indéfiniment : ajouter une expiration par inactivité et une absolue. (3) Déconnexion qui n’efface que le cookie client → si le cookie a été copié, la session reste valide côté serveur : la déconnexion doit supprimer/invalider la session côté serveur (et idéalement offrir « déconnecter partout »).
🧠 Quiz de révision
1. Quelle est la différence entre OAuth2 et OIDC ?
OAuth2 = autorisation déléguée (accès limité à des ressources, résultat : un access token). OIDC = couche d’identité au-dessus (ajoute un ID Token répondant à « qui es-tu ? »). Pour se connecter, on utilise OIDC, pas OAuth2 seul.
2. À quoi servent state et PKCE dans le flux ?
state et PKCE dans le flux ?state : anti-CSRF du flux — valeur aléatoire vérifiée au retour, qui lie la réponse à la requête du bon utilisateur (bloque le login CSRF). PKCE : protège le code d’autorisation contre l’interception (le code n’est échangeable qu’avec le verifier d’origine). Les deux sont recommandés, y compris pour le web.
3. Pourquoi faut-il verrouiller redirect_uri ?
redirect_uri ?Parce qu’une redirect_uri trop permissive (wildcards, http, domaine ouvert) permet de détourner le code d’autorisation vers un site attaquant, qui s’empare de la session. On enregistre des URI exactes et complètes, en HTTPS, sans wildcard, validées strictement.
4. Qu’est-ce que la session fixation et comment l’empêcher ?
L’attaquant fixe un identifiant de session qu’il connaît dans le navigateur de la victime avant qu’elle se connecte ; si l’id ne change pas au login, il obtient une session authentifiée. Parade : régénérer l’identifiant de session à la connexion (session_regenerate_id(true) / migration du framework).
5. Que doit faire une déconnexion correcte ?
Invalider la session côté serveur (pas seulement effacer le cookie client, qui pourrait avoir été copié). Idéalement, proposer « déconnecter partout », et invalider toutes les sessions lors d’un changement de mot de passe ou d’une compromission. Prévoir aussi les expirations (inactivité + absolue).
Fin de la Partie 4. L’identité est établie de façon robuste : sessions maîtrisées, mots de passe protégés, MFA, OIDC. Reste à décider ce que chacun a le droit de faire — c’est la Partie 5 — Autorisation & contrôle d’accès, où l’on affronte le risque n°1 des applis web : le contrôle d’accès cassé.