Saltar al contenido principal

Usa IA para integrar Auth0

Si usas un asistente de programación con IA como Claude Code, Cursor o GitHub Copilot, puedes añadir la autenticación de Auth0 automáticamente en minutos usando agent skills.Instala:
npx skills add auth0/agent-skills --skill auth0-quickstart --skill auth0-fastify
Luego, pídele a tu asistente de IA:
Add Auth0 authentication to my Fastify app
Tu asistente de IA creará automáticamente tu aplicación en Auth0, obtendrá las credenciales, instalará @auth0/auth0-fastify, configurará el plugin y creará todas las rutas y vistas necesarias. Documentación completa sobre agent skills →
Requisitos previos: Antes de comenzar, asegúrate de tener instalado lo siguiente:Verifica la instalación: node --version && npm --versionCompatibilidad de versiones de Fastify: Esta guía de inicio rápido funciona con Fastify 5.x y versiones posteriores.

Introducción

Esta guía de inicio rápido muestra cómo agregar la autenticación de Auth0 a una aplicación Fastify. Crearás una aplicación web segura con funciones de inicio de sesión, cierre de sesión y perfil de usuario mediante el SDK de Auth0 para Fastify.
1

Crear un proyecto nuevo

Cree un directorio nuevo para su aplicación Fastify e inicialice un proyecto de Node.js.
mkdir auth0-fastify && cd auth0-fastify
Inicializar el proyecto
npm init -y
Cree la estructura del proyecto
touch server.js .env
2

Instala el SDK de Auth0 para Fastify

Instala las dependencias necesarias
npm install @auth0/auth0-fastify fastify dotenv @fastify/view ejs
Usamos @fastify/view con ejs para el renderizado del lado del servidor. Puede usar cualquier motor de plantillas compatible con Fastify.
Actualice su package.json para añadir scripts de inicio:
package.json
{
  "name": "auth0-fastify",
  "version": "1.0.0",
  "type": "module",
  "main": "server.js",
  "scripts": {
    "start": "node server.js",
    "dev": "node --watch server.js"
  },
  "dependencies": {
    "@auth0/auth0-fastify": "^1.2.0",
    "@fastify/view": "^10.0.0",
    "dotenv": "^16.3.1",
    "ejs": "^3.1.9",
    "fastify": "^5.0.0"
  }
}
3

Configura tu aplicación de Auth0

A continuación, debe crear una nueva aplicación en su inquilino de Auth0 y agregar las variables de entorno a su proyecto.Tiene tres opciones para configurar su aplicación de Auth0: usar la herramienta Quick Setup (recomendada), ejecutar un comando de la CLI o configurarla manualmente desde el Dashboard:
Cree una aplicación de Auth0 y copie el archivo .env precompletado con los valores de configuración correctos.
Verifique que exista su archivo .env: cat .env (Mac/Linux) o type .env (Windows)
4

Configurar el plugin de Auth0

Cree su servidor Fastify y registre el complemento de Auth0:
server.js
import 'dotenv/config';
import Fastify from 'fastify';
import fastifyView from '@fastify/view';
import fastifyAuth0 from '@auth0/auth0-fastify';
import ejs from 'ejs';

const fastify = Fastify({ logger: true });
const port = process.env.PORT || 3000;

// Registrar motor de vistas
await fastify.register(fastifyView, {
  engine: { ejs },
  root: './views',
});

// Registrar plugin de Auth0
await fastify.register(fastifyAuth0, {
  domain: process.env.AUTH0_DOMAIN,
  clientId: process.env.AUTH0_CLIENT_ID,
  clientSecret: process.env.AUTH0_CLIENT_SECRET,
  appBaseUrl: process.env.APP_BASE_URL,
  sessionSecret: process.env.SESSION_SECRET,
});

