tutoriales·16 min de lectura

Autenticacion en Next.js con Auth.js v5: Guia Completa

Implementa autenticacion en tu app Next.js con Auth.js v5 (NextAuth). OAuth con Google y GitHub, sesiones, middleware para proteger rutas, y roles de usuario paso a paso.

Autenticacion en Next.js con Auth.js v5: Guia Completa

La autenticacion en Next.js con Auth.js v5 es el estandar actual para implementar login en aplicaciones con App Router. Auth.js (antes NextAuth.js) maneja OAuth con proveedores como Google y GitHub, sesiones con JWT o base de datos, middleware para proteger rutas, y callbacks para personalizar el flujo completo. Todo esto sin depender de un servicio de pago.

Esta guia cubre la implementacion completa: desde instalar el paquete hasta proteger rutas con roles de usuario. Codigo funcional, sin atajos.

Que es Auth.js v5 y por que importa

Auth.js v5 es la version actual de lo que antes conocias como NextAuth.js. El renombramiento refleja que la libreria ya no es exclusiva de Next.js -- soporta SvelteKit, SolidStart y otros frameworks. Pero para el ecosistema de Next.js, sigue siendo la solucion de autenticacion open-source por defecto.

Las diferencias principales con NextAuth v4:

  • Disenado para App Router: funciona nativamente con Server Components, Server Actions y middleware
  • Configuracion centralizada: un solo archivo auth.ts en la raiz del proyecto
  • Edge compatible: el middleware corre en el edge runtime sin problemas
  • API simplificada: menos boilerplate, mas convencion

Si ya usaste NextAuth v4, el salto a v5 cambia la estructura del proyecto pero los conceptos son los mismos.

Instalacion y configuracion inicial

Empecemos instalando Auth.js en un proyecto Next.js existente con App Router.

Terminal
$
ℹ️
Version correcta

El paquete sigue llamandose next-auth en npm, pero a partir de la version 5 es oficialmente Auth.js. Asegurate de instalar next-auth@5 y no la version 4.

Crear el archivo de configuracion

El corazon de Auth.js v5 es un archivo auth.ts en la raiz del proyecto. Aqui defines proveedores, callbacks y opciones de sesion:

typescript
// auth.ts
import NextAuth from 'next-auth'
import Google from 'next-auth/providers/google'
import GitHub from 'next-auth/providers/github'
 
export const { handlers, signIn, signOut, auth } = NextAuth({
  providers: [
    Google,
    GitHub,
  ],
})

Eso es todo para una configuracion basica. Auth.js lee automaticamente las variables de entorno AUTH_GOOGLE_ID, AUTH_GOOGLE_SECRET, AUTH_GITHUB_ID y AUTH_GITHUB_SECRET.

Variables de entorno

Agrega estas variables a tu archivo .env.local:

bash
# .env.local
 
# Secret para firmar tokens (genera uno con: npx auth secret)
AUTH_SECRET=tu_secret_generado_aqui
 
# Google OAuth
AUTH_GOOGLE_ID=tu_google_client_id
AUTH_GOOGLE_SECRET=tu_google_client_secret
 
# GitHub OAuth
AUTH_GITHUB_ID=tu_github_client_id
AUTH_GITHUB_SECRET=tu_github_client_secret
Terminal
$

Ese comando genera un AUTH_SECRET aleatorio y lo agrega a tu .env.local automaticamente. Si necesitas una guia mas detallada sobre como manejar variables de entorno en tu proyecto, revisa la guia de variables de entorno en Next.js y Vercel.

Configurar proveedores OAuth

Obtener credenciales de Google

Para configurar Google como proveedor de login, necesitas crear un proyecto en Google Cloud Console:

  1. Ve a Google Cloud Console
  2. Crea un proyecto nuevo o selecciona uno existente
  3. Ve a APIs & Services > Credentials
  4. Click en Create Credentials > OAuth client ID
  5. Selecciona Web application como tipo
  6. En Authorized redirect URIs agrega: http://localhost:3000/api/auth/callback/google
  7. Copia el Client ID y Client Secret a tu .env.local
