Cambiar tema

Tutorial de NextAuth.js: guía completa de inicio de sesión con Credentials y gestión de sesiones

Easton editorial illustration: component assembly loom

Abrí la documentación oficial de NextAuth.js y la pantalla se llenó de opciones de configuración. Provider, Session, Adapter, JWT, Callbacks… cada término lo conocía por separado, pero juntos no sabía qué significaban. La documentación dice «JWT por defecto», pero en el siguiente párrafo explica que con base de datos cambia automáticamente a Session: ¿cuál debo usar?

En ese momento casi me rendí y pasé a Clerk. Pero pensándolo bien, Clerk es rápido, pero cuando se agota la cuota gratuita de 10.000 usuarios activos al mes hay que pagar, y muchas personalizaciones no se pueden tocar. NextAuth.js cuesta más al principio, pero es gratis y tienes control total.

Este artículo no te va a listar todas las opciones de configuración (sería agotador). Se centra en el escenario más habitual: inicio de sesión con Credentials (usuario y contraseña), gestión de sesiones e integración con base de datos. Si dominas estos puntos, NextAuth.js deja de parecer tan difícil.

Entender los conceptos clave de NextAuth.js

¿Qué hace realmente NextAuth.js?

En una frase: NextAuth.js es un «middleware de autenticación» que gestiona «quién ha iniciado sesión» y «cómo se guarda ese estado». No le importa qué base de datos uses ni cómo sea tu UI; solo verifica la identidad del usuario y recuerda el estado de la sesión.

¿En qué se diferencia de Clerk y Supabase Auth? En resumen:

  • Clerk: te da una UI de login bonita y un panel de usuarios; en 30 minutos está listo, pero cuesta dinero (más de 10.000 usuarios activos al mes)
  • Supabase Auth: si usas la base de datos de Supabase, la autenticación prácticamente viene incluida y la integración es muy fluida
  • NextAuth.js: completamente gratuito y open source, pero la interfaz de login, el registro y la lógica de base de datos los implementas tú

La tendencia en 2025 es curiosa: NextAuth.js sigue siendo muy usado, pero soluciones «listas para usar» como Clerk están ganando mercado rápido. Lo entiendo: dedicar días a código de autenticación cuando podrías centrarte en la funcionalidad principal. Pero si es un proyecto personal o quieres control total, NextAuth.js merece la pena aprenderlo.

Tres conceptos que debes entender

1. Provider (proveedor): ¿cómo inicia sesión el usuario?

Es el método de login. NextAuth.js soporta más de 50; los más habituales:

  • Proveedores OAuth: Google, GitHub, Facebook… un clic y listo, sin gestionar contraseñas
  • Proveedor Credentials: usuario + contraseña, el método clásico y el foco de este artículo

Hay un matiz: Credentials es el más flexible, pero también el que más código exige. La documentación oficial ni siquiera lo recomienda del todo, porque el riesgo de seguridad recae en ti: cifrado de contraseñas, protección contra fuerza bruta, gestión de sesiones, todo depende de ti.

2. Session (sesión): ¿cómo recordar al usuario tras el login?

El usuario inicia sesión una vez; no puede volver a escribir la contraseña en cada petición. La sesión es el mecanismo que «recuerda» el estado de login. Hay dos formas:

  • JWT Session: la información de login se cifra y va en una cookie; el servidor no guarda nada
  • Database Session: la cookie solo guarda un ID; la información real está en la base de datos

Elegir entre estas dos es lo que más confunde a quien empieza con NextAuth.js. En la siguiente sección lo detallamos.

3. Adapter (adaptador): ¿dónde se guardan los datos del usuario?

Si quieres guardar información de usuario en base de datos (email, fecha de registro, etc.), necesitas un Adapter. NextAuth.js soporta Prisma, MongoDB, MySQL y otras.

Pero hay un punto clave: el proveedor Credentials no guarda automáticamente usuarios en la base de datos. La documentación oficial lo deja claro: con Credentials, las cuentas las gestionas tú. NextAuth.js solo verifica el login, no el registro ni el almacenamiento.

JWT vs Session: ¿cuál elegir?

Es la pregunta central. Leí una docena de artículos comparativos y seguía sin decidirme hasta entender la diferencia de fondo.

