Supabase Auth logo

supabase auth

Authentication and authorization for web applications

$ npx docs2skills add supabase-auth
SKILL.md

Supabase Auth

Authentication and authorization for web applications

What this skill does

Supabase Auth provides a complete authentication system built on PostgreSQL with JWT-based sessions. It handles user registration, login, password management, social OAuth, magic links, OTP verification, and multi-factor authentication. The system integrates deeply with Supabase's database layer, automatically generating REST APIs with Row Level Security (RLS) policies that scope data access based on the authenticated user's JWT claims.

Unlike standalone auth services, Supabase Auth stores user data directly in your PostgreSQL database in a special auth schema, allowing you to reference user records with foreign keys and trigger functions. The JWT tokens contain user metadata and custom claims that your database RLS policies can inspect, enabling sophisticated authorization patterns without round trips to external services.

Prerequisites

  • Supabase project with database and Auth enabled
  • Node.js 14+ for client SDKs
  • Email provider (built-in or custom SMTP) for email-based auth
  • SMS provider (Twilio, MessageBird, or Vonage) for phone auth
  • OAuth app credentials for social providers
  • HTTPS domain for production redirects

Quick start

npm install @supabase/supabase-js
import { createClient } from '@supabase/supabase-js'

const supabase = createClient(
  'https://your-project.supabase.co',
  'your-anon-key'
)

// Sign up with email/password
const { data, error } = await supabase.auth.signUp({
  email: 'user@example.com',
  password: 'password123',
  options: {
    data: {
      full_name: 'John Doe'
    }
  }
})

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

// Get current user
const { data: { user } } = await supabase.auth.getUser()

// Listen to auth state changes
supabase.auth.onAuthStateChange((event, session) => {
  console.log(event, session)
})

Core concepts

Sessions and JWTs: Supabase Auth uses JSON Web Tokens for stateless authentication. Each user session contains an access token (JWT) that's automatically included in database requests. The JWT contains user ID, email, metadata, and custom claims that RLS policies can access.

User Identity vs Authentication: A user can have multiple identities (email, phone, OAuth providers) but represents a single account. Identity linking allows users to sign in with different methods while maintaining a unified profile.

Row Level Security Integration: The auth JWT is automatically passed to PostgreSQL as auth.jwt() and auth.uid() functions. RLS policies use these to filter data on a per-row basis, ensuring users only access authorized records.

Email Confirmation Flow: New users receive confirmation emails before activation. This can be disabled for development but should remain enabled in production to prevent abuse and ensure deliverability.

Redirect URLs: After authentication actions (login, signup, password reset), users are redirected to configurable URLs. These must be added to your allowed redirect list in the Supabase dashboard.

Key API surface

MethodDescription
signUp(credentials)Register new user with email/password or phone
signInWithPassword(credentials)Sign in with email/password
signInWithOtp({ email/phone })Send magic link or SMS OTP
signInWithOAuth({ provider })Initiate OAuth flow (Google, GitHub, etc.)
verifyOtp({ email/phone, token })Verify OTP code
getUser()Get current authenticated user
getSession()Get current session with JWT tokens
refreshSession()Refresh expired access token
updateUser(attributes)Update user metadata or password
resetPasswordForEmail(email)Send password reset email
signOut()End current session
onAuthStateChange(callback)Listen for auth events
mfa.enroll({ type })Enroll in multi-factor authentication
mfa.verify({ factorId, code })Verify MFA challenge

Common patterns

Protected routes with session check

// Check auth state on page load
useEffect(() => {
  supabase.auth.getSession().then(({ data: { session } }) => {
    setSession(session)
    setUser(session?.user ?? null)
    setLoading(false)
  })

  const { data: { subscription } } = supabase.auth.onAuthStateChange(
    (_event, session) => {
      setSession(session)
      setUser(session?.user ?? null)
      setLoading(false)
    }
  )

  return () => subscription.unsubscribe()
}, [])

Magic link authentication

// Send magic link
const { error } = await supabase.auth.signInWithOtp({
  email: 'user@example.com',
  options: {
    emailRedirectTo: 'https://yoursite.com/dashboard'
  }
})

