Auth Kit
Componente de autenticación todo en uno (login, registro, cambio de contraseña, olvido/restablecimiento de contraseña, verificación de email + reenvío) con un widget opcional de Cloudflare Turnstile y campos opcionales declarativos (nombre/apellidos, teléfono, consentimiento legal, tipo de perfil). Núcleo headless + hooks que conectas a tu propio backend — este componente no incluye credenciales ni servidor de autenticación. Adaptadores para React, Svelte, Vue, Angular y vanilla, cada uno con presentaciones headless/tailwind/shadcn/vanilla.
Abrir playground · Auth Kit en vivoVariables de entorno
Este componente no requiere variables de entorno.
Instalación manual
Descarga el tarball y copia a tu proyecto los archivos de tu framework:
curl -L -o auth-kit.tar.gz https://modularcorehub.com/registry/auth-kit.tar.gz
tar -xzf auth-kit.tar.gz | Origen | Destino en tu proyecto |
|---|---|
adapters/react/use-auth-kit.ts | src/modularcore/auth-kit/adapters/react/use-auth-kit.ts |
core/auth-kit.ts | src/modularcore/auth-kit/core/auth-kit.ts |
core/field-config.ts | src/modularcore/auth-kit/core/field-config.ts |
core/password-strength.ts | src/modularcore/auth-kit/core/password-strength.ts |
core/turnstile.ts | src/modularcore/auth-kit/core/turnstile.ts |
core/types.ts | src/modularcore/auth-kit/core/types.ts |
core/validation.ts | src/modularcore/auth-kit/core/validation.ts |
ui/react/ChangePasswordForm.tsx | src/modularcore/auth-kit/ui/react/ChangePasswordForm.tsx |
ui/react/ForgotPasswordForm.tsx | src/modularcore/auth-kit/ui/react/ForgotPasswordForm.tsx |
ui/react/LoginForm.tsx | src/modularcore/auth-kit/ui/react/LoginForm.tsx |
ui/react/RegisterForm.tsx | src/modularcore/auth-kit/ui/react/RegisterForm.tsx |
ui/react/ResetPasswordForm.tsx | src/modularcore/auth-kit/ui/react/ResetPasswordForm.tsx |
ui/react/TurnstileWidget.tsx | src/modularcore/auth-kit/ui/react/TurnstileWidget.tsx |
ui/react/VerifyEmailForm.tsx | src/modularcore/auth-kit/ui/react/VerifyEmailForm.tsx |
ui/react/icons.tsx | src/modularcore/auth-kit/ui/react/icons.tsx |
ui/react/internal/change-password-form-state.ts | src/modularcore/auth-kit/ui/react/internal/change-password-form-state.ts |
ui/react/internal/forgot-password-form-state.ts | src/modularcore/auth-kit/ui/react/internal/forgot-password-form-state.ts |
ui/react/internal/login-form-state.ts | src/modularcore/auth-kit/ui/react/internal/login-form-state.ts |
ui/react/internal/register-form-state.ts | src/modularcore/auth-kit/ui/react/internal/register-form-state.ts |
ui/react/internal/reset-password-form-state.ts | src/modularcore/auth-kit/ui/react/internal/reset-password-form-state.ts |
ui/react/internal/verify-email-form-state.ts | src/modularcore/auth-kit/ui/react/internal/verify-email-form-state.ts |
ui/react/shadcn/ChangePasswordForm.tsx | src/modularcore/auth-kit/ui/react/shadcn/ChangePasswordForm.tsx |
ui/react/shadcn/ForgotPasswordForm.tsx | src/modularcore/auth-kit/ui/react/shadcn/ForgotPasswordForm.tsx |
ui/react/shadcn/LoginForm.tsx | src/modularcore/auth-kit/ui/react/shadcn/LoginForm.tsx |
ui/react/shadcn/RegisterForm.tsx | src/modularcore/auth-kit/ui/react/shadcn/RegisterForm.tsx |
ui/react/shadcn/ResetPasswordForm.tsx | src/modularcore/auth-kit/ui/react/shadcn/ResetPasswordForm.tsx |
ui/react/shadcn/VerifyEmailForm.tsx | src/modularcore/auth-kit/ui/react/shadcn/VerifyEmailForm.tsx |
ui/react/tailwind/ChangePasswordForm.tsx | src/modularcore/auth-kit/ui/react/tailwind/ChangePasswordForm.tsx |
ui/react/tailwind/ForgotPasswordForm.tsx | src/modularcore/auth-kit/ui/react/tailwind/ForgotPasswordForm.tsx |
ui/react/tailwind/LoginForm.tsx | src/modularcore/auth-kit/ui/react/tailwind/LoginForm.tsx |
ui/react/tailwind/RegisterForm.tsx | src/modularcore/auth-kit/ui/react/tailwind/RegisterForm.tsx |
ui/react/tailwind/ResetPasswordForm.tsx | src/modularcore/auth-kit/ui/react/tailwind/ResetPasswordForm.tsx |
ui/react/tailwind/VerifyEmailForm.tsx | src/modularcore/auth-kit/ui/react/tailwind/VerifyEmailForm.tsx |
ui/react/vanilla/ChangePasswordForm.tsx | src/modularcore/auth-kit/ui/react/vanilla/ChangePasswordForm.tsx |
ui/react/vanilla/ForgotPasswordForm.tsx | src/modularcore/auth-kit/ui/react/vanilla/ForgotPasswordForm.tsx |
ui/react/vanilla/LoginForm.tsx | src/modularcore/auth-kit/ui/react/vanilla/LoginForm.tsx |
ui/react/vanilla/RegisterForm.tsx | src/modularcore/auth-kit/ui/react/vanilla/RegisterForm.tsx |
ui/react/vanilla/ResetPasswordForm.tsx | src/modularcore/auth-kit/ui/react/vanilla/ResetPasswordForm.tsx |
ui/react/vanilla/VerifyEmailForm.tsx | src/modularcore/auth-kit/ui/react/vanilla/VerifyEmailForm.tsx |
ui/shadcn-theme.css | src/modularcore/auth-kit/ui/shadcn-theme.css |
ui/vanilla-styles.css | src/modularcore/auth-kit/ui/vanilla-styles.css |
Instalación con CLI
modularcore add auth-kitDocumentación
@modularcore/auth-kit es un componente de autenticación todo-en-uno — login, registro, cambio de contraseña, recuperar/resetear contraseña y verificación de email con reenvío — con un widget opcional de Cloudflare Turnstile y campos opcionales declarativos (nombre, apellidos, teléfono, checkbox de condiciones legales, selector de tipo de perfil). Adaptadores para React, Svelte, Vue, Angular standalone y páginas sin framework (vanilla).
Ninguna credencial ni librería de auth vive en este componente — y no la necesita
AuthKitHooks (ver core/types.ts) es el único punto de contacto con tu lógica de autenticación. Tú implementas onLogin, onRegister y el resto de hooks contra lo que ya uses: una API REST propia, Better Auth, Auth.js/NextAuth, Supabase Auth, Clerk, Firebase Auth, Laravel Sanctum/Fortify, Django, o cualquier otra cosa. El componente nunca inspecciona credenciales ni la forma de tu sesión: solo llama al hook que le diste y reporta su resultado a través de un estado por flujo (idle → submitting → success/error).
Better Auth no es un requisito ni una dependencia — es solo uno más de los backends contra los que puedes cablear los hooks, igual que cualquier otro.
Qué incluye el paquete
core/auth-kit.ts—AuthKit, el orquestador headless: una instancia por conjunto de hooks, con una máquina de estados por flujo para cada uno de los siete flujos. Llamar a un flujo cuyo hook no está configurado rechaza con un error claro en vez de no hacer nada.core/types.ts— el contratoAuthKitHooksy cada tipo de payload. SoloonLoginyonRegisterson obligatorios.core/field-config.ts— configuración declarativa y opt-in parafirstName,lastName,phone,legalConsent(checkbox + enlaces),profileType(selector, con tus propias opciones/etiquetas) yturnstile. Nada se activa si no lo indicas — por defecto es un formulario simple de email + contraseña.core/validation.ts— esquemas Zod v4 (buildLoginSchema,buildRegisterSchema, …) y una política de contraseña configurable. Puedes pasar tu propio esquema si necesitas otras reglas.core/turnstile.ts—TurnstileController+mountTurnstileWidget, un binding headless de Cloudflare Turnstile. No hace nada (trata el check como superado) si no hay site key configurada, así que el desarrollo local funciona sin cuenta de Cloudflare.adapters/react,adapters/svelte— bindings finos sobreAuthKit(el adaptador de Svelte usa runas de Svelte 5).adapters/vue,adapters/angular— bindings por componente sobre el mismo core. Vue usa unshallowRef; Angular usa unsignalmásDestroyRef(no es un servicio raíz — una instancia deAuthKitpor formulario/página, igual queMediaPickerService).adapters/vanilla— binding sin framework: exponesubscribe/destroyen vez de apoyarse en un sistema reactivo o en un ciclo de vida ajeno.ui/<framework>/*— los seis formularios (LoginForm,RegisterForm,ChangePasswordForm,ForgotPasswordForm,ResetPasswordForm,VerifyEmailForm) másTurnstileWidget, cada uno en cuatro presentaciones.
Cablear tu propio backend
Cada flujo es solo una función que tú implementas. Configuración mínima con solo email + contraseña (el valor por defecto, sin ninguna configuración de campos):
import { useAuthKit } from '@modularcore/auth-kit/react';
const authKit = useAuthKit({
onLogin: async ({ identifier, password }) => {
const res = await fetch('/api/login', {
method: 'POST',
body: JSON.stringify({ identifier, password }),
});
if (!res.ok) throw new Error('Credenciales incorrectas');
return res.json();
},
onRegister: async (payload) => {
const res = await fetch('/api/register', { method: 'POST', body: JSON.stringify(payload) });
if (!res.ok) throw new Error('No se pudo crear la cuenta');
return res.json();
},
});
onChangePassword, onForgotPassword, onResetPassword, onVerifyEmail y onResendVerification son opcionales — solo implementas los flujos para los que realmente renderizas un formulario. Llamar a <ChangePasswordForm> sin un hook onChangePassword rechaza con un error descriptivo en vez de fallar en silencio.
Con Better Auth
No hace falta ningún adaptador — el cliente de Better Auth ya expone lo que estos hooks necesitan:
import { createAuthClient } from 'better-auth/client';
import { useAuthKit } from '@modularcore/auth-kit/react';
const client = createAuthClient();
const authKit = useAuthKit({
onLogin: async ({ identifier, password }) => {
const { data, error } = await client.signIn.email({ email: identifier, password });
if (error) throw new Error(error.message);
return data;
},
onRegister: async ({ email, password, firstName, lastName }) => {
const { data, error } = await client.signUp.email({
email,
password,
name: [firstName, lastName].filter(Boolean).join(' '),
});
if (error) throw new Error(error.message);
return data;
},
});
Con Auth.js/NextAuth, Supabase, Clerk, Firebase, o cualquier otra
El patrón es idéntico: llamas a los métodos de sign-in/sign-up/contraseña de esa librería o backend dentro del hook correspondiente, y lanzas un error cuando reporta fallo. AuthKit no le importa qué librería produjo el resultado — solo necesita una promesa que resuelva en éxito y rechace en fallo.
Campos opcionales declarativos
Nada más allá de email + contraseña se renderiza hasta que lo activas:
import { RegisterForm } from '@modularcore/auth-kit/ui/react/RegisterForm';
<RegisterForm
authKit={authKit}
fieldConfig={{
phone: { enabled: true },
profileType: {
enabled: true,
label: 'Tipo de cuenta',
options: [
{ value: 'user', label: 'Usuario' },
{ value: 'collaborator', label: 'Colaborador' },
],
},
legalConsent: {
enabled: true,
text: 'Acepto los',
links: [
{ label: 'Términos', href: '/legal/terminos' },
{ label: 'Política de privacidad', href: '/legal/privacidad' },
],
},
turnstile: { enabled: true, siteKey: import.meta.env.PUBLIC_TURNSTILE_SITE_KEY },
}}
/>;
profileType es un campo de formulario configurable sin más — no lleva lógica de roles/permisos. Qué hace tu backend con ese valor (asignar un rol, encolar una aprobación, etiquetar la cuenta) es responsabilidad de tu hook onRegister.
Cloudflare Turnstile
TurnstileWidget (y la prop turnstile/fieldConfig.turnstile de cualquier formulario) es solo una comprobación del lado del cliente. Verifica el token en el servidor, dentro del mismo hook que lo recibe:
onLogin: async ({ identifier, password, turnstileToken }) => {
const verify = await fetch('https://challenges.cloudflare.com/turnstile/v0/siteverify', {
method: 'POST',
body: new URLSearchParams({ secret: process.env.TURNSTILE_SECRET_KEY!, response: turnstileToken ?? '' }),
});
if (!(await verify.json()).success) throw new Error('Verificación anti-bot fallida');
// ...sigue con el login real
};
Cuando no hay siteKey configurada, el widget no renderiza nada y reporta el check como ya superado (turnstileToken: '') — el mismo comportamiento no-op en desarrollo que tendría una integración de Turnstile hecha a mano.
Uso sin framework (Astro, Blade, HTMX…)
Los demás adaptadores traducen el estado del núcleo al sistema reactivo de su framework y usan su ciclo de vida para darse de baja. En una página sin framework no existe ninguno de los dos, así que createAuthKitStore expone la suscripción tal cual y deja la limpieza en tus manos:
import { createAuthKitStore } from '@modularcore/auth-kit/vanilla';
import { mountLoginForm } from '@modularcore/auth-kit/ui/vanilla/LoginForm';
const authKit = createAuthKitStore({ onLogin, onRegister });
const unmount = mountLoginForm(document.querySelector('#login'), { authKit });
// al desmontar la isla, la página o el widget:
unmount();
authKit.destroy();
Variantes de estilo de UI
Cada uno de los seis formularios más TurnstileWidget se distribuye en cuatro presentaciones, todas con las mismas props/opciones — solo cambia el marcado/CSS:
ui/<framework>/*— UI headless, sin clases CSS, totalmente estilizable por quien lo instala.ui/<framework>/tailwind/— solo clases utilitarias de Tailwind.ui/<framework>/shadcn/— aspecto Shadcn vía los tokens de diseño compartidos deui/shadcn-theme.css. La variante de React usa además@radix-ui/react-checkbox,@radix-ui/react-labely@radix-ui/react-selectcomo peer dependencies opcionales; Svelte, Vue, Angular y vanilla usan marcado accesible plano con los mismos tokens (no existe un puerto de Radix maduro para esos frameworks en este repositorio todavía).ui/<framework>/vanilla/— CSS plano (ui/vanilla-styles.css, prefijo de claseauth-kit-*), sin dependencia de librería de UI, funciona con o sin bundler.
Prueba este componente en vivo en el Playground de Auth Kit.