Apiplatform keycloak
AccessTokenHandler
Si vous utilisez un AccessTokenHandler
pour gérer l’authentification avec des tokens d’accès (par exemple, des tokens JWT émis par Keycloak), voici comment configurer votre fichier security.yaml
pour intégrer ce composant.
Configuration de security.yaml
Voici un exemple de configuration pour utiliser un AccessTokenHandler
avec Symfony :
security:
enable_authenticator_manager: true # Activer le système d'authentification moderne de Symfony
providers:
# Définir un fournisseur d'utilisateurs
keycloak:
jwt:
issuer: 'https://your-keycloak-server/auth/realms/your-realm' # URL de Keycloak
audience: 'your-client-id' # Client ID configuré dans Keycloak
public_key: 'your-public-key-from-keycloak' # Clé publique pour valider les tokens
firewalls:
main:
pattern: ^/v1 # Préfixe des routes API Platform (ajustez selon votre configuration)
stateless: true # L'authentification est sans état (stateless)
access_token:
token_handler: App\Security\AccessTokenHandler # Votre AccessTokenHandler personnalisé
provider: keycloak # Utiliser le fournisseur d'utilisateurs configuré
access_control:
# Protéger les routes API
- { path: ^/v1, roles: IS_AUTHENTICATED_FULLY }
Explication des sections
providers
:- Ici, vous définissez un fournisseur d’utilisateurs (
keycloak
) qui utilise un token JWT. - Le fournisseur est configuré pour valider les tokens JWT émis par Keycloak en utilisant :
issuer
: L’URL de Keycloak (le realm).audience
: Le client ID configuré dans Keycloak.public_key
: La clé publique pour valider la signature des tokens.
- Ici, vous définissez un fournisseur d’utilisateurs (
firewalls
:- Le firewall
main
est configuré pour protéger les routes commençant par/v1
(ajustez selon votre préfixe API Platform). - L’option
stateless: true
indique que l’authentification est sans état (typique pour les API). - Le
token_handler
pointe vers votre classeAccessTokenHandler
personnalisée. - Le
provider
est défini pour utiliser le fournisseur d’utilisateurskeycloak
.
- Le firewall
access_control
:- Cette section protège les routes commençant par
/v1
et exige que l’utilisateur soit authentifié (IS_AUTHENTICATED_FULLY
).
- Cette section protège les routes commençant par
Implémentation de AccessTokenHandler
Voici un exemple d’implémentation de AccessTokenHandler
pour valider les tokens JWT émis par Keycloak :
namespace App\Security;
use Symfony\Component\Security\Http\AccessToken\AccessTokenHandlerInterface;
use Symfony\Component\Security\Core\Exception\BadCredentialsException;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
use Jose\Component\Core\JWKSet;
use Jose\Component\Core\Util\JsonConverter;
use Jose\Component\Signature\JWSVerifier;
use Jose\Component\Signature\Serializer\CompactSerializer;
class AccessTokenHandler implements AccessTokenHandlerInterface
{
private JWKSet $jwkSet;
private JWSVerifier $jwsVerifier;
public function __construct(JWKSet $jwkSet, JWSVerifier $jwsVerifier)
{
$this->jwkSet = $jwkSet;
$this->jwsVerifier = $jwsVerifier;
}
public function getUserBadgeFrom(string $accessToken): UserBadge
{
// Désérialiser le token JWT
$serializer = new CompactSerializer();
$jws = $serializer->unserialize($accessToken);
// Valider la signature du token
if (!$this->jwsVerifier->verifyWithKeySet($jws, $this->jwkSet, 0)) {
throw new BadCredentialsException('Invalid token signature.');
}
// Extraire le payload du token
$payload = JsonConverter::decode($jws->getPayload());
// Récupérer l'identifiant de l'utilisateur (sub)
$userId = $payload['sub'];
// Retourner un UserBadge avec l'identifiant de l'utilisateur
return new UserBadge($userId);
}
}
Enregistrement des services
Dans config/services.yaml
, enregistrez votre AccessTokenHandler
et les services nécessaires :
services:
# Enregistrer l'AccessTokenHandler
App\Security\AccessTokenHandler:
arguments:
$jwkSet: '@web_token.key_set.keycloak' # Le JWKSet pour Keycloak
$jwsVerifier: '@jose.jws_verifier' # Le service de vérification JWS
tags: ['security.access_token_handler']
# Configuration de web-token/jwt-bundle
web_token.key_set.keycloak:
class: Jose\Component\Core\JWKSet
factory: ['Jose\Component\Core\JWKSet', 'createFromKeyData']
arguments:
- { url: 'https://your-keycloak-server/auth/realms/your-realm/protocol/openid-connect/certs' }
- true # is_public
jose.jws_verifier:
class: Jose\Component\Signature\JWSVerifier
arguments:
- '@jose.algorithm_manager' # Gestionnaire d'algorithmes
Tester l’authentification
- Obtenir un token JWT :
- Utilisez Keycloak pour obtenir un token JWT valide pour votre client.
- Envoyer une requête à l’API :
Envoyez une requête à votre API avec le token dans l’en-tête
Authorization
:Authorization: Bearer <votre-token-jwt>
- Vérifier la réponse :
- Si le token est valide, l’utilisateur sera authentifié.
- Si le token est invalide, une erreur
401 Unauthorized
sera retournée.
Conclusion
Avec cette configuration, vous pouvez utiliser un AccessTokenHandler
personnalisé pour valider les tokens JWT émis par Keycloak. Le fichier security.yaml
est configuré pour utiliser ce handler, et les tokens sont validés à l’aide de web-token/jwt-bundle. Cette approche est flexible et s’intègre bien avec Symfony et API Platform.
web-token/jwt-bundle
Si vous utilisez le bundle web-token/jwt-bundle (aussi connu sous le nom de web-token/jwt-framework) au lieu de lexik/jwt-authentication-bundle, la configuration sera légèrement différente. Ce bundle est plus flexible et permet de gérer des tokens JWT de manière plus avancée, mais il nécessite une configuration plus détaillée.
Voici comment configurer Symfony avec web-token/jwt-bundle pour utiliser Keycloak dans un contexte OIDC :
1. Installer le bundle
Si vous ne l’avez pas déjà fait, installez le bundle via Composer :
composer require web-token/jwt-bundle
2. Configurer le bundle
Le bundle web-token/jwt-bundle nécessite une configuration pour valider les tokens JWT émis par Keycloak. Voici comment procéder :
a. Configuration de base (config/packages/web_token.yaml
)
web_token:
key_sets:
keycloak:
jwkset:
url: 'https://your-keycloak-server/auth/realms/your-realm/protocol/openid-connect/certs'
is_public: true
tags: ['jwt.verifier.key_set']
checkers:
keycloak_checker:
claims:
- iss: 'https://your-keycloak-server/auth/realms/your-realm'
- aud: 'your-client-id'
tags: ['jwt.checker.claims']
loaders:
keycloak_loader:
serializers:
- jwt_compact
tags: ['jwt.loader']
b. Configuration de sécurité (config/packages/security.yaml
)
Configurez le firewall pour utiliser le bundle web-token/jwt-bundle :
security:
enable_authenticator_manager: true
providers:
keycloak:
jwt:
issuer: 'https://your-keycloak-server/auth/realms/your-realm'
audience: 'your-client-id'
public_key: 'your-public-key-from-keycloak' # Ou utilisez jwks_url
firewalls:
main:
pattern: ^/v1 # Assurez-vous que cela correspond au préfixe d'API Platform
stateless: true
jwt: ~
provider: keycloak
3. Créer un JWTTokenAuthenticator
Le bundle web-token/jwt-bundle ne fournit pas d’authentificateur par défaut, donc vous devez en créer un.
a. Créer l’authentificateur
namespace App\Security;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Http\Authenticator\AbstractAuthenticator;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\Passport;
use Symfony\Component\Security\Http\Authenticator\Passport\SelfValidatingPassport;
use Jose\Component\Core\JWKSet;
use Jose\Component\Core\Util\JsonConverter;
use Jose\Component\Signature\JWSVerifier;
use Jose\Component\Signature\Serializer\CompactSerializer;
class JWTTokenAuthenticator extends AbstractAuthenticator
{
private JWKSet $jwkSet;
private JWSVerifier $jwsVerifier;
public function __construct(JWKSet $jwkSet, JWSVerifier $jwsVerifier)
{
$this->jwkSet = $jwkSet;
$this->jwsVerifier = $jwsVerifier;
}
public function supports(Request $request): ?bool
{
return $request->headers->has('Authorization');
}
public function authenticate(Request $request): Passport
{
$token = $request->headers->get('Authorization');
if (null === $token || !preg_match('/^Bearer\s+(.*?)$/', $token, $matches)) {
throw new AuthenticationException('Invalid token.');
}
$jwt = $matches[1];
$serializer = new CompactSerializer();
$jws = $serializer->unserialize($jwt);
if (!$this->jwsVerifier->verifyWithKeySet($jws, $this->jwkSet, 0)) {
throw new AuthenticationException('Invalid token signature.');
}
$payload = JsonConverter::decode($jws->getPayload());
$userId = $payload['sub']; // L'identifiant utilisateur Keycloak
return new SelfValidatingPassport(new UserBadge($userId));
}
public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response
{
return null;
}
public function onAuthenticationFailure(Request $request, AuthenticationException $exception): ?Response
{
return new Response('Authentication failed.', Response::HTTP_UNAUTHORIZED);
}
}
b. Enregistrer l’authentificateur
Dans config/services.yaml
:
services:
App\Security\JWTTokenAuthenticator:
arguments:
$jwkSet: '@web_token.key_set.keycloak'
$jwsVerifier: '@jose.jws_verifier'
tags: ['security.authenticator']
4. Configurer le UserProvider
Si vous avez besoin de mapper les utilisateurs Keycloak à des entités locales, créez un UserProvider
personnalisé comme expliqué précédemment.
5. Tester l’authentification
- Assurez-vous que Keycloak émet des tokens JWT valides.
- Envoyez une requête à votre API avec un token JWT dans l’en-tête
Authorization: Bearer <token>
. - Vérifiez que l’utilisateur est bien authentifié et que les routes sont protégées.
6. (Optionnel) Utiliser un AccessTokenHandler
Si vous utilisez un AccessTokenHandler
pour valider les tokens, vous pouvez l’adapter pour utiliser web-token/jwt-bundle :
namespace App\Security;
use Symfony\Component\Security\Http\AccessToken\AccessTokenHandlerInterface;
use Symfony\Component\Security\Core\Exception\BadCredentialsException;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
use Jose\Component\Core\JWKSet;
use Jose\Component\Core\Util\JsonConverter;
use Jose\Component\Signature\JWSVerifier;
use Jose\Component\Signature\Serializer\CompactSerializer;
class AccessTokenHandler implements AccessTokenHandlerInterface
{
private JWKSet $jwkSet;
private JWSVerifier $jwsVerifier;
public function __construct(JWKSet $jwkSet, JWSVerifier $jwsVerifier)
{
$this->jwkSet = $jwkSet;
$this->jwsVerifier = $jwsVerifier;
}
public function getUserBadgeFrom(string $accessToken): UserBadge
{
$serializer = new CompactSerializer();
$jws = $serializer->unserialize($accessToken);
if (!$this->jwsVerifier->verifyWithKeySet($jws, $this->jwkSet, 0)) {
throw new BadCredentialsException('Invalid token signature.');
}
$payload = JsonConverter::decode($jws->getPayload());
$userId = $payload['sub']; // L'identifiant utilisateur Keycloak
return new UserBadge($userId);
}
}
Enregistrez-le dans config/services.yaml
:
services:
App\Security\AccessTokenHandler:
arguments:
$jwkSet: '@web_token.key_set.keycloak'
$jwsVerifier: '@jose.jws_verifier'
tags: ['security.access_token_handler']
Conclusion
Avec web-token/jwt-bundle, vous avez une configuration plus flexible pour gérer les tokens JWT émis par Keycloak. Vous devez créer un authentificateur personnalisé et configurer le bundle pour valider les tokens. Cette approche est plus complexe que lexik/jwt-authentication-bundle, mais elle offre plus de contrôle sur le processus de validation des tokens.
Compatiblité
1. Compatibilité avec Symfony 6.4
- Système d’authentification moderne :
- Symfony 6.4 utilise le système d’authentification moderne basé sur les authenticators, qui est pleinement supporté par la configuration proposée.
- Le
AccessTokenHandler
est une fonctionnalité introduite dans Symfony 5.4 et est toujours supportée dans Symfony 6.4.
- web-token/jwt-bundle :
- Ce bundle est compatible avec Symfony 6.4. Il fournit des outils pour valider et manipuler les tokens JWT, ce qui est essentiel pour interagir avec Keycloak.
- Configuration de sécurité :
- La structure du fichier
security.yaml
utilisée dans l’exemple est valide pour Symfony 6.4. Les options commeenable_authenticator_manager
,access_token
, etstateless
sont supportées.
- La structure du fichier
2. Compatibilité avec API Platform 4.1
- Authentification stateless :
- API Platform 4.1 est conçu pour fonctionner avec des authentifications stateless, ce qui correspond à la configuration proposée (utilisation de tokens JWT).
- Intégration avec Symfony Security :
- API Platform s’appuie sur le système de sécurité de Symfony pour gérer l’authentification et l’autorisation. La configuration du
AccessTokenHandler
et du firewall danssecurity.yaml
est donc parfaitement compatible.
- API Platform s’appuie sur le système de sécurité de Symfony pour gérer l’authentification et l’autorisation. La configuration du
- Préfixe des routes :
- API Platform 4.1 utilise un préfixe de route par défaut (
/api
), mais vous pouvez le modifier (comme montré dans l’exemple avec/v1
). Cela n’affecte pas la compatibilité.
- API Platform 4.1 utilise un préfixe de route par défaut (
3. Points à vérifier pour une intégration réussie
a. Vérifier les dépendances
Assurez-vous que les dépendances suivantes sont installées et à jour :
composer require symfony/security-bundle web-token/jwt-bundle
b. Configuration de Keycloak
- Vérifiez que Keycloak est correctement configuré pour émettre des tokens JWT valides.
- Assurez-vous que l’URL du serveur Keycloak, le realm, le client ID et la clé publique sont correctement configurés dans
security.yaml
.
c. Vérifier les logs
Si l’authentification ne fonctionne pas, consultez les logs Symfony (var/log/dev.log
ou var/log/prod.log
) pour identifier les erreurs potentielles. Les erreurs courantes incluent : - Des tokens JWT invalides ou mal formatés. - Des problèmes de configuration de la clé publique ou du JWKSet. - Des erreurs dans le AccessTokenHandler
.
d. Tester avec un token JWT
Pour vérifier que tout fonctionne correctement, testez avec un token JWT valide émis par Keycloak. Vous pouvez utiliser un outil comme jwt.io pour inspecter le token et vous assurer qu’il contient les claims attendus (iss
, aud
, sub
, etc.).
4. Exemple complet de configuration
Voici un rappel de la configuration complète pour Symfony 6.4 et API Platform 4.1 :
config/packages/security.yaml
security:
enable_authenticator_manager: true
providers:
keycloak:
jwt:
issuer: 'https://your-keycloak-server/auth/realms/your-realm'
audience: 'your-client-id'
public_key: 'your-public-key-from-keycloak' # Ou utilisez jwks_url
firewalls:
main:
pattern: ^/v1 # Préfixe des routes API Platform
stateless: true
access_token:
token_handler: App\Security\AccessTokenHandler
provider: keycloak
access_control:
- { path: ^/v1, roles: IS_AUTHENTICATED_FULLY }
config/packages/web_token.yaml
web_token:
key_sets:
keycloak:
jwkset:
url: 'https://your-keycloak-server/auth/realms/your-realm/protocol/openid-connect/certs'
is_public: true
tags: ['jwt.verifier.key_set']
checkers:
keycloak_checker:
claims:
- iss: 'https://your-keycloak-server/auth/realms/your-realm'
- aud: 'your-client-id'
tags: ['jwt.checker.claims']
loaders:
keycloak_loader:
serializers:
- jwt_compact
tags: ['jwt.loader']
config/services.yaml
services:
App\Security\AccessTokenHandler:
arguments:
$jwkSet: '@web_token.key_set.keycloak'
$jwsVerifier: '@jose.jws_verifier'
tags: ['security.access_token_handler']
5. Conclusion
La méthode décrite est entièrement compatible avec Symfony 6.4 et API Platform 4.1. Elle utilise les fonctionnalités modernes de Symfony pour l’authentification et s’intègre parfaitement avec Keycloak via le bundle web-token/jwt-bundle. Si vous suivez les étapes et les vérifications mentionnées, vous devriez pouvoir configurer une authentification JWT robuste pour votre API.
source:DeepSeek