JWT Session: modelo del pasaporte

Imagina que viajas al extranjero: el pasaporte lleva tu foto, nombre y fecha de caducidad. En cada control fronterizo miran el pasaporte sin consultar un sistema. JWT funciona igual: tras el login, el servidor genera un token cifrado (como un pasaporte) con userId, email, etc., y lo guarda en la cookie del navegador.

Ventajas:

  • Rápido. Sin consultar la base de datos; descifras el token y sabes quién es el usuario
  • Económico. No necesitas base de datos para sesiones, ideal en despliegues serverless
  • Escalable. Muchos usuarios no son problema; no hay tabla de sesiones que explote

Inconvenientes:

  • No puedes forzar el cierre de sesión. ¿Cuenta comprometida y quieres invalidar un login? No, hasta que expire el token (salvo que mantengas una lista negra, y eso vuelve a requerir base de datos)
  • No puedes limitar dispositivos simultáneos. ¿«Máximo 3 dispositivos a la vez»? Imposible
  • No actualizas información antes de que expire el token. ¿Guardaste el rol en el token y luego cambió? Hasta que expire, el usuario ve el rol antiguo

Database Session: modelo de la tarjeta de hotel

El hotel te da una tarjeta con solo el número de habitación; tus datos (pasaporte, consumos) están en el sistema del hotel. Cada vez que pasas la tarjeta, consultan tu habitación y confirman el acceso. Database Session es parecido: la cookie solo guarda un session ID; la información real está en la base de datos.

Ventajas:

  • Puedes revocar sesiones en cualquier momento. ¿El usuario pulsa «cerrar sesión en todos los dispositivos»? Borras los registros de sesión en la base de datos
  • Puedes limitar cuántos dispositivos pueden estar conectados
  • Puedes actualizar datos del usuario al instante (por ejemplo, si cambian los permisos, el siguiente request ya los refleja)

Inconvenientes:

  • Más lento. Cada petición consulta la base de datos
  • Hay que gestionar la tabla de sesiones (crear, limpiar expiradas)
  • Con muchos usuarios, la presión sobre la base de datos sube

Árbol de decisión (lo importante)

¿Cuál elegir? Mi recomendación:

¿Tu app necesita «forzar nuevo inicio de sesión»? (contraseña cambiada, cuenta bloqueada)
  ├─ Sí → Usa Database Session
  └─ No → Sigue leyendo

¿Quieres evitar base de datos o desplegar en serverless?
  ├─ Sí → Usa JWT
  └─ No → Sigue leyendo

¿Es un proyecto personal/MVP y priorizas salir rápido?
  ├─ Sí → Usa JWT (simple y práctico)
  └─ No → Usa Database Session (más sólido en entorno empresarial)

Mi primer proyecto usó JWT; luego añadí un panel de administración que necesitaba «echar» usuarios, y pasé a Database Session. La migración costó bastante; conviene decidirlo desde el principio.

Caso especial: Credentials + Database Session

Si usas Credentials y quieres Database Session, te encuentras un obstáculo grande: la documentación oficial dice que no está soportado.

En concreto, los proveedores OAuth (Google, GitHub) encajan bien con Database Session, pero Credentials no. NextAuth.js considera Credentials demasiado flexible para crear registros de sesión automáticamente.

Hay soluciones, pero implican código manual en el callback signIn para crear la sesión tú mismo. En GitHub hay muchos hilos (como este issue) con enfoques listos para usar. Si eres principiante, yo usaría JWT o cambiaría a OAuth; no te compliques la vida.

Configuración completa del proveedor Credentials

Bien, teoría hecha; ahora código. Tres ejemplos, de simple a completo; elige según tu fase.

Configuración básica: versión mínima que funciona

Primero instala dependencias:

npm install next-auth

Luego crea el archivo. En Next.js 13+ con App Router la ruta es app/api/auth/[...nextauth]/route.ts; con Pages Router, pages/api/auth/[...nextauth].js. Aquí uso App Router.

app/api/auth/[…nextauth]/route.ts:

import NextAuth from "next-auth"
import CredentialsProvider from "next-auth/providers/credentials"

