Codea Bien Logo
Auth Made Easy: Complete Authentication with Supabase
Supabase

Auth Made Easy: Complete Authentication with Supabase

Kevin Dávila

Kevin Dávila

supabase authautenticaciónoauthloginmfa

A confession: I hate setting up authentication. OAuth redirects, JWTs expiring at the worst possible moment, sessions lost between tabs, and that login bug that only shows up on a Sunday at midnight.

That's why I couldn't believe it when Supabase Auth had login with Google, GitHub, and email/password working in literally 15 lines of code. Here you'll put together complete authentication, from the basics to MFA, with no drama.

Supabase Auth Stack

Supabase uses GoTrue as its authentication service. Here's what it offers:

  • Email/Password

  • Social OAuth (Google, GitHub, Apple, Discord, etc.)

  • Magic Link (passwordless login)

  • Phone/SMS

  • MFA (Multi-Factor Authentication)

All of this is included in the free tier, with up to 50,000 users.

Initial setup

Environment variables

// .env
VITE_SUPABASE_URL=https://your-project.supabase.co
VITE_SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

Supabase client

// src/lib/supabase.ts
import { createClient } from '@supabase/supabase-js'

const supabaseUrl = import.meta.env.VITE_SUPABASE_URL
const supabaseKey = import.meta.env.VITE_SUPABASE_ANON_KEY

export const supabase = createClient(supabaseUrl, supabaseKey)

Email and Password

Sign up

// src/auth/signup.ts
import { supabase } from '../lib/supabase'

export async function signUp(email: string, password: string) {
  const { data, error } = await supabase.auth.signUp({
    email,
    password,
  })

  if (error) throw error
  return data
}

Sign in

// src/auth/login.ts
import { supabase } from '../lib/supabase'

export async function signIn(email: string, password: string) {
  const { data, error } = await supabase.auth.signInWithPassword({
    email,
    password,
  })

  if (error) throw error
  return data
}

Sign out

// src/auth/logout.ts
import { supabase } from '../lib/supabase'

export async function signOut() {
  const { error } = await supabase.auth.signOut()
  if (error) throw error
}

Social OAuth

Setting up login with Google or GitHub is trivial. You just need to add OAuth credentials in the Supabase dashboard.

Google

// src/auth/google.ts
import { supabase } from '../lib/supabase'

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

  if (error) throw error
  return data
}

GitHub

// src/auth/github.ts
import { supabase } from '../lib/supabase'

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

  if (error) throw error
  return data
}

Configure OAuth providers

In the Supabase dashboard:

  1. Go to Authentication > Providers

  2. Select Google or GitHub

  3. Add your Client ID and Client Secret

  4. Copy the callback URL Supabase gives you

  5. Configure that URL in the provider's console (Google Cloud Console, GitHub Settings)

Magic Link

Passwordless login, just a link to your email:

// src/auth/magic-link.ts
import { supabase } from '../lib/supabase'

export async function signInWithMagicLink(email: string) {
  const { data, error } = await supabase.auth.signInWithOtp({
    email,
    options: {
      emailRedirectTo: `${window.location.origin}/auth/callback`
    }
  })

  if (error) throw error
  return data
}

Listen to session changes

// src/auth/listener.ts
import { supabase } from '../lib/supabase'

// Listen to auth changes (login, logout, token refresh)
supabase.auth.onAuthStateChange((event, session) => {
  console.log('Auth event:', event)
  console.log('User:', session?.user)
  
  // Possible events:
  // - SIGNED_IN
  // - SIGNED_OUT
  // - TOKEN_REFRESHED
  // - USER_UPDATED
  // - PASSWORD_RECOVERY
})

Get current user

// src/auth/user.ts
import { supabase } from '../lib/supabase'

export async function getCurrentUser() {
  const { data: { user }, error } = await supabase.auth.getUser()
  
  if (error) throw error
  return user
}

MFA (Multi-Factor Authentication)

For apps that need extra security, Supabase supports TOTP (Time-based One-Time Password):

// src/auth/mfa.ts
import { supabase } from '../lib/supabase'

// Enroll MFA
export async function enrollMFA() {
  const { data, error } = await supabase.auth.mfa.enroll({
    factorType: 'totp',
    friendlyName: 'My App'
  })

  if (error) throw error
  
  // data.totp.qr_code - QR to scan with authenticator app
  // data.totp.secret - Secret to enter manually
  return data
}

// Verify MFA
export async function verifyMFA(factorId: string, challengeId: string, code: string) {
  const { data, error } = await supabase.auth.mfa.verify({
    factorId,
    challengeId,
    code
  })

  if (error) throw error
  return data
}

Protect routes on the client

With vanilla JS

// src/auth/guard.ts
import { supabase } from '../lib/supabase'

export async function requireAuth() {
  const { data: { session } } = await supabase.auth.getSession()
  
  if (!session) {
    window.location.href = '/login'
    return null
  }
  
  return session
}

With Angular

// src/app/guards/auth.guard.ts
import { inject } from '@angular/core'
import { CanActivateFn, Router } from '@angular/router'
import { SupabaseService } from '../services/supabase.service'

export const authGuard: CanActivateFn = async () => {
  const supabase = inject(SupabaseService)
  const router = inject(Router)
  
  const { data: { session } } = await supabase.client.auth.getSession()
  
  if (!session) {
    router.navigate(['/login'])
    return false
  }
  
  return true
}

Dashboard Configuration

Some important settings in Authentication > Settings:

  • Site URL: Your app URL (e.g., http://localhost:5173)

  • Redirect URLs: Allowed URLs after login

  • Email confirmations: Enable/disable email confirmation

  • Password requirements: Minimum length, special characters

Common errors

"Invalid login credentials"

  • Verify the user exists

  • Check that the email is confirmed (if you have confirmation enabled)

"Redirect URL not allowed"

  • Add the URL in Authentication > URL Configuration

  • Include both localhost and your production domain

Token expired

  • Supabase handles refresh tokens automatically

  • Verify that autoRefreshToken is enabled (default)

What's next

In the next article we cover RLS (Row Level Security): how to protect your data directly in the database, without relying only on client code. It's the piece that makes Supabase secure for production.