// Iniciar servidor
fastify.listen({ port }, (err) => {
  if (err) {
    fastify.log.error(err);
    process.exit(1);
  }
  fastify.log.info(`Server running at http://localhost:${port}`);
});
Qué hace esto:
  • Registra el motor de vistas para procesar plantillas HTML
  • Configura el complemento de Auth0 con tus credenciales
  • Crea automáticamente rutas en /auth/login, /auth/logout y /auth/callback
  • Gestiona la sesión mediante cookies cifradas
5

Crear plantillas de vista

Crea un directorio views y añade los archivos de plantilla:
Mac/Linux
mkdir views && touch views/home.ejs views/profile.ejs
Windows
New-Item -ItemType Directory -Path views
New-Item -ItemType File -Path views/home.ejs
New-Item -ItemType File -Path views/profile.ejs
Crea la plantilla de la página de inicio:
views/home.ejs
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Auth0 Fastify Quickstart</title>
  <style>
    body {
      font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
      background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
      margin: 0;
      padding: 2rem;
      min-height: 100vh;
      display: flex;
      justify-content: center;
      align-items: center;
    }
    .container {
      background: white;
      border-radius: 20px;
      box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
      padding: 3rem;
      max-width: 500px;
      width: 100%;
      text-align: center;
    }
    h1 {
      color: #2d3748;
      font-size: 2.5rem;
      margin-bottom: 1rem;
    }
    .status {
      padding: 1rem;
      border-radius: 10px;
      margin: 1.5rem 0;
      font-size: 1.1rem;
    }
    .logged-in {
      background: #d4edda;
      color: #155724;
    }
    .logged-out {
      background: #f8d7da;
      color: #721c24;
    }
    .button {
      display: inline-block;
      padding: 1rem 2rem;
      margin: 0.5rem;
      border-radius: 10px;
      text-decoration: none;
      font-weight: 600;
      transition: all 0.3s;
    }
    .button-primary {
      background: #667eea;
      color: white;
    }
    .button-primary:hover {
      background: #5568d3;
      transform: translateY(-2px);
    }
    .button-secondary {
      background: #e53e3e;
      color: white;
    }
    .button-secondary:hover {
      background: #c53030;
      transform: translateY(-2px);
    }
  </style>
</head>
<body>
  <div class="container">
    <h1>🚀 Auth0 Fastify</h1>
    <div class="status <%= isAuthenticated ? 'logged-in' : 'logged-out' %>">
      <%= isAuthenticated ? '✓ You are logged in' : '✗ You are logged out' %>
    </div>
    <div>
      <% if (isAuthenticated) { %>
        <a href="/profile" class="button button-primary">View Profile</a>
        <a href="/auth/logout" class="button button-secondary">Logout</a>
      <% } else { %>
        <a href="/auth/login" class="button button-primary">Login</a>
      <% } %>
    </div>
  </div>
</body>
</html>
Crea la plantilla de la página de perfil:
views/profile.ejs
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Profile - Auth0 Fastify</title>
  <style>
    body {
      font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
      background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
      margin: 0;
      padding: 2rem;
      min-height: 100vh;
    }
    .container {
      background: white;
      border-radius: 20px;
      box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
      padding: 3rem;
      max-width: 700px;
      margin: 0 auto;
    }
    h1 {
      color: #2d3748;
      margin-bottom: 2rem;
    }
    .profile-card {
      display: flex;
      align-items: center;
      gap: 2rem;
      padding: 2rem;
      background: #f7fafc;
      border-radius: 15px;
      margin-bottom: 2rem;
    }
    .profile-picture {
      width: 100px;
      height: 100px;
      border-radius: 50%;
      object-fit: cover;
      border: 3px solid #667eea;
    }
    .profile-info h2 {
      margin: 0 0 0.5rem 0;
      color: #2d3748;
    }
    .profile-info p {
      margin: 0;
      color: #718096;
    }
    .user-data {
      background: #f7fafc;
      padding: 1.5rem;
      border-radius: 10px;
      overflow-x: auto;
    }
    pre {
      margin: 0;
      white-space: pre-wrap;
      word-wrap: break-word;
    }
    .button {
      display: inline-block;
      padding: 0.75rem 1.5rem;
      margin-right: 1rem;
      border-radius: 10px;
      text-decoration: none;
      font-weight: 600;
      transition: all 0.3s;
    }
    .button-primary {
      background: #667eea;
      color: white;
    }
    .button-primary:hover {
      background: #5568d3;
    }
    .button-secondary {
      background: #e53e3e;
      color: white;
    }
    .button-secondary:hover {
      background: #c53030;
    }
  </style>