const handler = NextAuth({
  providers: [
    CredentialsProvider({
      name: 'Credentials',
      credentials: {
        email: { label: "Correo", type: "email" },
        password: { label: "Contraseña", type: "password" }
      },
      async authorize(credentials) {
        // Usuario hardcodeado para pruebas
        if (credentials?.email === "[email protected]" &&
            credentials?.password === "123456") {
          return {
            id: "1",
            name: "Usuario de prueba",
            email: "[email protected]"
          }
        }
        return null  // Login fallido
      }
    })
  ],
  session: {
    strategy: "jwt"  // Usar sesión JWT
  },
  pages: {
    signIn: '/login'  // Página de login personalizada (opcional)
  }
})

export { handler as GET, handler as POST }

Variables de entorno .env.local:

NEXTAUTH_SECRET=your-super-secret-key-change-this
NEXTAUTH_URL=http://localhost:3000

Puntos clave:

  • NEXTAUTH_SECRET: cifra el token. Obligatorio en producción; en local también conviene definirlo. Generación: openssl rand -base64 32
  • Función authorize: lógica central de verificación. Devuelve el objeto usuario si es correcto, null si falla

Esta versión arranca, pero los usuarios están hardcodeados; no sirve en producción. El siguiente paso es conectar la base de datos.

Configuración clave explicada

Antes del ejemplo completo, aclaremos las opciones que más confunden.

1. session.strategy: ¿“jwt” o “database”?

Por defecto es "jwt". Si usas un Adapter (base de datos), cambia automáticamente a "database". Pero con Credentials + JWT debes poner explícitamente strategy: "jwt" o puede fallar.

2. callbacks: ¿cómo añadir campos personalizados a la sesión?

Por defecto, useSession devuelve solo name, email, image. ¿Quieres userId o role?

Usa callbacks:

callbacks: {
  async jwt({ token, user }) {
    // user solo tiene valor al iniciar sesión
    if (user) {
      token.userId = user.id  // Añadir userId al token
    }
    return token
  },
  async session({ session, token }) {
    // Pasar userId del token a la sesión
    session.user.userId = token.userId
    return session
  }
}

Así, en el frontend const { data: session } = useSession() tendrá session.user.userId.

3. pages: página de login personalizada

NextAuth.js trae una página de login fea en /api/auth/signin. Para tu propia UI: pages: { signIn: '/login' }.

Tu página de login debe llamar a:

import { signIn } from "next-auth/react"

const handleSubmit = async (e) => {
  e.preventDefault()
  const result = await signIn('credentials', {
    redirect: false,
    email,
    password
  })

  if (result?.error) {
    // Login fallido
  } else {
    // Login correcto, redirigir
  }
}

Ejemplo completo: registro + login + gestión de sesión

Versión usable de verdad. Supongamos Prisma + PostgreSQL.

1. Crear tabla de usuarios

prisma/schema.prisma:

model User {
  id        String   @id @default(cuid())
  email     String   @unique
  password  String
  name      String?
  createdAt DateTime @default(now())
}

Ejecuta npx prisma migrate dev para crear la tabla.

2. Endpoint de registro

app/api/register/route.ts:

import { NextResponse } from "next/server"
import bcrypt from "bcryptjs"
import { prisma } from "@/lib/prisma"  // Suponiendo un cliente prisma

export async function POST(req: Request) {
  try {
    const { email, password, name } = await req.json()

    // Comprobar si el usuario ya existe
    const existingUser = await prisma.user.findUnique({
      where: { email }
    })

    if (existingUser) {
      return NextResponse.json(
        { error: "El correo ya está registrado" },
        { status: 400 }
      )
    }

    // Cifrar contraseña (¡importante!)
    const hashedPassword = await bcrypt.hash(password, 10)

    // Crear usuario
    const user = await prisma.user.create({
      data: {
        email,
        password: hashedPassword,
        name
      }
    })

    return NextResponse.json({
      user: {
        id: user.id,
        email: user.email,
        name: user.name
      }
    })
  } catch (error) {
    return NextResponse.json(
      { error: "Error en el registro" },
      { status: 500 }
    )
  }
}

Importante: las contraseñas deben cifrarse. Usa bcrypt o argon2; nunca en texto plano en la base de datos.

