Passer au contenu principal

Utiliser l’IA pour intégrer Auth0

Si vous utilisez un assistant de programmation IA comme Claude Code, Cursor ou GitHub Copilot, vous pouvez ajouter automatiquement l’authentification API d’Auth0 en quelques minutes à l’aide des agent skills.Installation :
npx skills add auth0/agent-skills --skill auth0-quickstart --skill auth0-fastify-api
Demandez ensuite à votre assistant IA :
Add Auth0 JWT authentication to my Fastify API
Votre assistant IA créera automatiquement votre API Auth0, récupérera les identifiants, installera @auth0/auth0-fastify-api, configurera le plugin et protégera les points de terminaison de votre API grâce à la validation JWT. Documentation complète des agent skills →
Prérequis : Avant de commencer, assurez-vous d’avoir installé les éléments suivants :Vérifiez l’installation : node --version && npm --versionCompatibilité des versions de Fastify : Ce guide de démarrage rapide fonctionne avec Fastify 5.x et les versions ultérieures.

Pour commencer

Ce guide de démarrage rapide montre comment protéger des points de terminaison de l’API Fastify à l’aide de jetons d’accès JWT. Vous créerez une API sécurisée qui valide les jetons d’accès d’Auth0 et autorise l’accès aux ressources protégées.
1

Créer un nouveau projet

Créez un nouveau répertoire pour votre API Fastify et initialisez un projet Node.js.
mkdir auth0-fastify-api && cd auth0-fastify-api
Initialiser le projet
npm init -y
Créez la structure du projet
touch server.js .env
2

Installez le SDK d’API Auth0 pour Fastify

Installer les dépendances requises
npm install @auth0/auth0-fastify-api fastify dotenv
Mettez à jour votre package.json pour y ajouter des scripts de démarrage :
package.json
{
  "name": "auth0-fastify-api",
  "version": "1.0.0",
  "type": "module",
  "main": "server.js",
  "scripts": {
    "start": "node server.js",
    "dev": "node --watch server.js"
  },
  "dependencies": {
    "@auth0/auth0-fastify-api": "^1.2.0",
    "dotenv": "^16.3.1",
    "fastify": "^5.0.0"
  }
}
3

Configurez votre API Auth0

Ensuite, vous devez créer une nouvelle API dans votre locataire Auth0 et ajouter les variables d’environnement à votre projet.Vous avez deux options pour configurer votre API Auth0 : utiliser une commande CLI ou faire la configuration manuellement dans le Dashboard :
Exécutez la commande suivante à la racine de votre projet pour créer une API Auth0 :
# Installer Auth0 CLI (si ce n’est pas déjà fait)
brew tap auth0/auth0-cli && brew install auth0

# Créer l’API Auth0
auth0 apis create \
  --name "My Fastify API" \
  --identifier https://my-fastify-api.example.com
Après la création, copiez les valeurs Identifier et Domain, puis créez votre fichier .env :
.env
AUTH0_DOMAIN=YOUR_AUTH0_DOMAIN
AUTH0_AUDIENCE=YOUR_API_IDENTIFIER
Cette commande va :
  1. Vérifier si vous êtes authentifié (et vous inviter à vous connecter au besoin)
  2. Créer une API Auth0 avec l’identifiant indiqué
  3. Afficher les détails de l’API, y compris le domaine et l’identifiant
Vérifiez que votre fichier .env existe : cat .env (Mac/Linux) ou type .env (Windows)
4

Configurer le plugin API Auth0

Créez votre serveur Fastify et enregistrez le plugin d’API Auth0 :
server.js
import 'dotenv/config';
import Fastify from 'fastify';
import fastifyAuth0Api from '@auth0/auth0-fastify-api';

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

// Enregistrer le plugin API Auth0
await fastify.register(fastifyAuth0Api, {
  domain: process.env.AUTH0_DOMAIN,
  audience: process.env.AUTH0_AUDIENCE,
});

