Skip to content

Comment utiliser l'authentification par token Bearer ​

NENE2 fournit BearerTokenMiddleware et LocalBearerTokenVerifier pour l'authentification basée sur JWT. Ce guide couvre la configuration, la mise en place, l'émission de tokens, et les pièges courants.

Configuration ​

Câbler le middleware dans RuntimeApplicationFactory en utilisant le paramètre nommé authMiddleware :

php
use Nene2\Auth\BearerTokenMiddleware;
use Nene2\Auth\LocalBearerTokenVerifier;
use Nene2\Error\ProblemDetailsResponseFactory;
use Nene2\Http\RuntimeApplicationFactory;

$secret   = getenv('NENE2_LOCAL_JWT_SECRET') ?: 'change-me';
$verifier = new LocalBearerTokenVerifier($secret);
$bearer   = new BearerTokenMiddleware($problemDetails, $verifier);

$app = (new RuntimeApplicationFactory(
    $psr17,
    $psr17,
    routeRegistrars: [static fn (Router $r) => $registrar->register($r)],
    authMiddleware:  $bearer, // ← le paramètre nommé est authMiddleware, pas middlewares
))->create();

Note : Le nom du paramètre est authMiddleware, pas middlewares. Utiliser middlewares: cause une Error: Unknown named parameter à l'exécution.

Protéger toutes les routes vs. protection sélective ​

BearerTokenMiddleware supporte quatre modes de correspondance de chemin (la première correspondance gagne) :

php
// 1. Protéger uniquement des chemins spécifiques (liste blanche)
new BearerTokenMiddleware($problems, $verifier, protectedPaths: ['/me', '/entries']);

// 2. Protéger les chemins commençant par un préfixe (liste blanche de préfixes)
new BearerTokenMiddleware($problems, $verifier, protectedPathPrefixes: ['/api/', '/me/']);

// 3. Protéger tout SAUF les chemins listés (liste noire — pattern courant)
new BearerTokenMiddleware($problems, $verifier, excludedPaths: ['/auth/login', '/auth/register', '/health']);

// 4. Protéger tous les chemins (défaut — aucun tableau fourni)
new BearerTokenMiddleware($problems, $verifier);

Lire les claims dans un handler ​

Après une vérification réussie, les claims sont stockés comme attribut de requête :

php
/** @var array<string, mixed> $claims */
$claims  = $request->getAttribute('nene2.auth.claims') ?? [];
$ownerId = (string) ($claims['sub'] ?? '');

Le type d'identifiant est stocké séparément :

php
$credType = $request->getAttribute('nene2.auth.credential_type'); // 'bearer'

Émettre des tokens (local / test) ​

LocalBearerTokenVerifier implémente également TokenIssuerInterface :

php
$token = $verifier->issue([
    'sub' => 'user-123',
    'iat' => time(),
    'exp' => time() + 3600, // ← toujours inclure exp
]);

Toujours inclure exp. Les tokens sans exp sont traités comme non-expirables. C'est sûr pour les tests mais dangereux si de tels tokens atteignent la production. Si exp est absent, le vérificateur ignore la vérification d'expiration.

Réponses d'erreur ​

En cas d'échec, BearerTokenMiddleware retourne une réponse Problem Details 401 Unauthorized avec un en-tête WWW-Authenticate :

HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer realm="NENE2", error="invalid_token", error_description="Token has expired."
Content-Type: application/problem+json

Codes d'erreur dans WWW-Authenticate :

  • missing_token — pas d'en-tête Authorization
  • invalid_token — schéma incorrect, expiré, signature invalide, malformé, mauvais algorithme, nbf dans le futur

Propriétés de sécurité de LocalBearerTokenVerifier ​

MenaceProtection
Falsification de signatureHMAC-HS256, hash_equals en temps constant
Substitution d'algorithme (alg:none)Seul HS256 accepté
Token expiréClaim exp vérifié
Token pas encore valideClaim nbf vérifié
Payload altéréLa signature couvre l'en-tête + le payload ; l'altération casse la signature

LocalBearerTokenVerifier est conçu pour le développement local et les tests. En production, injecter une implémentation de TokenVerifierInterface basée sur une bibliothèque (ex. firebase/php-jwt) qui supporte la rotation de clés et les algorithmes asymétriques.

Patterns de test ​

php
// Dans setUp() : créer le vérificateur avec un secret de test
$this->verifier = new LocalBearerTokenVerifier('test-secret');

// Émettre un token valide pour un utilisateur
$token = $this->verifier->issue(['sub' => 'alice', 'exp' => time() + 3600]);

// Émettre un token expiré (pour un test négatif)
$expired = $this->verifier->issue(['sub' => 'alice', 'exp' => time() - 1]);

// Émettre un token pas encore valide
$future = $this->verifier->issue(['sub' => 'alice', 'nbf' => time() + 3600, 'exp' => time() + 7200]);

Publié sous licence MIT.