From 808586e615103584ea5a1aa93339ec255953358d Mon Sep 17 00:00:00 2001 From: Arthur Ria Date: Mon, 24 Aug 2026 17:36:26 +0200 Subject: [PATCH] =?UTF-8?q?L3.2=20:=20fait=20remonter=20statut=20et=20corp?= =?UTF-8?q?s=20de=20la=20r=C3=A9ponse=20STS=20dans=20les=20erreurs=20d'aut?= =?UTF-8?q?h?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit authenticate() ré-enveloppait l'erreur axios en "Authentication failed: Request failed with status code 400" et jetait le corps de la réponse du STS, qui contenait le diagnostic exact ({"error":"invalid_request", "error_description":"Tenant not found"} sur le profil AD). Réutilise _enrichHttpError (L1.1) dans authenticate() et refreshOAuthToken(), avec payload volontairement omis : il contient les credentials (password grant) ou le refresh_token ; l'helper n'inclut jamais les headers. Le chemin "token trop vieux -> password grant" de refreshOAuthToken() sort du try : un échec d'authenticate() y était rattrapé pour... rappeler authenticate() à l'identique ; il propage désormais son erreur enrichie directement. Mesure (via le protocole) : switch_wms_profile("AD") puis count_wms_entities("Products") -> Count failed for Products: Authentication failed: POST https://10.255.255.2/EasySTS/OAuth/Token failed (HTTP 400): Request failed with status code 400 Response body: {"error":"invalid_request","error_description":"Tenant not found"} Aucun credential ni header dans la réponse (vérifié : pas de password/Basic/Bearer/payload). ROADMAP : L3.2 retirée, point ouvert "profil AD" mis à jour. Co-Authored-By: Claude Fable 5 --- ROADMAP.md | 20 +++----------------- src/services/api-service.js | 27 +++++++++++++++++---------- 2 files changed, 20 insertions(+), 27 deletions(-) diff --git a/ROADMAP.md b/ROADMAP.md index 97a5bf8..31baf83 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -26,21 +26,6 @@ garde-fous d'arguments manquants) est livré — voir **D21** et **D23**. - Renvoyer `truncated: true` explicitement plutôt que de laisser le client se faire rejeter. -### L3.2 — Erreurs d'authentification muettes - -`authenticate()` (`api-service.js`) ré-enveloppe l'erreur axios en -`new Error('Authentication failed: ' + error.message)` : le **corps de la -réponse du STS est perdu**, alors qu'il contient le diagnostic complet. - -Preuve (24/08/2026, profil `AD`) : le smoke test affiche seulement -`Authentication failed: Request failed with status code 400` ; en rejouant la -même requête à la main, le corps était -`{"error":"invalid_request","error_description":"Tenant not found"}` — le -diagnostic exact, invisible depuis les outils comme depuis le smoke test. - -Faire remonter `error.response.data` dans le message, comme L1.1 l'a fait pour -les outils. Même contrainte : ne jamais logguer les credentials. - --- ## Lot 4 — Modèle de données et applications @@ -197,8 +182,9 @@ plus riche que `/AD/api/Application/GetAll`), `GET /healthcheck?tenantCode=` et le tenant `AD`. L'hôte et le STS fonctionnent (LIMAGRAIN, même hôte, passe 4/4) : c'est la valeur `AD_TENANT` du `.env` qui ne correspond plus à un tenant existant. Correction côté propriétaire du dépôt (mettre à jour ou - retirer le profil) — pas un bug du code. Le message opaque du smoke test est - traité à part (L3.2). + retirer le profil) — pas un bug du code. Depuis L3.2, le corps de la réponse + du STS (`Tenant not found`) remonte dans les erreurs d'outils et du smoke + test. - **`select_expression`** : les projections via le paramètre `Select` provoquent des erreurs de compilation côté serveur (D13). Irritant principal restant. - **Déploiement SSH sur la VM** : l'exécutable est validé, la configuration SSH diff --git a/src/services/api-service.js b/src/services/api-service.js index adf9912..bf45e1b 100644 --- a/src/services/api-service.js +++ b/src/services/api-service.js @@ -77,8 +77,12 @@ class APIService { console.error(`[API] Authentication successful. Token expires in ~${this.tokenMaxAge}s`); return this.token; } catch (error) { - console.error('[API] Authentication failed:', error.message); - throw new Error(`Authentication failed: ${error.message}`); + // Statut + corps de la réponse STS dans le message : c'est là que vit le + // diagnostic ("Tenant not found", ...). Payload volontairement omis — il + // contient les credentials ; _enrichHttpError n'inclut jamais les headers. + const enriched = this._enrichHttpError(error, 'POST', profile.tokenUrl, undefined); + console.error('[API] Authentication failed:', enriched.message); + throw new Error(`Authentication failed: ${enriched.message}`); } } @@ -88,17 +92,17 @@ class APIService { async refreshOAuthToken() { const tokenAge = this.getTokenAge(); - try { - // If token is too old (>= maxAge), use password grant - if (tokenAge >= this.tokenMaxAge) { - console.error('[API] Token too old, re-authenticating with password...'); - return await this.authenticate(); - } + // If token is too old (>= maxAge), use password grant + if (tokenAge >= this.tokenMaxAge) { + console.error('[API] Token too old, re-authenticating with password...'); + return await this.authenticate(); + } + const profile = profileManager.getCurrent(); + try { // Otherwise use refresh_token grant console.error('[API] Refreshing token with refresh_token grant...'); - const profile = profileManager.getCurrent(); const response = await this.httpClient.post( profile.tokenUrl, new URLSearchParams({ @@ -120,7 +124,10 @@ class APIService { console.error('[API] Token refreshed successfully'); return this.token; } catch (error) { - console.error('[API] Token refresh failed, re-authenticating:', error.message); + // Même enrichissement que authenticate() : statut + corps STS, sans le + // payload (refresh_token) ni les headers. + const enriched = this._enrichHttpError(error, 'POST', profile.tokenUrl, undefined); + console.error('[API] Token refresh failed, re-authenticating:', enriched.message); return await this.authenticate(); } }