Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 | 12x 82x 82x 1x 12x 22x 22x 12x 54x 54x 12x 12x 49x 13x 12x 11x 11x 6x 6x 6x 5x 12x 11x 11x 6x 6x | /**
* BFF Session Module — ADR-001 Phase 2 (httpOnly-cookie auth).
*
* After Phase 2 the access token lives in an HttpOnly cookie the SPA cannot read,
* so identity comes from the server. `fetchSession()` probes `GET /v1/auth/me`
* (the cookie rides along via `withCredentials`); a 200 means authenticated.
*
* The user's ROLE is not a secret — every backend action is authorized
* server-side independently — so we cache the server-verified role as a
* non-sensitive UI hint in `localStorage.role` for synchronous route gating.
* The access token itself is NEVER stored client-side anymore (closes H1).
*
* Developer: Allan Ninal
*/
import axios from "axios";
import { API_ENDPOINTS } from "@/api/endpoints";
export const getStoredRole = () => {
try {
return localStorage.getItem("role");
} catch {
return null;
}
};
export const setStoredRole = (role) => {
try {
if (role) localStorage.setItem("role", role);
} catch {
/* ignore */
}
};
export const clearStoredRole = () => {
try {
localStorage.removeItem("role");
} catch {
/* ignore */
}
};
// CSRF token store (in-memory). The csrf_token cookie is host-only on the API
// host, which the SPA cannot read via document.cookie when the SPA and API run
// on different hosts (frontend www / api apex; the staff app is fully
// cross-domain). So the backend returns the token in the login / refresh / me
// response BODIES and we keep it here; the axios interceptor echoes it as the
// X-CSRF-Token header. The backend still double-submits it against the cookie.
let _csrfToken = null;
export const setCsrfToken = (token) => {
_csrfToken = token || null;
};
export const getCsrfToken = () => _csrfToken;
/**
* Server-backed session probe. Resolves to { isAuthenticated, role, user }.
* Never throws — a failed/401 probe resolves to an unauthenticated session.
*/
export const fetchSession = async () => {
try {
const { data } = await axios.get(API_ENDPOINTS.auth.me, { withCredentials: true });
if (data?.role) setStoredRole(data.role);
if (data?.csrf_token) setCsrfToken(data.csrf_token);
return { isAuthenticated: true, role: data?.role ?? null, user: data };
} catch {
return { isAuthenticated: false, role: null, user: null };
}
};
/**
* BFF logout — ADR-001 Phase 4. Calls POST /v1/auth/logout so the backend
* revokes the refresh token at Auth0 and clears all session cookies (fixes C3).
* Best-effort: a revoke/network failure never blocks the local sign-out, which
* the caller still performs (Auth0 SDK local logout + Redux + navigation).
*/
export const logoutSession = async () => {
try {
await axios.post(API_ENDPOINTS.auth.logout, {}, { withCredentials: true });
} catch {
/* best-effort — proceed with local logout regardless */
}
clearStoredRole();
setCsrfToken(null);
};
|