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 Worker (terminal 1)
The backend is a Cloudflare Worker backed by D1 (SQLite). Local development uses wrangler dev in local mode — no PostgreSQL or Docker needed.
cd /home/runner/work/neemias/neemias
pnpm installApply D1 migrations to the local database:
pnpm db:migrate:localSeed demo users and data (idempotent — returns 409 ALREADY_SEEDED if already seeded):
pnpm db:seedRun the Worker locally with wrangler dev --local (local SQLite D1; AUTH_MODE comes from workers/.dev.vars):
pnpm dev:worker
# equivalent direct command:
cd workers && npx wrangler dev --local --port 87882.2 Frontend (terminal 2)
Create app env:
cd /home/runner/work/neemias/neemias/app
cp .env.example .envRun frontend:
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:
curl http://localhost:8788/api/v1/health- Browser DevTools in frontend:
- login request should target
http://localhost:8788/api/v1/auth/login - sync request should target
http://localhost:8788/api/v1/sync/events
- login request should target
- If requests are missing:
- verify
app/.envhasVITE_BACKEND_URL=http://localhost:8788 - 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.