
Validation des tokens OAuth2 (introspection, JWK)
Apprenez comment un Resource Server Spring Security valide les Access Tokens OAuth2 via les méthodes d'introspection ou de vérification locale JWT avec JWK.
Le rôle crucial du Resource Server : valider les tokens
Lorsqu'une API REST est sécurisée à l'aide d'OAuth2, elle agit en tant que Resource Server. Sa responsabilité principale, avant d'accorder l'accès à une ressource protégée, est de valider l'Access Token (généralement un Bearer Token fourni dans l'en-tête `Authorization`) présenté par le client.
Cette validation est essentielle pour s'assurer que le token est authentique (émis par un Authorization Server de confiance), qu'il n'a pas été altéré, qu'il n'a pas expiré, et qu'il contient les informations nécessaires (scopes, identité de l'utilisateur) pour autoriser l'accès demandé. Sans validation fiable, l'API serait vulnérable à l'utilisation de tokens invalides, volés ou forgés.
Spring Security, via le module `spring-boot-starter-oauth2-resource-server`, fournit un support de première classe pour configurer le Resource Server afin qu'il valide les tokens en utilisant les mécanismes standards d'OAuth2, principalement l'introspection de token et la validation locale de JWT via JWK.
Méthode 1 : Introspection de Token (RFC 7662)
L'introspection de token est un mécanisme où le Resource Server demande directement à l'Authorization Server (AS) de valider un token. Le Resource Server envoie le token reçu à un endpoint spécifique de l'AS appelé endpoint d'introspection.
L'Authorization Server vérifie alors le token dans ses propres systèmes et renvoie une réponse au Resource Server, indiquant généralement si le token est actif (`active: true/false`) et, s'il est actif, des informations supplémentaires sur le token (comme les scopes, l'identifiant client, l'identifiant utilisateur, l'heure d'expiration).
Avantages :
- Fonctionne avec n'importe quel type de token (y compris les tokens opaques qui ne sont pas des JWT).
- La validation est toujours à jour (l'AS peut révoquer un token immédiatement).
- Relativement simple à configurer côté Resource Server (il suffit de connaître l'URL de l'endpoint d'introspection et potentiellement des identifiants client pour s'authentifier auprès de cet endpoint).
Inconvénients :
- Nécessite un appel réseau entre le Resource Server et l'Authorization Server pour chaque requête contenant un token à valider, ce qui introduit de la latence.
- Crée une dépendance forte et une charge sur l'Authorization Server. Si l'AS est indisponible, le Resource Server ne peut pas valider les tokens.
Dans Spring Security, l'introspection est gérée par l'interface `OpaqueTokenIntrospector`. Lorsque vous configurez votre `application.yml` pour l'introspection, Spring Security crée automatiquement un bean approprié.
Méthode 2 : Validation locale de JWT avec JWK (RFC 7517 & 7515)
Lorsque les Access Tokens sont des JSON Web Tokens (JWT), une approche plus performante et découplée est possible : la validation locale. Les JWT sont auto-contenus et signés numériquement par l'Authorization Server à l'aide d'une clé privée.
Le Resource Server peut valider le JWT localement s'il a accès à la clé publique correspondante de l'Authorization Server. Le standard JWK (JSON Web Key - RFC 7517) définit un format JSON pour représenter les clés cryptographiques, et le standard JWK Set définit une manière de publier un ensemble de clés JWK (souvent via un endpoint HTTP spécifique sur l'AS appelé JWK Set URI).
Le processus de validation locale par le Resource Server implique typiquement :
- Récupérer (et mettre en cache) le JWK Set depuis le JWK Set URI de l'Authorization Server.
- Extraire l'identifiant de clé (`kid`) de l'en-tête du JWT reçu.
- Trouver la clé publique correspondante dans le JWK Set mis en cache en utilisant le `kid`.
- Vérifier la signature du JWT en utilisant cette clé publique.
- Valider les claims standards du JWT (comme l'émetteur `iss`, l'audience `aud`, et surtout la date d'expiration `exp`).
Avantages :
- Très performant : pas d'appel réseau vers l'AS pour chaque validation de token une fois les clés publiques mises en cache.
- Le Resource Server est plus indépendant de la disponibilité immédiate de l'AS.
- Bien adapté aux architectures stateless et distribuées (microservices).
Inconvénients :
- Ne fonctionne que si les tokens sont des JWT signés.
- Il peut y avoir un délai dans la détection de la révocation d'un token (le token reste valide jusqu'à son expiration, même s'il a été révoqué côté AS, à moins que des mécanismes supplémentaires comme les listes de révocation ne soient utilisés).
- Légèrement plus complexe à configurer initialement (besoin de connaître le JWK Set URI).
Spring Security gère cette validation via l'interface `JwtDecoder`. En configurant le `jwk-set-uri` ou l'`issuer-uri` (pour la découverte OIDC), Spring Security met en place un `NimbusJwtDecoder` qui effectue ces étapes.
Configuration dans Spring Boot (`application.yml`)
La configuration de la validation se fait via les propriétés `spring.security.oauth2.resourceserver` dans votre fichier `application.yml` (ou `.properties`). Vous ne configurez généralement qu'une seule méthode de validation (soit `opaque-token` pour l'introspection, soit `jwt` pour la validation locale).
Exemple de configuration pour l'Introspection :
spring:
security:
oauth2:
resourceserver:
# Méthode d'introspection (Opaque Token)
opaque-token:
# URL de l'endpoint d'introspection de l'Authorization Server
introspection-uri: https://mon-auth-server.com/oauth2/introspect
# Identifiants du Resource Server pour s'authentifier auprès de l'endpoint d'introspection
client-id: mon-resource-server-id
client-secret: mon-resource-server-secret
Exemple de configuration pour la validation locale JWT via JWK Set URI :
spring:
security:
oauth2:
resourceserver:
# Méthode de validation locale JWT
jwt:
# URL où l'Authorization Server publie ses clés publiques JWK
jwk-set-uri: https://mon-auth-server.com/.well-known/jwks.json
# Optionnel: Spécifier l'émetteur attendu dans le claim 'iss'
# issuer-uri: https://mon-auth-server.com
# Optionnel: Spécifier les audiences attendues dans le claim 'aud'
# audiences:
# - api://ma-super-api
# - autre-audience-valide
Exemple de configuration pour la validation locale JWT via Issuer URI (Découverte OIDC) :
Si l'Authorization Server supporte OpenID Connect Discovery, vous pouvez souvent simplifier la configuration en fournissant uniquement l'`issuer-uri`. Spring Security l'utilisera pour découvrir automatiquement le `jwk-set-uri` et d'autres métadonnées.
spring:
security:
oauth2:
resourceserver:
jwt:
# URL de l'émetteur (Issuer) - Spring découvrira le jwk-set-uri via OIDC
issuer-uri: https://mon-auth-server.com/realms/mon-realm
# audiences: [ ... ] # Toujours utile si spécifique
Une fois ces propriétés définies, et avec le starter `spring-boot-starter-oauth2-resource-server` présent, Spring Security configure automatiquement le mécanisme de validation approprié dans la chaîne de filtres.
Configurer `HttpSecurity` pour le Resource Server
En complément de la configuration des propriétés, vous devez indiquer à `HttpSecurity` d'agir comme un Resource Server et d'utiliser le mécanisme de validation configuré. Cela se fait généralement avec la méthode `oauth2ResourceServer()`.
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.SecurityFilterChain;
import static org.springframework.security.config.Customizer.withDefaults;
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain resourceServerFilterChain(HttpSecurity http) throws Exception {
http
.csrf(AbstractHttpConfigurer::disable) // Désactiver CSRF pour API stateless
.sessionManagement(session -> session
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
)
.authorizeHttpRequests(authorize -> authorize
.requestMatchers("/api/public/**").permitAll()
.requestMatchers("/api/**").authenticated()
// Vous pouvez affiner l'autorisation basée sur les scopes du token
// .requestMatchers("/api/admin/**").hasAuthority("SCOPE_admin")
.anyRequest().denyAll() // Ou une autre règle par défaut
)
// Activer la validation d'Access Token (JWT ou Opaque via Introspection)
// La méthode (JWT ou Opaque) est déterminée par les propriétés application.yml
.oauth2ResourceServer(oauth2 -> oauth2
// Si validation JWT, on peut customiser ici si besoin
.jwt(withDefaults())
// Si validation Opaque (Introspection), on peut customiser ici si besoin
// .opaqueToken(withDefaults())
);
return http.build();
}
}Spring Security utilisera alors les propriétés définies dans `application.yml` pour configurer soit la validation JWT locale (si `spring.security.oauth2.resourceserver.jwt` est configuré), soit l'introspection (si `spring.security.oauth2.resourceserver.opaque-token` est configuré) comme mécanisme d'authentification principal pour les requêtes protégées par cette chaîne de filtres.
Quelle méthode choisir ?
Le choix entre l'introspection et la validation locale JWT dépend de plusieurs facteurs :
- Type de Token : Si l'Authorization Server émet des tokens opaques, l'introspection est la seule option. Si ce sont des JWT, vous avez le choix.
- Performance : Pour les API à fort trafic, la validation locale JWT est généralement préférable en raison de l'absence d'appel réseau par requête.
- Dépendance à l'AS : La validation locale rend le Resource Server moins dépendant de la disponibilité de l'AS au moment de la requête.
- Révocation : L'introspection permet une vérification de la révocation en temps réel. La validation locale de JWT détecte la révocation plus tardivement (à l'expiration du token ou via des mécanismes additionnels).
- Simplicité : L'introspection peut être légèrement plus simple à configurer côté Resource Server si l'on ne souhaite pas gérer les subtilités de la validation JWT.
Dans les architectures modernes basées sur les microservices et les API stateless, la validation locale de JWT via JWK est souvent privilégiée pour ses performances et son découplage.