⚠️
URI de redireccion en produccion

Cuando hagas deploy, necesitas agregar tambien la URI de produccion: https://tudominio.com/api/auth/callback/google. Si no la agregas, el login con Google va a fallar en produccion con un error de redirect_uri mismatch.

Obtener credenciales de GitHub

La configuracion de GitHub es mas directa:

  1. Ve a GitHub Developer Settings
  2. Click en New OAuth App
  3. Homepage URL: http://localhost:3000
  4. Authorization callback URL: http://localhost:3000/api/auth/callback/github
  5. Click en Register application
  6. Copia el Client ID y genera un Client Secret

Para produccion, crea una OAuth App separada con la URL de tu dominio real.

Route Handler

Auth.js necesita un Route Handler que maneje todas las rutas de autenticacion (/api/auth/*). Crealo con esta estructura:

Estructura de archivos

app/ └── api/ └── auth/ └── [...nextauth]/ └── route.ts

typescript
// app/api/auth/[...nextauth]/route.ts
import { handlers } from '@/auth'
 
export const { GET, POST } = handlers

Dos lineas. El objeto handlers que exportaste desde auth.ts contiene toda la logica para sign in, sign out, callbacks y sesiones. El catch-all route ([...nextauth]) captura todas las rutas bajo /api/auth/.

Componentes de Sign In y Sign Out

Boton de Sign In (Server Component)

Auth.js v5 te permite usar Server Actions directamente para el login:

tsx
// components/sign-in.tsx
import { signIn } from '@/auth'
 
export function SignIn() {
  return (
    <div>
      <form
        action={async () => {
          'use server'
          await signIn('google')
        }}
      >
        <button type="submit">Iniciar sesion con Google</button>
      </form>
 
      <form
        action={async () => {
          'use server'
          await signIn('github')
        }}
      >
        <button type="submit">Iniciar sesion con GitHub</button>
      </form>
    </div>
  )
}

Boton de Sign Out (Server Component)

tsx
// components/sign-out.tsx
import { signOut } from '@/auth'
 
export function SignOut() {
  return (
    <form
      action={async () => {
        'use server'
        await signOut()
      }}
    >
      <button type="submit">Cerrar sesion</button>
    </form>
  )
}

Boton de Sign In (Client Component)

Si necesitas un boton con interactividad del lado del cliente (por ejemplo, mostrar un loading state):

tsx
// components/sign-in-button.tsx
'use client'
 
import { signIn } from 'next-auth/react'
import { useState } from 'react'
 
export function SignInButton() {
  const [cargando, setCargando] = useState(false)
 
  async function handleSignIn(proveedor: string) {
    setCargando(true)
    await signIn(proveedor, { callbackUrl: '/dashboard' })
  }
 
  return (
    <div>
      <button
        onClick={() => handleSignIn('google')}
        disabled={cargando}
      >
        {cargando ? 'Redirigiendo...' : 'Google'}
      </button>
 
      <button
        onClick={() => handleSignIn('github')}
        disabled={cargando}
      >
        {cargando ? 'Redirigiendo...' : 'GitHub'}
      </button>
    </div>
  )
}
💡
Server vs Client

Nota la diferencia de imports: en Server Components importas signIn desde @/auth, en Client Components desde next-auth/react. Cada uno tiene su propio mecanismo. Si esta distincion te genera dudas, la guia de Server Components vs Client Components lo explica a fondo.

Obtener la sesion del usuario

Este es el punto mas importante de toda la configuracion. Necesitas acceder a la sesion en tres contextos diferentes: Server Components, Client Components y middleware.

En Server Components: auth()

tsx
// app/dashboard/page.tsx
import { auth } from '@/auth'
import { redirect } from 'next/navigation'
import { SignOut } from '@/components/sign-out'
 
export default async function DashboardPage() {
  const sesion = await auth()
 
  // Si no hay sesion, redirigir al login
  if (!sesion?.user) {
    redirect('/api/auth/signin')
  }
 
  return (
    <div>
      <h1>Dashboard</h1>
      <p>Bienvenido, {sesion.user.name}</p>
      <p>Email: {sesion.user.email}</p>
      {sesion.user.image && (
        <img
          src={sesion.user.image}
          alt={sesion.user.name ?? 'Avatar'}
          width={64}
          height={64}
        />
      )}
      <SignOut />
    </div>
  )
}

La funcion auth() retorna la sesion completa o null si el usuario no esta autenticado. Al ser una funcion async, funciona perfectamente en Server Components.

En Client Components: useSession()

Para Client Components, necesitas envolver tu aplicacion con el SessionProvider y usar el hook useSession:

tsx
// app/layout.tsx
import { SessionProvider } from 'next-auth/react'
 
export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="es">
      <body>
        <SessionProvider>
          {children}
        </SessionProvider>
      </body>
    </html>
  )
}
tsx
// components/user-info.tsx
'use client'
 
import { useSession } from 'next-auth/react'
 
export function UserInfo() {
  const { data: sesion, status } = useSession()
 
  if (status === 'loading') {
    return <p>Cargando...</p>
  }
 
  if (status === 'unauthenticated') {
    return <p>No has iniciado sesion</p>
  }
 
  return (
    <div>
      <p>Hola, {sesion?.user?.name}</p>
      <p>{sesion?.user?.email}</p>
    </div>
  )
}

El hook useSession retorna tres estados posibles: loading, authenticated y unauthenticated. Siempre maneja los tres.

En middleware: auth como wrapper

Auth.js v5 exporta auth como un wrapper de middleware que te da acceso a la sesion:

typescript
// middleware.ts
import { auth } from '@/auth'
 
export default auth((req) => {
  const estaAutenticado = !!req.auth
 
  // Logica de proteccion de rutas (ver siguiente seccion)
})
 
export const config = {
  matcher: ['/dashboard/:path*', '/perfil/:path*', '/admin/:path*'],
}

Proteger rutas con middleware

El middleware es la primera linea de defensa para rutas protegidas. Se ejecuta antes de que el Server Component cargue, asi que el usuario nunca ve contenido que no deberia ver.

typescript
// middleware.ts
import { auth } from '@/auth'
import { NextResponse } from 'next/server'
 
// Rutas que requieren autenticacion
const RUTAS_PROTEGIDAS = ['/dashboard', '/perfil', '/configuracion']
 
// Rutas solo para usuarios NO autenticados
const RUTAS_AUTH = ['/login', '/registro']
 
export default auth((req) => {
  const { nextUrl } = req
  const estaAutenticado = !!req.auth
 
  const esRutaProtegida = RUTAS_PROTEGIDAS.some(ruta =>
    nextUrl.pathname.startsWith(ruta)
  )
 
  const esRutaAuth = RUTAS_AUTH.some(ruta =>
    nextUrl.pathname.startsWith(ruta)
  )
 
  // Sin sesion intentando acceder a ruta protegida
  if (esRutaProtegida && !estaAutenticado) {
    const loginUrl = new URL('/api/auth/signin', nextUrl.origin)
    loginUrl.searchParams.set('callbackUrl', nextUrl.pathname)
    return NextResponse.redirect(loginUrl)
  }
 
  // Con sesion intentando acceder a login/registro
  if (esRutaAuth && estaAutenticado) {
    return NextResponse.redirect(new URL('/dashboard', nextUrl.origin))
  }
 
  return NextResponse.next()
})
 
export const config = {
  matcher: [
    '/dashboard/:path*',
    '/perfil/:path*',
    '/configuracion/:path*',
    '/login',
    '/registro',
  ],
}
ℹ️
Matcher especifico

Usa matcher para ejecutar el middleware solo en las rutas necesarias. No uses matcher: ['/((?!api|_next/static|_next/image|favicon.ico).*)'] a menos que realmente necesites proteger todas las rutas. Un matcher especifico es mas eficiente y mas facil de debuggear.

Para una guia mas completa sobre seguridad en rutas y otros patrones de proteccion, revisa la guia de seguridad en aplicaciones Next.js.

Sesiones con base de datos: Prisma Adapter

Por defecto, Auth.js usa JWT para las sesiones. El token se guarda en una cookie y no necesitas base de datos. Pero si necesitas mas control -- revocar sesiones, ver sesiones activas, o guardar datos adicionales del usuario -- necesitas un adapter de base de datos.

Instalar dependencias

Terminal
$

Schema de Prisma

Auth.js requiere tablas especificas para manejar usuarios, cuentas, sesiones y tokens de verificacion:

prisma
// prisma/schema.prisma
datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}
 
generator client {
  provider = "prisma-client-js"
}
 
model User {
  id            String    @id @default(cuid())
  name          String?
  email         String?   @unique
  emailVerified DateTime?
  image         String?
  role          String    @default("user")
  accounts      Account[]
  sessions      Session[]
}
 
model Account {
  id                String  @id @default(cuid())
  userId            String
  type              String
  provider          String
  providerAccountId String
  refresh_token     String?
  access_token      String?
  expires_at        Int?
  token_type        String?
  scope             String?
  id_token          String?
  session_state     String?
  user              User    @relation(fields: [userId], references: [id], onDelete: Cascade)
 
  @@unique([provider, providerAccountId])
}
 
model Session {
  id           String   @id @default(cuid())
  sessionToken String   @unique
  userId       String
  expires      DateTime
  user         User     @relation(fields: [userId], references: [id], onDelete: Cascade)
}
 
model VerificationToken {
  identifier String
  token      String   @unique
  expires    DateTime
 
  @@unique([identifier, token])
}

Sincronizar la base de datos

Terminal
$

Configurar el adapter

Actualiza tu archivo auth.ts para usar el adapter de Prisma:

typescript
// auth.ts
import NextAuth from 'next-auth'
import Google from 'next-auth/providers/google'
import GitHub from 'next-auth/providers/github'
import { PrismaAdapter } from '@auth/prisma-adapter'
import { PrismaClient } from '@prisma/client'
 
const prisma = new PrismaClient()
 
export const { handlers, signIn, signOut, auth } = NextAuth({
  adapter: PrismaAdapter(prisma),
  providers: [
    Google,
    GitHub,
  ],
  session: {
    strategy: 'database', // Cambiar de JWT a sesiones en DB
  },
})
⚠️
JWT vs Database

Al agregar un adapter, Auth.js cambia automaticamente a sesiones en base de datos. Si quieres seguir usando JWT (por ejemplo, para compatibilidad con edge runtime en middleware), especifica session: { strategy: 'jwt' } explicitamente.

Callbacks: personalizar el flujo de autenticacion

Los callbacks son funciones que Auth.js ejecuta en momentos clave del flujo de autenticacion. Los dos mas importantes son jwt y session.

JWT Callback

Se ejecuta cada vez que se crea o actualiza un JWT. Aqui puedes agregar datos personalizados al token:

typescript
// auth.ts
import NextAuth from 'next-auth'
import Google from 'next-auth/providers/google'
import GitHub from 'next-auth/providers/github'
 
export const { handlers, signIn, signOut, auth } = NextAuth({
  providers: [Google, GitHub],
  callbacks: {
    // Se ejecuta al crear/actualizar el JWT
    jwt({ token, user }) {
      // 'user' solo esta disponible en el primer sign in
      if (user) {
        token.role = user.role ?? 'user'
        token.id = user.id
      }
      return token
    },
 
    // Se ejecuta al leer la sesion (auth(), useSession, etc.)
    session({ session, token }) {
      if (session.user) {
        session.user.role = token.role as string
        session.user.id = token.id as string
      }
      return session
    },
  },
})

Extender los tipos de TypeScript

Para que TypeScript reconozca los campos personalizados que agregaste a la sesion y al token, necesitas extender los tipos:

typescript
// types/next-auth.d.ts
import { DefaultSession } from 'next-auth'
 
declare module 'next-auth' {
  interface Session {
    user: {
      id: string
      role: string
    } & DefaultSession['user']
  }
 
  interface User {
    role?: string
  }
}
 
declare module 'next-auth/jwt' {
  interface JWT {
    role?: string
    id?: string
  }
}

Authorized Callback

El callback authorized se ejecuta en el middleware y determina si una peticion debe continuar o redirigirse:

typescript
// auth.ts (agregando authorized callback)
export const { handlers, signIn, signOut, auth } = NextAuth({
  providers: [Google, GitHub],
  callbacks: {
    authorized({ auth, request }) {
      const estaAutenticado = !!auth?.user
      const esRutaProtegida = request.nextUrl.pathname.startsWith('/dashboard')
 
      if (esRutaProtegida && !estaAutenticado) {
        return false // Redirige a la pagina de sign in
      }
 
      return true // Permite el acceso
    },
 
    jwt({ token, user }) {
      if (user) {
        token.role = user.role ?? 'user'
        token.id = user.id
      }
      return token
    },
 
    session({ session, token }) {
      if (session.user) {
        session.user.role = token.role as string
        session.user.id = token.id as string
      }
      return session
    },
  },
})

Control de acceso por roles

Con los callbacks configurados, implementar roles es cuestion de leer el campo role de la sesion en tus componentes.

Middleware con roles

typescript
// middleware.ts
import { auth } from '@/auth'
import { NextResponse } from 'next/server'
 
const PERMISOS_RUTA: Record<string, string[]> = {
  '/dashboard': ['user', 'editor', 'admin'],
  '/editor': ['editor', 'admin'],
  '/admin': ['admin'],
}
 
export default auth((req) => {
  const { nextUrl } = req
  const usuario = req.auth?.user
 
  if (!usuario) {
    return NextResponse.redirect(
      new URL('/api/auth/signin', nextUrl.origin)
    )
  }
 
  // Buscar si la ruta tiene restriccion de roles
  const permisoRequerido = Object.entries(PERMISOS_RUTA).find(
    ([ruta]) => nextUrl.pathname.startsWith(ruta)
  )
 
  if (permisoRequerido) {
    const [, rolesPermitidos] = permisoRequerido
    const rolUsuario = usuario.role ?? 'user'
 
    if (!rolesPermitidos.includes(rolUsuario)) {
      return NextResponse.redirect(
        new URL('/sin-acceso', nextUrl.origin)
      )
    }
  }
 
  return NextResponse.next()
})
 
export const config = {
  matcher: ['/dashboard/:path*', '/editor/:path*', '/admin/:path*'],
}

Verificacion de roles en Server Components

El middleware protege la ruta, pero tambien debes verificar en el Server Component para operaciones sensibles:

tsx
// app/admin/page.tsx
import { auth } from '@/auth'
import { redirect } from 'next/navigation'
 
export default async function AdminPage() {
  const sesion = await auth()
 
  if (!sesion?.user || sesion.user.role !== 'admin') {
    redirect('/sin-acceso')
  }
 
  return (
    <div>
      <h1>Panel de administracion</h1>
      <p>Solo usuarios con rol admin pueden ver esto.</p>
    </div>
  )
}

Componente condicional por rol

tsx
// components/role-gate.tsx
import { auth } from '@/auth'
 
interface RoleGateProps {
  rolesPermitidos: string[]
  children: React.ReactNode
  fallback?: React.ReactNode
}
 
export async function RoleGate({
  rolesPermitidos,
  children,
  fallback = null,
}: RoleGateProps) {
  const sesion = await auth()
  const rolUsuario = sesion?.user?.role ?? 'user'
 
  if (!rolesPermitidos.includes(rolUsuario)) {
    return <>{fallback}</>
  }
 
  return <>{children}</>
}
tsx
// Uso en cualquier page
import { RoleGate } from '@/components/role-gate'
 
export default function DashboardPage() {
  return (
    <div>
      <h1>Dashboard</h1>
 
      {/* Visible para todos los autenticados */}
      <p>Contenido general del dashboard</p>
 
      {/* Solo visible para admins */}
      <RoleGate rolesPermitidos={['admin']}>
        <section>
          <h2>Estadisticas del sistema</h2>
          {/* contenido de admin */}
        </section>
      </RoleGate>
    </div>
  )
}

