Hugo Fund Formation Platform16 Apr, 05:07 CET

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

REQUIRE_CF_ACCESS=true (fail-closed)

When REQUIRE_CF_ACCESS=true (set in wrangler.toml), the worker enforces a strict three-mode dispatch in src/middleware/auth.ts:

  1. Valid JWT: decode CF-Access-JWT-Assertion, look up the user in users table by email, set userRole from the DB row. The JWT path is the primary identity source in production.
  2. Service token only (CF-Access-Client-Id + CF-Access-Client-Secret, no JWT): the request is allowed through with userRole: '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.
  3. 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

Mock-user fallback (REQUIRE_CF_ACCESS=false)

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_id cookie selects the user from the users table (regex-capped at 64 chars)
  • If absent, defaults to u_alice with admin-equivalent access
  • This branch is gated on REQUIRE_CF_ACCESS !== 'true' — in production it never fires
  • See .dev.vars.example for the keys to set locally

Fund access control

User typeAccessEnforcement
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

  1. Request arrives at Cloudflare Access edge
  2. CF Access validates identity (human SSO or service token) and issues / passes through the JWT + service-token headers
  3. Hugo worker's authMiddleware reads CF-Access-JWT-Assertion first, falls back to service-token headers
  4. If REQUIRE_CF_ACCESS=true and neither is present: 401
  5. JWT path: extract email claim, look up users table via retry-wrapped DB, set userRole from DB row
  6. Service-token-only path: pass through as userRole: 'user', no admin promotion
  7. fundAccessMiddleware checks user_funds for route-specific fund access
  8. Admin-gated routes additionally check userRole === 'admin'
  9. 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.

Ctrl+K to open · ↑↓ navigate · Enter go · Esc close
Copied