9.7 KiB
🔐 Authentication Setup Guide
Project: AI Speedrun - Mini-ECD Prototype Epic: E2 - Database & Auth Story: E2.S3 - Demo auth flow Last Updated: 2024-11-15
Overview
This document describes the authentication implementation for the EPD prototype, including magic link login and demo user accounts.
Authentication Methods
1. Magic Link (Primary Method)
Users can sign in using email-only authentication:
- User enters email on
/login - Supabase sends magic link to email
- User clicks link → auto-logged in
- New users: Account is automatically created on first magic link request
Benefits:
- No password to remember
- More secure than traditional passwords
- Better UX for demo environment
- Auto-creates accounts (no separate signup flow needed)
2. Demo Accounts (For Presentations)
Pre-configured demo accounts for public demos and presentations:
| Password | Access Level | Purpose | |
|---|---|---|---|
| demo@mini-ecd.demo | Demo2024! | interactive | Main demo account - full CRUD |
| readonly@mini-ecd.demo | Demo2024! | read_only | View-only for public demos |
| presenter@mini-ecd.demo | Demo2024! | presenter | Live presentations |
Access Levels:
read_only: Can view all data, cannot create/edit/deleteinteractive: Full CRUD access to all featurespresenter: Full access + special presenter features (future)
Setup Instructions
1. Environment Variables
Ensure these are set in your .env.local:
# Supabase
NEXT_PUBLIC_SUPABASE_URL=https://dqugbrpwtisgyxscpefg.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key
# Service role key (for admin operations)
SUPABASE_SERVICE_ROLE_KEY=your-service-role-key
2. Create Demo Users
Run the seed script to create demo user accounts:
# Make sure you have tsx installed
pnpm add -D tsx
# Run the seed script
tsx scripts/seed-demo-users.ts
Expected output:
🌱 Starting demo user seed...
Creating user: demo@mini-ecd.demo...
✅ Created auth user: xxx-xxx-xxx
✅ Created demo_users entry
✨ demo@mini-ecd.demo ready!
Creating user: readonly@mini-ecd.demo...
✅ Created auth user: xxx-xxx-xxx
✅ Created demo_users entry
✨ readonly@mini-ecd.demo ready!
Creating user: presenter@mini-ecd.demo...
✅ Created auth user: xxx-xxx-xxx
✅ Created demo_users entry
✨ presenter@mini-ecd.demo ready!
✅ Demo user seed complete!
3. Configure Supabase Auth Settings
Go to Supabase Dashboard → Authentication → Settings:
Email Templates
Customize the magic link email template:
Subject: "Login to Mini-ECD"
Body:
<h2>Je magic link is klaar!</h2>
<p>Klik op de knop hieronder om in te loggen bij Mini-ECD:</p>
<p><a href="{{ .ConfirmationURL }}">Login naar EPD</a></p>
<p>Of kopieer deze link naar je browser:</p>
<p>{{ .ConfirmationURL }}</p>
<p><small>Deze link is 1 uur geldig.</small></p>
Redirect URLs
Add these redirect URLs under "Redirect URLs":
http://localhost:3000/auth/callback
https://yourdomain.com/auth/callback
Email Auth Settings
- ✅ Enable Email provider
- ✅ Confirm email: OFF (for demo convenience)
- ✅ Secure email change: ON
- ⏱️ Rate limits: Default (4 emails per hour)
File Structure
app/
login/
page.tsx # Login UI (magic link + demo login)
auth/
callback/
route.ts # Handles magic link callback
logout/
route.ts # Logout endpoint
lib/
auth/
client.ts # Client-side auth helpers
server.ts # Server-side auth helpers
database.types.ts # Generated Supabase types
middleware.ts # Route protection
scripts/
seed-demo-users.ts # Demo user creation script
Usage Examples
Client-Side (React Components)
import {
loginWithMagicLink,
loginWithPassword,
logout,
getUser,
isDemoUser
} from '@/lib/auth/client'
// Magic link login
async function handleMagicLink(email: string) {
const result = await loginWithMagicLink(email)
console.log(result.message) // "Check je email voor de magic link!"
}
// Demo account login
async function handleDemoLogin() {
await loginWithPassword('demo@mini-ecd.demo', 'Demo2024!')
router.push('/clients')
}
// Check current user
const user = await getUser()
const isDemo = await isDemoUser()
// Logout
await logout() // Redirects to /login
Server-Side (API Routes, Server Components)
import {
requireAuth,
getUser,
canWrite,
getDemoUserInfo
} from '@/lib/auth/server'
// Require authentication in API route
export async function GET() {
const session = await requireAuth() // Throws if not authenticated
// ... handle request
}
// Check write permissions
export async function POST() {
const hasWriteAccess = await canWrite()
if (!hasWriteAccess) {
return NextResponse.json(
{ error: 'Read-only demo account cannot create data' },
{ status: 403 }
)
}
// ... create resource
}
// Get demo user info
const demoInfo = await getDemoUserInfo()
if (demoInfo) {
console.log(`Access level: ${demoInfo.access_level}`)
console.log(`Usage count: ${demoInfo.usage_count}`)
}
Route Protection
Routes are protected via middleware.ts:
Public Routes (No Auth Required)
/- Landing page/login- Login page/epd- EPD demo info/contact- Contact form/auth/callback- Auth callback
Protected Routes (Auth Required)
/clients- Client list/clients/*- Client details, intake, etc.- Any other route not in public list
Behavior:
- ✅ Unauthenticated → Redirect to
/login?redirect=/original-path - ✅ Authenticated on
/login→ Redirect to/clients - ✅ Session auto-refreshed in middleware
Security Features
✅ Implemented
- RLS Policies: All database queries filtered by
auth.uid() - Session Management: Auto-refresh tokens via middleware
- Secure Cookies: HTTP-only, secure flags set
- CSRF Protection: Built-in Next.js CSRF protection
- Rate Limiting: Supabase default (4 emails/hour)
- Demo User Tracking: Usage count and last login tracked
🔒 Production Enhancements
For production deployment:
- Email Confirmation: Enable email confirmation
- Password Requirements: Enforce strong passwords
- MFA: Add multi-factor authentication
- Session Timeout: Implement auto-logout after inactivity
- IP Whitelisting: Restrict demo accounts to specific IPs
- Audit Logging: Enhanced tracking of all auth events
Demo User Management
Checking Demo Status
// Check if user is demo user
const isDemo = await isDemoUser()
// Get access level
const accessLevel = await getDemoAccessLevel()
// Returns: 'read_only' | 'interactive' | 'presenter' | null
Restricting Actions
// In API route
const demoInfo = await getDemoUserInfo()
if (demoInfo?.access_level === 'read_only') {
return NextResponse.json(
{ error: 'This demo account is read-only' },
{ status: 403 }
)
}
Resetting Demo Accounts
To reset a demo account (clear data, reset usage):
-- Reset usage count
UPDATE demo_users
SET usage_count = 0, last_login_at = NULL
WHERE access_level = 'interactive';
-- Or via Supabase Dashboard: Authentication → Users → Delete user data
Troubleshooting
Issue: Magic link not arriving
Causes:
- Email in spam folder
- Rate limit exceeded (4 emails/hour)
- Email provider blocking Supabase emails
Solutions:
- Check spam folder
- Wait 1 hour and try again
- Use demo account instead
- Configure custom SMTP in Supabase
Issue: "Invalid login credentials"
Causes:
- Wrong email/password for demo account
- Demo user not created yet
Solutions:
- Check credentials match exactly (case-sensitive)
- Run seed script:
tsx scripts/seed-demo-users.ts - Verify in Supabase Dashboard → Authentication → Users
Issue: Redirect loop on /login
Causes:
- Middleware configuration error
- Session cookie issues
Solutions:
- Clear browser cookies
- Check middleware.ts public routes config
- Verify
NEXT_PUBLIC_SUPABASE_URLis correct
Issue: "Row violates RLS policy" errors
Causes:
- User not properly authenticated
- Session expired
- RLS policies misconfigured
Solutions:
- Logout and login again
- Check
auth.uid()returns valid UUID - Verify RLS policies allow user access
Testing Checklist
Magic Link Flow
- Can enter email on /login
- Magic link email received
- Clicking link redirects to /clients
- Session persists after page refresh
- New users auto-created on first login
Demo Account Flow
- Can login with demo@mini-ecd.demo
- Can login with readonly@mini-ecd.demo
- Interactive account can create/edit data
- Read-only account blocked from editing
- Demo usage tracked in demo_users table
Route Protection
- /clients redirects to /login when not authenticated
- /login redirects to /clients when authenticated
- Public routes accessible without auth
- Session auto-refreshes
Logout
- Logout clears session
- Redirects to /login
- Cannot access protected routes after logout
References
- Supabase Auth Documentation
- Next.js Middleware
- Build Plan:
docs/specs/bouwplan-ai-speedrun-marketing-first-v1.1.mdEpic 2 - Technical Design:
docs/specs/to-mini-ecd-v1_2.md§ 5.7
Status: ✅ Implemented and Ready for Testing Next Steps: E2.S4 - Seed data script (clients + dossiers)