Estructura final del proyecto

Despues de implementar todo, tu proyecto deberia tener esta estructura:

Estructura de archivos

mi-app/ ├── auth.ts (configuracion central de Auth.js) ├── middleware.ts (proteccion de rutas) ├── types/ │ └── next-auth.d.ts (tipos extendidos de sesion) ├── app/ │ ├── api/ │ │ └── auth/ │ │ └── [...nextauth]/ │ │ └── route.ts (route handler) │ ├── dashboard/ │ │ └── page.tsx (ruta protegida) │ ├── admin/ │ │ └── page.tsx (ruta protegida por rol) │ └── layout.tsx (SessionProvider) ├── components/ │ ├── sign-in.tsx (boton de login) │ ├── sign-out.tsx (boton de logout) │ ├── sign-in-button.tsx (version client component) │ ├── user-info.tsx (info del usuario en client) │ └── role-gate.tsx (acceso condicional por rol) ├── prisma/ │ └── schema.prisma (schema con tablas de Auth.js) └── .env.local (secrets de OAuth y DB)

Seguridad: proteger tus secrets de OAuth

Los client secrets de Google y GitHub son credenciales sensibles. Si se filtran en tu repositorio, un atacante podria usarlos para suplantar tu aplicacion y obtener acceso a las cuentas de tus usuarios.

