Back to Blog
Security

Authentication Best Practices in Supabase

Sarah Johnson
11 min read

Authentication is the foundation of application security. Supabase provides a complete auth solution, but implementing it correctly requires understanding best practices. Let's dive into secure authentication patterns.

Understanding Supabase Auth

Supabase Auth provides:

  • Email/password authentication
  • OAuth providers (Google, GitHub, etc.)
  • Magic links
  • Phone authentication
  • Multi-factor authentication
  • Row Level Security integration

Email/Password Authentication

Implement secure password-based auth:

// Sign up
const { data, error } = await supabase.auth.signUp({
  email: 'user@example.com',
  password: 'securePassword123!',
  options: {
    data: {
      full_name: 'John Doe',
      avatar_url: 'https://example.com/avatar.jpg'
    }
  }
})

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

Password Requirements

Enforce strong passwords:

  • Minimum 8 characters
  • Mix of uppercase, lowercase, numbers
  • Special characters
  • Not in common password lists

OAuth Integration

Add social login for better UX:

// Google OAuth
const { data, error } = await supabase.auth.signInWithOAuth({
  provider: 'google',
  options: {
    redirectTo: 'https://yourdomain.com/auth/callback'
  }
})

// GitHub OAuth const { data, error } = await supabase.auth.signInWithOAuth({ provider: 'github', options: { scopes: 'repo read:user' } }) ```

Magic Links

Passwordless authentication:

const { data, error } = await supabase.auth.signInWithOtp({
  email: 'user@example.com',
  options: {
    emailRedirectTo: 'https://yourdomain.com/welcome'
  }
})

Session Management

Handle sessions securely:

// Get current session
const { data: { session } } = await supabase.auth.getSession()

// Refresh session const { data, error } = await supabase.auth.refreshSession()

// Sign out const { error } = await supabase.auth.signOut() ```

Session Persistence

Configure session storage:

const supabase = createClient(url, key, {
  auth: {
    persistSession: true,
    storageKey: 'my-app-auth',
    storage: window.localStorage,
    autoRefreshToken: true,
    detectSessionInUrl: true
  }
})

Protected Routes

Implement route protection in Next.js:

// middleware.ts
import { createMiddlewareClient } from '@supabase/auth-helpers-nextjs'
import { NextResponse } from 'next/server'

export async function middleware(req) { const res = NextResponse.next() const supabase = createMiddlewareClient({ req, res }) const { data: { session } } = await supabase.auth.getSession() if (!session && req.nextUrl.pathname.startsWith('/dashboard')) { return NextResponse.redirect(new URL('/login', req.url)) } return res } ```

User Roles & Permissions

Implement role-based access:

-- Create roles table
CREATE TABLE user_roles (
  id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
  user_id UUID REFERENCES auth.users NOT NULL,
  role TEXT NOT NULL CHECK (role IN ('admin', 'user', 'moderator')),
  UNIQUE(user_id, role)
);

-- Create policy CREATE POLICY "Users can view their own roles" ON user_roles FOR SELECT USING (auth.uid() = user_id); ```

Check roles in your application:

const checkUserRole = async (requiredRole: string) => {
  const { data, error } = await supabase
    .from('user_roles')
    .select('role')
    .eq('user_id', user.id)
    .eq('role', requiredRole)
    .single()
  
  return !!data
}

Multi-Factor Authentication

Add an extra security layer:

// Enroll MFA
const { data, error } = await supabase.auth.mfa.enroll({
  factorType: 'totp'
})

// Verify MFA const { data, error } = await supabase.auth.mfa.verify({ factorId: data.id, code: '123456' }) ```

Email Verification

Ensure email ownership:

// Send verification email
const { data, error } = await supabase.auth.signUp({
  email: 'user@example.com',
  password: 'password123',
  options: {
    emailRedirectTo: 'https://yourdomain.com/verify-email'
  }
})

// Check verification status const { data: { user } } = await supabase.auth.getUser() if (!user?.email_confirmed_at) { // Email not verified } ```

Password Reset

Implement secure password reset:

// Request reset
const { data, error } = await supabase.auth.resetPasswordForEmail(
  'user@example.com',
  { redirectTo: 'https://yourdomain.com/reset-password' }
)

// Update password const { data, error } = await supabase.auth.updateUser({ password: 'newSecurePassword123!' }) ```

Security Best Practices

  • Always use HTTPS in production
  • Implement rate limiting
  • Log authentication events
  • Use secure cookie settings
  • Validate tokens server-side
  • Implement session timeouts
  • Monitor for suspicious activity

Monitoring & Logging

Track authentication events:

supabase.auth.onAuthStateChange((event, session) => {
  console.log('Auth event:', event)
  
  if (event === 'SIGNED_IN') {
    // Log successful login
    logAuthEvent('sign_in', session?.user.id)
  }
  
  if (event === 'SIGNED_OUT') {
    // Clear local state
    clearUserData()
  }
})

Conclusion

Secure authentication requires attention to detail and following established patterns. Supabase provides the tools, but implementing them correctly is crucial. Start with these best practices and adapt them to your specific needs.