Authentication
Cloudflare Access JWT validation, mock-user fallback, and fund-level access control.Hugo is fronted by Cloudflare Access. Every request to hugo.nordiclawfirm.com must carry a valid CF-Access-JWT-Assertion header. The JWT payload's email field is used to identify the user.
Production mode
When REQUIRE_CF_ACCESS=true (set in wrangler.toml), the worker enforces a strict three-mode dispatch in src/middleware/auth.ts:
- Valid JWT: decode
CF-Access-JWT-Assertion, look up the user inuserstable by email, setuserRolefrom the DB row. The JWT path is the primary identity source in production. - Service token only (
CF-Access-Client-Id+CF-Access-Client-Secret, no JWT): the request is allowed through withuserRole: 'user',userId: null,userFullAccess: false. Service tokens do not auto-promote to admin — admin-gated routes will 403. Use a real human identity for admin operations. - Neither JWT nor service-token headers: returns 401 Unauthorized immediately. This is the fail-closed safety net — if CF Access is ever misconfigured (policy flipped, tunnel rerouted), the worker refuses the request rather than falling through to a default user.
Auth-relevant DB calls go through the retry-wrapped proxy (c.var.db if available, else retryableDB(c.env.DB)), so transient D1 errors on the first query of a request don't surface as 500s.
Local development
When REQUIRE_CF_ACCESS is not "true" (e.g. local wrangler dev with .dev.vars), the worker falls back to a mock-user system so a fresh D1 still boots:
- A
hugo_user_idcookie selects the user from theuserstable (regex-capped at 64 chars) - If absent, defaults to
u_alicewith admin-equivalent access - This branch is gated on
REQUIRE_CF_ACCESS !== 'true'— in production it never fires - See
.dev.vars.examplefor the keys to set locally
Fund access control
| User type | Access | Enforcement |
|---|---|---|
| Admin | All funds | Bypass fundAccessMiddleware |
| Full access | All funds (full_access = 1) |
Bypass fundAccessMiddleware |
| Standard user | Only granted funds | user_funds table check on all /funds/:fundId/* routes |
The fundAccessMiddleware enforces access control on all /funds/:fundId/* routes. It loads the user's fund grants from the user_funds table and returns 403 if the user does not have access to the requested fund.
Auth flow summary
- Request arrives at Cloudflare Access edge
- CF Access validates identity (human SSO or service token) and issues / passes through the JWT + service-token headers
- Hugo worker's
authMiddlewarereadsCF-Access-JWT-Assertionfirst, falls back to service-token headers - If
REQUIRE_CF_ACCESS=trueand neither is present: 401 - JWT path: extract
emailclaim, look upuserstable via retry-wrapped DB, setuserRolefrom DB row - Service-token-only path: pass through as
userRole: 'user', no admin promotion fundAccessMiddlewarechecksuser_fundsfor route-specific fund access- Admin-gated routes additionally check
userRole === 'admin' - Request proceeds or 403
Tested guarantees
tests/middleware/auth.test.ts covers each branch: 401 fail-closed, JWT happy path with D1 lookup, service-token passthrough (userId null, userRole user), JWT-but-no-row, malformed JWT, local-dev cookie/u_alice/admin fallthrough, and the c.var.db retry-wrapped DB usage.