This repository has been archived on 2026-01-03. You can view files and clone it, but cannot push or open issues or pull requests.
Files
lions-user-manager/SOLUTION_PROPAGATION_TOKEN.md

9.5 KiB

Solution: Propagation du Token JWT depuis JSF vers Backend

Date: 2025-12-05 Problème: 401 Unauthorized lors des appels frontend → backend malgré authentification OIDC réussie


🔍 Analyse du Problème

Symptômes Observés

  1. Frontend (Port 8080):

    • Authentification OIDC réussie avec PKCE
    • Token JWT reçu avec tous les rôles dans realm_access.roles
    • Erreur: Received: 'Unauthorized, status code 401' lors des appels API
  2. Backend (Port 8081):

    • Démarre sans erreur
    • Logs: Bearer access token is not available
    • Rejette les requêtes avec 401 Unauthorized

Configuration Initiale (Insuffisante)

# application.properties:56
quarkus.rest-client."lions-user-manager-api".bearer-token-propagation=true

Pourquoi ça ne fonctionnait pas ?

La propriété bearer-token-propagation=true ne fonctionne QUE pour:

  • Appels backend → backend (service-to-service)
  • Appels JSF managed bean → backend (notre cas)

Raison technique: Les managed beans JSF s'exécutent dans un contexte serveur différent où le token OIDC n'est pas automatiquement injecté dans les appels REST Client.


Solution Implémentée

1. Création de AuthHeaderFactory

Factory personnalisé qui intercepte TOUS les appels REST Client et ajoute automatiquement le header Authorization avec le token JWT.

Fichier: lions-user-manager-client-quarkus-primefaces-freya/src/main/java/dev/lions/user/manager/client/filter/AuthHeaderFactory.java

@ApplicationScoped
public class AuthHeaderFactory implements ClientHeadersFactory {

    private static final Logger LOGGER = Logger.getLogger(AuthHeaderFactory.class.getName());

    @Inject
    JsonWebToken jwt;

    @Override
    public MultivaluedMap<String, String> update(
            MultivaluedMap<String, String> incomingHeaders,
            MultivaluedMap<String, String> clientOutgoingHeaders) {

        MultivaluedMap<String, String> result = new MultivaluedHashMap<>();

        try {
            // Vérifier si le JWT est disponible et non expiré
            if (jwt != null && jwt.getRawToken() != null && !jwt.getRawToken().isEmpty()) {
                String token = jwt.getRawToken();
                result.add("Authorization", "Bearer " + token);
                LOGGER.fine("Token Bearer ajouté au header Authorization");
            } else {
                LOGGER.warning("Token JWT non disponible ou vide");
            }
        } catch (Exception e) {
            LOGGER.severe("Erreur lors de l'ajout du token Bearer: " + e.getMessage());
        }

        return result;
    }
}

Points clés:

  • @ApplicationScoped - Bean CDI singleton
  • @Inject JsonWebToken jwt - Injecte le token OIDC actuel
  • jwt.getRawToken() - Récupère le token brut (chaîne Base64)
  • Ajoute Authorization: Bearer {token} à chaque requête

2. Enregistrement sur tous les REST Clients

Ajout de l'annotation @RegisterClientHeaders(AuthHeaderFactory.class) sur chaque interface REST Client.

UserServiceClient

@Path("/api/users")
@RegisterRestClient(configKey = "lions-user-manager-api")
@RegisterClientHeaders(AuthHeaderFactory.class)  // ← AJOUTÉ
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public interface UserServiceClient {
    // ...
}

Autres REST Clients modifiés

  • RoleServiceClient.java:19
  • AuditServiceClient.java:20
  • SyncServiceClient.java:16

🧪 Test de la Solution

1. Recompiler le Frontend

cd lions-user-manager-client-quarkus-primefaces-freya
mvn compile

Résultat attendu: BUILD SUCCESS

2. Redémarrer le Frontend (si nécessaire)

Si le frontend ne recharge pas automatiquement les changements:

# Arrêter le frontend actuel (Ctrl+C)
mvn quarkus:dev

3. Test Complet

  1. Accéder à http://localhost:8080
  2. Se déconnecter (important pour obtenir un nouveau token)
  3. Se reconnecter avec testuser / test123
  4. Naviguer vers http://localhost:8080/pages/user-manager/users/list.xhtml
  5. Vérifier: La liste des utilisateurs se charge sans erreur 401

4. Vérification des Logs

Frontend - Token propagé

FINE Token Bearer ajouté au header Authorization

Backend - Token reçu et validé

DEBUG [io.qu.oi.ru.BearerAuthenticationMechanism] Token validation succeeded

