This document describes the authentication and authorization model for the internal delivery note browser.
The system uses:
branch, admin, dev).NOTE: This document is a living document. As we extend the auth system (sessions, routes, policies, password flows), we will update this file.
The main goals of the authentication and authorization system are:
This document covers:
Auth depends on the following environment variables:
SESSION_SECRET (required)
Auth endpoints also require DB connectivity:
MONGODB_URI (required)SESSION_COOKIE_SECURE (optional)
Secure cookie flag.true or false.Default behavior:
Secure cookie is enabled when NODE_ENV === "production".Local HTTP testing (e.g. http://localhost:3000 with Docker + next start):
SESSION_COOKIE_SECURE=false in your local .env.docker.Staging/Production:
SESSION_COOKIE_SECURE unset (or true) and run the app behind HTTPS.The repo provides centralized env validation:
lib/config/validateEnv.js validates required env vars and basic sanity checks.scripts/validate-env.mjs runs validation against process.env.In Docker, run validation before starting the server:
node scripts/validate-env.mjs && npm run start
branchbranchId (e.g. "NL01").Intended access pattern:
adminbranchId = null).Intended access pattern:
devbranchId = null).Intended access pattern:
RBAC is enforced on branch-related filesystem APIs.
401 Unauthorized: no valid session (getSession() returns null).
{ "error": "Unauthorized" }
403 Forbidden: session exists but the user is not allowed to access the requested branch.
{ "error": "Forbidden" }
RBAC rules live in lib/auth/permissions.js:
canAccessBranch(session, branchId)filterBranchesForSession(session, branchIds)These endpoints require a valid session:
GET /api/branchesGET /api/branches/[branch]/yearsGET /api/branches/[branch]/[year]/monthsGET /api/branches/[branch]/[year]/[month]/daysGET /api/files?branch=&year=&month=&day=Sessions are implemented as signed JWTs stored in HTTP-only cookies.
{
"userId": "<MongoDB ObjectId as string>",
"role": "branch | admin | dev",
"branchId": "NL01 | null",
"iat": 1700000000,
"exp": 1700003600
}
HS256.SESSION_SECRET.SESSION_MAX_AGE_SECONDS = 8 hours.Cookie name: auth_session
Attributes:
httpOnly: truesecure: resolved via NODE_ENV + optional SESSION_COOKIE_SECURE overridesameSite: "lax"path: "/"maxAge: 8 hoursImplementation lives in lib/auth/session.js:
createSession({ userId, role, branchId })getSession()destroySession()POST /api/auth/loginAuthenticate a user and set the session cookie.
Responses:
200 { "ok": true }400 { "error": "Invalid request body" }400 { "error": "Missing username or password" }401 { "error": "Invalid credentials" }500 { "error": "Internal server error" }GET /api/auth/logoutClears the session cookie.
200 { "ok": true } on success.SESSION_SECRET secret and rotate when needed.SESSION_COOKIE_SECURE=false.