Skip to main content
Use this page when the UI loads but an API fails, Google sign-in bounces to /login, or two people can edit the same step. Related: Errors and rate limits, Environment variables, Role preview.

FAQ

All of these must be true:
  1. Email ends with @schools.nyc.gov.
  2. A users document exists for that lowercase email.
  3. isActive is not false.
  4. Google OAuth redirect URI matches the origin (/api/auth/callback/google).
  5. NEXTAUTH_URL is that same origin (http://localhost:3000 locally).
Seed with npm run seed-users. Demo mailboxes (d79.demo.*) cannot Google-sign-in — preview them from a Super Admin session.
The form GET allows a level 4 principal at the same school. Presence used to omit level 4 unless principalEmail matched.Current rule: same-school levels 2, 3, and 4 may read editors/locks. Assistant Principals still need assignment to edit answers.If you preview as Principal on a plan owned by someone else at that school, editors should return 200. If you preview as AP on a plan you were never assigned, expect 403 on write.
Intended. Level 3 is a collaborator. The principal (or Super Admin) must share/assign the plan (User.assignedForms or sharedWithEmails). Same school is not enough. See Roles and access.
Logout adds sess:deny:{jti} in Redis. Without REDIS_URL, the JWT cookie remains valid until the 8-hour maxAge. Set Redis in production.
Auth POSTs are limited to 20 / 60s per IP. User mutations and form create are limited per user. In production, if Redis is down those limits fail closed (you get 429 instead of unlimited traffic). Check /admin/system → Redis, then Retry Redis.
Step locks last 5 minutes (form:{id}:step:{stepKey}). Presence heartbeats last 60s. Two serverless instances without Redis cannot see each other’s locks — configure REDIS_URL. Closing the tab should POST .../step/:n/unlock.
src/lib/auth.js throws if the secret is missing. Copy .env.example to .env.local and run openssl rand -base64 32. Preview/impersonation also needs this secret on Vercel.
Next.js 16 type-checks page.js. Only the default page export (plus reserved fields such as metadata) is allowed. Keep helpers as local functions or move them to src/components.
Restart npm run dev. The connection is cached on global.mongoose.
GET /api/public/overview is cached 60s in Redis. Super Admin → System → Flush caches.

Diagnose a 403

1

Confirm who the API thinks you are

Session email and level come from the JWT. During role preview they are the demo user, not your Super Admin mailbox.
2

Compare with the form document

userId (owner), principalEmail, schoolName, assignedTo / collaborator assignedForms, sharedWithEmails.
3

Match the handler

GET /api/forms/[id] allows same-school level 2 and 4. Writes also allow assigned level 3. Presence/locks also allow same-school level 3 to see who is editing.

Local checklist

Open http://localhost:3000 → Sign in. Super Admin: /admin/system should show MongoDB connected.

Support