Purpose
How the SPA acquires and stores a JWT to call /api/* endpoints.
Pieces
frontend/kundkort/hooks/useAuth.ts— token state, login, logoutfrontend/kundkort/components/LoginModal.tsx— email/password formfrontend/kundkort/app.tsx:98-100— gates the app:!tokenrenders<LoginModal>, otherwise renders<KundkortPage>or<SearchPage>
Token storage
sessionStorage key enrichnode_jwt (useAuth.ts:3). Token is read once on mount via useState initializer (useAuth.ts:17-28). Cleared on logout().
Login
POST /api/auth/login with { email, password }. On 401 the modal shows “Felaktigt e-post eller lösenord”; other errors fall through to “Inloggning misslyckades — försök igen” (useAuth.ts:39-44). Success reads data.token and stashes it.
DEV_MODE auto-login
Warning
useAuth.ts:6has a hard-codedconst DEV_MODE = true. When set, the hook synchronously stuffs'dev-token'intosessionStorageon first mount and skips the login modal entirely (useAuth.ts:22-26). The login form is unreachable until this flag is flipped tofalse. Production deploys must set it tofalse.
The backend separately exposes dev_mode via GET /api/config for the footer DEVMODE badge — this comes from KEYCLOAK_DEV_MODE env var (src/api/index.ts:115-124), independent of the frontend flag.
Token usage
Every fetch call sends Authorization: Bearer ${token}. See Kundkort API Client for the full set of endpoints.
Logout flow
onLogout in app.tsx:47-50 calls useAuth.logout() then navigates back to the search view, also clearing ?org= from the URL.
See also
Kundkort API Client, Kundkort Page, Search Page, Frontend Overview.