Autenticación en Supabase: Cómo Funciona
Supabase usa GoTrue como servicio de autenticación. GoTrue es un servidor de auth open-source escrito en Go que maneja registro, login, tokens y sesiones. Cada vez que un usuario se autentica, GoTrue genera tokens, los valida y gestiona su ciclo de vida.
No necesitas interactuar con GoTrue directamente. El SDK de Supabase expone todos los métodos necesarios a través de supabase.auth.
Qué es un JWT
Un JWT (JSON Web Token -- un token firmado que contiene información del usuario) es la pieza central del sistema de auth en Supabase. Cuando un usuario inicia sesión, Supabase genera un JWT que contiene:
- El ID del usuario (
sub) - El rol (
role) - El tiempo de expiración (
exp) - Metadata adicional que tú configures
Este token está firmado con el JWT Secret de tu proyecto. Nadie puede modificarlo sin invalidarlo. Cuando tu app hace una petición a Supabase, el SDK envía este token en el header Authorization, y Supabase verifica que sea válido antes de ejecutar cualquier query.
// Estructura simplificada de un JWT de Supabase
// (decodificado, NO lo decodifiques manualmente en produccion)
{
"sub": "uuid-del-usuario", // ID unico del usuario
"email": "usuario@ejemplo.com",
"role": "authenticated", // Rol asignado
"exp": 1700000000, // Expiracion (Unix timestamp)
"iat": 1699996400, // Fecha de emision
"aud": "authenticated" // Audiencia
}No guardes datos sensibles en el JWT
El JWT se puede decodificar en el cliente (es base64). Nunca incluyas información sensible como contraseñas o datos privados en la metadata del token.
Sesiones: access_token y refresh_token
Cuando un usuario se autentica exitosamente, Supabase devuelve una sesión que contiene dos tokens:
access_token
Es el JWT principal. Se envía en cada petición a Supabase para identificar al usuario. Tiene una vida corta (por defecto 1 hora) por seguridad: si alguien lo intercepta, solo es válido por un tiempo limitado.
refresh_token
Es un token de larga duración que sirve para obtener un nuevo access_token cuando el actual expira. El SDK maneja este proceso automáticamente -- tú no tienes que renovar tokens manualmente.
import { createClient } from '@supabase/supabase-js'
const supabase = createClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
)
// Después de un login exitoso, la sesión tiene esta estructura:
const { data } = await supabase.auth.getSession()
if (data.session) {
console.log(data.session.access_token) // JWT de corta duracion
console.log(data.session.refresh_token) // Token para renovar el JWT
console.log(data.session.expires_at) // Timestamp de expiracion
console.log(data.session.user) // Datos del usuario
}Renovación automática
El SDK de Supabase detecta cuando el access_token está por expirar y usa el refresh_token para obtener uno nuevo. No necesitas implementar esta lógica.
Flujo de autenticación
Este es el flujo completo cuando un usuario se registra o inicia sesión:
1. Usuario envia credenciales (email + password, OAuth, magic link, etc.)
|
v
2. Supabase (GoTrue) valida las credenciales
|
v
3. GoTrue genera access_token (JWT) + refresh_token
|
v
4. El SDK almacena la sesión en el cliente (localStorage por defecto)
|
v
5. Cada peticion al API incluye el access_token en el header Authorization
|
v
6. Supabase valida el JWT y aplica RLS (Row Level Security) segun el usuario
|
v
7. Cuando el access_token expira, el SDK usa el refresh_token para renovarloEl SDK se encarga de los pasos 4-7 automáticamente. Tú solo necesitas manejar el paso 1 (enviar las credenciales) y reaccionar a los cambios de estado.
Métodos principales de supabase.auth
El objeto supabase.auth expone todos los métodos que necesitas para manejar autenticación:
Registro y login
// Registro con email y contraseña
const { data, error } = await supabase.auth.signUp({
email: 'usuario@ejemplo.com',
password: 'contrasena-segura'
})
// Login con email y contraseña
const { data, error } = await supabase.auth.signInWithPassword({
email: 'usuario@ejemplo.com',
password: 'contrasena-segura'
})
// Login con OAuth (Google, GitHub, etc.)
const { data, error } = await supabase.auth.signInWithOAuth({
provider: 'google'
})
// Login con magic link (enlace enviado al email)
const { data, error } = await supabase.auth.signInWithOtp({
email: 'usuario@ejemplo.com'
})
// Cerrar sesión
const { error } = await supabase.auth.signOut()Obtener información del usuario
// Obtener la sesión actual (desde cache local)
const { data: { session } } = await supabase.auth.getSession()
// Obtener el usuario actual (validando contra el servidor)
const { data: { user } } = await supabase.auth.getUser()getSession vs getUser
getSession() lee la sesión almacenada localmente y es más rápido. getUser() hace una petición al servidor para validar que el token siga siendo válido. Usa getUser() cuando necesites certeza de que el usuario sigue autenticado (por ejemplo, antes de operaciones sensibles).
Actualizar datos del usuario
// Actualizar email o contraseña
const { data, error } = await supabase.auth.updateUser({
email: 'nuevo@ejemplo.com',
password: 'nueva-contrasena'
})
// Actualizar metadata del usuario
const { data, error } = await supabase.auth.updateUser({
data: {
nombre: 'Juan',
avatar_url: 'https://ejemplo.com/avatar.jpg'
}
})onAuthStateChange: escuchar cambios de sesión
onAuthStateChange es un listener (escuchador de eventos) que te notifica cada vez que cambia el estado de autenticación. Esto incluye: login, logout, renovación de token, y actualización de usuario.
const { data: { subscription } } = supabase.auth.onAuthStateChange(
(event, session) => {
console.log('Evento:', event)
console.log('Sesión:', session)
switch (event) {
case 'SIGNED_IN':
// El usuario acaba de iniciar sesión
break
case 'SIGNED_OUT':
// El usuario cerró sesión
break
case 'TOKEN_REFRESHED':
// El access_token se renovo automaticamente
break
case 'USER_UPDATED':
// Los datos del usuario cambiaron
break
case 'INITIAL_SESSION':
// Se cargo la sesión inicial al montar el componente
break
}
}
)
// Para dejar de escuchar (por ejemplo, al desmontar un componente):
subscription.unsubscribe()Siempre limpia el listener
Si usas onAuthStateChange en un componente de React, llama a subscription.unsubscribe() en la función de cleanup del useEffect. Si no lo haces, tendrás memory leaks (fugas de memoria) y comportamiento inesperado.
Ejemplo con React y useEffect
'use client'
import { useEffect, useState } from 'react'
import { createClient } from '@/lib/supabase/client'
import type { User } from '@supabase/supabase-js'
export function AuthProvider({ children }: { children: React.ReactNode }) {
const [user, setUser] = useState<User | null>(null)
const [loading, setLoading] = useState(true)
const supabase = createClient()
useEffect(() => {
const { data: { subscription } } = supabase.auth.onAuthStateChange(
(_event, session) => {
setUser(session?.user ?? null)
setLoading(false)
}
)
return () => {
subscription.unsubscribe()
}
}, [])
if (loading) return <div>Cargando...</div>
return <>{children}</>
}Métodos de autenticación disponibles
Supabase soporta múltiples formas de autenticar usuarios:
| Método | Descripción |
|---|---|
| Email + password | Registro e inicio de sesión clásico |
| OAuth | Login con proveedores externos (Google, GitHub, etc.) |
| Magic link | Enlace enviado al email que autentica sin contraseña |
| OTP por teléfono | Código de verificación enviado por SMS |
| SSO / SAML | Single Sign-On para entornos enterprise |
Cada método se cubre en detalle en las siguientes páginas.
Dónde se almacena la sesión
Por defecto, el SDK de Supabase en el navegador almacena la sesión en localStorage. Esto significa que la sesión persiste entre recargas de página y pestañas del mismo dominio.
En entornos de servidor (como Server Components de NextJS o API routes), no hay localStorage. Para estos casos existe @supabase/ssr, un paquete que almacena la sesión en cookies. Lo veremos en detalle en la sección de protección de rutas.
Próximos pasos
Ahora que entiendes cómo funciona el sistema de auth, es momento de implementarlo:
- Email y password -- Registro e inicio de sesión clásico
- OAuth -- Login con Google y GitHub
- Magic links -- Autenticación sin contraseña
- Proteger rutas -- Middleware y protección en NextJS