Backend-Mandatory Auth + Sync Local Flow
Date: 2026-04-15
Scope: exact implementation flow for backend-authenticated frontend sessions, refresh rotation, sync replay, and local testing.
1) Exact implementation flow
1.1 Login (frontend → backend)
app/src/app/pages/LoginPage.tsxcallsAuthContext.login(identifier, password).- If
VITE_BACKEND_URLis set, frontend uses backend auth mode (app/src/modules/auth/backendAuth.ts). - Frontend sends
POST /api/v1/auth/loginwith{ email, password }.- If the user typed
admin,chamador,relatorios, orcadastro, frontend normalizes to:admin@neemias.localchamador@neemias.localrelatorios@neemias.localcadastro@neemias.local
- If the user typed
- Backend verifies password (
workers/src/modules/auth/password.ts) and creates auth session (workers/src/routes/auth.ts):- creates refresh token
- stores hashed refresh token in
auth_refresh_sessions - creates CSRF token
- signs short-lived access token
- Backend returns:
accessTokenaccessTokenExpiresAtrefreshToken(bearer mode) or httpOnly cookie (cookie mode)csrfTokenuserpayload
- Frontend persists session metadata in IndexedDB
sessionsand user metadata inusers(AuthContext.persistBackendSession()), then loads students from backend (fetchStudentsFromBackend).
1.2 Refresh-token rotation
- Frontend calls
POST /api/v1/auth/refreshwhen needed (hydration/retry path). - Backend validates refresh token hash in
auth_refresh_sessions. - Backend creates a new refresh/access pair and revokes old refresh session:
- sets
revoked_at,rotated_at,replaced_by
- sets
- Frontend replaces local session metadata with the new session artifacts.
1.3 Sync replay and auth bridge
- Offline queue drain (
OfflineContext) callssyncQueueEntryWithBackend()for each pending item. - Each request sends:
Authorization: Bearer <accessToken>X-Device-IdIdempotency-KeyX-CSRF-Tokenwhen available
- If backend returns
401:- frontend attempts refresh (
AuthContext.refreshAccessToken()) - if refresh fails, session is marked
EXPIRED - queue entries remain pending/retrying (not force-synced)
- frontend attempts refresh (
- Conflict metadata from backend
conflicts[]is written to local attendance events and surfaced in UI.
1.4 Cookie-mode CSRF behavior
When backend is configured with AUTH_SESSION_MODE=cookie:
- refresh token is stored in httpOnly cookie
- mutating routes enforce CSRF via
X-CSRF-Token - frontend includes credentials (
credentials: "include") for auth/sync requests
2) Local setup (backend mandatory)
2.1 Backend (terminal 1)
bash
cd /home/runner/work/neemias/neemias
pnpm installStart PostgreSQL locally (choose one):
- Option A (recommended): Docker Compose stack
bash
cd /home/runner/work/neemias/neemias/infra/compose
cp .env.example .env
docker compose up -d db- Option B: run your own local Postgres and set
DATABASE_URLaccordingly.
Run migrations and seed demo backend users:
bash
cd /home/runner/work/neemias/neemias
pnpm --dir backend migrate
pnpm --dir backend seed:demoRun backend:
bash
cd /home/runner/work/neemias/neemias
AUTH_MODE=dev AUTH_SESSION_MODE=bearer pnpm --dir backend dev2.2 Frontend (terminal 2)
Create app env:
bash
cd /home/runner/work/neemias/neemias/app
cp .env.example .envRun frontend:
bash
cd /home/runner/work/neemias/neemias
pnpm --dir app dev2.3 Demo login credentials (backend seed)
admin/senha123chamador/senha123relatorios/senha123cadastro/senha123
Equivalent backend emails:
admin@neemias.localchamador@neemias.localrelatorios@neemias.localcadastro@neemias.local
2.4 Connectivity checks (frontend must see backend)
- Backend health:
bash
curl http://localhost:3000/api/v1/health- Browser DevTools in frontend:
- login request should target
http://localhost:3000/api/v1/auth/login - sync request should target
http://localhost:3000/api/v1/sync/events
- login request should target
- If requests are missing:
- verify
app/.envhasVITE_BACKEND_URL=http://localhost:3000 - restart frontend dev server after editing
.env
- verify
3) Known boundary
PII encryption-at-rest lifecycle is now implemented for session secrets and sensitive IndexedDB payload fields using browser-native crypto envelopes, in-memory key clear on logout/expiry, and re-auth-triggered key rotation.