« Apiplatform keycloak » : différence entre les versions
Balise : Révocation manuelle |
|||
| (6 versions intermédiaires par le même utilisateur non affichées) | |||
| Ligne 159 : | Ligne 159 : | ||
Avec cette configuration, vous pouvez utiliser un <code>AccessTokenHandler</code> personnalisé pour valider les tokens JWT émis par Keycloak. Le fichier <code>security.yaml</code> 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. | Avec cette configuration, vous pouvez utiliser un <code>AccessTokenHandler</code> personnalisé pour valider les tokens JWT émis par Keycloak. Le fichier <code>security.yaml</code> 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 : | |||
----- | |||
<span id="installer-le-bundle"></span> | |||
=== 1. Installer le bundle === | |||
Si vous ne l’avez pas déjà fait, installez le bundle via Composer : | |||
<syntaxhighlight lang="bash">composer require web-token/jwt-bundle</syntaxhighlight> | |||
----- | |||
<span id="configurer-le-bundle"></span> | |||
=== 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 : | |||
<span id="a.-configuration-de-base-configpackagesweb_token.yaml"></span> | |||
==== a. Configuration de base (<code>config/packages/web_token.yaml</code>) ==== | |||
<syntaxhighlight lang="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']</syntaxhighlight> | |||
<span id="b.-configuration-de-sécurité-configpackagessecurity.yaml"></span> | |||
==== b. Configuration de sécurité (<code>config/packages/security.yaml</code>) ==== | |||
Configurez le firewall pour utiliser le bundle '''web-token/jwt-bundle''' : | |||
<syntaxhighlight lang="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 # Assurez-vous que cela correspond au préfixe d'API Platform | |||
stateless: true | |||
jwt: ~ | |||
provider: keycloak</syntaxhighlight> | |||
----- | |||
<span id="créer-un-jwttokenauthenticator"></span> | |||
=== 3. Créer un <code>JWTTokenAuthenticator</code> === | |||
Le bundle '''web-token/jwt-bundle''' ne fournit pas d’authentificateur par défaut, donc vous devez en créer un. | |||
<span id="a.-créer-lauthentificateur"></span> | |||
==== a. Créer l’authentificateur ==== | |||
<syntaxhighlight lang="php">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); | |||
} | |||
}</syntaxhighlight> | |||
<span id="b.-enregistrer-lauthentificateur"></span> | |||
==== b. Enregistrer l’authentificateur ==== | |||
Dans <code>config/services.yaml</code> : | |||
<syntaxhighlight lang="yaml">services: | |||
App\Security\JWTTokenAuthenticator: | |||
arguments: | |||
$jwkSet: '@web_token.key_set.keycloak' | |||
$jwsVerifier: '@jose.jws_verifier' | |||
tags: ['security.authenticator']</syntaxhighlight> | |||
----- | |||
<span id="configurer-le-userprovider"></span> | |||
=== 4. Configurer le <code>UserProvider</code> === | |||
Si vous avez besoin de mapper les utilisateurs Keycloak à des entités locales, créez un <code>UserProvider</code> personnalisé comme expliqué précédemment. | |||
----- | |||
<span id="tester-lauthentification"></span> | |||
=== 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 <code>Authorization: Bearer <token></code>. | |||
* Vérifiez que l’utilisateur est bien authentifié et que les routes sont protégées. | |||
----- | |||
<span id="optionnel-utiliser-un-accesstokenhandler"></span> | |||
=== 6. (Optionnel) Utiliser un <code>AccessTokenHandler</code> === | |||
Si vous utilisez un <code>AccessTokenHandler</code> pour valider les tokens, vous pouvez l’adapter pour utiliser '''web-token/jwt-bundle''' : | |||
<syntaxhighlight lang="php">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); | |||
} | |||
}</syntaxhighlight> | |||
Enregistrez-le dans <code>config/services.yaml</code> : | |||
<syntaxhighlight lang="yaml">services: | |||
App\Security\AccessTokenHandler: | |||
arguments: | |||
$jwkSet: '@web_token.key_set.keycloak' | |||
$jwsVerifier: '@jose.jws_verifier' | |||
tags: ['security.access_token_handler']</syntaxhighlight> | |||
----- | |||
<span id="conclusion"></span> | |||
=== 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é == | |||
----- | |||
<span id="compatibilité-avec-symfony-6.4"></span> | |||
=== 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 <code>AccessTokenHandler</code> 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 <code>security.yaml</code> utilisée dans l’exemple est valide pour Symfony 6.4. Les options comme <code>enable_authenticator_manager</code>, <code>access_token</code>, et <code>stateless</code> sont supportées. | |||
----- | |||
<span id="compatibilité-avec-api-platform-4.1"></span> | |||
=== 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 <code>AccessTokenHandler</code> et du firewall dans <code>security.yaml</code> est donc parfaitement compatible. | |||
* '''Préfixe des routes''' : | |||
** API Platform 4.1 utilise un préfixe de route par défaut (<code>/api</code>), mais vous pouvez le modifier (comme montré dans l’exemple avec <code>/v1</code>). Cela n’affecte pas la compatibilité. | |||
----- | |||
<span id="points-à-vérifier-pour-une-intégration-réussie"></span> | |||
=== 3. Points à vérifier pour une intégration réussie === | |||
<span id="a.-vérifier-les-dépendances"></span> | |||
==== a. Vérifier les dépendances ==== | |||
Assurez-vous que les dépendances suivantes sont installées et à jour : | |||
<syntaxhighlight lang="bash">composer require symfony/security-bundle web-token/jwt-bundle</syntaxhighlight> | |||
<span id="b.-configuration-de-keycloak"></span> | |||
==== 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 <code>security.yaml</code>. | |||
<span id="c.-vérifier-les-logs"></span> | |||
==== c. Vérifier les logs ==== | |||
Si l’authentification ne fonctionne pas, consultez les logs Symfony (<code>var/log/dev.log</code> ou <code>var/log/prod.log</code>) 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 <code>AccessTokenHandler</code>. | |||
<span id="d.-tester-avec-un-token-jwt"></span> | |||
==== 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 [https://jwt.io/ jwt.io] pour inspecter le token et vous assurer qu’il contient les claims attendus (<code>iss</code>, <code>aud</code>, <code>sub</code>, etc.). | |||
----- | |||
<span id="exemple-complet-de-configuration"></span> | |||
=== 4. Exemple complet de configuration === | |||
Voici un rappel de la configuration complète pour Symfony 6.4 et API Platform 4.1 : | |||
<span id="configpackagessecurity.yaml"></span> | |||
==== <code>config/packages/security.yaml</code> ==== | |||
<syntaxhighlight lang="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 }</syntaxhighlight> | |||
<span id="configpackagesweb_token.yaml"></span> | |||
==== <code>config/packages/web_token.yaml</code> ==== | |||
<syntaxhighlight lang="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']</syntaxhighlight> | |||
<span id="configservices.yaml"></span> | |||
==== <code>config/services.yaml</code> ==== | |||
<syntaxhighlight lang="yaml">services: | |||
App\Security\AccessTokenHandler: | |||
arguments: | |||
$jwkSet: '@web_token.key_set.keycloak' | |||
$jwsVerifier: '@jose.jws_verifier' | |||
tags: ['security.access_token_handler']</syntaxhighlight> | |||
----- | |||
<span id="conclusion"></span> | |||
=== 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 | source:DeepSeek | ||
[[Catégorie: Apiplatform]] [[category:api]] [[category:Keycloak]] | [[Catégorie: Apiplatform]] [[category:api]] [[category:Keycloak]] [[category:DeepSeek]] [[category:Symfony]] | ||