Verifica que tu .gitignore incluya:

bash
# .gitignore
.env
.env.local
.env.*.local

Si tu proyecto vive en GitHub, datahogo puede escanear tu repositorio para detectar secrets de OAuth o API keys que se hayan commiteado accidentalmente. Si encuentra algo, genera un PR con el fix.

Ademas de proteger las credenciales, asegurate de configurar correctamente las URIs de callback en tus proveedores OAuth. No uses wildcards ni patrones permisivos -- especifica la URL exacta de tu dominio.

Auth.js vs Clerk vs Supabase Auth

Auth.js no es la unica opcion. Dependiendo de tu proyecto, otra solucion puede ser mejor:

CaracteristicaAuth.js v5ClerkSupabase Auth
PrecioGratis (open-source)Gratis hasta 10k MAU, luego de pagoGratis con Supabase
SetupManual (tu configuras todo)Minimo (SDK con UI incluida)Medio (parte del ecosistema Supabase)
UI de loginTu la construyesPre-construida y personalizableComponente opcional
OAuth providers80+ proveedores20+ proveedores15+ proveedores
Base de datosTu la eliges (Prisma, Drizzle, etc.)Managed por ClerkPostgreSQL de Supabase
Self-hostedSiNoSi
App RouterNativoNativoCon @supabase/ssr
Roles/permisosManual con callbacksIncluido (Organizations)Con RLS de PostgreSQL