// Handle the callback (in your redirect page)
useEffect(() => {
  const { data: { subscription } } = supabase.auth.onAuthStateChange(
    (event, session) => {
      if (event === 'SIGNED_IN') {
        router.push('/dashboard')
      }
    }
  )
  return () => subscription.unsubscribe()
}, [])

Social OAuth login

const { data, error } = await supabase.auth.signInWithOAuth({
  provider: 'google',
  options: {
    redirectTo: 'https://yoursite.com/auth/callback',
    queryParams: {
      access_type: 'offline',
      prompt: 'consent',
    }
  }
})

Row Level Security policy

-- Enable RLS on your table
ALTER TABLE profiles ENABLE ROW LEVEL SECURITY;

-- Policy: users can only see their own profile
CREATE POLICY "Users can view own profile" 
ON profiles FOR SELECT 
USING (auth.uid() = id);

-- Policy: users can update their own profile
CREATE POLICY "Users can update own profile" 
ON profiles FOR UPDATE 
USING (auth.uid() = id);

Server-side auth (Next.js)

import { createServerSupabaseClient } from '@supabase/auth-helpers-nextjs'

export async function getServerSideProps({ req, res }) {
  const supabase = createServerSupabaseClient({ req, res })
  const { data: { session } } = await supabase.auth.getSession()

  if (!session) {
    return { redirect: { destination: '/login', permanent: false } }
  }

  return { props: { session } }
}

Configuration

Site URL: Base URL for redirects (set in Supabase dashboard under Auth > URL Configuration)

Redirect URLs: Allowed callback URLs after authentication (comma-separated list)

JWT Settings: Access token lifetime (default 1 hour), refresh token lifetime (default 30 days)

Email Templates: Customize confirmation, reset, and magic link emails in Auth > Email Templates

Rate Limiting: API calls per hour limits (default varies by plan)

Providers: OAuth app credentials and scopes in Auth > Providers

SMTP: Custom email provider in Auth > Settings (optional, uses built-in by default)

Best practices

Always handle auth state changes: Use onAuthStateChange to update UI when users sign in/out, don't rely on local state alone.

Validate sessions server-side: For sensitive operations, verify JWTs on your backend using the project's JWT secret.

Use RLS policies: Don't rely solely on client-side filtering - implement proper database-level authorization with Row Level Security.

Store sensitive data in user_metadata: Use signUp({ options: { data: {} }}) for additional user fields that should be in JWT claims.

Implement proper error handling: Auth operations can fail due to network issues, rate limiting, or invalid credentials - always check the error object.

Set appropriate redirect URLs: Use different redirects for different flows (signup confirmation vs password reset).

Enable email confirmation in production: Prevents spam signups and ensures users own their email addresses.

Use refresh tokens properly: Access tokens expire after 1 hour - let the SDK handle refresh automatically or call refreshSession() manually.

Gotchas and common mistakes

  • User not immediately available after signUp: User object is null until email confirmation. Check data.user and handle the confirmation flow.
  • Session persists after signOut: Clear local storage manually if needed: localStorage.removeItem('supabase.auth.token')
  • RLS policies block everything by default: Create explicit SELECT/INSERT/UPDATE/DELETE policies or nothing works.
  • Auth state changes fire multiple times: Use proper cleanup in onAuthStateChange listeners to prevent memory leaks.
  • OAuth redirects fail locally: Use http://localhost:3000 not 127.0.0.1 in redirect URLs for local development.
  • Custom claims not in JWT: Use Auth Hooks or triggers to add claims - they don't appear automatically from user metadata.
  • Rate limiting on auth endpoints: Implement proper error handling and retry logic for production apps.
  • Session cookies not set: Use @supabase/auth-helpers for proper SSR cookie management in Next.js/SvelteKit.
  • Password reset links expire: Default 1 hour expiry - handle expired token errors gracefully.
  • Phone auth requires verification: SMS OTP must be verified with verifyOtp() - users aren't signed in after receiving the code.
  • MFA enrollment requires existing session: Users must be signed in before enrolling in multi-factor authentication.
  • Identity linking overwrites metadata: When linking OAuth accounts, user_metadata from the OAuth provider may overwrite existing values.
Supabase Auth — docs2skills