</head>
<body>
  <div class="container">
    <h1>User Profile</h1>
    <div class="profile-card">
      <img src="<%= user.picture || 'https://via.placeholder.com/100' %>" alt="Profile" class="profile-picture">
      <div class="profile-info">
        <h2><%= user.name || user.nickname || 'User' %></h2>
        <p><strong>Email:</strong> <%= user.email || 'N/A' %></p>
      </div>
    </div>
    <h3>Full User Object</h3>
    <div class="user-data">
      <pre><%= JSON.stringify(user, null, 2) %></pre>
    </div>
    <div style="margin-top: 2rem;">
      <a href="/" class="button button-primary">← Back to Home</a>
      <a href="/auth/logout" class="button button-secondary">Logout</a>
    </div>
  </div>
</body>
</html>
6

Crear rutas

Agrega las rutas a tu archivo server.js:
server.js
import 'dotenv/config';
import Fastify from 'fastify';
import fastifyView from '@fastify/view';
import fastifyAuth0 from '@auth0/auth0-fastify';
import ejs from 'ejs';

const fastify = Fastify({ logger: true });
const port = process.env.PORT || 3000;

// Registrar motor de vistas
await fastify.register(fastifyView, {
  engine: { ejs },
  root: './views',
});

// Registrar plugin de Auth0
await fastify.register(fastifyAuth0, {
  domain: process.env.AUTH0_DOMAIN,
  clientId: process.env.AUTH0_CLIENT_ID,
  clientSecret: process.env.AUTH0_CLIENT_SECRET,
  appBaseUrl: process.env.APP_BASE_URL,
  sessionSecret: process.env.SESSION_SECRET,
});

// Ruta de inicio - pública
fastify.get('/', async (request, reply) => {
  const session = await fastify.auth0Client.getSession({ request, reply });
  return reply.view('views/home.ejs', {
    isAuthenticated: !!session,
  });
});

// Ruta de perfil - protegida
fastify.get('/profile', {
  preHandler: async (request, reply) => {
    const session = await fastify.auth0Client.getSession({ request, reply });
    if (!session) {
      return reply.redirect('/auth/login');
    }
  }
}, async (request, reply) => {
  const user = await fastify.auth0Client.getUser({ request, reply });
  return reply.view('views/profile.ejs', { user });
});

// Iniciar servidor
fastify.listen({ port }, (err) => {
  if (err) {
    fastify.log.error(err);
    process.exit(1);
  }
  fastify.log.info(`Server running at http://localhost:${port}`);
});
Puntos clave:
  • La ruta de inicio comprueba el estado de autenticación y lo pasa a la plantilla
  • La ruta de perfil usa un preHandler para protegerla
  • getSession() devuelve la sesión del usuario o null si no está autenticado
  • getUser() devuelve la información del perfil del usuario autenticado
7

Ejecuta tu aplicación

Inicie el servidor de desarrollo:
npm run dev
Abre http://localhost:3000 en tu navegador.
La opción --watch en Node.js 20+ reinicia automáticamente el servidor cuando cambian los archivos.
VerificaciónAhora deberías tener una página de inicio de sesión de Auth0 totalmente funcional. Cuando:
  1. Haces clic en “Login” - se te redirige a la página de Universal Login de Auth0
  2. Completas la autenticación - se te redirige de nuevo a tu aplicación
  3. Visitas “/profile” - ves la información de tu usuario
  4. Haces clic en “Logout” - se cierra tu sesión tanto en tu aplicación como en Auth0

Uso avanzado

