¡Autenticación Segura en Next.js! Implementa OAuth con NextAuth.js y Protege tus Rutas 🔒
Este tutorial te guiará paso a paso en la implementación de autenticación segura en Next.js. Explorarás NextAuth.js para gestionar sesiones y autenticación OAuth, protegiendo tus rutas y ofreciendo una experiencia de usuario robusta.
La autenticación es un pilar fundamental en la mayoría de las aplicaciones web modernas. Permite a los usuarios identificarse, acceder a recursos personalizados y garantiza la seguridad de la información. En el ecosistema de Next.js, existen varias opciones para gestionar la autenticación, pero una de las más robustas y fáciles de integrar es NextAuth.js.
NextAuth.js es una biblioteca completa que simplifica la adición de autenticación a aplicaciones Next.js. Soporta múltiples proveedores OAuth (Google, GitHub, Facebook, etc.), autenticación sin contraseña (Magic Link), JWTs y gestión de sesiones, todo con una configuración mínima y máxima flexibilidad.
En este tutorial, profundizaremos en cómo integrar NextAuth.js con un proveedor OAuth (como Google) para autenticar usuarios y cómo proteger rutas específicas en tu aplicación Next.js.
🎯 ¿Qué aprenderás en este tutorial?
Al finalizar este tutorial, serás capaz de:
- Configurar un proyecto Next.js para usar NextAuth.js.
- Integrar un proveedor OAuth (Google) para la autenticación.
- Gestionar sesiones de usuario y acceder a la información de la sesión.
- Proteger rutas de tu aplicación para que solo usuarios autenticados puedan acceder a ellas.
- Implementar botones de inicio y cierre de sesión.
🛠️ Requisitos Previos
Antes de empezar, asegúrate de tener instalado lo siguiente:
- Node.js (versión 18 o superior).
- npm o Yarn (gestor de paquetes).
- Una cuenta de Google Developer Console (para configurar las credenciales OAuth).
🚀 Paso 1: Configuración Inicial del Proyecto Next.js
Si ya tienes un proyecto Next.js, puedes omitir este paso. De lo contrario, crearemos uno nuevo.
Primero, crea un nuevo proyecto Next.js utilizando create-next-app:
npx create-next-app@latest nextjs-auth-tutorial --typescript --eslint --tailwind --app
Cuando te pregunte, selecciona las opciones predeterminadas o las que prefieras. En este tutorial usaremos TypeScript y el App Router.
Navega a tu nuevo directorio de proyecto:
cd nextjs-auth-tutorial
Ahora, instalaremos NextAuth.js:
npm install next-auth
# o con yarn
yarn add next-auth
🔑 Paso 2: Configurar las Credenciales OAuth de Google
Para usar Google como proveedor de autenticación, necesitas crear credenciales de cliente OAuth en la Consola de Desarrolladores de Google.
- Ve a la Google Cloud Console: Abre tu navegador y navega a console.cloud.google.com.
- Crea un nuevo proyecto (si es necesario): Si no tienes uno, haz clic en el selector de proyectos en la parte superior y luego en
Nuevo Proyecto. - Habilita la API de Google People: En el menú de navegación lateral, ve a
APIs y Servicios>Biblioteca. BuscaGoogle People APIy haz clic enHabilitar. - Crea credenciales OAuth:
- En el menú de navegación, ve a
APIs y Servicios>Credenciales. - Haz clic en
Crear credenciales>ID de cliente de OAuth. - Si es la primera vez, se te pedirá que configures la
Pantalla de consentimiento de OAuth. Sigue los pasos para configurar el tipo de usuario (Externo) y la información básica de tu aplicación. - Una vez configurada la pantalla de consentimiento, vuelve a
Crear credenciales>ID de cliente de OAuth. - Selecciona
Aplicación webcomo tipo de aplicación. - Dale un nombre a tu cliente OAuth (ej.,
Next.js Auth App). - En
Orígenes de JavaScript autorizados, añadehttp://localhost:3000. - En
URI de redireccionamiento autorizados, añadehttp://localhost:3000/api/auth/callback/google. - Haz clic en
Crear. Se te mostrará el ID de Cliente y el Secreto de Cliente.
- En el menú de navegación, ve a
📝 Paso 3: Configuración de Variables de Entorno
Para almacenar de forma segura tus credenciales, crea un archivo .env en la raíz de tu proyecto (si aún no existe) y añade lo siguiente:
AUTH_SECRET="SUPER_SECRET_KEY_RANDOM"
GOOGLE_CLIENT_ID="TU_ID_DE_CLIENTE_DE_GOOGLE"
GOOGLE_CLIENT_SECRET="TU_SECRETO_DE_CLIENTE_DE_GOOGLE"
NEXTAUTH_URL="http://localhost:3000"
AUTH_SECRET: Una cadena aleatoria y fuerte. Puedes generarla, por ejemplo, conopenssl rand -base64 32en tu terminal Linux/macOS. Esta clave se usa para firmar y cifrar tokens, garantizando la seguridad de la sesión.GOOGLE_CLIENT_ID: El ID de Cliente que obtuviste de Google.GOOGLE_CLIENT_SECRET: El Secreto de Cliente que obtuviste de Google.NEXTAUTH_URL: La URL base de tu aplicación. En desarrollo, eshttp://localhost:3000.
⚙️ Paso 4: Configuración de NextAuth.js con el App Router
En el App Router, la configuración de NextAuth.js se maneja a través de un Route Handler en api/auth/[...nextauth]/route.ts.
Crea el siguiente archivo:
app/api/auth/[...nextauth]/route.ts
import NextAuth from "next-auth";
import GoogleProvider from "next-auth/providers/google";
const handler = NextAuth({
providers: [
GoogleProvider({
clientId: process.env.GOOGLE_CLIENT_ID as string,
clientSecret: process.env.GOOGLE_CLIENT_SECRET as string,
}),
],
secret: process.env.AUTH_SECRET,
pages: {
signIn: '/auth/signin',
signOut: '/auth/signout',
error: '/auth/error',
verifyRequest: '/auth/verify-request',
newUser: '/auth/new-user', // If you want to add an account creation page
},
callbacks: {
async jwt({ token, user }) {
if (user) {
token.id = user.id;
}
return token;
},
async session({ session, token }) {
if (token) {
session.user.id = token.id as string;
}
return session;
},
},
session: {
strategy: "jwt",
maxAge: 30 * 24 * 60 * 60, // 30 days
updateAge: 24 * 60 * 60, // 24 hours
},
});
export { handler as GET, handler as POST };
Explicación del código:
providers: Un array donde defines los proveedores de autenticación. Aquí usamosGoogleProvidery pasamos las credenciales de entorno.secret: La clave secreta para la firma y cifrado de los tokens.pages: Permite personalizar las rutas de NextAuth.js (inicio de sesión, error, etc.). Esto es útil para mantener la coherencia de UI/UX de tu aplicación.callbacks: Funciones asíncronas que se ejecutan durante la autenticación. Aquí, extendemos el token JWT y el objeto de sesión para incluir eliddel usuario. Esto es crucial si necesitas el ID del usuario en tu frontend o para interactuar con una base de datos.session: Configuración de la estrategia de sesión. Usamosjwtpara un manejo de sesiones sin estado (ideal para arquitecturas escalables) y definimos la duración máxima de la sesión.
Ahora, necesitamos instalar el proveedor de Google:
npm install @next-auth/google
# o con yarn
yarn add @next-auth/google
🌐 Paso 5: Crear un Provider Context para el Cliente
Para que los componentes de React puedan acceder a la sesión, necesitamos envolver nuestra aplicación con un SessionProvider.
Crea el archivo app/providers.tsx:
'use client';
import { SessionProvider } from 'next-auth/react';
interface ProvidersProps {
children: React.ReactNode;
}
export function Providers({ children }: ProvidersProps) {
return (
<SessionProvider>
{children}
</SessionProvider>
);
}
Ahora, envuelve tu componente RootLayout con este Providers en app/layout.tsx:
import type { Metadata } from 'next';
import { Inter } from 'next/font/google';
import './globals.css';
import { Providers } from './providers'; // Importa el proveedor
const inter = Inter({ subsets: ['latin'] });
export const metadata: Metadata = {
title: 'Next.js Auth Tutorial',
description: 'Authentication with NextAuth.js and Google',
};
export default function RootLayout({
children,
}: Readonly<{ children: React.ReactNode }>)
{
return (
<html lang="en">
<body className={inter.className}>
<Providers> {/* Envuelve con el proveedor */}
{children}
</Providers>
</body>
</html>
);
);
}
Este SessionProvider hará que la sesión esté disponible en todos los componentes de la aplicación mediante los hooks de NextAuth.js.
🚪 Paso 6: Implementar Botones de Inicio y Cierre de Sesión
Ahora crearemos una página de inicio donde los usuarios podrán iniciar y cerrar sesión.
Modifica app/page.tsx para incluir la lógica de autenticación:
'use client';
import { useSession, signIn, signOut } from 'next-auth/react';
export default function HomePage() {
const { data: session, status } = useSession();
if (status === 'loading') {
return <p>Cargando sesión...</p>;
}
if (session) {
return (
<div className="flex flex-col items-center justify-center min-h-screen py-2">
<h1 className="text-4xl font-bold mb-4">Bienvenido, {session.user?.name}</h1>
<p className="text-lg mb-6">Estás autenticado como: {session.user?.email}</p>
<img
src={session.user?.image || 'https://via.placeholder.com/150'}
alt="Avatar de usuario"
className="rounded-full w-24 h-24 mb-4"
/>
<button
onClick={() => signOut()}
className="bg-red-500 hover:bg-red-700 text-white font-bold py-2 px-4 rounded"
>
Cerrar Sesión
</button>
</div>
);
}
return (
<div className="flex flex-col items-center justify-center min-h-screen py-2">
<h1 className="text-4xl font-bold mb-4">No estás autenticado</h1>
<button
onClick={() => signIn('google')}
className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"
>
Iniciar Sesión con Google
</button>
</div>
);
}
Explicación:
useSession(): Este hook del lado del cliente te proporciona el estado de la sesión (dataystatus).signIn('google'): Inicia el flujo de autenticación de Google.signOut(): Cierra la sesión del usuario.
Ahora, inicia tu servidor de desarrollo:
npm run dev
Abre tu navegador en http://localhost:3000. Deberías ver el botón para iniciar sesión con Google. ¡Pruébalo!
🔐 Paso 7: Proteger Rutas del Lado del Cliente
Para proteger rutas del lado del cliente, puedes usar el hook useSession y redirigir al usuario si no está autenticado.
Crea una nueva página app/dashboard/page.tsx:
'use client';
import { useSession } from 'next-auth/react';
import { useRouter } from 'next/navigation';
import { useEffect } from 'react';
export default function DashboardPage() {
const { data: session, status } = useSession();
const router = useRouter();
useEffect(() => {
if (status === 'loading') return; // Do nothing while loading
if (!session) {
router.push('/'); // Redirect to home if not authenticated
}
}, [session, status, router]);
if (status === 'loading') {
return (
<div className="flex items-center justify-center min-h-screen">
<p className="text-xl">Cargando dashboard...</p>
</div>
);
}
if (!session) {
return null; // Or a loading spinner, as the redirect will happen soon
}
return (
<div className="flex flex-col items-center justify-center min-h-screen py-2">
<h1 className="text-4xl font-bold mb-4">Página Protegida del Dashboard</h1>
<p className="text-lg">Solo los usuarios autenticados pueden ver esto.</p>
<p className="text-md mt-4">Tu ID de usuario es: <span class="badge green">{session.user?.id}</span></p>
</div>
);
}
Intenta acceder a http://localhost:3000/dashboard sin iniciar sesión. Deberías ser redirigido a la página de inicio. Una vez que inicies sesión, podrás acceder al dashboard.
🔒 Paso 8: Proteger Rutas del Lado del Servidor (Server Components)
Con el App Router de Next.js, puedes proteger rutas y renderizar componentes del lado del servidor basados en el estado de autenticación. Esto es muy potente para SEO y rendimiento.
Para obtener la sesión en un Server Component, necesitas usar la función getServerSession.
Modifica app/dashboard/page.tsx para ser un Server Component y protegerlo directamente. Primero, elimina el use client del archivo app/dashboard/page.tsx.
Luego, actualiza app/dashboard/page.tsx a lo siguiente:
import { getServerSession } from 'next-auth';
import { redirect } from 'next/navigation';
import { authOptions } from '../api/auth/[...nextauth]/route'; // Importa las opciones de autenticación
export default async function DashboardPage() {
const session = await getServerSession(authOptions);
if (!session) {
redirect('/'); // Redirige a la página de inicio si no hay sesión
}
return (
<div className="flex flex-col items-center justify-center min-h-screen py-2">
<h1 className="text-4xl font-bold mb-4">Página Protegida del Dashboard (Server)</h1>
<p className="text-lg">¡Bienvenido, {session.user?.name}! Solo los usuarios autenticados pueden ver esto.</p>
<p className="text-md mt-4">Tu correo es: <span class="badge blue">{session.user?.email}</span></p>
</div>
);
}
Actualización de app/api/auth/[...nextauth]/route.ts:
import NextAuth from "next-auth";
import GoogleProvider from "next-auth/providers/google";
import { NextAuthOptions } from 'next-auth';
export const authOptions: NextAuthOptions = {
providers: [
GoogleProvider({
clientId: process.env.GOOGLE_CLIENT_ID as string,
clientSecret: process.env.GOOGLE_CLIENT_SECRET as string,
}),
],
secret: process.env.AUTH_SECRET,
pages: {
signIn: '/auth/signin',
signOut: '/auth/signout',
error: '/auth/error',
verifyRequest: '/auth/verify-request',
newUser: '/auth/new-user',
},
callbacks: {
async jwt({ token, user }) {
if (user) {
token.id = user.id;
}
return token;
},
async session({ session, token }) {
if (token) {
session.user.id = token.id as string;
}
return session;
},
},
session: {
strategy: "jwt",
maxAge: 30 * 24 * 60 * 60, // 30 days
updateAge: 24 * 60 * 60, // 24 hours
},
};
const handler = NextAuth(authOptions);
export { handler as GET, handler as POST };
Reinicia el servidor de desarrollo y prueba de nuevo http://localhost:3000/dashboard. Si no estás autenticado, deberías ser redirigido inmediatamente a la página de inicio antes de que el componente del dashboard sea siquiera renderizado en el servidor. Esto es más seguro y eficiente.
✨ Personalización y Consideraciones Adicionales
🎨 Páginas de Inicio de Sesión Personalizadas
NextAuth.js te permite crear tus propias páginas de inicio de sesión, error, etc. En la configuración de NextAuth (en route.ts), establecimos pages:
pages: {
signIn: '/auth/signin',
// ... otras páginas
},
Para crear una página de inicio de sesión personalizada, simplemente crea el archivo app/auth/signin/page.tsx:
'use client';
import { signIn, getProviders } from 'next-auth/react';
import { useEffect, useState } from 'react';
import type { ClientSafeProvider, LiteralUnion } from 'next-auth/react';
import type { BuiltInProviderType } from 'next-auth/providers';
export default function SignInPage() {
const [providers, setProviders] = useState<Record<LiteralUnion<BuiltInProviderType>, ClientSafeProvider> | null>(null);
useEffect(() => {
const fetchProviders = async () => {
const res = await getProviders();
setProviders(res);
};
fetchProviders();
}, []);
if (!providers) {
return <p>Cargando proveedores de autenticación...</p>;
}
return (
<div className="flex flex-col items-center justify-center min-h-screen py-2">
<h1 className="text-4xl font-bold mb-8">Inicia Sesión en Tu Aplicación</h1>
{
Object.values(providers).map((provider) => (
<div key={provider.name} className="mb-4">
<button
onClick={() => signIn(provider.id, { callbackUrl: '/' })}
className="bg-green-500 hover:bg-green-700 text-white font-bold py-3 px-6 rounded-lg text-lg"
>
Iniciar Sesión con {provider.name}
</button>
</div>
))
}
</div>
);
}
Ahora, cuando intentes iniciar sesión, serás redirigido a http://localhost:3000/auth/signin.
📊 Tabla Comparativa: Protección en Cliente vs. Servidor
| Característica | Protección del Lado del Cliente (useSession) | Protección del Lado del Servidor (getServerSession) |
|---|---|---|
| --- | --- | --- |
| Componente | use client (Client Component) | Por defecto (Server Component) |
| Rendimiento | Redirección después del renderizado inicial del cliente. | Redirección antes del renderizado del servidor. Más eficiente. |
| --- | --- | --- |
| Seguridad | Menos segura para datos sensibles, ya que se renderiza primero. | Más segura, el acceso a datos sensibles se controla en el servidor. |
| SEO | El contenido se oculta o redirige después del JavaScript. | El contenido nunca se envía al cliente si no está autorizado. |
| --- | --- | --- |
| Facilidad de uso | Simple para UI reactiva y protección básica. | Requiere exportar authOptions y manejar async/await. |
| Uso Principal | Elementos de UI, contenido condicional en el cliente. | Rutas protegidas, obtención de datos sensibles, APIs. |
🗺️ Flujo de Autenticación con NextAuth.js
Comprender cómo funciona el flujo es clave para depurar y extender tu autenticación.
Este diagrama muestra cómo interactúan el cliente, el servidor Next.js y el proveedor OAuth para establecer y mantener la sesión del usuario.
✅ Conclusión
Has aprendido a implementar un sistema de autenticación robusto en tu aplicación Next.js utilizando NextAuth.js y un proveedor OAuth como Google. Hemos cubierto desde la configuración inicial, la integración de proveedores, la gestión de sesiones y, lo más importante, la protección de rutas tanto en el lado del cliente como en el lado del servidor.
NextAuth.js es una herramienta increíblemente potente y flexible que te permite añadir autenticación compleja con relativa facilidad, liberándote para centrarte en la lógica de negocio de tu aplicación. Recuerda siempre priorizar la seguridad de tus credenciales y considerar las implicaciones de seguridad al elegir entre protección del lado del cliente y del servidor.
¡Ahora estás listo para construir aplicaciones Next.js seguras y personalizadas!
Tutoriales relacionados
- Aprovechando la Carga de Datos en el Cliente con SWR en Next.js App Router ⚡intermediate15 min
- ¡Rutas Dinámicas y Anidadas en Next.js con el App Router! 🚀 Guía Completaintermediate20 min
- ¡Construyendo un Blog SEO-Friendly con Next.js y MDX! 🚀intermediate25 min
- ¡Next.js en el Edge! Cómo Usar Edge Functions para Apps Más Rápidas y Globales 🚀intermediate15 min
- ¡Despliega tu App Next.js como un Pro! Guía Completa con Vercelbeginner15 min
Comentarios (0)
Aún no hay comentarios. ¡Sé el primero!