Añadir 2FA a tu app Next.js en 30 minutos con Better Auth
Tutorial paso a paso para añadir autenticación en dos pasos (TOTP) a una app Next.js usando el plugin de Better Auth.
Israel Palma
3 min de lectura
Añadir 2FA (autenticación en dos pasos) a una app Next.js solía ser doloroso. En 2026, con Better
Auth y su plugin de TOTP, son 30 minutos bien organizados.
Esta guía asume que ya tienes Better Auth funcionando. Si no, empieza por su quickstart y luego
vuelve aquí.
## Qué vas a tener al final
- Generación de QR para apps tipo Google Authenticator, 1Password, Authy
- Verificación TOTP en login
- Backup codes para recuperación
- Endpoint para activar/desactivar 2FA por el usuario
## Paso 1: instalar el plugin
```bash
bun add better-auth
```
Ya lo tienes instalado, solo activamos el plugin.
## Paso 2: configurar Better Auth
`src/lib/auth/server.ts`:
```ts
import { betterAuth } from 'better-auth';
import { twoFactor } from 'better-auth/plugins';
import { prismaAdapter } from 'better-auth/adapters/prisma';
import { db } from '@/lib/db/client';
export const auth = betterAuth({
database: prismaAdapter(db, { provider: 'postgresql' }),
emailAndPassword: { enabled: true },
plugins: [
twoFactor({
issuer: 'CREA.MBA',
}),
],
});
```
## Paso 3: schema de Prisma
Better Auth necesita tablas adicionales para 2FA. En tu `schema.prisma`:
```prisma
model TwoFactor {
id String @id
secret String
backupCodes String
userId String
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
}
```
Después:
```bash
bunx prisma migrate dev --name add-2fa
```
## Paso 4: endpoint para iniciar setup
Cuando el usuario activa 2FA, generas un secreto TOTP y le devuelves un QR para que escanee.
`src/app/api/auth/2fa/enable/route.ts`:
```ts
import { auth } from '@/lib/auth/server';
import { headers } from 'next/headers';
import { NextResponse } from 'next/server';
export async function POST() {
const session = await auth.api.getSession({ headers: await headers() });
if (!session) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}
const result = await auth.api.enableTwoFactor({
body: { password: '' }, // pedir contraseña en producción
headers: await headers(),
});
return NextResponse.json({
qrCode: result.totpURI,
backupCodes: result.backupCodes,
});
}
```
## Paso 5: componente de UI
```tsx
'use client';
import { useState } from 'react';
import QRCode from 'react-qr-code';
export function Enable2FA() {
const [qrCode, setQrCode] = useState(null);
const [backupCodes, setBackupCodes] = useState([]);
const [verificationCode, setVerificationCode] = useState('');
async function startSetup() {
const res = await fetch('/api/auth/2fa/enable', { method: 'POST' });
const data = await res.json();
setQrCode(data.qrCode);
setBackupCodes(data.backupCodes);
}
async function verifyAndActivate() {
const res = await fetch('/api/auth/2fa/verify', {
method: 'POST',
body: JSON.stringify({ code: verificationCode }),
headers: { 'Content-Type': 'application/json' },
});
if (res.ok) alert('2FA activado');
}
if (!qrCode) {
return ;
}
return (
Escanea con tu app de autenticación:
Códigos de respaldo (guarda estos):
{backupCodes.map((c) => (
{c}
))}
setVerificationCode(e.target.value)}
placeholder="Código de 6 dígitos"
/>
);
}
```
## Paso 6: integrar en el login
En tu pantalla de login, después de validar email + password, comprueba si el usuario tiene 2FA
activo. Si lo tiene, pídele el código TOTP antes de crear la sesión.
```tsx
async function handleLogin(email: string, password: string) {
const res = await auth.signIn.email({ email, password });
if (res.requiresTwoFactor) {
setShow2FAInput(true);
return;
}
// login completo
}
async function handle2FACode(code: string) {
const res = await auth.twoFactor.verifyTotp({ code });
if (res.success) {
// login completo
}
}
```
## Paso 7: backup codes
Si el usuario pierde el móvil, los backup codes salvan vidas. Better Auth los genera
automáticamente. Cuando uno se usa, se invalida (no son reutilizables).
Tu UI debe permitir regenerarlos cuando el usuario quiera. Es buena práctica regenerarlos cada 6-12
meses.
## Errores comunes
**1. El código no es válido**: la hora del servidor y del móvil deben estar sincronizadas. Si tu
servidor desfasa más de 30 segundos, los códigos TOTP fallan.
**2. El QR no escanea**: muchas apps prefieren `otpauth://totp/...` en lugar de la URL pelada.
Better Auth ya lo da en formato correcto.
**3. Backup codes en texto plano**: nunca guardes los backup codes en plano. Better Auth los hashea
por defecto.
## Conclusión
30 minutos para 2FA bien hecho. La parte más larga suele ser la UI del setup; la lógica del plugin
Better Auth es prácticamente plug-and-play.
Hoy no hay excusa para no tenerlo. Y a tus usuarios B2B les va a parar el oído.
¿Te gustó este artículo?
Suscríbete para más tutoriales y tips sobre crear productos con IA