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
| Type | Durée | Usage |
|---|---|---|
| Access Token | 1 heure | Authentifier les requêtes API |
| Refresh Token | 7 jours | Renouveler 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
}
| Champ | Description |
|---|---|
iat | Issued At - Timestamp de création |
exp | Expiration - Timestamp d'expiration |
roles | Rôles de l'utilisateur |
email | Email de l'utilisateur |
user_id | ID 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
-
Clés sécurisées
- Utiliser une passphrase forte
- Ne jamais commiter les clés dans Git
- Rotationner les clés régulièrement
-
Durée de vie courte
- Access token : 1 heure maximum
- Refresh token : 7 jours maximum
-
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 :
- Liste noire : Stocker les tokens révoqués dans Redis
- Durée courte : Tokens de courte durée + refresh
- 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
- Générer une nouvelle paire de clés
- Configurer le système pour accepter les deux clés
- Attendre l'expiration des anciens tokens
- 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