Migration complète vers PrimeFaces Freya - Corrections des incompatibilités et intégration de primefaces-freya-extension

This commit is contained in:
lionsdev
2025-12-27 00:18:31 +00:00
parent 5e272a8256
commit 5c996931a6
206 changed files with 36646 additions and 1593 deletions

View File

@@ -0,0 +1,301 @@
# 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)
```properties
# 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`
```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
```java
@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
```bash
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:
```bash
# 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
- [x] `AuthHeaderFactory.java` créé dans `client/filter/`
- [x] Tous les REST Clients annotés avec `@RegisterClientHeaders`
- [x] Compilation Maven réussie
- [x] 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**:
```bash
# 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`:
```properties
quarkus.http.auth.permission.public.paths=/,/index.xhtml,...
quarkus.http.auth.permission.authenticated.paths=/pages/user-manager/*
```
---
## 📚 Références
### Quarkus Documentation
- [Quarkus OIDC Token Propagation](https://quarkus.io/guides/security-openid-connect-client-reference#token-propagation)
- [Quarkus REST Client](https://quarkus.io/guides/rest-client)
- [ClientHeadersFactory](https://download.eclipse.org/microprofile/microprofile-rest-client-2.0/microprofile-rest-client-spec-2.0.html#_clientheadersfactory)
### Keycloak
- [JWT Token Structure](https://www.keycloak.org/docs/latest/securing_apps/#_token-exchange)
- [Realm Roles vs Client Roles](https://www.keycloak.org/docs/latest/server_admin/#realm-roles)
---
**Auteur**: Claude Code
**Date**: 2025-12-05
**Version**: 1.0.0