3. Configuración de NextAuth (con base de datos)

app/api/auth/[...nextauth]/route.ts:

import NextAuth from "next-auth"
import CredentialsProvider from "next-auth/providers/credentials"
import bcrypt from "bcryptjs"
import { prisma } from "@/lib/prisma"

const handler = NextAuth({
  providers: [
    CredentialsProvider({
      credentials: {
        email: { label: "Correo", type: "email" },
        password: { label: "Contraseña", type: "password" }
      },
      async authorize(credentials) {
        if (!credentials?.email || !credentials?.password) {
          return null
        }

        // Buscar usuario en la base de datos
        const user = await prisma.user.findUnique({
          where: { email: credentials.email }
        })

        if (!user) {
          return null  // Usuario no existe
        }

        // Verificar contraseña
        const isValid = await bcrypt.compare(
          credentials.password,
          user.password
        )

        if (!isValid) {
          return null  // Contraseña incorrecta
        }

        // Devolver datos del usuario (¡sin la contraseña!)
        return {
          id: user.id,
          email: user.email,
          name: user.name
        }
      }
    })
  ],
  session: {
    strategy: "jwt",
    maxAge: 30 * 24 * 60 * 60  // 30 días
  },
  callbacks: {
    async jwt({ token, user }) {
      if (user) {
        token.userId = user.id
      }
      return token
    },
    async session({ session, token }) {
      session.user.userId = token.userId as string
      return session
    }
  },
  pages: {
    signIn: '/login'
  }
})

export { handler as GET, handler as POST }

4. Usar la sesión en el frontend

En Server Component:

import { getServerSession } from "next-auth"

export default async function ProfilePage() {
  const session = await getServerSession()

  if (!session) {
    redirect('/login')
  }

  return <div>Bienvenido, {session.user.name}</div>
}

En Client Component:

'use client'
import { useSession } from "next-auth/react"

export default function Dashboard() {
  const { data: session, status } = useSession()

  if (status === "loading") {
    return <div>Cargando...</div>
  }

  if (!session) {
    return <div>Inicia sesión primero</div>
  }

  return <div>Tu ID de usuario: {session.user.userId}</div>
}

Nota:

  • En servidor usa getServerSession()
  • En cliente usa useSession()
  • Los componentes cliente deben ir dentro de <SessionProvider> (normalmente en el layout)

Estrategias de sesión y problemas habituales

Usar bien las sesiones JWT

Si elegiste JWT, conviene aprovecharlo bien. Puntos clave:

1. Guardar información extra en el JWT (userId, role, etc.)

El ejemplo de callbacks ya lo mostró: el callback jwt añade al token; el callback session pasa esos datos a la sesión.

En proyectos reales a menudo hace falta el rol:

callbacks: {
  async jwt({ token, user }) {
    if (user) {
      token.userId = user.id
      token.role = user.role  // Suponiendo campo role en la BD
    }
    return token
  },
  async session({ session, token }) {
    session.user.userId = token.userId as string
    session.user.role = token.role as string
    return session
  }
}

2. Tiempo de expiración del token

Por defecto son 30 días, pero puedes cambiarlo:

session: {
  strategy: "jwt",
  maxAge: 7 * 24 * 60 * 60  // 7 días
}

Matiz: si el usuario cierra el navegador y lo vuelve a abrir, el token sigue ahí (salvo que cierre sesión manualmente). Para «cerrar sesión al cerrar el navegador» hay que manejarlo en el frontend; el JWT del backend no lo hace solo.

3. La mayor limitación del JWT: no se puede revocar activamente

Es lo que más frustra del JWT. ¿Cuenta comprometida y quieres cerrar la sesión al instante? No se puede. Mientras el token no expire, quien lo tenga sigue accediendo.

Alternativas:

  • Caducidad corta (por ejemplo 1 hora), a cambio de peor experiencia
  • Lista negra de tokens (pero vuelves a necesitar base de datos y pierdes ventaja del JWT)
  • Database Session (solución definitiva en muchos casos)

Implementar Database Session (incluido Credentials)

Si desde el principio elegiste Database Session, la configuración es más simple, siempre que uses proveedores OAuth (Google, GitHub).

