ignitionstack.pro v1.0 is out! Read the announcement →
Skip to Content

Supabase Authentication

This guide covers how to set up and use Supabase Authentication in this project.

Overview

This project uses Supabase Auth for user authentication, which provides:

Database Schema

1. auth.users (Managed by Supabase)

2. public.profiles (Custom)

3. public.admin_users (Custom)

Configuration

1. Configure Auth Providers

Email/Password (Enabled by default)

No additional configuration needed. Users can sign up with email/password out of the box.

OAuth Providers (Optional)

To enable Google, GitHub, or other OAuth providers:

  1. Go to Supabase Dashboard → Authentication → Providers
  2. Enable desired provider (e.g., Google)
  3. Add OAuth credentials (Client ID, Client Secret)
  4. Configure redirect URLs
Google Cloud Console setup

Use these steps to create the OAuth client inside Google:

  1. Visit console.cloud.google.com  → APIs & Services → Credentials.
  2. If you don’t have one yet, click Create Credentials → OAuth client ID and choose Web application.
  3. Under Authorized JavaScript origins, add your app domains (example):
    • http://localhost:3000
    • https://your-production-domain.com
  4. Under Authorized redirect URIs, add Supabase’s callback endpoint for each environment:
    • http://localhost:3000/auth/callback
    • https://your-production-domain.com/auth/callback
    • https://<your-project-ref>.supabase.co/auth/v1/callback (Supabase uses this internally, so include it as well.)
  5. Save and copy the Client ID and Client Secret, then paste them into Supabase Dashboard → Auth → Providers → Google.

Supabase will handle the final redirect using whatever redirectTo you pass in signInWithOAuth. In our code we always use window.location.origin, so once Google lists the origins/URIs above the callback will return to the current environment instead of any hard-coded URL.

2. Environment Variables

Ensure these are set in .env.local:

# Required NEXT_PUBLIC_SUPABASE_URL=https://your-project.supabase.co NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key # For admin operations (server-side only) SUPABASE_SERVICE_ROLE_KEY=your-service-role-key # Admin configuration (for session encryption) ADMIN_AUTH_SECRET=your-secret-key-here

3. Add Admin Users

Insert admin emails into the admin_users table:

INSERT INTO public.admin_users (email) VALUES ('your-admin@example.com') ON CONFLICT (email) DO NOTHING;

Authentication Methods

Email/Password Sign Up

import { createClient } from '@/app/lib/supabase/client' const supabase = createClient() const { data, error } = await supabase.auth.signUp({ email: 'user@example.com', password: 'securepassword', options: { data: { display_name: 'John Doe', }, }, })

Email/Password Sign In

const { data, error } = await supabase.auth.signInWithPassword({ email: 'user@example.com', password: 'securepassword', })

OAuth Sign In (Google)

const { data, error } = await supabase.auth.signInWithOAuth({ provider: 'google', options: { redirectTo: `${window.location.origin}/auth/callback`, }, })

Sign Out

const { error } = await supabase.auth.signOut()

Get Current User

// Client Components const { data: { user } } = await supabase.auth.getUser() // Server Components/Actions import { createClient } from '@/app/lib/supabase/server' const supabase = await createClient() const { data: { user } } = await supabase.auth.getUser()

Usage Examples

Client Component with Auth

'use client' import { useEffect, useState } from 'react' import { createClient } from '@/app/lib/supabase/client' import type { User } from '@supabase/supabase-js' export function MyComponent() { const [user, setUser] = useState<User | null>(null) const [loading, setLoading] = useState(true) const supabase = createClient() useEffect(() => { // Get initial user supabase.auth.getUser().then(({ data }) => { setUser(data.user) setLoading(false) }) // Listen to auth changes const { data: { subscription } } = supabase.auth.onAuthStateChange( (_event, session) => { setUser(session?.user ?? null) } ) return () => subscription.unsubscribe() }, [supabase]) if (loading) return <div>Loading...</div> if (!user) return <div>Not authenticated</div> return <div>Welcome, {user.email}!</div> }

Server Action with Auth

'use server' import { createClient } from '@/app/lib/supabase/server' export async function myServerAction() { const supabase = await createClient() const { data: { user }, error } = await supabase.auth.getUser() if (error || !user) { return { error: 'Unauthorized' } } // User is authenticated, proceed with action return { success: true } }

Admin Authentication

Check if User is Admin

'use server' import { createClient } from '@/app/lib/supabase/server' export async function checkIsAdmin() { const supabase = await createClient() const { data: { user } } = await supabase.auth.getUser() if (!user?.email) return false const { data } = await supabase .from('admin_users') .select('email') .eq('email', user.email) .single() return !!data }

Security Best Practices

1. Always Use RLS Policies

Never disable Row Level Security on tables that contain user data:

ALTER TABLE public.profiles ENABLE ROW LEVEL SECURITY;

2. Validate User on Server

Always verify authentication on the server, never trust client-side:

// BAD - Client can manipulate this const user = getClientUser() await updateProfile(user.id, data) // GOOD - Server verifies auth const { data: { user } } = await supabase.auth.getUser() if (!user) throw new Error('Unauthorized') await updateProfile(user.id, data)

3. Use Service Role Key Carefully

The SUPABASE_SERVICE_ROLE_KEY bypasses RLS. Only use it for:

4. Secure Environment Variables