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.

typescript
// 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.

typescript
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:

plaintext
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 renovarlo

El 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

typescript
// 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

typescript
// 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

typescript
// 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.

typescript
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

typescript
'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étodoDescripción
Email + passwordRegistro e inicio de sesión clásico
OAuthLogin con proveedores externos (Google, GitHub, etc.)
Magic linkEnlace enviado al email que autentica sin contraseña
OTP por teléfonoCódigo de verificación enviado por SMS
SSO / SAMLSingle 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:

  1. Email y password -- Registro e inicio de sesión clásico
  2. OAuth -- Login con Google y GitHub
  3. Magic links -- Autenticación sin contraseña
  4. Proteger rutas -- Middleware y protección en NextJS