Pièges classiques d'intégration
Les 10 erreurs les plus fréquentes lors de l'intégration d'une Wakapp : symptôme, cause et solution.
Pièges classiques d'intégration
Ce chapitre documente les erreurs observées en pratique lors de l'intégration de Wakapps tierces. Pour chaque piège : le symptôme observable, la cause racine et la solution.
1. « Aucun realm n'est associé à ce sous-domaine »
Symptôme : start-login retourne 404 not_found.
| Cause | Diagnostic | Solution |
|---|---|---|
| Extraction du subdomain incorrecte | Logs Discovery : host="app" au lieu de "app.test" | Implémenter extractSubdomain avec strip du suffix plateforme |
network.subdomain NULL ou différent en DB | SELECT subdomain FROM networks WHERE wid='...' | Mettre à jour le subdomain en DB |
appId hardcodé ne match pas app.client_id (auto-généré du WID) | Logs Discovery : appId="wakatest-app" mais DB a "7OWWAA" | Ne pas envoyer appId — Discovery résout depuis le host |
URL local (localhost) sans suffix | Logs : host="" | Implémenter un champ de saisie manuelle du subdomain en mode dev |
Fonction extractSubdomain correcte :
typescriptfunction extractSubdomain(hostname: string): string { const suffix = process.env.NEXT_PUBLIC_PLATFORM_DOMAIN_SUFFIX; if (suffix && hostname.endsWith(`.${suffix}`)) { return hostname.slice(0, -(suffix.length + 1)); } const labels = hostname.split("."); if (labels.length <= 2) return ""; return labels.slice(0, -2).join("."); } // "app.test.wakastart-dev.app" (suffix="wakastart-dev.app") → "app.test" // "localhost" → ""
2. « Invalid client or Invalid client credentials » au callback
Symptôme : Le handler /api/auth/callback POST retourne 401 avec ce message Keycloak.
| Cause | Diagnostic | Solution |
|---|---|---|
Client OIDC en mode Client authentication: ON | Console Keycloak → Client → Capability config | Basculer OFF (public client + PKCE) |
client_id incorrect dans l'échange token | sessionStorage login_client_id vaut un fallback ("wakastart") | Extraire client_id depuis la keycloakUrl AVANT le redirect |
redirect_uri ne correspond pas exactement | Console Keycloak → Client → Access settings | Ajouter l'URI exacte (sensible au trailing slash) |
code_verifier recalculé après redirect | Le verifier en sessionStorage a été effacé | Ne jamais effacer pkce_verifier avant le callback |
Pattern de stockage correct :
typescript// Stocker AVANT window.location.assign() const { keycloakUrl } = await startLogin(payload); const parsed = new URL(keycloakUrl); sessionStorage.setItem("login_realm", parsed.pathname.match(/\/realms\/([^/]+)\//)?.[1] ?? ""); sessionStorage.setItem("login_client_id", parsed.searchParams.get("client_id") ?? ""); // pkce_verifier déjà stocké juste après génération window.location.assign(keycloakUrl);
3. « Token enrichi manquant » sur tous les appels API
Symptôme : L'utilisateur est connecté (cookies existants, /me répond), mais tous les appels proxifiés vers les services downstream retournent 401 Token enrichi manquant ou invalide.
| Cause | Diagnostic | Solution |
|---|---|---|
Cookie wakastart_token absent | DevTools → Application → Cookies | L'appel /api/auth/enrich au callback a échoué — vérifier la réponse HTTP dans Network |
Claim organization absent du JWT Keycloak | Décoder le JWT — chercher "organization" ou "kc_org" | Activer Organizations sur le realm + attacher le scope organization au client |
Header x-enriched-token pas forwardé sur les appels proxifiés | Capturer la requête : header présent ? | Lire le cookie wakastart_token et l'ajouter dans x-enriched-token |
keycloak_id en DB ne correspond pas au sub Keycloak | Logs ws-serv-token : user not found for sub | Synchroniser les keycloak_id en DB (via backfill ou re-provisioning) |
| ws-serv-token en erreur Prisma | Logs du pod token : Unknown field 'keycloakOrgId' | Regénérer le client Prisma depuis ws-serv-config (npx prisma generate) |
4. Claim organization absent — dashboard vide
Symptôme : L'utilisateur se connecte sans erreur, mais le dashboard est vide de droits et /me retourne appRights: [], wakaRoles: [].
Cause : Le token Keycloak ne contient pas le claim organization requis par ws-serv-token. L'enrichissement retourne un token minimal sans droits métier.
Solution — 3 étapes obligatoires :
- Console Keycloak → realm → Realm settings → General → activer « Organizations enabled »
- Console Keycloak → realm → Clients →
{client_id}→ Client scopes → ajouterorganizationen Default - Exécuter
npx ts-node prisma/backfill-keycloak-orgs.tsdans ws-serv-config pour créer les organisations Keycloak et y rattacher les users.
Vérification : Décoder le token Keycloak après login. Il doit contenir :
json{ "organization": { "ACM001": {} } }
5. Cookie sameSite: strict bloque la session au retour de Keycloak
Symptôme : Après le redirect Keycloak vers /api/auth/callback, les cookies ne sont pas transmis. Le handler ne peut pas lire pkce_verifier ou les cookies de session. L'utilisateur est redirigé en boucle vers le login.
Cause : Les cookies avec sameSite: strict ne sont pas transmis lors d'une navigation cross-site (le redirect Keycloak → votre app est considéré cross-site).
Solution : Utiliser sameSite: lax pour tous les cookies du flow auth.
typescriptcookieStore.set("keycloak_token", token, { httpOnly: true, secure: true, sameSite: "lax", // ← pas "strict" ... });
6. Build Next.js du container échoue — EACCES sur .next/cache/images
Symptôme :
Error: EACCES: permission denied, mkdir '/app/.next/cache/images'
Cause : Le COPY .next ./.next dans le Dockerfile ne transfère pas les permissions correctes au user non-root du container.
Solution :
dockerfile# Dans le stage production du Dockerfile Wakapp FROM node:22-alpine AS runner RUN addgroup --system --gid 1001 nodejs && \ adduser --system --uid 1001 nextjs WORKDIR /app COPY /app/.next/standalone ./ COPY /app/.next/static ./.next/static # Créer le dossier de cache images avec les bonnes permissions RUN mkdir -p /app/.next/cache/images && chown -R nextjs:nodejs /app/.next/cache USER nextjs
7. Rate limit déclenché sur Discovery en intégration initiale
Symptôme : Après quelques tests rapides, le service Discovery répond 429 pour toutes les requêtes.
Cause : En phase d'intégration, les développeurs appellent start-login en boucle pour déboguer. La limite 5 req/s est atteinte rapidement.
Solution :
- Ne pas appeler
start-loginà chaque refresh de page — mémoriser lakeycloakUrlsi l'utilisateur n'est pas connecté. - En intégration locale, utiliser un mock du service Discovery.
- Attendre l'expiration de la fenêtre (
Retry-After) avant de retenter.
8. redirect_uri avec trailing slash
Symptôme : 400 invalid_redirect_uri lors de l'échange token avec Keycloak.
Cause : Keycloak compare l'URI de callback exactement (sensible au / final et au casing).
| Configuré dans Keycloak | Envoyé au token endpoint | Résultat |
|---|---|---|
https://app.test/callback | https://app.test/callback/ | invalid_redirect_uri |
https://app.test/callback/ | https://app.test/callback | invalid_redirect_uri |
https://app.test/callback | https://app.test/callback | OK |
Solution : Normaliser l'URI de callback (supprimer le trailing slash) et s'assurer que la valeur est identique dans start-login, dans le handler callback, et dans la console Keycloak.
typescriptconst REDIRECT_URI = process.env.AUTH_CALLBACK_URL!.replace(/\/$/, ""); // Trim trailing slash
9. appRights vides malgré un profil assigné
Symptôme : GET /me retourne appRights: [] mais l'utilisateur a bien un profil assigné visible dans l'UI admin.
Causes et diagnostics :
| Cause | Diagnostic | Solution |
|---|---|---|
Le profil existe en DB mais aucun AppRight n'y est associé | Admin → Profils → vérifier les droits du profil | Assigner des droits au profil |
| Le profil est assigné sur un autre Customer | Vérifier profile.customer_id vs user.customer_id | Les profils sont scopés par customer — créer un profil dans le bon customer |
Cache /me stale côté front | Le context React n'a pas été invalidé après modification | Appeler auth.refresh() ou re-logger l'utilisateur |
10. Email avec majuscules cause un 404 not_found Discovery
Symptôme : Lookup par email retourne { users: [] } pour John@Acme.com alors que l'utilisateur existe avec john@acme.com.
Cause : Discovery normalise les emails en lowercase avant la recherche. L'email envoyé doit déjà être normalisé côté client.
Solution :
typescript// Normaliser l'email AVANT d'appeler Discovery const normalizedEmail = email.toLowerCase().trim(); const result = await discoverByEmail(normalizedEmail);
Récapitulatif — Checklist de débogage
Quand ça ne fonctionne pas, dans l'ordre :
start-loginretourne404→ vérifierextractSubdomain, vérifiernetwork.subdomainen DBInvalid clientau callback →Client authentication: OFFdans Keycloak ?client_idextrait depuiskeycloakUrl?wakastart_tokenabsent → l'enricha-t-il réussi ? Décoder le JWT Keycloak : claimorganizationprésent ?401 Token enrichi manquant→ headerx-enriched-tokenforwardé par le proxy ?appRights: []→ profil assigné ET droits configurés sur le profil ?403→ droit exact correspondant à l'opération ? Feature activée en cascade ?429→ respecterRetry-After, implémenter backoff exponentiel
Aller plus loin
- Erreurs courantes : codes HTTP et messages exacts
- Authentification : flow complet avec code
- Référence endpoints : checklist d'intégration complète