Why Authentication Is Critical
Authentication is the front door of your application. Get it wrong and everything behind it is vulnerable — user data, payment info, business logic.
Most tutorials show you the happy path: user signs up, user logs in. This guide covers the full picture including what happens when things go wrong.
Firebase vs Supabase vs Custom — Which to Choose?
| Feature | Firebase | Supabase | Custom |
|---|---|---|---|
| Setup time | 30 min | 30 min | 2-3 days |
| Cost | Free then expensive | Free tier generous | Cheap (just server) |
| Control | Low | Medium | Full |
| SQL support | No | Yes (PostgreSQL) | Yes |
| Best for | Quick prototypes | Most startups | Large companies |
My recommendation: Supabase for 90% of Tanzanian startups. It gives you a real PostgreSQL database, generous free tier, and excellent Next.js integration.
Step 1: Set Up Supabase
- Go to supabase.com and create a free account
- Create a new project (choose a server close to Tanzania — Mumbai or Frankfurt)
- Go to Settings → API and copy your Project URL and anon public key
Create a .env.local file in your project root:
NEXT_PUBLIC_SUPABASE_URL=your_project_url_here
NEXT_PUBLIC_SUPABASE_ANON_KEY=your_anon_key_here
Important: Never commit .env.local to GitHub. Add it to .gitignore.
Step 2: Install and Configure Supabase Client
npm install @supabase/supabase-js @supabase/ssr
Create lib/supabase.ts:
import { createBrowserClient } from '@supabase/ssr'
export function createClient() {
return createBrowserClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
)
}
Step 3: Sign Up with Email Verification
const supabase = createClient()
async function signUp(email: string, password: string) {
const { data, error } = await supabase.auth.signUp({
email,
password,
options: {
emailRedirectTo: `${window.location.origin}/auth/callback`
}
})
if (error) throw error
return data
}
Supabase sends the verification email automatically — no extra configuration needed on your side.
Step 4: Sign In and Session Management
async function signIn(email: string, password: string) {
const { data, error } = await supabase.auth.signInWithPassword({
email,
password
})
if (error) throw error
return data.session
}
Supabase handles JWT refresh tokens automatically. The session persists across page reloads.
Step 5: Protecting Pages with Middleware
Create middleware.ts in your project root:
import { createServerClient } from '@supabase/ssr'
import { NextResponse, type NextRequest } from 'next/server'
export async function middleware(request: NextRequest) {
const response = NextResponse.next()
const supabase = createServerClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
{ cookies: { /* cookie handlers */ } }
)
const { data: { user } } = await supabase.auth.getUser()
if (!user && request.nextUrl.pathname.startsWith('/dashboard')) {
return NextResponse.redirect(new URL('/login', request.url))
}
return response
}
export const config = {
matcher: ['/dashboard/:path*']
}
Step 6: Role-Based Access Control
In Supabase, add a role column to your users table:
ALTER TABLE auth.users ADD COLUMN role TEXT DEFAULT 'user';
Then check the role in your API routes:
const { data: { user } } = await supabase.auth.getUser()
const userRole = user?.user_metadata?.role
if (userRole !== 'admin') {
return NextResponse.json({ error: 'Forbidden' }, { status: 403 })
}
Security Best Practices
- Always validate on the server, not just the client — client-side checks are for UX only
- Use Row Level Security in Supabase — users can only access their own data
- Rate limit your auth endpoints — limit to 5 failed attempts per IP per minute
- Keep access tokens short-lived — Supabase default of 1 hour is fine
- Validate email domains for B2B apps — only allow company email addresses during signup
Common Vulnerabilities to Avoid
- Never store passwords in plain text — Supabase handles hashing automatically
- Never trust user-provided role claims — always read role from your database
- Never skip email verification — it prevents fake accounts and spam
- Never expose your service role key — only use the anon key on the frontend
Conclusion
Supabase gives you production-ready authentication in under an hour — but only if you configure it correctly. The steps above cover everything you need for a real application with real users.
The security section is not optional. Every point there is something I have seen exploited in Tanzanian apps in the last two years.
Want me to review your authentication setup? Book a free consultation — I will check it against this checklist and give you specific improvements.