Auth sin dolor: Autenticación completa con Supabase
Kevin Dávila
Confesión: odio configurar autenticación. Los redirects de OAuth, los JWT que expiran justo cuando no deben, las sesiones que se pierden entre pestañas y ese bug de login que solo aparece un domingo a media noche.
Por eso no lo podía creer cuando con Supabase Auth tuve login con Google, GitHub y email/password funcionando en literalmente 15 líneas de código. Aquí vas a armar autenticación completa, de lo básico hasta MFA, sin drama.
El stack de Auth de Supabase
Supabase usa GoTrue como servicio de autenticación. Esto es lo que ofrece:
Email/Password
OAuth social (Google, GitHub, Apple, Discord, etc.)
Magic Link (login sin contraseña)
Phone/SMS
MFA (Multi-Factor Authentication)
Todo esto viene incluido en el free tier, con hasta 50,000 usuarios.
Configuración inicial
Variables de entorno
// .env
VITE_SUPABASE_URL=https://tu-proyecto.supabase.co
VITE_SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...Cliente de Supabase
// 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 y Password
Registro
// 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
}Login
// 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
}Logout
// src/auth/logout.ts
import { supabase } from '../lib/supabase'
export async function signOut() {
const { error } = await supabase.auth.signOut()
if (error) throw error
}OAuth Social
Configurar login con Google o GitHub es trivial. Solo necesitas agregar las credenciales OAuth en el dashboard de Supabase.
// 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
}Configurar OAuth providers
En el dashboard de Supabase:
Ve a Authentication > Providers
Selecciona Google o GitHub
Agrega tu Client ID y Client Secret
Copia la URL de callback que te da Supabase
Configura esa URL en la consola del proveedor (Google Cloud Console, GitHub Settings)
Magic Link
Login sin contraseña, solo con un link al 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
}Escuchar cambios de sesión
// src/auth/listener.ts
import { supabase } from '../lib/supabase'
// Escuchar cambios de auth (login, logout, token refresh)
supabase.auth.onAuthStateChange((event, session) => {
console.log('Auth event:', event)
console.log('User:', session?.user)
// Eventos posibles:
// - SIGNED_IN
// - SIGNED_OUT
// - TOKEN_REFRESHED
// - USER_UPDATED
// - PASSWORD_RECOVERY
})Obtener el usuario actual
// 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)
Para apps que necesitan seguridad extra, Supabase soporta 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: 'Mi App'
})
if (error) throw error
// data.totp.qr_code - QR para escanear con app authenticator
// data.totp.secret - Secret para ingresar manualmente
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
}Proteger rutas en el cliente
Con 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
}Con 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
}Configuración en el Dashboard
Algunas configuraciones importantes en Authentication > Settings:
Site URL: URL de tu app (ej: http://localhost:5173)
Redirect URLs: URLs permitidas después del login
Email confirmations: Habilitar/deshabilitar confirmación por email
Password requirements: Longitud mínima, caracteres especiales
Errores comunes
"Invalid login credentials"
Verifica que el usuario exista
Revisa que el email esté confirmado (si tienes confirmación habilitada)
"Redirect URL not allowed"
Agrega la URL en Authentication > URL Configuration
Incluye tanto localhost como tu dominio de producción
Token expirado
Supabase maneja refresh tokens automáticamente
Verifica que autoRefreshToken esté habilitado (viene por defecto)
Qué sigue
En el próximo artículo vamos con RLS (Row Level Security): cómo proteger tus datos directamente en la base de datos, sin depender solo del código del cliente. Es la pieza que hace que Supabase sea seguro para producción.