Flux Authorization Code + PKCE
Le flux Authorization Code + PKCE est le pattern OAuth 2.0 recommandé pour toutes les applications qui authentifient des utilisateurs réels. Cette page explique ce qu’est PKCE, pourquoi il a été introduit et comment fonctionne le flux complet du début à la fin — incluant les propriétés de sécurité à chaque étape.
Pourquoi PKCE Existe
Le flux Authorization Code OAuth 2.0 original (sans PKCE) a une vulnérabilité de sécurité dans les clients publics : l’interception du code.
Quand Auris redirige vers ton application avec un authorization code, ce code passe par la barre URL du browser. Sur les appareils mobiles, une application malveillante enregistrée pour gérer le même schéma URI personnalisé pourrait intercepter la redirection.
Si un attaquant intercepte le code, il peut l’échanger contre des tokens à l’endpoint token. Dans le flux classique, rien ne l’empêche.
PKCE (Proof Key for Code Exchange, RFC 7636) résout ce problème en liant l’authorization code à la session client spécifique qui l’a demandé. Seul le client qui a originellement généré le code verifier peut échanger le code — même si un attaquant a le code lui-même.
Auris applique PKCE à tous les flux authorization code. La méthode de challenge plain est rejetée — seule la méthode S256 (SHA-256) est acceptée. Il n’y a pas de moyen de contourner PKCE dans Auris.
Le Mécanisme PKCE
PKCE ajoute deux valeurs au flux standard :
Code Verifier : Une chaîne aléatoire cryptographique, de 43 à 128 caractères, utilisant uniquement des caractères URL non réservés (A-Z, a-z, 0-9, -, ., _, ~). Cette valeur est gardée secrète par le client.
Code Challenge : Une version transformée du code verifier, calculée comme :
code_challenge = BASE64URL(SHA256(ASCII(code_verifier)))Le client envoie le code challenge (le hash) à l’endpoint d’autorisation. Quand il échange le code, le client envoie le code verifier brut. Auris recalcule le challenge et confirme qu’il correspond. Comme un attaquant ne peut pas inverser un hash SHA-256, il ne peut pas falsifier le verifier.
Flux Étape par Étape
Étape 1 : Générer le Code Verifier
Le client génère un code_verifier aléatoire cryptographiquement. Il doit avoir 43 à 128 caractères de l’alphabet [A-Za-z0-9-._~].
function generateCodeVerifier(): string {
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~'
const array = new Uint8Array(64)
crypto.getRandomValues(array)
return Array.from(array)
.map(b => chars[b % chars.length])
.join('')
}
const codeVerifier = generateCodeVerifier()
// Exemple : "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk"Stocke le code_verifier dans sessionStorage (pas localStorage) — il doit seulement survivre au round-trip de la redirection.
Étape 2 : Dériver le Code Challenge
Calcule SHA-256(code_verifier) et encode le résultat en Base64URL :
async function generateCodeChallenge(verifier: string): Promise<string> {
const encoder = new TextEncoder()
const data = encoder.encode(verifier)
const digest = await crypto.subtle.digest('SHA-256', data)
return btoa(String.fromCharCode(...new Uint8Array(digest)))
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=/g, '')
}
const codeChallenge = await generateCodeChallenge(codeVerifier)
// Exemple : "E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM"Étape 3 : Générer le State
Génère un paramètre state aléatoire pour la protection CSRF.
function generateState(): string {
const array = new Uint8Array(16)
crypto.getRandomValues(array)
return btoa(String.fromCharCode(...array))
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=/g, '')
}
const state = generateState()
sessionStorage.setItem('pkce_state', state)
sessionStorage.setItem('pkce_code_verifier', codeVerifier)Étape 4 : Rediriger vers l’Endpoint Authorize
Construis l’URL d’autorisation et redirige le browser :
const params = new URLSearchParams({
response_type: 'code',
client_id: 'votre-client-id',
redirect_uri: 'https://app.votredomaine.com/callback',
state: state,
code_challenge: codeChallenge,
code_challenge_method: 'S256',
scope: 'openid profile email',
})
window.location.href = `https://api.altovar.net/api/oauth/authorize?${params}`Étape 5 : L’Utilisateur S’Authentifie sur Auris
Sur la page de login hébergée d’Auris, l’utilisateur :
- Saisit email et mot de passe (ou utilise login social, magic link, etc.)
- Complète le MFA si requis par la policy du tenant ou le risk scoring adaptatif
- Voit un écran de consentement pour les scopes sensibles (seulement la première fois)
Le code de ton application ne s’exécute pas pendant cette étape.
Étape 6 : Auris Redirige avec le Code
En cas d’authentification réussie, Auris émet un authorization code de courte durée (5 minutes) et redirige le browser vers ton redirect_uri :
https://app.votredomaine.com/callback?code=SplxlOBeZQQYbYS6WxSbIA&state=xyz789abcL’authorization code est à usage unique. Toute tentative de l’utiliser deux fois est rejetée.
Étape 7 : Valider le State (Protection CSRF)
Avant de faire quoi que ce soit avec le code, compare le state retourné dans la query string avec celui stocké dans sessionStorage :
const returnedState = new URLSearchParams(window.location.search).get('state')
const storedState = sessionStorage.getItem('pkce_state')
if (!returnedState || returnedState !== storedState) {
throw new Error('State non correspondant — possible attaque CSRF')
}Étape 8 : Échanger le Code contre des Tokens
Récupère le code_verifier stocké et envoie-le avec l’authorization code à l’endpoint token :
const code = new URLSearchParams(window.location.search).get('code')
const codeVerifier = sessionStorage.getItem('pkce_code_verifier')
const response = await fetch('https://api.altovar.net/api/auth/token', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
grant_type: 'authorization_code',
code: code,
code_verifier: codeVerifier,
redirect_uri: 'https://app.votredomaine.com/callback',
client_id: 'votre-client-id',
}),
})
const data = await response.json()Auris vérifie : SHA256(code_verifier) === stored_code_challenge. S’ils correspondent, les tokens sont émis.
Étape 9 : Recevoir les Tokens
{
"ok": true,
"data": {
"accessToken": "eyJhbGciOiJSUzI1NiJ9...",
"refreshToken": "rt_...",
"idToken": "eyJhbGciOiJSUzI1NiJ9...",
"expiresIn": 900,
"tokenType": "Bearer"
}
}Nettoie les valeurs PKCE de sessionStorage :
sessionStorage.removeItem('pkce_state')
sessionStorage.removeItem('pkce_code_verifier')Propriétés de Sécurité
Propriété 1 : Résistance à l’Interception du Code
Si un attaquant intercepte l’authorization code, il ne peut quand même pas l’échanger contre des tokens. Pour échanger le code, il a besoin du code_verifier. Le code_verifier n’est jamais envoyé à Auris jusqu’à l’étape 8 — et à ce moment, il va directement via HTTPS à l’endpoint token, pas par la barre URL du browser.
Propriété 2 : Protection CSRF via State
Le paramètre state est une valeur aléatoire générée par le client et retournée inchangée par Auris. Si un attaquant crée une redirection malveillante avec un code falsifié, ton application détectera que le state ne correspond pas.
Propriété 3 : Authorization Code à Usage Unique
Chaque authorization code ne peut être utilisé qu’une seule fois. Si Auris détecte une tentative de replay du code, il rejette la deuxième requête.
Propriété 4 : Liaison du Redirect URI
Le redirect_uri utilisé lors de l’échange doit correspondre exactement à celui enregistré dans la Console Auris pour cette application.
Propriété 5 : Courte Durée du Code
Les authorization codes expirent après 5 minutes.
Utiliser le SDK Auris
Si tu utilises un SDK Auris, tout ce qui précède est géré automatiquement :
import { AurisClient } from '@auris/js'
const auris = new AurisClient({
domain: 'api.altovar.net',
clientId: 'votre-client-id',
redirectUri: 'https://app.votredomaine.com/callback',
autoRefresh: true,
})
// Au clic du bouton de login :
await auris.loginWithRedirect()
// Dans ta page callback :
const result = await auris.handleRedirectCallback()
console.log(result.user) // L'utilisateur authentifiéConcepts Associés
- OAuth 2.0 & OIDC — Les standards de protocole sous-jacents
- Hosted Login (Universal Login) — Comment Auris utilise PKCE dans les pages de login hébergées
- Les Tokens Expliqués — Quels tokens sont émis après l’échange PKCE
- Guide Hosted Login — Intègre le login PKCE dans ton application