Aller au contenu principal

Authentification JWT

Ce guide explique le fonctionnement et la configuration de l'authentification JWT dans PCH-SIG.

Fonctionnement

Flux d'authentification

┌─────────────┐     ┌─────────────┐     ┌─────────────┐
│ Client │ │ Backend │ │ PostgreSQL │
│ (Browser) │ │ (Symfony) │ │ │
└──────┬──────┘ └──────┬──────┘ └──────┬──────┘
│ │ │
│ POST /api/login │ │
│ {email, password} │ │
│──────────────────▶│ │
│ │ Vérifier user │
│ │──────────────────▶│
│ │◀──────────────────│
│ │ │
│ {token, refresh} │ │
│◀──────────────────│ │
│ │ │
│ GET /api/menages │ │
│ Authorization: │ │
│ Bearer <token> │ │
│──────────────────▶│ │
│ │ Vérifier token │
│ │ (clé publique) │
│ {data} │ │
│◀──────────────────│ │

Tokens

TypeDuréeUsage
Access Token1 heureAuthentifier les requêtes API
Refresh Token7 joursRenouveler l'access token

Configuration

Variables d'environnement

Fichier : backend/.env

# Chemin des clés
JWT_SECRET_KEY=%kernel.project_dir%/config/jwt/private.pem
JWT_PUBLIC_KEY=%kernel.project_dir%/config/jwt/public.pem

# Passphrase pour la clé privée
JWT_PASSPHRASE=votre_passphrase_securisee

# Durée de validité (en secondes)
JWT_TTL=3600

Configuration LexikJWT

Fichier : backend/config/packages/lexik_jwt_authentication.yaml

lexik_jwt_authentication:
secret_key: '%env(resolve:JWT_SECRET_KEY)%'
public_key: '%env(resolve:JWT_PUBLIC_KEY)%'
pass_phrase: '%env(JWT_PASSPHRASE)%'
token_ttl: 3600

token_extractors:
authorization_header:
enabled: true
prefix: Bearer
name: Authorization

Génération des clés

Créer les clés JWT

# Via la commande Symfony
docker exec pch_backend php bin/console lexik:jwt:generate-keypair

# Ou manuellement avec OpenSSL
openssl genrsa -out config/jwt/private.pem -aes256 4096
openssl rsa -pubout -in config/jwt/private.pem -out config/jwt/public.pem

Vérifier les clés

# Vérifier que les fichiers existent
docker exec pch_backend ls -la config/jwt/

# Tester la clé privée
docker exec pch_backend openssl rsa -in config/jwt/private.pem -check

# Vérifier la clé publique
docker exec pch_backend openssl rsa -in config/jwt/public.pem -pubin -text

Permissions

# La clé privée doit être protégée
docker exec pch_backend chmod 600 config/jwt/private.pem
docker exec pch_backend chmod 644 config/jwt/public.pem

Utilisation de l'API

Authentification

# Obtenir un token
curl -X POST http://localhost:8000/api/login_check \
-H "Content-Type: application/json" \
-d '{"email":"admin@pch-sig.sn","password":"Admin123!"}'

Réponse :

{
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9...",
"refresh_token": "abc123..."
}

Utiliser le token

# Requête authentifiée
curl http://localhost:8000/api/menages \
-H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9..."

Rafraîchir le token

curl -X POST http://localhost:8000/api/token/refresh \
-H "Content-Type: application/json" \
-d '{"refresh_token":"abc123..."}'

Structure du token

Payload JWT

{
"iat": 1705312345,
"exp": 1705315945,
"roles": ["ROLE_ADMIN"],
"email": "admin@pch-sig.sn",
"user_id": 1
}
ChampDescription
iatIssued At - Timestamp de création
expExpiration - Timestamp d'expiration
rolesRôles de l'utilisateur
emailEmail de l'utilisateur
user_idID de l'utilisateur

Décoder un token

# En ligne de commande
echo "eyJ0eXAi..." | cut -d'.' -f2 | base64 -d | jq

# Via jwt.io (ne pas utiliser en production avec de vrais tokens)

Personnalisation

Ajouter des données au token

Fichier : src/EventListener/JWTCreatedListener.php

namespace App\EventListener;

use Lexik\Bundle\JWTAuthenticationBundle\Event\JWTCreatedEvent;

class JWTCreatedListener
{
public function onJWTCreated(JWTCreatedEvent $event): void
{
$user = $event->getUser();
$payload = $event->getData();

// Ajouter des données personnalisées
$payload['user_id'] = $user->getId();
$payload['permissions'] = $user->getPermissions();

$event->setData($payload);
}
}

Configuration du listener

# config/services.yaml
services:
App\EventListener\JWTCreatedListener:
tags:
- { name: kernel.event_listener, event: lexik_jwt_authentication.on_jwt_created }

Sécurité

Bonnes pratiques

  1. Clés sécurisées

    • Utiliser une passphrase forte
    • Ne jamais commiter les clés dans Git
    • Rotationner les clés régulièrement
  2. Durée de vie courte

    • Access token : 1 heure maximum
    • Refresh token : 7 jours maximum
  3. HTTPS obligatoire

    • Toujours utiliser HTTPS en production
    • Le token transit en clair dans les headers

Révocation de tokens

JWT ne supporte pas nativement la révocation. Solutions :

  1. Liste noire : Stocker les tokens révoqués dans Redis
  2. Durée courte : Tokens de courte durée + refresh
  3. Version utilisateur : Invalider tous les tokens si la version change

Exemple de liste noire

// Révoquer un token
public function revokeToken(string $token): void
{
$payload = $this->jwtManager->parse($token);
$expiration = $payload['exp'] - time();

$this->redis->setex(
'blacklist:' . hash('sha256', $token),
$expiration,
'1'
);
}

// Vérifier si révoqué
public function isRevoked(string $token): bool
{
return $this->redis->exists('blacklist:' . hash('sha256', $token));
}

Dépannage

Token invalide

# Vérifier la configuration
docker exec pch_backend php bin/console debug:config lexik_jwt_authentication

# Régénérer les clés
docker exec pch_backend php bin/console lexik:jwt:generate-keypair --overwrite

# Vider le cache
docker exec pch_backend php bin/console cache:clear

Token expiré

# Vérifier l'heure du serveur
docker exec pch_backend date

# Synchroniser l'heure si nécessaire

Erreur de signature

Causes possibles :

  • Clés différentes entre environnements
  • Passphrase incorrecte
  • Fichier de clé corrompu
# Tester la clé
docker exec pch_backend openssl rsa -in config/jwt/private.pem -check

# Si erreur, régénérer
docker exec pch_backend php bin/console lexik:jwt:generate-keypair --overwrite

Rotation des clés

Procédure

  1. Générer une nouvelle paire de clés
  2. Configurer le système pour accepter les deux clés
  3. Attendre l'expiration des anciens tokens
  4. Supprimer l'ancienne clé

Script de rotation

# Sauvegarder les anciennes clés
cp config/jwt/private.pem config/jwt/private.pem.old
cp config/jwt/public.pem config/jwt/public.pem.old

# Générer les nouvelles
php bin/console lexik:jwt:generate-keypair --overwrite

# Redémarrer l'application
docker restart pch_backend

Prochaines étapes