Aujourd'hui j'ai passé la journée à corriger trois problèmes qui traînaient dans Nortrip Toolbox, mon SaaS B2B de back-office pour agences de voyage. Rien de glamour — de la sécurité, de la robustesse, et un peu d'automatisation. Voilà ce que ça donne.
Nortrip Toolbox vivait dans le navigateur de nos utilisateurs via localStorage. Ce qui veut dire que si un agent de voyage laissait son poste allumé au bureau, n'importe qui pouvait ouvrir l'onglet et avoir accès au back-office. Pas idéal pour un outil qui manipule des devis, des factures et des données clients.
La migration vers sessionStorage semble simple sur le papier. En pratique, c'est une cascade de changements : chaque lecture et écriture de token doit être revue, les hooks d'authentification aussi, et il faut gérer proprement la fermeture d'onglet.
// Avant — données persistantes même après fermeture navigateur
localStorage.setItem("auth_token", token)
// Après — scope limité à l'onglet, effacé à la fermeture
sessionStorage.setItem("auth_token", token)
J'ai ajouté deux mécanismes supplémentaires : un timeout d'inactivité à 2 heures (un addEventListener sur mousemove, keydown et click qui reset un timer), et une déconnexion immédiate dès qu'on quitte la route /admin. La session ne survit plus à la navigation hors du périmètre sécurisé.
Microsoft Azure AD (via MSAL.js) délivre des tokens OAuth qui expirent. Jusqu'ici, quand un token expirait pendant une session active, l'utilisateur voyait une erreur cryptique et devait recharger la page manuellement. Pas acceptable.
Le vrai problème était double. D'abord, une erreur InteractionRequiredAuthError non interceptée faisait planter silencieusement les appels API. Ensuite, un readyRef partagé entre composants était empoisonné : une fois passé à false suite à une erreur, il ne repassait jamais à true, bloquant toute tentative de refresh ultérieure.
// Le catch qui change tout
try {
const result = await msalInstance.acquireTokenSilent(request)
return result.accessToken
} catch (error) {
if (error instanceof InteractionRequiredAuthError) {
// Popup de reconnexion propre, pas d'erreur visible
const result = await msalInstance.acquireTokenPopup(request)
return result.accessToken
}
throw error
}
Maintenant, quand le token expire, une popup Microsoft s'ouvre automatiquement. L'utilisateur se reauthentifie en deux clics, et le travail continue sans perdre son contexte. Le readyRef a été isolé par composant pour éviter la contamination.
Keewe est notre outil de gestion des devises. Chaque mois, il génère des relevés PDF. Jusqu'ici, quelqu'un les téléchargeait, les renommait, et les glissait manuellement dans le bon dossier OneDrive. Trois clics minimum, fois douze mois, fois les oublis.
L'automatisation que j'ai construite fonctionne ainsi : l'agent dépose le PDF via un bouton dans Toolbox, une API Next.js reçoit le fichier, envoie le contenu à Claude AI (Anthropic) pour extraire la value_date, puis construit le chemin OneDrive cible :
NORTRIP/ADMINISTRATIF/Devises/KEEWE/{yyyy} Relevés Keewe/{yyyy}.{mm} Keewe/
Ensuite, Microsoft Graph API prend le relais pour l'upload :
await graphClient
.api(`/drives/${driveId}/root:/${targetPath}:/content`)
.put(fileBuffer)
Une fois le fichier déposé, l'interface affiche un bouton Valider avec une icône cloud verte — confirmation visuelle que tout est en ordre côté OneDrive. Plus de doute, plus de vérification manuelle.
Ces trois chantiers ne sont pas spectaculaires à présenter. Personne ne demande un SaaS qui gère bien l'expiration de ses tokens. Mais c'est exactement ce genre de travail qui fait la différence entre un outil qu'on utilise avec confiance et un outil qu'on surveille du coin de l'oeil.
Si vous construisez un back-office B2B et que vous vous demandez comment structurer l'authentification, l'intégration Microsoft ou l'automatisation documentaire, je fais des audits gratuits — on regarde ensemble ce qui coince.
Prêt à automatiser votre business ?
Audit gratuit 1h — on identifie ensemble les 3 automatisations les plus rentables pour vous.
Réserver l'audit gratuit