fix(auth): préremplir l'identifiant à la barrière — plus de re-saisie à l'arrivée

Symptôme (vraie app) : au retour dans Festipod, la barrière redemandait un
identifiant NU et VIDE alors qu'il était déjà choisi/stocké.

Cause racine (pas une perte de localStorage — l'identifiant survit au round-trip) :
au rechargement, AccountProvider restaure `username` depuis le store, mais
NextGraphContext repart en `disconnected`, donc AuthGate réaffiche la barrière ;
et AccessGateScreen initialisait son champ à useState('') → vide malgré le stocké.

Fix : AuthGate passe `initialIdentifier={username}` ; AccessGateScreen préremplit
le champ. L'identifiant est saisi UNE FOIS au premier accès, persisté, puis
prérempli au retour — jamais retapé.

Test garde-fou @ui (barriere-acces-identifiant.feature) : prérempli / vide au
premier accès / Entrer remonte la valeur. Rouge si on remet useState(''). Utile
car le flux de barrière est désactivé en @e2e (__FESTIPOD_ACCESS_GATE_DISABLED__),
donc invisible à cette couche. renderElement() ajouté au harness @ui pour rendre
un composant prop-driven hors registre/providers.

Doctrine: app-security/knowledge_authentication documente la saisie-unique + prérempli.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Sylvain Duchesne
2026-07-13 11:13:50 +02:00
parent 7302936502
commit 13da2d9e03
6 changed files with 182 additions and 3 deletions
+31
View File
@@ -104,6 +104,37 @@ export async function renderScreen(screenId: string, path?: string): Promise<Doc
return doc;
}
/**
* Render an arbitrary React element (a prop-driven component) into the same
* happy-dom container. Unlike {@link renderScreen}, this does NOT go through the
* screen registry or wrap in the data/router providers — use it for standalone,
* prop-driven components such as the AccessGateScreen (the access barrier), which
* take their state as props rather than reading it from context. Returns the
* rendered document for DOM assertions.
*/
export async function renderElement(element: React.ReactElement): Promise<Document> {
await ensureDomGlobals();
if (!window) throw new Error('DOM globals not installed');
if (root) {
root.unmount();
root = null;
}
const doc = window.document as unknown as Document;
doc.body.innerHTML = '<div id="root"></div>';
const container = doc.getElementById('root')!;
root = createRoot(container);
await new Promise<void>((resolve) => {
root.render(element);
setTimeout(resolve, 0);
});
return doc;
}
/**
* Convert a registry path with `:id` placeholders to a concrete URL using the
* first seed event/user when applicable. Tests can override via the explicit