Instala un Adapter:

npm install @next-auth/prisma-adapter

Configuración:

import { PrismaAdapter } from "@next-auth/prisma-adapter"
import { prisma } from "@/lib/prisma"

const handler = NextAuth({
  adapter: PrismaAdapter(prisma),
  providers: [
    GoogleProvider({
      clientId: process.env.GOOGLE_CLIENT_ID!,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET!
    })
  ]
  // strategy pasa a "database" automáticamente
})

La base de datos crea las tablas User, Session, Account, etc.

Pero con Credentials se complica. La documentación oficial dice claro: Credentials no soporta database session.

Hay muchas soluciones en la red; la idea central es crear registros de sesión a mano en el callback signIn. No lo recomiendo a principiantes: es fácil equivocarse. Si necesitas Credentials + Database Session, mira estos hilos:

O valora Clerk o Supabase Auth, que soportan esa combinación de forma nativa.

Los 5 errores más habituales

Los he pisado todos; te ahorran tiempo:

1. Olvidar NEXTAUTH_SECRET y romper producción

En local puedes no definir NEXTAUTH_SECRET (NextAuth.js avisa pero funciona). En Vercel, Railway, etc., sin esa variable: error 500 directo.

Generación: openssl rand -base64 32, y añádelo a las variables de entorno del despliegue.

2. Credentials no puede usar database session por defecto

Ya lo comentamos: Adapter + Credentials suele fallar. O pones session: { strategy: "jwt" } explícitamente, o implementas tú la creación de sesión.

3. useSession no funciona en Server Components

En Next.js 13+ App Router el default es Server Component. const session = useSession() en servidor falla: los hooks solo van en cliente.

En servidor:

import { getServerSession } from "next-auth"

const session = await getServerSession()

En cliente: directiva 'use client' y useSession().

4. Problemas de cookies entre dominios

Desarrollo en localhost:3000, producción en example.com: dominios distintos y se pierde el estado de sesión.

Solución: NEXTAUTH_URL en producción debe ser el dominio correcto. No hardcodees localhost.

5. Error JWEDecryptionFailed

Error: JWEDecryptionFailed: decryption operation failed

Causa: cambiaste NEXTAUTH_SECRET pero el navegador guarda tokens antiguos cifrados con el secreto viejo.

Solución: borra cookies del navegador o vuelve a iniciar sesión.

Extra: proteger rutas con Middleware

Para exigir login en ciertas páginas:

middleware.ts:

export { default } from "next-auth/middleware"

export const config = {
  matcher: ["/dashboard/:path*", "/profile/:path*"]
}

Así todo bajo /dashboard y /profile comprueba la sesión y redirige al login si hace falta.

Recomendaciones para proyectos reales y ruta de aprendizaje

¿Qué esquema elegir? Recomendaciones

Escenario 1: proyecto personal/MVP/blog

  • Recomendación: NextAuth.js + JWT + Credentials
  • Por qué: gratis, configuración simple, sin tabla de sesiones
  • Inconveniente: si luego necesitas «echar usuarios», migrar cuesta

Escenario 2: app empresarial/SaaS (con presupuesto)

  • Recomendación: Clerk
  • Por qué: configuración en 30 minutos, UI cuidada, gestión de usuarios completa, ahorra 40-80 horas de desarrollo
  • Inconveniente: de pago por encima de 10.000 MAU; personalización limitada
  • Alternativa: con base de datos Supabase, Supabase Auth también va muy bien (50.000 MAU gratis)

Escenario 3: app empresarial (sin presupuesto / control total)

  • Recomendación: NextAuth.js + Database Session + OAuth (Google/GitHub)
  • Por qué: gratis, funciones completas, puedes revocar sesiones
  • Inconveniente: UI de login y base de datos por tu cuenta

Escenario 4: ya tienes usuarios y solo necesitas una capa de auth

  • Recomendación: NextAuth.js + Credentials + JWT
  • Por qué: flexible, no invade tu esquema de base de datos
  • Ojo: cifrado, fuerza bruta, etc., son tu responsabilidad

