Authentication
Last updated on 2026-08-30
The kit uses Supabase Auth for all authentication, with three client types for different contexts and middleware for route protection.
Auth Flow Overview
User signs up → Supabase creates auth.users row
→ Trigger creates profiles row
→ User is redirected to /account
User logs in → Cookie-based session created via @supabase/ssr
→ Middleware validates session on protected routes
→ Admin routes check app_metadata.role
Supabase Client Types
The kit uses three Supabase clients, each for a different context:
Browser Client (lib/supabase/client.ts)
Used in client components for real-time subscriptions and client-side auth:
import { createBrowserClient } from "@supabase/ssr"
export function createClient() {
return createBrowserClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
)
}
Server Client (lib/supabase/server.ts)
Used in Server Components and Server Actions. Reads cookies for the current session:
import { createServerClient } from "@supabase/ssr"
import { cookies } from "next/headers"
This client respects RLS policies — queries return only data the authenticated user is allowed to see.
Admin Client (lib/supabase/admin.ts)
Uses the service role key to bypass RLS. Used only in Server Actions for admin operations:
import { createClient } from "@supabase/supabase-js"
export const supabaseAdmin = createClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.SUPABASE_SERVICE_ROLE_KEY!
)
Security: The service role key is never exposed to the client. It only runs in Server Actions and API routes.
Auth Pages
Login (/login)
- Email and password sign-in via
supabase.auth.signInWithPassword() - Google OAuth via
supabase.auth.signInWithOAuth({ provider: 'google' }) - GitHub OAuth via
supabase.auth.signInWithOAuth({ provider: 'github' }) - Error messages for invalid credentials
- Redirects to
/accounton success
Register (/register)
- Email, password, and full name
- Calls
supabase.auth.signUp()withuser_metadata: { full_name } - Shows "Check your email" confirmation message
- Database trigger creates the profile automatically
Forgot Password (/forgot-password)
- Calls
supabase.auth.resetPasswordForEmail() - Shows success state after sending
OAuth Callback (/auth/callback)
Handles the OAuth code exchange after social login:
// Exchanges the code for a session
const { searchParams } = new URL(request.url)
const code = searchParams.get("code")
const supabase = await createClient()
await supabase.auth.exchangeCodeForSession(code)
Email Confirmation (/auth/confirm)
Handles email verification links for new signups.
Middleware
The middleware.ts file at the project root handles:
- Session refresh — updates the auth cookie on every request
- Route protection — redirects unauthenticated users from
/account/* - Admin protection — redirects non-admin users from
/admin/* - Auth redirect — redirects authenticated users away from
/loginand/register
// Simplified middleware logic
if (pathname.startsWith("/account") && !user) {
redirect("/login")
}
if (pathname.startsWith("/admin") && !isAdmin) {
redirect("/")
}
if ((pathname === "/login" || pathname === "/register") && user) {
redirect("/account")
}
Admin Access
Admin access is controlled via app_metadata.role:
- The seed data creates an admin user:
admin@example.com/password123 - For your own admin, set
app_metadatain the Supabase Dashboard:- Go to Authentication > Users
- Click on the user
- Edit
app_metadatato include{"role": "admin"}
The is_admin() database function checks this value in RLS policies:
SELECT auth.jwt() -> 'app_metadata' ->> 'role' = 'admin'
useUser Hook
The hooks/use-user.ts hook provides the current user in client components:
const { user, loading } = useUser()
It listens for auth state changes and updates automatically on login/logout.
Session Management
- Sessions are stored in HTTP-only cookies via
@supabase/ssr - The middleware refreshes sessions on every request
- Sessions expire based on your Supabase project settings (default: 1 hour, with refresh)
- Logout calls
supabase.auth.signOut()and clears the cookie