Auth sin dolor: Autenticación completa con Supabase
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
autoRefreshTokenesté 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.