Documentation
Documentation

Authentication Guide

Learn how to implement authentication in your Next.js application using Supabase Auth. This guide covers database setup, middleware configuration, and route protection.

Authentication Routes

The following authentication routes are pre-implemented and ready to use:

RouteDescription
/sign-inSign in page with email/password and social providers
/sign-upSign up page for new users
/forgot-passwordPassword reset request page
/private/reset-passwordProtected route for password reset

Database Setup

Create the necessary tables in your Supabase database for user management. This includes the profiles table and admin flags.

supabase/migrations/01_create_profiles.sqlsql
1-- Create a table for user profiles
2create table public.profiles (
3 id uuid references auth.users on delete cascade primary key,
4 username text unique,
5 full_name text,
6 avatar_url text,
7 is_admin boolean default false,
8 created_at timestamp with time zone default timezone('utc'::text, now()) not null,
9 updated_at timestamp with time zone default timezone('utc'::text, now()) not null
10);
11
12-- Enable Row Level Security
13alter table public.profiles enable row level security;
14
15-- Create a trigger to automatically create a profile for new users
16create or replace function public.handle_new_user()
17returns trigger as $$
18begin
19 insert into public.profiles (id, username, full_name, avatar_url)
20 values (
21 new.id,
22 new.raw_user_meta_data->>'username',
23 new.raw_user_meta_data->>'full_name',
24 new.raw_user_meta_data->>'avatar_url'
25 );
26 return new;
27end;
28$$ language plpgsql security definer;
29
30create trigger on_auth_user_created
31 after insert on auth.users
32 for each row execute procedure public.handle_new_user();
33
34-- Update updated_at when profile is modified
35create trigger handle_updated_at before update on public.profiles
36 for each row execute procedure moddatetime (updated_at);

Middleware

Use Next.js middleware to protect routes and handle authentication state. The middleware runs before requests are completed, making it perfect for auth checks and redirects.

middleware.tstypescript
1import { createMiddlewareClient } from '@supabase/auth-helpers-nextjs'
2import { NextResponse } from 'next/server'
3import type { NextRequest } from 'next/server'
4import type { Database } from '@/lib/database.types'
5
6export async function middleware(req: NextRequest) {
7 const res = NextResponse.next()
8 const supabase = createMiddlewareClient<Database>({ req, res })
9 const {
10 data: { session },
11 } = await supabase.auth.getSession()
12
13 // Authentication check
14 if (!session && req.nextUrl.pathname.startsWith('/dashboard')) {
15 return NextResponse.redirect(new URL('/sign-in', req.url))
16 }
17
18 // Admin check
19 if (
20 req.nextUrl.pathname.startsWith('/admin') &&
21 session?.user.user_metadata.is_admin !== true
22 ) {
23 return NextResponse.redirect(new URL('/', req.url))
24 }
25
26 // Public routes when authenticated
27 if (
28 session &&
29 (req.nextUrl.pathname === '/sign-in' ||
30 req.nextUrl.pathname === '/sign-up')
31 ) {
32 return NextResponse.redirect(new URL('/dashboard', req.url))
33 }
34
35 return res
36}
37
38export const config = {
39 matcher: [
40 '/dashboard/:path*',
41 '/admin/:path*',
42 '/sign-in',
43 '/sign-up',
44 '/private/:path*',
45 ],
46}

Row Level Security

Implement Row Level Security (RLS) policies to control access to your data at the database level.

supabase/migrations/02_security_policies.sqlsql
1-- Profiles policies
2create policy "Public profiles are viewable by everyone"
3 on public.profiles for select
4 using ( true );
5
6create policy "Users can update their own profile"
7 on public.profiles for update
8 using ( auth.uid() = id );
9
10-- Admin policies
11create policy "Only admins can delete profiles"
12 on public.profiles for delete
13 using (
14 auth.uid() in (
15 select id from public.profiles
16 where is_admin = true
17 )
18 );
19
20-- Function to check if user is admin
21create or replace function auth.is_admin()
22returns boolean as $$
23 select exists(
24 select 1 from public.profiles
25 where id = auth.uid()
26 and is_admin = true
27 );
28$$ language sql security definer;

Auth Hooks

Use these hooks to manage authentication state and protect routes in your application.

lib/hooks/use-auth.tstypescript
1import { createClientComponentClient } from '@supabase/auth-helpers-nextjs'
2import { useRouter } from 'next/navigation'
3import { useEffect, useState } from 'react'
4import type { User } from '@supabase/auth-helpers-nextjs'
5import type { Database } from '@/lib/database.types'
6
7export function useAuth() {
8 const [user, setUser] = useState<User | null>(null)
9 const [loading, setLoading] = useState(true)
10 const router = useRouter()
11 const supabase = createClientComponentClient<Database>()
12
13 useEffect(() => {
14 const {
15 data: { subscription },
16 } = supabase.auth.onAuthStateChange((event, session) => {
17 setUser(session?.user ?? null)
18 setLoading(false)
19
20 if (event === 'SIGNED_OUT') {
21 router.push('/sign-in')
22 }
23 })
24
25 return () => {
26 subscription.unsubscribe()
27 }
28 }, [router, supabase])
29
30 return { user, loading }
31}
32
33// Usage in a protected component
34export function ProtectedComponent() {
35 const { user, loading } = useAuth()
36
37 if (loading) {
38 return <div>Loading...</div>
39 }
40
41 if (!user) {
42 return null
43 }
44
45 return <div>Protected content for {user.email}</div>
46}