feat(auth): wallet partagé = seul mode + purge identifiant-wallet↔username
AccessGateScreen 3-branches (erreur config si pas de wallet partagé) ; renommage username→identifier de l'identité du wallet (registration, ngSession, hooks, steps auth) sans toucher UserProfile.username ; .env.example. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014GbGgNEHRejVKoREvFuDFg
This commit is contained in:
@@ -49,9 +49,9 @@ import { setCurrentUser } from '@ng-eventually/client/polyfill';
|
||||
|
||||
// Festipod localStorage key for the current identifier (same-partition
|
||||
// prefill/convenience only — never the cross-frontier carrier; that's the URL
|
||||
// param). Changed from the historical 'festipod.account.username' → any
|
||||
// pre-existing stored "logins" under the old key are dropped (acceptable: this
|
||||
// is a stopgap test env; the URL param carries identity anyway).
|
||||
// param). Renamed to `.identifier` from a historical key that mislabeled this
|
||||
// account id → any pre-existing stored logins under the old key are dropped
|
||||
// (acceptable: this is a stopgap test env; the URL param carries identity anyway).
|
||||
const STORAGE_KEY = 'festipod.account.identifier';
|
||||
|
||||
/** Name of the URL param that carries the identifier across the broker frontier. */
|
||||
|
||||
@@ -343,17 +343,17 @@ export async function readRegistrationNotifications(
|
||||
* just-written participation, so a second join checking only the reactive set would
|
||||
* write a duplicate. Querying the broker sees the real state regardless of read lag.
|
||||
*
|
||||
* Scoped to the CURRENT account (`username`) via `listMyEntityDocs` — a user's own
|
||||
* Scoped to the CURRENT account (`identifier`) via `listMyEntityDocs` — a user's own
|
||||
* participations live in their own account, so there is NO need to fan out over all
|
||||
* accounts (which would open/sync other accounts' unsynced docs → the ~75s hang).
|
||||
*/
|
||||
export async function countUserParticipations(
|
||||
username: string,
|
||||
identifier: string,
|
||||
eventId: string,
|
||||
userId: string,
|
||||
): Promise<number> {
|
||||
const sid = (await sessionPromise).session_id;
|
||||
const docs_ = await listMyEntityDocs(username, 'protected');
|
||||
const docs_ = await listMyEntityDocs(identifier, 'protected');
|
||||
let total = 0;
|
||||
for (const g of docs_) {
|
||||
total += await countParticipations(sid, g, eventId, userId).catch(() => 0);
|
||||
|
||||
@@ -22,7 +22,7 @@ setDefaultTimeout(90000);
|
||||
// account (whose shim key uses a sentinel prefix `normalizeIdentifier` can't emit).
|
||||
const RUN_NONCE = Date.now().toString(36) + Math.random().toString(36).slice(2, 6);
|
||||
let scenarioSeq = 0;
|
||||
function freshScenarioUsername(): string {
|
||||
function freshScenarioIdentifier(): string {
|
||||
scenarioSeq += 1;
|
||||
return `test-${RUN_NONCE}-${scenarioSeq}`;
|
||||
}
|
||||
@@ -580,7 +580,7 @@ Before({ timeout: 60000 }, async function (this: FestipodWorld, scenario) {
|
||||
// the run self-heals instead of cascading failures across the rest.
|
||||
this.page = await newWalletPageResilient();
|
||||
|
||||
// FRESH VIRTUAL WALLET per scenario (see freshScenarioUsername above). Set a
|
||||
// FRESH VIRTUAL WALLET per scenario (see freshScenarioIdentifier above). Set a
|
||||
// UNIQUE app-level identifier into localStorage['festipod.account.identifier']
|
||||
// on EVERY origin (the init script runs in each frame before its scripts do —
|
||||
// including the harness iframe on 127.0.0.1). At mount the harness's
|
||||
@@ -588,11 +588,11 @@ Before({ timeout: 60000 }, async function (this: FestipodWorld, scenario) {
|
||||
// login(DEFAULT_HARNESS_USER)` is skipped and the scenario runs on a fresh,
|
||||
// empty virtual wallet. Overwrites any value persisted in the Chromium profile
|
||||
// (init scripts run on each navigation), so no accumulated wallet leaks in.
|
||||
const freshUser = freshScenarioUsername();
|
||||
(this as any).freshUser = freshUser;
|
||||
const freshIdentifier = freshScenarioIdentifier();
|
||||
(this as any).freshIdentifier = freshIdentifier;
|
||||
await this.page.addInitScript((u: string) => {
|
||||
try { window.localStorage.setItem('festipod.account.identifier', u); } catch { /* opaque origin */ }
|
||||
}, freshUser);
|
||||
}, freshIdentifier);
|
||||
|
||||
// Capture console for debugging AND collect into the World so smoke
|
||||
// scenarios can assert no runtime error was emitted during the connected
|
||||
@@ -622,8 +622,8 @@ Before({ timeout: 60000 }, async function (this: FestipodWorld, scenario) {
|
||||
);
|
||||
|
||||
// NO per-scenario registry/wallet reset needed anymore (was T03.j
|
||||
// resetDataState). Each @data scenario now runs under a UNIQUE username
|
||||
// (freshScenarioUsername, set into localStorage above), so the shim hands it
|
||||
// resetDataState). Each @data scenario now runs under a UNIQUE identifier
|
||||
// (freshScenarioIdentifier, set into localStorage above), so the shim hands it
|
||||
// a FRESH, EMPTY virtual wallet whose account registry starts empty by
|
||||
// construction — nothing to purge. This also drops the ≤10s reset cost that
|
||||
// shared the Before hook's budget with the (slow) broker login.
|
||||
|
||||
@@ -82,7 +82,7 @@ export async function login() {
|
||||
* REAL NextGraph logout — stops the session of the SHARED wallet.
|
||||
*
|
||||
* STOPGAP: must stay HIDDEN (Settings/debug only). The everyday "Déconnexion"
|
||||
* is the FAUX one (AccountContext.logout, clears the username only). Calling
|
||||
* is the FAUX one (AccountContext.logout, clears the identifier only). Calling
|
||||
* this forces a new broker redirect on the next access — see
|
||||
* decision_2026-06-15_shared-wallet-login-flow.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user