// Démarrer le serveur
fastify.listen({ port }, (err) => {
  if (err) {
    fastify.log.error(err);
    process.exit(1);
  }
  fastify.log.info(`API server running at http://localhost:${port}`);
});
Ce que cela fait :
  • Enregistre le plugin d’API Auth0 pour votre domaine Auth0 et l’audience de l’API
  • Configure la validation du JWT pour les requêtes entrantes
  • Met le prégestionnaire requireAuth() à disposition pour protéger les routes
5

Créer des routes API

Ajoutez des routes publiques et protégées à votre fichier server.js :
server.js
import 'dotenv/config';
import Fastify from 'fastify';
import fastifyAuth0Api from '@auth0/auth0-fastify-api';

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

// Enregistrer le plugin API Auth0
await fastify.register(fastifyAuth0Api, {
  domain: process.env.AUTH0_DOMAIN,
  audience: process.env.AUTH0_AUDIENCE,
});

// Route publique - aucune authentification requise
fastify.get('/api/public', async (request, reply) => {
  return {
    message: 'Hello from a public endpoint! You don\'t need to be authenticated to see this.',
    timestamp: new Date().toISOString(),
  };
});

// Route protégée - nécessite un jeton d'accès valide
fastify.get('/api/private', {
  preHandler: fastify.requireAuth()
}, async (request, reply) => {
  return {
    message: 'Hello from a protected endpoint! You successfully authenticated.',
    user: request.user.sub,
    timestamp: new Date().toISOString(),
  };
});

// Route protégée - retourne les informations de l'utilisateur à partir du jeton
fastify.get('/api/profile', {
  preHandler: fastify.requireAuth()
}, async (request, reply) => {
  return {
    message: 'Your user profile from the access token',
    profile: request.user,
  };
});

// Démarrer le serveur
fastify.listen({ port }, (err) => {
  if (err) {
    fastify.log.error(err);
    process.exit(1);
  }
  fastify.log.info(`API server running at http://localhost:${port}`);
});
Points clés :
  • Les routes publiques ne nécessitent pas d’authentification
  • Les routes protégées utilisent preHandler: fastify.requireAuth() pour exiger un JWT valide
  • request.user contient les claims du JWT décodés pour les requêtes authentifiées
  • Le claim sub contient l’identifiant unique de l’utilisateur
6

Lancez votre API

Démarrez le serveur de développement :
npm run dev
Votre API est maintenant accessible à l’adresse http://localhost:3001.
L’option --watch de Node.js 20+ redémarre automatiquement le serveur lorsque des fichiers sont modifiés.
7

Testez votre API

Testez le point de terminaison public (aucune authentification n’est requise) :
curl http://localhost:3001/api/public
Vous devriez voir :
{
  "message": "Hello from a public endpoint! You don't need to be authenticated to see this.",
  "timestamp": "2024-01-15T10:30:00.000Z"
}
Testez le point de terminaison protégé sans Token (échec attendu) :
curl http://localhost:3001/api/private
Vous devriez voir une erreur 401 Unauthorized :
{
  "error": "Unauthorized",
  "message": "No authorization token was found"
}
Pour tester avec un jeton valide, vous devez :
  1. Créer une application cliente (application Web ou mobile) qui authentifie les utilisateurs
  2. Configurer l’application pour demander un jeton d’accès pour votre API (à l’aide du paramètre audience)
  3. Utiliser ce jeton d’accès dans l’en-tête Authorization
Exemple avec un jeton :
curl http://localhost:3001/api/private \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"
Point de contrôleVous devriez maintenant avoir une API protégée. Votre API :
  1. Accepte les requêtes vers des points de terminaison publics sans authentification
  2. Rejette les requêtes vers des points de terminaison protégés sans jeton valide
  3. Valide les jetons JWT par rapport à votre domaine Auth0 et à votre audience
  4. Fournit les renseignements sur l’utilisateur à partir des claims du jeton au moyen de request.user

Utilisation avancée

Étendez l’interface Token pour assurer un typage sûr de vos claims personnalisées dans vos jetons d’accès :
server.ts
import '@auth0/auth0-fastify-api';