Cuando elegir cada uno

Auth.js v5 es la mejor opcion cuando quieres control total, no quieres depender de un servicio externo, y no te importa escribir mas codigo. Es gratis sin limites de usuarios.

Clerk es ideal cuando necesitas ir rapido. Su SDK incluye componentes de UI para sign in, sign up, perfil de usuario y organizaciones. El programa de creadores de Clerk ofrece beneficios para proyectos open-source y creadores de contenido. Si tu prioridad es velocidad de desarrollo sobre control, vale la pena evaluarlo.

Supabase Auth tiene sentido si ya usas Supabase como tu base de datos. Viene integrado y los permisos se manejan con Row Level Security directamente en PostgreSQL. Si quieres explorar esta opcion, revisa la guia de Supabase con Next.js.

Errores comunes y como solucionarlos

Error: "OAUTH_CALLBACK_ERROR"

plaintext
[auth][error] OAuthCallbackError: ...

Causa: La URI de callback registrada en el proveedor (Google/GitHub) no coincide con la URL de tu aplicacion.

Solucion: Verifica que la URI en la consola del proveedor sea exactamente https://tudominio.com/api/auth/callback/google (o /github). No olvides el protocolo https en produccion.

Error: "SESSION_NOT_FOUND" con adapter de base de datos

