/** * The AUTH-HINT COOKIE -, ยง1c item 20. * * WHY IT EXISTS: the portal's frame keys off AUTHENTICATION STATE (logged out you are on the * practice's website and keep their header and menu; logged in you are in the app and get ours). * The session itself is a Cognito token in `localStorage`, which is invisible to PHP - so without a * server-readable hint, WordPress cannot know which frame to render until React has booted. * * THAT DELAY IS THE WHOLE POINT. Deciding the frame in JavaScript means the theme's header paints * first and is then removed - a visible chrome FLASH on every page load, on the one page a practice * sends patients to. The hint lets PHP choose before the first byte of markup. * * IT CARRIES NO SECRET AND NO PHI. It is the single character `1`. It says only "a session most * likely exists" - never who, never which org, never a token. * It is therefore NOT an authorisation signal and must never be treated as one. It decides * PRESENTATION only. Every actual authorisation still happens at the API against a real token; a * forged cookie buys an attacker a differently-shaped empty page and nothing else. * Deliberately NOT `httpOnly` - JavaScript has to write it, since JavaScript is what knows. */ export const AUTH_HINT_COOKIE = 'akashere_authed'; /** * `SameSite=Lax` rather than `Strict`: a patient arriving from an emailed appointment link is a * cross-site navigation, and under `Strict` the cookie would not be sent on that first request - * so the one arrival that matters most would flash the chrome anyway. * * `Secure` is set only on HTTPS so local development over plain HTTP still works; a browser silently * DROPS a `Secure` cookie on an insecure origin, which would look like the feature simply not working. */ const attributes = (maxAgeSeconds: number): string => { const secure = typeof location !== 'undefined' && location.protocol === 'https:' ? '; Secure' : ''; return `; Path=/; Max-Age=${maxAgeSeconds}; SameSite=Lax${secure}`; }; /** * The class the frame CSS is gated on -. * * THE COOKIE ALONE WAS NOT ENOUGH, and the way it failed is the point. PHP decided whether to print * the frame CSS AT ALL, from a cookie read when the page rendered. So the decision was made once, before * the browser had done anything, and there was no way back from a wrong answer: * - signing in without a reload left the theme's header on screen, because nothing re-ran; * - and if the cookie was missing or stale, the chrome survived a FULL RELOAD too - claude-4 observed * exactly that on the signed-in staff portal, which is why this card's original title ("until a * refresh") described the trigger rather than the remedy. * * NOW PHP ALWAYS PRINTS THE CSS ON A PORTAL PAGE and gates every rule on this class, which it also * stamps server-side when the cookie says so. the cookie keeps its whole original job - no chrome * FLASH before first paint - while the app can correct it either way, immediately, with no reload. * Still not detect-and-hide archaeology: the selectors are unchanged, only what turns them on. */ export const AUTH_FRAME_CLASS = 'akashere-authed'; const applyFrameClass = (authed: boolean): void => { try { document.body.classList.toggle(AUTH_FRAME_CLASS, authed); } catch { /* no document โ€” the server-rendered state stands, which is the safe default */ } }; export const setAuthHint = (): void => { applyFrameClass(true); try { // 12 hours - comfortably longer than a session, short enough that an abandoned browser stops // claiming a session that has long expired. A stale hint costs a wrong frame, never access. document.cookie = `${AUTH_HINT_COOKIE}=1${attributes(60 * 60 * 12)}`; } catch { /* cookies disabled โ€” the frame falls back to the logged-out shape, which is the safe default */ } }; export const clearAuthHint = (): void => { // THE SYMMETRIC HALF, and only one direction was ever reported. Signing out must hand the visitor // back to the practice's own header just as cleanly - otherwise they land on a login page inside our // frame, which is the "signed out into a room with no doors" state the auth-state rule exists to // prevent. applyFrameClass(false); try { document.cookie = `${AUTH_HINT_COOKIE}=${attributes(0)}`; } catch { /* nothing to do โ€” see above */ } }; /** * Bring the frame into line with the session that actually exists, at boot. * * THIS IS WHAT CLOSES THE SURVIVES-A-RELOAD CASE. `setAuthHint` runs when a session BEGINS; a reload * with a session already in `localStorage` never calls it, so a cookie that was missing, expired or * dropped stayed missing and the theme's chrome stayed on screen through every reload. * * It re-writes the cookie as well as the class, so the NEXT load is served the suppressed markup * server-side and gets no flash - the wrong state repairs itself once rather than on every page. */ export const syncAuthFrame = (hasSession: boolean): void => { if (hasSession) { setAuthHint(); return; } // Only clears when there is genuinely no session. Clearing on an indeterminate boot would flash // the practice's header onto a signed-in app on every single load. if (hasAuthHint()) clearAuthHint(); }; export const hasAuthHint = (): boolean => typeof document !== 'undefined' && document.cookie.split(';').some((c) => c.trim().startsWith(`${AUTH_HINT_COOKIE}=1`));