Busca en todas las páginas de la documentación
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥
npm install next-auth@beta// auth.ts (raíz del proyecto)
import NextAuth from "next-auth";
import GitHub from "next-auth/providers/github";
import Google from "next-auth/providers/google";
export const { handlers, signIn, signOut, auth } = NextAuth({
providers: [
GitHub({
clientId: process.env.AUTH_GITHUB_ID!,
clientSecret: process.env.AUTH_GITHUB_SECRET!,
}),
Google({
clientId: process.env.AUTH_GOOGLE_ID!,
clientSecret: process.env.AUTH_GOOGLE_SECRET!,
}),
],
});// app/api/auth/[...nextauth]/route.ts
import { handlers } from "@/auth";
export const { GET, POST } = handlers;// .env.local
AUTH_SECRET=your-random-secret-at-least-32-chars
AUTH_GITHUB_ID=...
AUTH_GITHUB_SECRET=...
AUTH_GOOGLE_ID=...
AUTH_GOOGLE_SECRET=...Cuándo usarlo: Necesitas inicio de sesión OAuth (GitHub, Google, etc.), gestión de sesiones y protección de rutas en una aplicación Next.js App Router.
// app/components/AuthButtons.tsx
import { auth, signIn, signOut } from "@/auth";
export async function SignInButton() {
const session = await auth();
if (session?.user) {
return (
<div className="flex items-center gap-3">
{session.user.image && (
<img
src={session.user.image}
alt=""
className="w-8 h-8 rounded-full"
/>
)}
<span className="text-sm">{session.user.name}</span>
<form
action={async () => {
"use server";
await signOut();
}}
>
<button className="text-sm text-gray-500 hover:text-gray-700">
Cerrar Sesión
</button>
</form>
</div>
);
}
return (
<div className="flex gap-2">
<form
action={async () => {
"use server";
await signIn("github");
}}
>
<button className="bg-gray-900 text-white px-4 py-2 rounded text-sm">
Iniciar sesión con GitHub
</button>
</form>
<form
action={async () => {
"use server";
await signIn("google");
}}
>
<button className="bg-blue-600 text-white px-4 py-2 rounded text-sm">
Iniciar sesión con Google
</button>
</form>
</div>
);
}// app/dashboard/page.tsx
import { auth } from "@/auth";
import { redirect } from "next/navigation";
export default async function DashboardPage() {
const session = await auth();
if (!session) {
redirect("/api/auth/signin");
}
return (
<div className="max-w-2xl mx-auto p-6">
<h1 className="text-2xl font-bold">Panel de Control</h1>
<p className="mt-2">¡Bienvenido, {session.user?.name}!</p>
<pre className="mt-4 bg-gray-100 p-4 rounded text-sm">
{JSON.stringify(session, null, 2)}
</pre>
</div>
);
}// middleware.ts
import { auth } from "./auth";
export default auth((req) => {
const isLoggedIn = !!req.auth;
const isProtected = req.nextUrl.pathname.startsWith("/dashboard");
if (isProtected && !isLoggedIn) {
return Response.redirect(new URL("/api/auth/signin", req.nextUrl));
}
});
export const config = {
matcher: ["/dashboard/:path*", "/settings/:path*"],
};Lo que esto demuestra:
auth()auth(), signIn(), signOut() y handlers desde una única configuraciónauth() devuelve la sesión actual en Componentes de Servidor, Acciones de Servidor, rutas de API y middlewarehandlers proporciona manejadores de rutas GET y POST para la ruta catch-all /api/auth/[...nextauth]AUTH_SECRET se requiere en producción para firmar JWTs; genéralo con npx auth secretProveedor Credentials (correo electrónico/contraseña):
import Credentials from "next-auth/providers/credentials";
import { z } from "zod";
import bcrypt from "bcryptjs";
import { prisma } from "@/lib/prisma";
export const { handlers, signIn, signOut, auth } = NextAuth({
providers: [
Credentials({
credentials: {
email: { label: "Email", type: "email" },
password: { label: "Password", type: "password" },
},
authorize: async (credentials) => {
const parsed = z
.object({
email: z.string().email(),
password: z.string().min(8),
})
.safeParse(credentials);
if (!parsed.success) return null;
const user = await prisma.user.findUnique({
where: { email: parsed.data.email },
});
if (!user || !user.password) return null;
const valid = await bcrypt.compare(parsed.data.password, user.password);
if (!valid) return null;
return { id: String(user.id), name: user.name, email: user.email };
},
}),
],
});Adaptador de base de datos Prisma:
npm install @auth/prisma-adapterimport { PrismaAdapter } from "@auth/prisma-adapter";
import { prisma } from "@/lib/prisma";
export const { handlers, signIn, signOut, auth } = NextAuth({
adapter: PrismaAdapter(prisma),
providers: [GitHub, Google],
session: { strategy: "database" }, // Usa sesiones de base de datos en lugar de JWT
});Extendiendo la sesión con campos personalizados:
export const { handlers, signIn, signOut, auth } = NextAuth({
providers: [GitHub],
callbacks: {
async jwt({ token, user }) {
if (user) {
token.role = user.role; // Agrega rol del usuario en la base de datos
}
return token;
},
async session({ session, token }) {
session.user.id = token.sub!;
session.user.role = token.role as string;
return session;
},
},
});// types/next-auth.d.ts
import { DefaultSession } from "next-auth";
declare module "next-auth" {
interface Session {
user: {
id: string;
role: string;
} & DefaultSession["user"];
}
}Acceso a sesión del lado del cliente:
// app/providers.tsx
"use client";
import { SessionProvider } from "next-auth/react";
export function AuthProvider({ children }: { children: React.ReactNode }) {
return <SessionProvider>{children}</SessionProvider>;
}
// app/components/ClientProfile.tsx
"use client";
import { useSession } from "next-auth/react";
export function ClientProfile() {
const { data: session, status } = useSession();
if (status === "loading") return <p>Cargando...</p>;
if (!session) return <p>No iniciaste sesión</p>;
return <p>Iniciaste sesión como {session.user?.name}</p>;
}Session y JWT mediante aumentación de módulo en types/next-auth.d.tsauth() devuelve Session | null; siempre verifica null antes de acceder a session.userUser se puede extender para campos de base de datos personalizadosimport type { Session } from "next-auth";
async function getUser(): Promise<Session["user"] | null> {
const session = await auth();
return session?.user ?? null;
}AUTH_SECRET faltante - La aplicación falla en producción sin AUTH_SECRET. Solución: Genéralo con npx auth secret y agrégalo a tus variables de entorno. Requerido para firmar JWTs.
Desajuste de URL de callback - Los proveedores OAuth rechazan redirecciones si la URL de callback no coincide. Solución: Registra https://yourdomain.com/api/auth/callback/github (y /google) en la configuración del desarrollador de cada proveedor.
Sesión nula en componentes cliente - auth() solo funciona del lado del servidor. Solución: Usa SessionProvider y el hook useSession() para componentes cliente. Envuelve tu layout con <SessionProvider>.
El middleware se ejecuta en cada solicitud - Sin un matcher, el middleware de autenticación se ejecuta en activos estáticos, rutas de API, etc. Solución: Siempre configura config.matcher para limitar el middleware a rutas protegidas.
Limitaciones del proveedor Credentials - El proveedor Credentials no soporta sesiones de base de datos directamente (solo JWT). Solución: Usa la estrategia JWT con credenciales. Para sesiones de base de datos, usa proveedores OAuth con un adaptador de base de datos.
Compatibilidad de Edge runtime - Algunos adaptadores de base de datos y bcrypt no funcionan en Edge runtime. Solución: Usa bcryptjs en lugar de bcrypt. Verifica la compatibilidad del adaptador con Edge. Usa runtime: "nodejs" en el middleware si es necesario.
Migración de v4 a v5 - Auth.js v5 tiene una API significativamente diferente a NextAuth v4. Solución: Sigue la guía de migración oficial. Cambios clave: NextAuth() devuelve auth en lugar de usar getServerSession(), y next-auth/react solo es para componentes cliente.
| Librería | Mejor Para | Compensación |
|---|---|---|
| Auth.js / NextAuth v5 | OAuth completo + gestión de sesiones para Next.js | Configuración compleja para flujos personalizados |
| Clerk | Componentes de IU de autenticación listos para usar | Servicio pago, bloqueo de proveedor |
| Supabase Auth | Integración del ecosistema Supabase | Vinculado a Supabase |
| Lucia | Autenticación ligera, DIY | Más configuración manual, menos abstracción |
| Kinde | Autenticación B2B con organizaciones | Servicio pago |
| Custom JWT | Control total | Debes manejar seguridad, actualización y revocación tú mismo |
Session | null que contiene la información del usuario y la expiraciónuseSession() de next-auth/react en su lugarnull antes de acceder a session.usersignIn("github")auth() devuelven la sesiónAUTH_SECRET se requiere para firmar JWTs en producciónnpx auth secret y agrégalo a tus variables de entorno// middleware.ts
import { auth } from "./auth";
export default auth((req) => {
if (!req.auth && req.nextUrl.pathname.startsWith("/dashboard")) {
return Response.redirect(new URL("/api/auth/signin", req.nextUrl));
}
});
export const config = {
matcher: ["/dashboard/:path*"],
};Siempre establece config.matcher para limitar el middleware a rutas protegidas.
<SessionProvider> de next-auth/reactuseSession() en componentes clienteuseSession() devuelve { data: session, status } donde status es "loading", "authenticated" o "unauthenticated"Credentials de next-auth/providers/credentialscredentials y una función authorizeauthorize valida las credenciales y devuelve un objeto de usuario o nullcallbacks: {
async jwt({ token, user }) {
if (user) token.role = user.role;
return token;
},
async session({ session, token }) {
session.user.role = token.role as string;
return session;
},
}También extiende el tipo Session mediante aumentación de módulo en types/next-auth.d.ts.
// types/next-auth.d.ts
import { DefaultSession } from "next-auth";
declare module "next-auth" {
interface Session {
user: {
id: string;
role: string;
} & DefaultSession["user"];
}
}Esto agrega id y role a session.user en toda tu aplicación.
auth() es una función solo para servidor y no se puede llamar en componentes cliente<SessionProvider> y el hook useSession() para acceso a sesión del lado del clienteSessionProvider envuelva el árbol de componentes en tu layoutimport { PrismaAdapter } from "@auth/prisma-adapter";
export const { handlers, auth, signIn, signOut } = NextAuth({
adapter: PrismaAdapter(prisma),
session: { strategy: "database" },
providers: [GitHub, Google],
});Instala @auth/prisma-adapter y establece session.strategy en "database".
matcher, el middleware se ejecuta en todas las rutas incluyendo _next/static, imágenes y rutas de APIconfig.matcher para limitarlo a rutas protegidasmatcher: ["/dashboard/:path*", "/settings/:path*"]auth() en lugar de requerir getServerSession(authOptions)NextAuth() que devuelve auth, signIn, signOut y handlersnext-auth/react (useSession, SessionProvider) solo es para componentes clienteauth.ts en la raíz del proyectoRevisado por Chris St. John·Última actualización: 19 jul 2026
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