Causa: Las tablas de Auth.js no existen en tu base de datos.

Solucion:

Terminal
$

Verifica que tu schema de Prisma tenga los modelos User, Account, Session y VerificationToken exactamente como los define la documentacion de Auth.js.

El middleware no detecta la sesion

Causa comun: Estas usando session: { strategy: 'database' } pero el middleware corre en edge runtime, que no tiene acceso a la base de datos.

Solucion: Usa JWT para las sesiones si necesitas que el middleware funcione en el edge:

typescript
// auth.ts
export const { handlers, signIn, signOut, auth } = NextAuth({
  adapter: PrismaAdapter(prisma),
  providers: [Google, GitHub],
  session: {
    strategy: 'jwt', // JWT funciona en edge runtime
  },
})

Con esta configuracion, el adapter se usa para guardar usuarios y cuentas, pero las sesiones se manejan con JWT.

useSession retorna undefined

Causa: Falta el SessionProvider en el layout.

Solucion: Asegurate de envolver tu aplicacion con <SessionProvider> en el layout raiz, como se muestra en la seccion de Client Components.

Resumen

Implementar autenticacion en Next.js con Auth.js v5 se reduce a estos pasos:

  1. Instalar next-auth@5 y configurar auth.ts con tus proveedores
  2. Obtener credenciales de OAuth en Google Cloud Console y GitHub Developer Settings
  3. Crear el Route Handler en app/api/auth/[...nextauth]/route.ts
  4. Componentes de login/logout con Server Actions o Client Components
  5. Leer la sesion con auth() en el servidor, useSession() en el cliente
  6. Proteger rutas con middleware y el callback authorized
  7. Agregar Prisma adapter si necesitas sesiones en base de datos
  8. Implementar roles con callbacks y verificacion en componentes