Mi experiencia: el primer proyecto usó JWT; a los seis meses hacía falta administración de usuarios y migrar a Database Session me llevó dos días. En el segundo evalué requisitos al inicio y fui directo a Database Session.

Ruta de aprendizaje avanzada

Si ya tienes Credentials funcionando, sigue con:

1. Proveedores OAuth (más simple, prioridad recomendada)

  • Google y GitHub son mucho más sencillos que Credentials
  • Sin cifrado de contraseñas ni registro: lo hace el proveedor OAuth
  • Mejor experiencia (login en un clic)

2. Middleware: proteger rutas

  • Como vimos, una línea protege un directorio entero
  • Más cómodo que comprobar sesión en cada página

3. Gestión de roles y permisos (RBAC)

  • Guardar role en la sesión
  • Mostrar contenido o permisos según el rol
  • Avanzado: librería CASL (permisos granulares)

4. Verificación de email y restablecimiento de contraseña

  • Email Provider de NextAuth.js (login por correo)
  • Flujo propio de «olvidé mi contraseña» (enlace de restablecimiento)

Recursos de referencia

Documentación oficial (imprescindible):

Tutoriales prácticos:

Discusiones en GitHub (cuando te atasques):

Comparativas (para decidir):

Conclusión

En el fondo, NextAuth.js se reduce a tres decisiones:

  1. ¿Qué método de login? ¿Credentials u OAuth? En la mayoría de casos OAuth (Google/GitHub) es más simple
  2. ¿Cómo guardar la sesión? ¿JWT o Database? Proyectos personales: JWT; apps empresariales: Database
  3. ¿Cómo verificar al usuario? Con Credentials escribes la lógica; con OAuth la lleva el proveedor

Mi consejo: empieza con el ejemplo mínimo (la «versión mínima que funciona» de este artículo), consigue que el login funcione y añade funciones poco a poco. No intentes dominar toda la configuración de golpe; te desanimará.

NextAuth.js tiene curva de aprendizaje, pero cuando lo dominas controlas todo el flujo de autenticación. Si quieres velocidad, Clerk es buena opción; si quieres ahorrar y aprender, NextAuth.js merece el tiempo.

Por último, cuéntanos en comentarios dónde te atascas: ¿qué parte de la configuración? ¿Sigues dudando entre JWT y Session?

Lectura recomendada:

  • Próximo artículo previsto: «Guía completa de Next.js Middleware», con protección de rutas, pruebas A/B, etc.
  • Si te interesa la gestión de permisos de usuario, mira mi artículo anterior «Práctica de diseño RBAC»

Flujo completo de configuración de inicio de sesión con Credentials en NextAuth.js

Pasos completos desde la instalación hasta el inicio de sesión con usuario y contraseña y la gestión de sesiones

