Security model
Your token never reaches the browser
The GitHub token (PAT or OAuth) lives in an encrypted, HTTP-only session cookie that only the server can read. Pages call GitDash's own API; the server calls GitHub.
Request flow
text
Browser ── /api/... ──► proxy.ts
│ decrypts the session cookie
│ no session → /login or /setup (pages), 401 (API)
│ organization mode → identity + groups, then the route's grant
▼
API route ── token from the session ──► GitHub REST API
│
▼
JSON response (never contains the token)Protection layers
| Layer | Mechanism |
|---|---|
| Session | iron-session (AES-256-GCM); cookie is HttpOnly, SameSite=Lax, Secure in production, 7-day lifetime. SESSION_SECRET must be at least 32 characters. |
| Sign-in | The session is replaced on every sign-in, so an old session cannot carry over to a new account. |
| Cross-site requests | State-changing API requests from another origin are rejected; token sign-in only accepts JSON from GitDash's own pages. |
| Rate limits | /api/auth/setup 5 per minute per IP · /api/auth/login 10 per minute · issue creation 5 per hour |
| Input validation | Owner, repo and org parameters are validated before any GitHub call (src/lib/validation.ts). |
| Authorization | Organization mode checks the route's grant on the server for every request (see Access control). |
| Secrets in responses | Alert destinations (webhooks, email) and AI/email settings are visible to admins only in organization mode. |
| HTTP headers | Content-Security-Policy, HSTS, X-Frame-Options: DENY, X-Content-Type-Options: nosniff, Referrer-Policy, Permissions-Policy. |
| Container | Runs as the non-root nextjs user on node:20-alpine. |