La documentacion oficial de Auth.js cubre escenarios mas avanzados como email/password, magic links y proveedores personalizados. Lo que tienes aqui es una base solida para la mayoria de proyectos con Next.js y App Router.


Recursos adicionales

#autenticacion#nextjs#auth.js#oauth#middleware#sesiones

Preguntas frecuentes

Cual es la diferencia entre Auth.js y NextAuth?

Auth.js es la evolucion de NextAuth.js. A partir de la version 5, el proyecto se renombro a Auth.js para soportar multiples frameworks (no solo Next.js). Si usabas NextAuth v4, Auth.js v5 es su sucesor directo.

Auth.js es gratis?

Si, Auth.js es completamente open-source y gratuito. No hay plan de pago ni limites de usuarios. Los unicos costos son los de tu base de datos si usas sesiones en DB.

Debo usar JWT o sesiones en base de datos?

JWT es mas simple y no requiere base de datos, ideal para apps sin backend propio. Sesiones en DB dan mas control (puedes revocar sesiones, ver sesiones activas) y son mejor para apps con datos sensibles.

Puedo usar Auth.js con el App Router de Next.js?

Si, Auth.js v5 fue disenado especificamente para el App Router. Funciona con Server Components, Server Actions, middleware y Route Handlers nativamente.

Auth.js vs Clerk vs Supabase Auth, cual es mejor?

Auth.js es gratis y open-source pero requiere mas configuracion. Clerk es un servicio managed con UI pre-construida, ideal si quieres velocidad de desarrollo. Supabase Auth viene incluido si ya usas Supabase. La eleccion depende de tu stack y presupuesto.