A Next.js template with UN branding and magic link authentication.
Based on: https://github.com/kleinlennart/un-website-boilerplate
- UN branding (logo, colors, Roboto font)
- Magic link authentication (configurable email domains via DB)
- Rate limiting on magic link requests (2 min cooldown)
- 30-day session duration
- PostgreSQL session/user storage
- Configurable database schema per app
- Entity selection on first login (with "Other" option)
- Entity change dialog (click entity badge in header)
- Public landing page (
/about) + protected dashboard (/)
npm installcp .env.template .env.localEdit .env.local:
DATABASE_URL- PostgreSQL connection stringDB_SCHEMA- Schema for auth tables (e.g.sg_reports_survey→sg_reports_survey.users,sg_reports_survey.magic_tokens)AUTH_SECRET- Generate withopenssl rand -hex 32SMTP_*- Mail server for magic linksBASE_URL- Your app URL (for magic link emails)
Edit sql/auth_tables.sql and replace sg_reports_survey with your schema name (must match DB_SCHEMA), then:
psql $DATABASE_URL -f sql/auth_tables.sqlThe schema includes an allowed_domains table pre-populated with UN system domains (un.org, undp.org, unicef.org, who.int, etc.). Edit the SQL to remove domains you don't need, or add custom ones:
-- Add a custom domain
INSERT INTO myapp.allowed_domains (entity, domain) VALUES
('*', 'example.org'), -- global: allow for all entities
('PARTNER', 'partner.org'); -- entity-specificnpm run devOpen http://localhost:3000 with your browser.
- User visits
/about(public landing page) - User clicks "Sign In" →
/login - User enters email, magic link sent (rate limited: 2 min cooldown)
- User clicks link →
/verify?token=... - First login: select entity (combobox with "Other" option); returning users: direct sign-in
- Session cookie set (30 days)
- Header shows user email, clickable entity badge (to change), and logout
- Unauthenticated users accessing protected routes → redirect to
/about
- Site title/subtitle: Edit
SITE_TITLEandSITE_SUBTITLEinsrc/components/Header.tsx - Allowed email domains: Add to
allowed_domainstable in database - Entity list: Query in
fetchEntities()insrc/app/api/entities/route.ts - Document search: Query in
src/app/api/documents/search/route.ts - Protected routes: Edit
PUBLIC_PATHSinsrc/middleware.ts - Auth schema: Set
DB_SCHEMAenv var and updatesql/auth_tables.sql
src/
├── app/
│ ├── about/ # Public landing page
│ ├── api/
│ │ ├── auth/ # Auth API routes (backup, actions preferred)
│ │ ├── documents/search/ # Document search
│ │ └── entities/ # Entity list + fetchEntities()
│ ├── login/ # Login page + layout
│ ├── verify/ # Token verification + entity selection
│ └── page.tsx # Protected dashboard
├── components/
│ ├── DocumentSearch.tsx # Document autocomplete
│ ├── EntityChangeDialog.tsx # Dialog to change entity
│ ├── EntityCombobox.tsx # Entity dropdown with "Other" option
│ ├── EntitySearch.tsx # Entity autocomplete (for search)
│ ├── Footer.tsx # Site footer
│ ├── Header.tsx # Site header with maxWidth, hideAbout props
│ ├── LoginForm.tsx # Login form (uses server actions)
│ ├── UserMenu.tsx # Email + entity badge + logout
│ └── VerifyForm.tsx # Verify form with returning user detection
├── lib/
│ ├── actions.ts # Server actions for auth
│ ├── auth.ts # Auth logic (isAllowedDomain, sessions, etc.)
│ ├── config.ts # DB_SCHEMA config + table names
│ ├── db.ts # PostgreSQL pool
│ ├── mail.ts # Magic link emails
│ └── utils.ts # Tailwind cn() helper
└── middleware.ts # Route protection
sql/
└── auth_tables.sql # Database schema (users, tokens, allowed_domains)
npm audit # Security vulnerabilities
npm outdated # Outdated packages
npm run lint # ESLint errors
npx tsc --noEmit # TypeScript errorsnpm update # Safe patch/minor updates
npm install next@latest eslint-config-next@latest # Update Next.jsrm -rf node_modules .next && npm install-
use
npx shadcn@latest add <component-name>when you need to add components. -
https://nextjs.org/docs/app/api-reference/file-conventions/src-folder
-
https://nextjs.org/docs/app/getting-started/project-structure
-
The
/publicdirectory should remain in the root of your project. -
Config files like
package.json,next.config.jsandtsconfig.jsonshould remain in the root of your project. -
.env.*files should remain in the root of your project.