fix(auth): persist session in localStorage so login survives the language switch

The language switch is a full-page navigation to a separate bundle (nl at /, en at
/en/); sessionStorage's per-tab semantics dropped the login across it. localStorage
is unambiguously shared same-origin and survives the hard navigation. Keeps G1 (naam
only, never the BSN). Trade-off: the demo session now survives tab close — a real
portal keeps auth in an httpOnly cookie/token.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
eho
2026-07-23 22:18:33 +02:00
co-authored by Claude Opus 4.8
parent c00e607b8f
commit ed264be714
+9 -7
View File
@@ -11,7 +11,7 @@ const STORAGE_KEY = 'session-v1';
unused after login; only `naam` is shown in the chrome. */
function restore(): Session | null {
try {
const raw = sessionStorage.getItem(STORAGE_KEY);
const raw = localStorage.getItem(STORAGE_KEY);
if (!raw) return null;
const parsed = JSON.parse(raw) as Partial<Session>;
return typeof parsed?.naam === 'string' ? { bsn: '', naam: parsed.naam } : null;
@@ -24,10 +24,12 @@ function restore(): Session | null {
* Holds the current session for the whole app. Because it is providedIn:'root'
* there is exactly one instance — every component that injects it sees the same
* session signal, so logging in is instantly visible everywhere (the guard, the
* header, etc.). The session is mirrored to sessionStorage so a refresh or a
* deep-link to a protected route keeps you logged in; it clears when the tab
* closes. ponytail: sessionStorage, not localStorage — no cross-tab sync, which
* matches a single-session portal.
* header, etc.). The session is mirrored to localStorage so a refresh, a deep-link,
* or the full-page navigation the language switch performs (nl at `/` ⇄ en at `/en/`,
* separate bundles) keeps you logged in. ponytail: localStorage, not sessionStorage —
* sessionStorage's per-tab clearing dropped the login on the cross-bundle language
* switch. Trade-off: the demo session now survives tab close; a real portal keeps auth
* in an httpOnly cookie/token, not web storage.
*/
@Injectable({ providedIn: 'root' })
export class SessionStore {
@@ -41,8 +43,8 @@ export class SessionStore {
effect(() => {
const s = this._session();
// 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);
if (s) localStorage.setItem(STORAGE_KEY, JSON.stringify({ naam: s.naam }));
else localStorage.removeItem(STORAGE_KEY);
});
}