Para llamar a API externas que requieren un token de acceso, configure el SDK con una audiencia:
server.js
await fastify.register(fastifyAuth0, {
  domain: process.env.AUTH0_DOMAIN,
  clientId: process.env.AUTH0_CLIENT_ID,
  clientSecret: process.env.AUTH0_CLIENT_SECRET,
  appBaseUrl: process.env.APP_BASE_URL,
  sessionSecret: process.env.SESSION_SECRET,
  audience: process.env.AUTH0_AUDIENCE, // Añada esto
});
Añada lo siguiente a su archivo .env:
.env
AUTH0_AUDIENCE=https://your-api.example.com
Luego obtenga y use el token de acceso:
server.js
fastify.get('/api-data', {
  preHandler: async (request, reply) => {
    const session = await fastify.auth0Client.getSession({ request, reply });
    if (!session) {
      return reply.redirect('/auth/login');
    }
  }
}, async (request, reply) => {
  try {
    const { accessToken } = await fastify.auth0Client.getAccessToken({ request, reply });

    // Llame a su API protegida
    const response = await fetch('https://your-api.example.com/data', {
      headers: {
        Authorization: `Bearer ${accessToken}`,
      },
    });

    const data = await response.json();
    return data;
  } catch (error) {
    fastify.log.error('API call failed:', error);
    return reply.status(500).send({ error: 'Failed to fetch data' });
  }
});
De forma predeterminada, las rutas de Auth0 se montan en /auth/*. Puede deshabilitar el montaje automático y crear rutas personalizadas:
server.js
await fastify.register(fastifyAuth0, {
  domain: process.env.AUTH0_DOMAIN,
  clientId: process.env.AUTH0_CLIENT_ID,
  clientSecret: process.env.AUTH0_CLIENT_SECRET,
  appBaseUrl: process.env.APP_BASE_URL,
  sessionSecret: process.env.SESSION_SECRET,
  mountRoutes: false, // Desactive el montaje automático
});

// Ruta de inicio de sesión personalizada
fastify.get('/custom-login', async (request, reply) => {
  const authorizationUrl = await fastify.auth0Client.startInteractiveLogin(
    {
      authorizationParams: {
        redirect_uri: `${process.env.APP_BASE_URL}/custom-callback`
      }
    },
    { request, reply }
  );
  return reply.redirect(authorizationUrl.href);
});

// Ruta de callback personalizada
fastify.get('/custom-callback', async (request, reply) => {
  await fastify.auth0Client.completeInteractiveLogin(
    new URL(request.url, process.env.APP_BASE_URL),
    { request, reply }
  );
  return reply.redirect('/');
});

// Ruta de cierre de sesión personalizada
fastify.get('/custom-logout', async (request, reply) => {
  const logoutUrl = await fastify.auth0Client.logout(
    { returnTo: process.env.APP_BASE_URL },
    { request, reply }
  );
  return reply.redirect(logoutUrl.href);
});
Recuerde actualizar sus Allowed Callback URLs en el Auth0 Dashboard para incluir la URL de callback personalizada.
Permita a los usuarios vincular varios proveedores de autenticación a una sola cuenta:
server.js
await fastify.register(fastifyAuth0, {
  domain: process.env.AUTH0_DOMAIN,
  clientId: process.env.AUTH0_CLIENT_ID,
  clientSecret: process.env.AUTH0_CLIENT_SECRET,
  appBaseUrl: process.env.APP_BASE_URL,
  sessionSecret: process.env.SESSION_SECRET,
  mountConnectRoutes: true, // Habilite las rutas de vinculación de cuentas
});
Esto crea automáticamente las siguientes rutas:
  • /auth/connect - Vincular un nuevo proveedor
  • /auth/connect/callback - Gestionar el callback de vinculación
  • /auth/unconnect - Desvincular un proveedor
  • /auth/unconnect/callback - Gestionar el callback de desvinculación
Añada botones de vinculación a su página de perfil:
views/profile.ejs
<div>
  <a href="/auth/connect?connection=google-oauth2">Vincular cuenta de Google</a>
  <a href="/auth/unconnect?connection=google-oauth2">Desvincular cuenta de Google</a>
</div>
Convierte tu proyecto a TypeScript para mejorar la seguridad de tipos:
npm install --save-dev typescript @types/node tsx
Crea un archivo tsconfig.json:
tsconfig.json
{
  "compilerOptions": {
    "target": "ES2022",
    "module": "ESNext",
    "moduleResolution": "node",
    "esModuleInterop": true,
    "strict": true,
    "skipLibCheck": true,
    "outDir": "./dist"
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules"]
}
Cambia el nombre de server.js a server.ts y añade tipos:
server.ts
import 'dotenv/config';
import Fastify, { FastifyRequest, FastifyReply } from 'fastify';
import fastifyView from '@fastify/view';
import fastifyAuth0 from '@auth0/auth0-fastify';
import ejs from 'ejs';

const fastify = Fastify({ logger: true });
const port = process.env.PORT || 3000;

await fastify.register(fastifyView, {
  engine: { ejs },
  root: './views',
});

await fastify.register(fastifyAuth0, {
  domain: process.env.AUTH0_DOMAIN!,
  clientId: process.env.AUTH0_CLIENT_ID!,
  clientSecret: process.env.AUTH0_CLIENT_SECRET!,
  appBaseUrl: process.env.APP_BASE_URL!,
  sessionSecret: process.env.SESSION_SECRET!,
});

fastify.get('/', async (request: FastifyRequest, reply: FastifyReply) => {
  const session = await fastify.auth0Client.getSession({ request, reply });
  return reply.view('views/home.ejs', {
    isAuthenticated: !!session,
  });
});

fastify.listen({ port: Number(port) });
Actualiza package.json:
package.json
{
  "scripts": {
    "dev": "tsx watch server.ts",
    "build": "tsc",
    "start": "node dist/server.js"
  }
}

Solución de problemas

Error de “Invalid state” después de iniciar sesión

Problema: El valor de state no coincide entre la solicitud de autenticación y el callback.Soluciones:
  1. Asegúrate de que las cookies se estén configurando correctamente (que el navegador no las bloquee)
  2. Verifica que la URL de callback coincida exactamente en Auth0 Dashboard (incluido /auth/callback)
  3. Comprueba que SESSION_SECRET esté configurada y tenga al menos 64 caracteres

Error de “session is undefined”

Problema: No se pueden recuperar los datos de la sesión.Solución: Asegúrate de que el plugin de Auth0 se registre antes de acceder a los métodos de sesión:
// ✅ Orden correcto
await fastify.register(fastifyAuth0, { ... });
fastify.get('/profile', async (request, reply) => {
  const session = await fastify.auth0Client.getSession({ request, reply });
});

// ❌ Incorrecto: no se esperó al plugin
fastify.register(fastifyAuth0, { ... }); // Falta await
fastify.get('/profile', async (request, reply) => { ... });

La URL de callback no coincide

Problema: Error “Callback URL mismatch” de Auth0.Solución:
  1. Ve a Auth0 Dashboard → Applications → Your App → Settings
  2. Agrega http://localhost:3000/auth/callback a Allowed Callback URLs
  3. La URL debe coincidir exactamente (incluida la ruta /auth/callback)

Las variables de entorno no se cargan

Problema: Los valores de configuración son undefined.Solución:
  1. Asegúrate de que import 'dotenv/config' esté al principio del archivo de entrada
  2. Verifica que el archivo .env esté en el directorio raíz
  3. Comprueba que no haya errores tipográficos en los nombres de las variables
// Depuración: muestra los valores de configuración (¡elimínalo en producción!)
console.log('Config check:', {
  hasDomain: !!process.env.AUTH0_DOMAIN,
  hasClientID: !!process.env.AUTH0_CLIENT_ID,
  hasSecret: !!process.env.SESSION_SECRET,
});

Próximos pasos

Ahora que ya tienes la autenticación en funcionamiento, considera explorar lo siguiente:

Recursos