Step 3 (production-readiness): PII storage, validated reads, seams
Implement-now:
- G1: keep PII out of persistent storage — never persist BSN (only `naam`);
move both wizard drafts (address/email, work data) localStorage → sessionStorage
so they clear on tab close.
- G2: validate storage reads before trusting the cast — shape/tag guard in every
restore() (mirrors the parse* HTTP boundary); corrupt/foreign shape → start fresh.
- G3: already satisfied (debug-state redacts via mask.ts).
Show-the-seam (hook + doc, not fully built):
- G4: problemFieldErrors() maps a server validation envelope (ASP.NET
ValidationProblemDetails `errors`) to the field-keyed map the wizards already
render; returns {} until the backend sends it. +spec.
- G5: documented the retry/backoff seam at the adapter GET loader; reads may
retry, mutating submits never do.
Out of scope (named): unsaved-changes warning (persistence prevents data loss),
real auth/tokens, axe-core in CI.
Gate green: lint, check:tokens, build, test 79/79.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -5,11 +5,16 @@ import { DigidAdapter } from '../infrastructure/digid.adapter';
|
||||
|
||||
const STORAGE_KEY = 'session-v1';
|
||||
|
||||
/** Restore a persisted session (best-effort; corrupt entry → logged out). */
|
||||
/** Restore a persisted session (best-effort; corrupt entry → logged out).
|
||||
G2: validate the shape before trusting it. G1: the BSN is never persisted
|
||||
(see the effect below), so a restored session carries an empty one — it is
|
||||
unused after login; only `naam` is shown in the chrome. */
|
||||
function restore(): Session | null {
|
||||
try {
|
||||
const raw = sessionStorage.getItem(STORAGE_KEY);
|
||||
return raw ? (JSON.parse(raw) as Session) : null;
|
||||
if (!raw) return null;
|
||||
const parsed = JSON.parse(raw) as Partial<Session>;
|
||||
return typeof parsed?.naam === 'string' ? { bsn: '', naam: parsed.naam } : null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
@@ -35,7 +40,8 @@ export class SessionStore {
|
||||
constructor() {
|
||||
effect(() => {
|
||||
const s = this._session();
|
||||
if (s) sessionStorage.setItem(STORAGE_KEY, JSON.stringify(s));
|
||||
// G1: persist only `naam` — never write the BSN (national ID) to storage.
|
||||
if (s) sessionStorage.setItem(STORAGE_KEY, JSON.stringify({ naam: s.naam }));
|
||||
else sessionStorage.removeItem(STORAGE_KEY);
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user