⏱️ Estimated time: 2 hr

  1. 1

    Step 1: Instalar e inicializar NextAuth.js

    Instalar dependencias:
    • npm install next-auth
    • Crear app/api/auth/[...nextauth]/route.ts

    Configuración básica:
    • Definir NEXTAUTH_URL (local: http://localhost:3000)
    • Definir NEXTAUTH_SECRET (generar una cadena aleatoria)
    • Configurar el array providers
  2. 2

    Step 2: Configurar Credentials Provider

    Implementar la lógica de verificación:
    1. Validar al usuario en la función authorize de CredentialsProvider
    2. Consultar el usuario en la base de datos (o usar datos hardcodeados para pruebas)
    3. Verificar la contraseña (con bcrypt u otra librería)
    4. Devolver el objeto de usuario (id, name, email, etc.)

    Notas:
    • authorize debe devolver el objeto de usuario o null
    • El objeto devuelto se guarda en la sesión
    • La verificación de contraseña debe hacerse en el servidor
  3. 3

    Step 3: Elegir estrategia de sesión (JWT o Session)

    Estrategia JWT (por defecto):
    • Adecuada para apps sin estado
    • La información de sesión va en el token JWT
    • No requiere base de datos
    • Configuración: session: { strategy: 'jwt' }

    Estrategia Session (requiere base de datos):
    • Adecuada cuando necesitas revocar inicios de sesión
    • La información de sesión se guarda en la base de datos
    • Hay que configurar un Adapter (Prisma, MongoDB, etc.)
    • Configuración: session: { strategy: 'database' }
  4. 4

    Step 4: Configurar Callbacks para personalizar el flujo

    Callbacks habituales:
    • signIn: controla si se permite el inicio de sesión
    • jwt: personaliza el contenido del token JWT (estrategia JWT)
    • session: personaliza el contenido de la sesión

    Ejemplo:
    callbacks: {
    async jwt({ token, user }) {
    if (user) token.role = user.role
    return token
    },
    async session({ session, token }) {
    session.user.role = token.role
    return session
    }
    }
  5. 5

    Step 5: Crear página y componentes de inicio de sesión

    Usar la API de NextAuth.js:
    • signIn('credentials', { username, password }): inicia sesión
    • signOut(): cierra sesión
    • useSession(): obtiene la sesión actual
    • SessionProvider: envuelve la app y proporciona contexto de sesión

    Ejemplo:
    const { data: session } = useSession()
    if (session) {
    return <div>Sesión iniciada: {session.user.name}</div>
    }
  6. 6

    Step 6: Probar y depurar

    Puntos de prueba:
    • Probar que usuario y contraseña correctos permiten iniciar sesión
    • Probar que credenciales incorrectas se rechazan
    • Probar que la sesión persiste
    • Probar que cerrar sesión la elimina

    Depuración:
    • Revisar errores en la consola del navegador
    • Revisar logs del servidor
    • Activar el modo debug de NextAuth.js
    • Comprobar que las variables de entorno están bien configuradas

FAQ

¿Cuál es la diferencia entre NextAuth.js, Clerk y Supabase Auth?
NextAuth.js:
• Solución autoalojada, gratuita y de código abierto
• Debes implementar tú la UI de login y la gestión de usuarios
• Pero tienes control total

Clerk:
• Ofrece UI y panel de administración completos
• Pero de pago por encima de 10.000 usuarios activos mensuales

Supabase Auth:
• Encaja en proyectos que usan la base de datos de Supabase
• Integración sencilla pero ligada a la base de datos
¿Debo elegir JWT o Session?
JWT encaja en apps sin estado y proyectos personales, no requiere base de datos, pero no puedes revocar una sesión concreta. Session encaja en apps empresariales y escenarios donde hay que revocar inicios de sesión; requiere configurar un Adapter de base de datos, pero permite controlar las sesiones con precisión.
¿Es seguro el inicio de sesión con Credentials?
Sí, pero hay que tener en cuenta:
1) Las contraseñas deben almacenarse cifradas (con bcrypt u otra librería)
2) La lógica de verificación debe ejecutarse en el servidor
3) Usar HTTPS en la transmisión
4) Implementar requisitos de fortaleza de contraseña
5) Valorar añadir captcha para evitar fuerza bruta
¿Cómo personalizar la página de inicio de sesión?
En la configuración de NextAuth usa la opción pages: pages: { signIn: '/auth/login' }.

Luego crea tu página de login y llama a signIn('credentials', { username, password }) para iniciar sesión.

También puedes personalizar por completo la UI y usar solo la API de NextAuth.js.
¿Cómo obtener el usuario con sesión iniciada?
En el cliente usa el hook useSession():
const { data: session } = useSession()

En el servidor usa getServerSession():
const session = await getServerSession(authOptions)

El objeto session incluye la información del usuario (id, name, email, etc.).
¿Cómo implementar control de roles y permisos?
En los callbacks personaliza el contenido de JWT o Session y añade el campo role. Luego en páginas o rutas API comprueba session.user.role y decide el acceso según el rol. También puedes usar Middleware para comprobaciones globales de permisos.
¿Qué bases de datos soporta NextAuth.js?
NextAuth.js soporta varias bases de datos mediante Adapters: Prisma (PostgreSQL, MySQL, SQLite, etc.), MongoDB, TypeORM, Drizzle ORM, etc. Al usar un Adapter se cambia automáticamente a la estrategia Session y la información de sesión se guarda en la base de datos.

16 min de lectura · Publicado el: 19 dic 2025 · Actualizado el: 21 ago 2026

Comentarios

Inicia sesión con GitHub para dejar un comentario

Easton BlogEaston Blog