Si vous voyez encore Bearer access token is not available → le token n'est toujours pas propagé (problème de contexte CDI ou hot reload).


📊 Comparaison Avant/Après

AVANT (avec bearer-token-propagation uniquement)

Frontend JSF Bean → REST Client → Backend
                         ↓
                    ❌ Pas de token
                         ↓
                   401 Unauthorized

APRÈS (avec AuthHeaderFactory)

Frontend JSF Bean → REST Client → AuthHeaderFactory
                                       ↓
                              @Inject JsonWebToken
                                       ↓
                          Authorization: Bearer {token}
                                       ↓
                                   Backend
                                       ↓
                              ✅ Token validé
                                       ↓
                               200 OK + Données

🔧 Architecture Technique

Flux d'exécution complet

  1. Utilisateur s'authentifie via OIDC (Keycloak)

    • PKCE flow avec S256
    • Redirection vers Keycloak → Retour avec code → Échange contre tokens
  2. Quarkus OIDC reçoit les tokens

    • access_token (contient realm_access.roles)
    • id_token (identité utilisateur)
    • refresh_token (renouvellement)
  3. Token injecté dans contexte CDI

    • JsonWebToken bean disponible via @Inject
    • Contient toutes les claims du token
  4. JSF Bean appelle REST Client

    • Ex: userServiceClient.searchUsers(criteria)
  5. AuthHeaderFactory intercepte l'appel

    • Méthode update() appelée avant l'envoi HTTP
    • Injecte Authorization: Bearer {access_token}
  6. Backend reçoit la requête

    • BearerAuthenticationMechanism extrait le token
    • Valide la signature JWT avec clé publique Keycloak
    • Extrait les rôles depuis realm_access.roles
    • Autorise l'accès si rôles suffisants
  7. Backend retourne les données

    • HTTP 200 OK + JSON response

🎯 Points Importants

Pourquoi JsonWebToken et pas d'autres solutions ?

  1. Native Quarkus - Fait partie du stack OIDC standard
  2. Thread-safe - Géré par CDI avec contexte de requête
  3. Type-safe - Interface fortement typée
  4. Validation automatique - Token déjà validé par Quarkus OIDC

Alternatives (non retenues)

  • SecurityContext - Ne contient pas le token brut
  • OidcSession - Trop couplé à la session HTTP
  • Header manuel dans chaque méthode - Code dupliqué et fragile
  • Filter JAX-RS - Plus complexe, moins naturel avec REST Client

Avantages de cette solution

  1. Automatique - Aucun code dans les beans JSF
  2. Centralisé - Une seule classe factory
  3. Réutilisable - Fonctionne pour tous les REST Clients
  4. Maintenable - Facile à déboguer et à tester
  5. Performant - Aucune copie du token, juste une référence

📝 Checklist de Validation

Après implémentation de cette solution:

Frontend

  • AuthHeaderFactory.java créé dans client/filter/
  • Tous les REST Clients annotés avec @RegisterClientHeaders
  • Compilation Maven réussie
  • Aucune erreur de hot reload

Runtime

  • Se déconnecter puis reconnecter pour obtenir nouveau token
  • Naviguer vers la liste des utilisateurs
  • Vérifier logs frontend: "Token Bearer ajouté au header Authorization"
  • Vérifier logs backend: "Token validation succeeded"
  • Liste des utilisateurs s'affiche sans erreur 401

Backend

  • Backend accepte les requêtes avec token
  • Rôles correctement extraits et appliqués
  • Pas de logs "Bearer access token is not available"

🐛 Troubleshooting

Problème: Token toujours pas propagé après changements

Cause: Hot reload Quarkus n'a pas détecté les changements de factory

Solution:

# Arrêter le frontend (Ctrl+C)
cd lions-user-manager-client-quarkus-primefaces-freya
mvn clean compile quarkus:dev

Problème: "Token JWT non disponible ou vide"

Cause: Contexte CDI ne trouve pas le JsonWebToken

Solution:

  1. Vérifier que l'utilisateur est authentifié
  2. Se déconnecter et reconnecter
  3. Vérifier logs OIDC: token doit être présent

Problème: 401 sur certaines pages mais pas d'autres

Cause: Chemins publics mal configurés

Solution: Vérifier application.properties:

quarkus.http.auth.permission.public.paths=/,/index.xhtml,...
quarkus.http.auth.permission.authenticated.paths=/pages/user-manager/*

📚 Références

Quarkus Documentation

Keycloak


Auteur: Claude Code Date: 2025-12-05 Version: 1.0.0