Aller au contenu principal

PKCE Expliqué

Proof Key for Code Exchange (PKCE) - Sécurité OAuth améliorée.


Qu'est-ce que PKCE ?

PKCE (prononcé "pixy") est une extension de sécurité à OAuth 2.0 qui protège contre les attaques par interception de code d'autorisation.

Défini dans la RFC 7636.


Le Problème

Sans PKCE, les codes d'autorisation peuvent être interceptés :

1. L'utilisateur lance la connexion
2. L'app redirige vers le serveur d'auth avec une demande de code
3. L'utilisateur s'authentifie
4. Le serveur d'auth retourne le code vers l'URI de redirection

⚠️ L'attaquant intercepte le code

5. L'attaquant échange le code contre des tokens
6. L'attaquant gagne l'accès

La Solution

PKCE ajoute un secret connu seulement du client original :

1. L'app génère un code_verifier aléatoire
2. L'app calcule code_challenge = sha256(code_verifier)
3. L'app stocke code_verifier localement
4. L'app envoie code_challenge au serveur d'auth
5. L'utilisateur s'authentifie
6. Le serveur d'auth retourne un code d'autorisation
7. L'app échange code + code_verifier contre des tokens
8. Le serveur d'auth vérifie sha256(code_verifier) == code_challenge
9. Tokens émis seulement si la vérification réussit

Si un attaquant intercepte le code, il ne peut pas l'échanger sans le code_verifier.


Comment PKCE Fonctionne

Étape 1 : Générer Code Verifier

Une chaîne aléatoire à haute entropie :

function generateCodeVerifier(): string {
const array = new Uint8Array(32);
crypto.getRandomValues(array);
return base64url(array);
}

// Exemple de sortie :
// "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk"

Étape 2 : Créer Code Challenge

Hash SHA-256 du verifier :

async function generateCodeChallenge(verifier: string): Promise<string> {
const encoder = new TextEncoder();
const data = encoder.encode(verifier);
const hash = await crypto.subtle.digest('SHA-256', data);
return base64url(new Uint8Array(hash));
}

// Exemple de sortie :
// "E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM"

Étape 3 : Requête d'Autorisation

Inclure le challenge dans la requête :

GET /authorize?
response_type=code
&client_id=xxx
&redirect_uri=https://app.com/callback
&code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM
&code_challenge_method=S256
&state=random123

Étape 4 : Échange de Token

Inclure le verifier dans l'échange :

POST /oauth/token

grant_type=authorization_code
&code=received_code
&redirect_uri=https://app.com/callback
&client_id=xxx
&code_verifier=dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk

Étape 5 : Vérification Serveur

Le serveur calcule sha256(code_verifier) et compare au code_challenge stocké :

sha256("dBjftJeZ...") == "E9Melhoa2O..."

CORRESPOND ✓

Émettre tokens

Méthodes de Challenge

MéthodeDescriptionRecommandé
S256Hash SHA-256✅ Oui
plainPas de hachage❌ Non

Utilisez toujours S256 (SHA-256).


Implémentation SDK

Le SDK SyAuth gère PKCE automatiquement :

// Tout ceci se produit automatiquement :
// 1. Générer code_verifier
// 2. Calculer code_challenge
// 3. Stocker verifier dans cookie sécurisé
// 4. Envoyer challenge dans requête auth
// 5. Récupérer verifier pour échange de token

loginWithRedirect(); // PKCE est automatique

Avantages de Sécurité

AttaqueSans PKCEAvec PKCE
Interception de codeVulnérableProtégé
Man-in-the-middleVulnérableProtégé
App malveillanteVulnérableProtégé
Attaques par rejeuPossibleEmpêché

Pourquoi Pas Juste un Client Secret ?

Les secrets clients ne fonctionnent pas pour :

  • Clients publics (apps mobiles, SPAs) - ne peuvent pas stocker de secrets en sécurité
  • Apps natives - les secrets peuvent être extraits du binaire

PKCE fonctionne pour TOUS les types de clients.


Problèmes Courants

"Invalid code_verifier"

Causes :

  • Verifier perdu entre les requêtes
  • Client différent faisant la requête de token
  • Verifier corrompu dans le stockage

Solution :

  • Assurez-vous que le verifier est stocké dans un stockage sécurisé et persistant
  • La même instance client doit compléter le flux

"Code already used"

Les codes d'autorisation sont à usage unique. Vous ne pouvez pas réessayer l'échange de token.


Prochaines Étapes