// Étendre l’interface Token avec vos claims personnalisées
declare module '@auth0/auth0-fastify-api' {
  interface Token {
    sub: string;
    permissions?: string[];
    'https://myapp.com/roles'?: string[];
    email?: string;
    email_verified?: boolean;
  }
}
TypeScript reconnaîtra maintenant vos claims personnalisées :
server.ts
fastify.get('/api/profile', {
  preHandler: fastify.requireAuth()
}, async (request, reply) => {
  // TypeScript reconnaît ces propriétés
  const userRoles = request.user['https://myapp.com/roles']; // string[] | undefined
  const permissions = request.user.permissions; // string[] | undefined
  const email = request.user.email; // string | undefined

  return {
    userId: request.user.sub,
    roles: userRoles || [],
    permissions: permissions || [],
    email: email,
  };
});
Les claims personnalisées doivent utiliser des URL avec espace de noms (par ex. https://myapp.com/roles), sauf s’il s’agit de claims OIDC standard. En savoir plus sur les claims personnalisées.
Vérifiez la présence de permissions précises dans le jeton d’accès :
server.js
// Middleware pour vérifier une permission précise
function requirePermission(permission) {
  return async (request, reply) => {
    const permissions = request.user.permissions || [];

    if (!permissions.includes(permission)) {
      return reply.status(403).send({
        error: 'Forbidden',
        message: `Missing required permission: ${permission}`
      });
    }
  };
}

// Route nécessitant la permission 'read:messages'
fastify.get('/api/messages', {
  preHandler: [
    fastify.requireAuth(),
    requirePermission('read:messages')
  ]
}, async (request, reply) => {
  return {
    messages: ['Message 1', 'Message 2', 'Message 3']
  };
});

// Route nécessitant la permission 'write:messages'
fastify.post('/api/messages', {
  preHandler: [
    fastify.requireAuth(),
    requirePermission('write:messages')
  ]
}, async (request, reply) => {
  return {
    message: 'Message created successfully',
    id: 'msg_123'
  };
});
Les permissions doivent être configurées dans les paramètres de votre API Auth0 et accordées aux applications. En savoir plus sur les permissions d’API.
Mettez en œuvre le contrôle d’accès basé sur les rôles à l’aide de claims personnalisées :
server.js
// Middleware pour vérifier un rôle précis
function requireRole(role) {
  return async (request, reply) => {
    const roles = request.user['https://myapp.com/roles'] || [];

    if (!roles.includes(role)) {
      return reply.status(403).send({
        error: 'Forbidden',
        message: `Missing required role: ${role}`
      });
    }
  };
}

// Route réservée aux administrateurs
fastify.get('/api/admin/users', {
  preHandler: [
    fastify.requireAuth(),
    requireRole('admin')
  ]
}, async (request, reply) => {
  return {
    users: [
      { id: 1, name: 'User 1' },
      { id: 2, name: 'User 2' }
    ]
  };
});

// Route pour les gestionnaires ou les administrateurs
function requireAnyRole(...roles) {
  return async (request, reply) => {
    const userRoles = request.user['https://myapp.com/roles'] || [];
    const hasRole = roles.some(role => userRoles.includes(role));

    if (!hasRole) {
      return reply.status(403).send({
        error: 'Forbidden',
        message: `Missing required role. Need one of: ${roles.join(', ')}`
      });
    }
  };
}

fastify.get('/api/reports', {
  preHandler: [
    fastify.requireAuth(),
    requireAnyRole('admin', 'manager')
  ]
}, async (request, reply) => {
  return { reports: [] };
});
Les rôles doivent être ajoutés aux jetons à l’aide des Actions d’Auth0. Découvrez comment ajouter des rôles aux jetons.
Activez CORS pour autoriser les requêtes provenant d’applications web :
npm install @fastify/cors
server.js
import cors from '@fastify/cors';

await fastify.register(cors, {
  origin: ['http://localhost:3000', 'http://localhost:5173'], // URL de votre application web
  credentials: true,
});
En production, précisez les origines exactes :
server.js
await fastify.register(cors, {
  origin: [
    'https://myapp.com',
    'https://www.myapp.com'
  ],
  credentials: true,
  methods: ['GET', 'POST', 'PUT', 'DELETE'],
});
Ajoutez une gestion complète des erreurs d’authentification :
server.js
// Gestionnaire d’erreurs personnalisé
fastify.setErrorHandler((error, request, reply) => {
  fastify.log.error(error);

  // Gère les erreurs de validation JWT
  if (error.statusCode === 401) {
    return reply.status(401).send({
      error: 'Unauthorized',
      message: error.message || 'Invalid or missing access token',
      code: 'UNAUTHORIZED'
    });
  }

  // Gère les erreurs de permission ou de rôle
  if (error.statusCode === 403) {
    return reply.status(403).send({
      error: 'Forbidden',
      message: error.message || 'Insufficient permissions',
      code: 'FORBIDDEN'
    });
  }

  // Gère les autres erreurs
  return reply.status(error.statusCode || 500).send({
    error: 'Internal Server Error',
    message: 'An unexpected error occurred',
    code: 'INTERNAL_ERROR'
  });
});

// Gestionnaire des routes introuvables
fastify.setNotFoundHandler((request, reply) => {
  return reply.status(404).send({
    error: 'Not Found',
    message: `Route ${request.method} ${request.url} not found`,
    code: 'NOT_FOUND'
  });
});
Protégez votre API contre les abus grâce à la limitation du nombre de requêtes :
npm install @fastify/rate-limit
server.js
import rateLimit from '@fastify/rate-limit';

await fastify.register(rateLimit, {
  max: 100, // Nombre maximal de requêtes
  timeWindow: '1 minute', // Fenêtre temporelle
  errorResponseBuilder: (request, context) => {
    return {
      error: 'Too Many Requests',
      message: `Rate limit exceeded. Try again in ${context.after}`,
      retryAfter: context.after
    };
  }
});

// Appliquer des limites plus strictes à certaines routes
fastify.get('/api/expensive-operation', {
  preHandler: fastify.requireAuth(),
  config: {
    rateLimit: {
      max: 10,
      timeWindow: '1 minute'
    }
  }
}, async (request, reply) => {
  return { result: 'expensive operation result' };
});

Dépannage

”Aucun jeton d’autorisation n’a été trouvé”

Problème : L’API ne trouve pas le jeton d’accès dans la requête.Solutions :
  1. Assurez-vous que l’en-tête Authorization est présent : Authorization: Bearer YOUR_TOKEN
  2. Vérifiez que “Bearer” figure bien avant le jeton
  3. Vérifiez que le jeton n’a pas expiré

”Jeton invalide” ou “jwt malformed”

Problème : Le format du jeton n’est pas valide.Solutions :
  1. Assurez-vous d’utiliser un jeton d’accès, et non un jeton d’identité
  2. Le jeton doit être obtenu avec le paramètre audience de votre API
  3. Vérifiez que le jeton est un JWT valide (il doit comporter trois parties séparées par des points)

“Signature invalide”

Problème : La signature du jeton ne correspond pas.Solutions :
  1. Vérifiez que AUTH0_DOMAIN correspond au domaine qui a émis le jeton
  2. Assurez-vous d’utiliser l’algorithme de signature RS256 (par défaut)
  3. Vérifiez que le jeton n’a pas été modifié

”Audience invalide”

Problème : L’audience du jeton ne correspond pas à votre API.Solution : L’application cliente doit demander un jeton avec la bonne audience :
// Dans votre application cliente
const token = await getAccessTokenSilently({
  authorizationParams: {
    audience: 'https://my-fastify-api.example.com' // Doit correspondre à l’identifiant de votre API
  }
});

Erreurs CORS dans le navigateur

Problème : Le navigateur bloque les requêtes API en raison de la stratégie CORS.Solution : Installez et configurez @fastify/cors :
npm install @fastify/cors
import cors from '@fastify/cors';

await fastify.register(cors, {
  origin: 'http://localhost:3000', // URL de votre interface frontend
  credentials: true
});

Prochaines étapes

Maintenant que vous disposez d’une API protégée, vous pouvez explorer :

Ressources