Patrones de Autenticación con Clerk
Guía rápida: Clerk proporciona autenticación gestionada con componentes de UI preconstruidos, ayudantes del lado del servidor y multi-tenant basado en organizaciones. Usa
clerkMiddleware()para proteger rutas,<Show>para renderizado condicional, hooks para el estado del cliente, yauth()/currentUser()para autenticación del lado del servidor. Clerk Core 3 (2026) reemplaza<SignedIn>/<SignedOut>con<Show>, renombra el archivo de middleware aproxy.ts(Next.js 16+) y consolida los paquetes.
<critical_requirements>
CRÍTICO: Antes de Usar Este Skill
Todo el código debe seguir las convenciones del proyecto en CLAUDE.md (kebab-case, exportaciones nombradas, orden de importaciones,
import type, constantes nombradas)
(DEBES usar @clerk/nextjs/server para TODAS las importaciones del lado del servidor -- NUNCA importes ayudantes del servidor desde @clerk/nextjs)
(DEBES verificar los webhooks usando el helper verifyWebhook de Clerk -- NUNCA confíes en payloads de webhook no verificados)
(DEBES usar el componente <Show> en lugar de los obsoletos <SignedIn>/<SignedOut>/<Protect> -- estos se eliminan en Core 3)
(NO DEBES pasar el objeto completo de currentUser() al cliente -- contiene privateMetadata que debe permanecer en el servidor)
(DEBES proteger las rutas TANTO en el middleware COMO en la capa de acceso a datos -- el middleware solo es insuficiente)
</critical_requirements>
Auto-detección: Clerk, ClerkProvider, clerkMiddleware, @clerk/nextjs, useUser, useAuth, useClerk, useSession, useOrganization, SignIn, SignUp, UserButton, UserProfile, OrganizationSwitcher, auth(), currentUser(), CLERK_SECRET_KEY, NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY, Show when="signed-in"
Cuándo usar:
- Agregar autenticación y gestión de usuarios a una aplicación
- Construir aplicaciones B2B multi-tenant con control de acceso basado en organizaciones
- Usar componentes de UI preconstruidos para inicio de sesión/registro con personalización de temas
- Proteger rutas con middleware y verificaciones de autorización del lado del servidor
- Sincronizar datos de usuario de Clerk con tu base de datos mediante webhooks
Patrones clave cubiertos:
- Configuración de ClerkProvider, variables de entorno, configuración de middleware
- Componentes de UI preconstruidos (
<SignIn>,<SignUp>,<UserButton>,<Show>) - Hooks del lado del cliente (
useUser,useAuth,useSession,useOrganization) - Autenticación del lado del servidor (
auth(),currentUser()) en Server Components, Route Handlers, Server Actions - Protección de rutas con middleware usando
clerkMiddleware()ycreateRouteMatcher() - Multi-tenant basado en organizaciones con roles y permisos
- Manejo de webhooks con verificación de firma Svix
Cuándo NO usar:
- Requisito de autenticación autoalojada (necesitas control total sobre el almacenamiento de datos de autenticación)
- No se puede usar un servicio de autenticación de terceros (restricciones regulatorias/de cumplimiento)
- Autenticación simple con clave API (el middleware personalizado es suficiente)
- Restricciones presupuestarias impiden usar un servicio gestionado
Recursos detallados:
- reference.md - Marcos de decisión, referencia rápida de hooks, hoja de trucos de migración a Core 3
- examples/core.md - ClerkProvider, variables de entorno, configuración de middleware
- examples/components.md - Componentes preconstruidos, personalización, prop de apariencia
- examples/hooks.md - useUser, useAuth, useSession, estados de carga, renderizado condicional
- examples/server.md - Server Components, rutas API, Server Actions, manejo de webhooks
- examples/organizations.md - Gestión de organizaciones, roles, permisos, RBAC
Filosofía
Clerk es una plataforma de autenticación gestionada que maneja todo el ciclo de vida de autenticación: registro, inicio de sesión, gestión de sesiones, perfiles de usuario, organizaciones y MFA. En lugar de construir la autenticación desde cero, integras el SDK de Clerk y sus componentes preconstruidos.
Principios fundamentales:
- Defensa en profundidad -- Protege las rutas en la capa de middleware Y verifica la autenticación en cada punto de acceso a datos. El middleware solo es insuficiente (CVE-2025-29927 demostró vulnerabilidades de omisión del middleware).
- Autenticación primero en el servidor -- Usa
auth()ycurrentUser()en Server Components y Route Handlers. Usa hooks del cliente (useUser,useAuth) solo cuando necesites estado reactivo en el cliente. - Preconstruido sobre personalizado -- Usa los componentes
<SignIn>,<SignUp>,<UserButton>de Clerk. Solo construye flujos personalizados cuando los componentes preconstruidos realmente no puedan cumplir con los requisitos. - Organizaciones para multi-tenant -- Usa las Organizaciones de Clerk con roles y permisos para aplicaciones B2B. No construyas sistemas de tenant personalizados sobre el modelo de usuario de Clerk.
- Sincronización impulsada por webhooks -- Sincroniza los datos de Clerk con tu base de datos mediante webhooks, no mediante sondeo. Siempre verifica las firmas de los webhooks con
verifyWebhook.
Cuándo usar Clerk:
- Necesitas autenticación rápidamente con un mínimo de código personalizado
- Quieres componentes de UI preconstruidos para inicio de sesión/registro/gestión de usuarios
- Necesitas multi-tenant basado en organizaciones con RBAC
- Quieres MFA, SSO (SAML/OIDC) e inicio de sesión social gestionados
Cuándo NO usar Clerk:
- Necesitas control total sobre el almacenamiento de datos de autenticación (requisito autoalojado)
- No puedes usar un servicio de autenticación de terceros (restricciones regulatorias/de cumplimiento)
- Tu aplicación solo necesita autenticación simple con clave API
- Restricciones presupuestarias impiden usar un servicio gestionado
Patrones Esenciales
Patrón 1: Configuración de ClerkProvider y Middleware
Cada aplicación Clerk necesita <ClerkProvider> envolviendo la aplicación y clerkMiddleware() protegiendo las rutas. Consulta examples/core.md para ejemplos completos de configuración.
Reglas clave:
ClerkProviderva dentro de<body>, no envolviendo<html>-- Core 3 lo requiere- Usa la variable de entorno
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY, nunca codifiques las claves - El archivo de middleware es
proxy.tsen Next.js 16+ omiddleware.tsen Next.js <=15 - El endpoint del webhook debe estar en la lista de rutas públicas (verificado por separado por
verifyWebhook) CLERK_WEBHOOK_SIGNING_SECRETes la variable de entorno para la firma del webhook (noCLERK_WEBHOOK_SECRET)
// proxy.ts (Next.js 16+) o middleware.ts (Next.js <=15)
const isPublicRoute = createRouteMatcher([
"/",
"/sign-in(.*)",
"/sign-up(.*)",
"/api/webhooks(.*)",
]);
export default clerkMiddleware(async (auth, req) => {
if (!isPublicRoute(req)) {
await auth.protect();
}
});
Por qué es bueno: Las rutas públicas se incluyen explícitamente en la lista blanca, todo lo demás requiere autenticación, el endpoint del webhook es público (verificado por separado)
// MALO: clerkMiddleware() sin callback
export default clerkMiddleware(); // ¡TODAS las rutas son PÚBLICAS por defecto!
Por qué es malo: clerkMiddleware() sin callback adjunta datos de autenticación pero no protege ninguna ruta
Patrón 2: Componentes de UI Preconstruidos y <Show>
Usa <Show> para el renderizado condicional (reemplazo de Core 3 para <SignedIn>/<SignedOut>/<Protect>). Consulta examples/components.md para ejemplos completos.
<Show when="signed-out"><SignInButton /></Show>
<Show when="signed-in"><UserButton /></Show>
<Show when={{ role: "org:admin" }}><AdminPanel /></Show>
<Show when={(has) => has({ permission: "org:invoices:manage" })}><InvoiceManager /></Show>
Por qué es bueno: <Show> es la API de Core 3, soporta condiciones de cadena/objeto/función de callback, prop fallback para no autorizado
// MALO: Obsoleto -- eliminado en Core 3
<SignedIn>...</SignedIn>
<SignedOut>...</SignedOut>
<Protect role="admin">...</Protect>
Por qué es malo: <SignedIn>, <SignedOut>, <Protect> se eliminan en Core 3, usa <Show> con la prop when
Las páginas de inicio de sesión/registro requieren segmentos de ruta comodín [[...sign-in]] para flujos de varios pasos (MFA, callbacks OAuth).
Patrón 3: Hooks del Lado del Cliente
Usa hooks en Client Components para el estado de autenticación reactivo. Siempre verifica isLoaded antes de acceder a los datos. Consulta examples/hooks.md para ejemplos completos.
"use client";
const { isLoaded, isSignedIn, user } = useUser();
if (!isLoaded) return <div>Loading...</div>;
if (!isSignedIn) return <div>Please sign in</div>;
// Ahora es seguro acceder a user.firstName, user.emailAddresses, etc.
Por qué es bueno: Previene errores de hidratación al acceder a undefined durante la inicialización, previene TypeError al acceder a null cuando no has iniciado sesión
// MALO: Acceder a user sin protecciones
const { user } = useUser();
return <p>{user.firstName}</p>; // Error en tiempo de ejecución cuando isLoaded=false o isSignedIn=false
Por qué es malo: user es undefined hasta que se carga y null cuando no has iniciado sesión
Hooks clave:
useUser()-- datos del perfil del usuario (nombre, email, avatar)useAuth()-- tokens de sesión (getToken()),userId,orgId,has()para autorizaciónuseOrganization()-- datos de la organización activa, membresía, lista paginada de miembrosuseReverification()-- re-autenticación antes de acciones sensibles (Core 3)
En Core 3, getToken() lanza ClerkOfflineError cuando está desconectado en lugar de devolver null -- siempre envuélvelo en try/catch.
Patrón 4: Autenticación del Lado del Servidor
Usa auth() y currentUser() desde @clerk/nextjs/server. Consulta examples/server.md para ejemplos completos.
auth() -- ligero, sin llamada API, lee las reclamaciones de la sesión:
import { auth } from "@clerk/nextjs/server";
const { userId, orgId, isAuthenticated, redirectToSignIn } = await auth();
if (!isAuthenticated) return redirectToSignIn();
currentUser() -- objeto de usuario completo, realiza una llamada a la API de Backend:
import { currentUser } from "@clerk/nextjs/server";
const user = await currentUser();
// CRÍTICO: Selecciona campos seguros antes de pasarlos a los componentes del cliente
const safeData = { firstName: user.firstName, imageUrl: user.imageUrl };
Por qué es crítico: currentUser() devuelve privateMetadata que nunca debe llegar al cliente. Selecciona los campos explícitamente.
Defensa en profundidad: La autenticación DEBE verificarse en cada Server Action y Route Handler, no solo en el middleware:
"use server";
const { userId } = await auth();
if (!userId) throw new Error("Unauthorized");
// userId limita todas las consultas/mutaciones al usuario actual
Patrón 5: Autorización con Roles y Permisos
Usa auth.protect() o has() para control de acceso granular. Consulta examples/organizations.md para patrones RBAC.
// Middleware: protección de rutas basada en roles
await auth.protect((has) => has({ role: "org:admin" }));
// Server Component: verificación de permisos
const { has } = await auth();
if (!has({ permission: "org:invoices:delete" })) redirect("/dashboard");
// Cliente: renderizado condicional por rol
<Show when={{ role: "org:admin" }}><AdminPanel /></Show>
Formato de permiso: org:<feature>:<action> (ej., org:invoices:create, org:reports:read)
Roles predeterminados: org:admin (todos los permisos), org:member (solo lectura). Roles personalizados configurados en el Panel de Clerk.
Patrón 6: Manejo de Webhooks
Usa los webhooks de Clerk (a través de Svix) para sincronizar los datos de usuario con tu base de datos. Consulta examples/server.md para el manejador completo.
import { verifyWebhook } from "@clerk/nextjs/webhooks";
export async function POST(req: Request) {
const evt = await verifyWebhook(req); // lee la variable de entorno CLERK_WEBHOOK_SIGNING_SECRET
// evt.type: "user.created" | "user.updated" | "user.deleted" | "organization.*" | ...
// evt.data: UserJSON | OrganizationJSON | etc.
}
Por qué es bueno: verifyWebhook valida la firma Svix, siempre devuelve 200 para evitar reintentos, la ruta del webhook es pública en el middleware (verificada por la firma, no por la autenticación)
// MALO: Sin verificación de firma
const body = await req.json(); // ¡Cualquiera puede enviar datos falsos POST!
await db.insert(users).values(body.data); // Vulnerabilidad de seguridad
Por qué es malo: Sin verificación de firma, cualquiera puede enviar eventos de webhook falsos a tu endpoint
Importa tipos desde @clerk/shared/types (Core 3): UserJSON, OrganizationJSON, OrganizationMembershipJSON
<decision_framework>
Marco de Decisión
Autenticación Cliente vs Servidor
¿Dónde necesitas los datos de autenticación?
|-- Server Component, Route Handler, Server Action
| |-- ¿Solo necesitas userId/sessionId? --> auth()
| |-- ¿Necesitas el objeto de usuario completo? --> currentUser()
| |-- ¿Necesitas proteger la ruta? --> auth.protect()
| +-- ¿Necesitas contexto de org? --> auth() devuelve orgId, orgRole
|
+-- Client Component (interfaz de usuario interactiva)
|-- ¿Necesitas datos del perfil del usuario? --> useUser()
|-- ¿Necesitas datos de sesión/token? --> useAuth()
|-- ¿Necesitas datos de la org? --> useOrganization()
+-- ¿Necesitas la API de Clerk de bajo nivel? --> useClerk()
Estrategia de Protección de Rutas
¿Qué tipo de ruta es?
|-- Pública (landing, inicio de sesión, registro, webhooks)
| +-- Añadir al matcher de isPublicRoute, omitir auth.protect()
|
|-- Autenticada (dashboard, perfil, configuración)
| +-- auth.protect() en middleware + verificación auth() en la capa de datos
|
+-- Autorizada (admin, específica de org, con control de permisos)
|-- ¿Basada en roles? --> auth.protect({ role: "org:admin" })
+-- ¿Basada en permisos? --> auth.protect((has) => has({ permission: "org:feature:action" }))
Elección del Componente
¿Qué UI de autenticación necesitas?
|-- Página de inicio de sesión completa --> <SignIn /> en ruta comodín
|-- Página de registro completa --> <SignUp /> en ruta comodín
|-- Botón de inicio de sesión (modal) --> <SignInButton />
|-- Avatar de usuario + menú --> <UserButton />
|-- Editor de perfil completo --> <UserProfile />
|-- Selector de org --> <OrganizationSwitcher />
+-- Contenido condicional --> <Show when="signed-in"> o <Show when={{ role: "..." }}>
</decision_framework>
<red_flags>
BANDERAS ROJAS
Problemas de Alta Prioridad:
- Importar ayudantes del servidor desde
@clerk/nextjsen lugar de@clerk/nextjs/server(se rompe en Server Components) - Usar componentes obsoletos
<SignedIn>/<SignedOut>/<Protect>(eliminados en Core 3) - Pasar el objeto completo de
currentUser()a los componentes del cliente (filtraprivateMetadata) - Confiar en payloads de webhook sin verificación de firma
verifyWebhook - Depender únicamente del middleware para la protección de rutas (el middleware puede ser omitido)
- Importar tipos desde
@clerk/typesen lugar de@clerk/shared/types(cambio de ruta en Core 3) - Usar variables de entorno incorrectas (ej.,
CLERK_WEBHOOK_SECRETen lugar deCLERK_WEBHOOK_SIGNING_SECRET) - No usar claves de entorno y codificarlas directamente en el código (
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY,CLERK_SECRET_KEY)
Problemas de Mediana Prioridad:
- No verificar
isLoadedantes de acceder a datos en hooks del cliente (causa errores de hidratación) - No envolver
getToken()en try/catch ignorandoClerkOfflineError(Core 3) - Usar segmentos de ruta estáticos en lugar de comodín
[[...sign-in]]para páginas de inicio de sesión
Problemas de Baja Prioridad:
- Ubicación incorrecta de ClerkProvider (Core 3 requiere que esté dentro de
<body>, no envolviendo<html>) - No desestructurar props de ClerkProvider antes de pasarlas (ej., pasar
appearancepara componentes internos)
</red_flags>
<tools_and_commands>
Comandos y Herramientas
CLI de Clerk
# Instalar Clerk en un proyecto existente
npx clerk@latest sync
# Iniciar sesión en Clerk desde el terminal
npx clerk@latest sign-in
# Listar todos los comandos disponibles
npx clerk@latest --help
Variables de Entorno
Crea un archivo .env.local (nunca .env o .env.production para evitar commits accidentales):
# OBLIGATORIO: Clave publicable del Dashboard de Clerk
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_live_Y2xlcmsuZXhhbXBsZS5jb20=
# OBLIGATORIO: Clave secreta del Dashboard de Clerk
CLERK_SECRET_KEY=sk_live_ZXhhbXBsZS5jb20=
# OPCIONAL: Secreto de firma para webhooks (solo si usas webhooks)
CLERK_WEBHOOK_SIGNING_SECRET=whsec_ZXhhbXBsZS5jb20=
Comandos de Referencia Rápida
| Comando | Descripción |
|---|---|
npx clerk@latest sync | Sincronizar la configuración local con Clerk Dashboard |
npx clerk@latest sign-in | Iniciar sesión en Clerk CLI |
npx clerk@latest --help | Mostrar todos los comandos de CLI |
npm install @clerk/nextjs | Instalar el SDK de Clerk para Next.js (Core 2) |
npm install @clerk/nextjs@core3 | Instalar Clerk Core 3 (Next.js 16+) |
Dependencias de Paquete
{
"dependencies": {
"@clerk/nextjs": "^5.0.0"
}
}
Para Core 3 (Next.js 16+):
{
"dependencies": {
"@clerk/nextjs": "^6.0.0-core.0"
}
}
</tools_and_commands>
<quality_assurance>
Garantía de Calidad
Lista de Verificación de Integración
-
ClerkProviderenvuelve la aplicación dentro de<body>(Core 3 no permite envolver<html>) -
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEYyCLERK_SECRET_KEYestablecidas en.env.local -
clerkMiddleware()configurado concreateRouteMatcher()para rutas públicas - Las rutas de la API de webhook son públicas en el middleware (verificadas por separado por
verifyWebhook) -
auth.protect()o verificaciónauth()en cada Server Action y Route Handler -
currentUser()nunca se pasa directamente al cliente (seleccionar campos seguros) - Uso de
<Show>con la propwhenen lugar de<SignedIn>/<SignedOut>/<Protect> - Los hooks del lado del cliente verifican
isLoadedantes de acceder a datos - Las organizaciones están configuradas con roles y permisos (si se usan)
- Los manejadores de webhook usan
verifyWebhookpara verificar firmas - Todos los imports del servidor provienen de
@clerk/nextjs/server, nunca de@clerk/nextjs - Los tipos importados de
@clerk/shared/types(Core 3) o@clerk/types(Core 2) -
getToken()envuelto en try/catch para manejarClerkOfflineError(Core 3)
Escenarios de Prueba
- Prueba básica de autenticación: Acceder a una ruta protegida sin sesión → redirigir a inicio de sesión
- Prueba de cierre de sesión: Cerrar sesión → no poder acceder a rutas protegidas
- Prueba de semilla de webhook: Enviar un webhook de prueba desde el Dashboard de Clerk → los datos aparecen en la base de datos
- Prueba de organización: Cambiar de organización → el contenido cambia para reflejar la organización activa
- Prueba de permiso: Usuario sin permiso intenta acceder a ruta restringida → redirigir/bloquear
- Prueba de rol: Admin vs vistas de miembro → renderizado condicional funciona
- Prueba de estado de carga: Esperar a que ClerkProvider cargue → no hay parpadeo de contenido no autenticado
- Prueba de falsificación de webhook: Enviar payload POST sin firma → el manejador rechaza (código de estado 401)
Problemas Conocidos
- Next.js 16 cambia el nombre del archivo de middleware: Usar
proxy.tsen lugar demiddleware.ts(Core 3) getToken()puede lanzarClerkOfflineError: En Core 3, getToken lanza cuando está desconectado en lugar de devolver null -- siempre usar try/catch- La protección de middleware puede ser omitida: CVE-2025-29927 demostró que el middleware de Next.js puede ser eludido -- siempre verificar la autenticación en el backend
- ClerkProvider no puede envolver
<html>: Core 3 requiere que ClerkProvider esté dentro de<body> - Los tipos de webhook cambian en Core 3: Importar desde
@clerk/shared/typesen lugar de@clerk/types
</quality_assurance>