Pourquoi j'ai délégué toute la sécurité à Keycloak plutôt que de coder ma propre authentification.
La première version de mon auth service avait une table users, un endpoint /login qui générait un JWT maison avec HS256, et un filtre Spring Security qui validait ce token. Ça marchait. C'était aussi une bombe à retardement.
Keycloak résout tous ces problèmes. C'est un Identity and Access Management (IAM) open-source qui implémente OAuth2 et OpenID Connect (OIDC) selon les RFC. Vous ne codez pas l'auth — vous la configurez.
Voici ce qui se passe quand un utilisateur s'inscrit sur ma banking platform :
Pour la connexion, le client obtient un access token JWT directement depuis Keycloak via le flux Authorization Code ou le flux Resource Owner Password (pour les apps mobiles internes). L'API Gateway valide ce token via les JWKS de Keycloak sans appel réseau supplémentaire.
Avec HS256 (symétrique), tous les services qui valident le token doivent connaître la clé secrète. Si cette clé fuite depuis n'importe lequel de vos 10 services, c'est game over. Avec RS256 (asymétrique), seul Keycloak connaît la clé privée. N'importe quel service peut vérifier le token avec la clé publique exposée via le endpoint JWKS — sans risque.
// Dans le gateway — validation via JWKS public
.oauth2ResourceServer(oauth2 -> oauth2
.jwt(jwt -> jwt
.jwkSetUri("http://keycloak:8180/realms/bank/protocol/openid-connect/certs")
.jwtAuthenticationConverter(jwtAuthConverter())
)
)
// Converter pour extraire les rôles Keycloak
private Converter<Jwt, AbstractAuthenticationToken> jwtAuthConverter() {
JwtGrantedAuthoritiesConverter converter = new JwtGrantedAuthoritiesConverter();
converter.setAuthoritiesClaimName("realm_access.roles");
converter.setAuthorityPrefix("ROLE_");
return new ReactiveJwtAuthenticationConverterAdapter(
new JwtAuthenticationConverter()
);
}
Keycloak gère les rôles directement dans le realm. J'ai défini trois rôles : USER, ADMIN, et AUDITOR. Ces rôles sont inclus dans le JWT sous realm_access.roles. Spring Security les mappe automatiquement en authorities.
@PreAuthorize("hasRole('ADMIN')")
@GetMapping("/api/accounts/all")
public Flux<Account> getAllAccounts() {
return accountService.findAll();
}
@PreAuthorize("hasAnyRole('USER', 'ADMIN')")
@GetMapping("/api/accounts/{id}")
public Mono<Account> getAccount(@PathVariable String id, JwtAuthenticationToken auth) {
// Vérification ownership : un USER ne peut voir que son compte
return accountService.findByIdAndOwner(id, auth.getName());
}
Attention au claim realm_access vs resource_access dans Keycloak. Les rôles au niveau du realm sont dans realm_access.roles, les rôles spécifiques à un client sont dans resource_access.{client-id}.roles. Selon votre besoin, le converter est différent.
Keycloak tourne dans le Docker Compose de prod sur le même réseau que les microservices. La configuration du realm est exportée en JSON et versionnée dans le repo infra — reproductible à 100% en cas de crash.
Ces articles reflètent mes choix d'architecture sur des projets réels. Les retours et axes d'amélioration sont les bienvenus.