Lot 1, séquence B2 — Coquille du back-office
1er octobre 2026. Mise en œuvre de la séquence B2 du plan du Lot 1. Décisions : EC-079, EC-080. Routes lues : routes du back-office.
- Le back-office (
apps/backoffice) se connecte à Keycloak, affiche la navigation du design system, une campagne active et la file « À traiter ». - Le second facteur est imposé par Keycloak (rôle
second_facteur_exige), pas par l’application. - Contrat d’API 1.1.0 : le champ
bloquantest ajouté àControle(additif).
Connexion
Section intitulée « Connexion »Flux « code d’autorisation + PKCE (S256) », écrit sans dépendance (src/auth/). Client Keycloak public biotrace-backoffice.
| Élément | Choix |
|---|---|
| Jetons | En mémoire seulement. Un rechargement de page repasse par Keycloak ; la session SSO rend l’aller-retour invisible. |
| Traversée de la redirection | code_verifier, state et page demandée, dans sessionStorage, effacés à la première lecture. |
| Renouvellement | À moins de 30 s de l’expiration, un seul renouvellement à la fois. |
| Compte non rattaché | La réponse 403 de GET /v1/moi donne un écran qui dit quoi faire. |
Variables (apps/backoffice/.env.example) : VITE_KEYCLOAK_URL, VITE_REALM, VITE_API_URL (vide : même origine). En développement, /v1 est relayé vers l’API (API_URL, par défaut http://localhost:3000).
Navigation
Section intitulée « Navigation »Six sections du design system §7 (src/mise-en-page/navigation.ts). Une entrée sans page est grisée, aria-disabled, sans lien. Une entrée dont le droit manque est masquée. Seule « À traiter » (review.resolve) est livrée ; les écrans de référentiels arrivent en B3.
Campagne active
Section intitulée « Campagne active »Dans l’URL (?campagne=code), sinon le dernier choix de ce navigateur, sinon la plus récente campagne ouverte, sinon la plus récente. Le stockage local n’est qu’un confort. Dates en JJ/MM/AAAA par formaterDate de @biotrace/i18n.
« À traiter »
Section intitulée « « À traiter » »Liste de GET /v1/controles, bloquants d’abord puis du plus ancien au plus récent ; détail du contrôle ; levée par POST /v1/controles/{id}/lever avec une décision parmi decisions_possibles et un motif écrit obligatoire. Les erreurs (levee_par_auteur, controle_deja_leve, decision_non_permise) sont traduites par messageErreur ; un 409 actualise la file.
bloquant est calculé par controle.effetBloquant (packages/domain, VT-CTRL-014 à 017) : voir EC-079.
pnpm --filter @biotrace/backoffice test : B2-01 à B2-06 (PKCE contre le vecteur de la RFC 7636, state, échange du code, renouvellement, configuration, navigation, choix de la campagne).
Pas encore essayé de bout en bout avec un compte réel sur Keycloak (à faire à la recette) ; pas de test de composants (Vitest en mode navigateur : ADR à écrire).