Aller au contenu principal

Authentification/autorisation API

Authentification et autorisation d'une API

Notions théoriques

1. Introduction

Lorsqu'une application communique avec une API, il est souvent nécessaire de contrôler l'accès aux ressources.

Deux concepts fondamentaux permettent de sécuriser une API :

  • L'authentification : Vérification de l'identité d'un utilisateur ou d'un système.
  • L'autorisation : Définition des actions qu'un utilisateur authentifié a le droit d'effectuer.

2. Différence entre authentification et autorisation

ConceptDéfinition
AuthentificationVérifie que l'utilisateur est bien celui qu'il prétend être.
AutorisationDétermine les actions et ressources accessibles après authentification.

Exemple :

  • Authentification : Se connecter avec un identifiant et un mot de passe.
  • Autorisation : Accéder à certaines pages en fonction du rôle de l'utilisateur.

3. Méthodes d'authentification

a) Authentification par mot de passe

L'utilisateur fournit un identifiant et un mot de passe stockés dans une base de données sécurisée.

b) Authentification par token (JWT)

Un JSON Web Token (JWT) est généré après connexion et envoyé dans chaque requête pour prouver l'identité de l'utilisateur.

c) Authentification OAuth 2.0

Permet à un utilisateur d'autoriser une application tierce à accéder à ses données sans partager son mot de passe.

4. Gestion de l'autorisation

a) Rôles et permissions

Chaque utilisateur peut avoir un rôle (admin, utilisateur, modérateur), et chaque rôle définit des permissions (lecture, écriture, suppression).

b) Middleware d'autorisation

Un middleware est un programme intermédiaire qui vérifie si un utilisateur a les droits nécessaires pour accéder à une ressource.


Exemple pratique

Il est possible de sécuriser une API avec Express.js :

  • en utilisant JWT pour l'authentification
  • et un middleware pour l'autorisation.

1. Installation des dépendances

npm install express jsonwebtoken bcryptjs dotenv

2. Mise en place du serveur Express

require("dotenv").config();
const express = require("express");
const jwt = require("jsonwebtoken");
const bcrypt = require("bcryptjs");

const app = express();
app.use(express.json());

const users = [{ id: 1, username: "admin", password: "$2a$10$..." }];

const generateToken = (user) => {
return jwt.sign({ id: user.id, role: "admin" }, process.env.JWT_SECRET, { expiresIn: "1h" });
};

app.post("/login", async (req, res) => {
const { username, password } = req.body;
const user = users.find(u => u.username === username);
if (!user || !await bcrypt.compare(password, user.password)) {
return res.status(401).json({ error: "Identifiants incorrects" });
}
const token = generateToken(user);
res.json({ token });
});

const verifyToken = (req, res, next) => {
const token = req.headers["authorization"];
if (!token) return res.status(403).json({ error: "Accès interdit" });

jwt.verify(token, process.env.JWT_SECRET, (err, user) => {
if (err) return res.status(401).json({ error: "Token invalide" });
req.user = user;
next();
});
};

app.get("/protected", verifyToken, (req, res) => {
res.json({ message: "Accès autorisé", user: req.user });
});

app.listen(3000, () => {
console.log("Serveur démarré sur http://localhost:3000");
});

Test de mémorisation/compréhension


Quel est le rôle de l'authentification ?


Quelle méthode ne permet PAS d'authentifier un utilisateur ?


Que signifie JWT ?


Quelle est la fonction d'un middleware d'autorisation ?


Quel code HTTP est retourné en cas d'accès interdit ?


Quel package est utilisé pour gérer les tokens en Node.js ?


Quelle est la durée de validité d'un JWT dans l'exemple pratique ?


Que fait bcryptjs ?


Quel est le rôle de process.env.JWT_SECRET ?


Que se passe-t-il si un utilisateur fournit un token expiré ?



TP pour réfléchir et résoudre des problèmes

Sécurisation d'une API avec JWT et gestion des rôles

Ce TP a pour objectif de mettre en place une authentification sécurisée avec JWT et d'implémenter un système d'autorisation basé sur les rôles.


Étape 1 : Initialisation du projet

Avant de commencer à coder, il est nécessaire de mettre en place un projet Node.js qui servira de base pour l'API.

Instructions :

  1. Ouvrir un terminal et naviguer vers un dossier de travail.
  2. Créer un nouveau dossier pour le projet :
mkdir secure-api
cd secure-api
  1. Initialiser un projet Node.js :
npm init -y
  1. Installer les dépendances nécessaires :
npm install express jsonwebtoken bcryptjs dotenv cors
  1. Vérifier que les fichiers suivants sont bien présents dans le dossier :
    • package.json (créé automatiquement)
    • node_modules/ (dossier contenant les bibliothèques installées)

Vérifier si :

  • Un projet Node.js est correctement initialisé.
  • Les modules Express, jsonwebtoken, bcryptjs, dotenv et cors sont installés pour gérer l'authentification et la sécurité.

Étape 2 : Configuration du projet

L'authentification par JWT nécessite une clé secrète pour signer les tokens. Cette clé doit être stockée dans un fichier .env pour éviter qu'elle ne soit exposée dans le code source.

Instructions :

  1. Créer un fichier .env à la racine du projet :
touch .env
  1. Ajouter la clé secrète dans ce fichier :
JWT_SECRET=supersecretkey
  1. Modifier le fichier package.json pour ajouter un script de démarrage :
"scripts": {
"start": "node server.js"
}

Vérifier si :

  • Un fichier .env est créé pour stocker la clé secrète.
  • Le fichier package.json est mis à jour avec un script de démarrage.

Étape 3 : Création du serveur et gestion des utilisateurs

L'API doit permettre aux utilisateurs de se connecter et d'obtenir un token JWT.

Instructions :

  1. Créer un fichier server.js et ajouter le code suivant :
require("dotenv").config();
const express = require("express");
const jwt = require("jsonwebtoken");
const bcrypt = require("bcryptjs");

const app = express();
app.use(express.json());

const users = [
{ id: 1, username: "admin", password: bcrypt.hashSync("password", 10), role: "admin" },
{ id: 2, username: "user", password: bcrypt.hashSync("password", 10), role: "user" }
];

app.post("/login", (req, res) => {
const { username, password } = req.body;
const user = users.find(u => u.username === username);
if (!user || !bcrypt.compareSync(password, user.password)) {
return res.status(401).json({ error: "Identifiants incorrects" });
}
const token = jwt.sign({ id: user.id, role: user.role }, process.env.JWT_SECRET, { expiresIn: "1h" });
res.json({ token });
});

app.listen(3000, () => {
console.log("Serveur démarré sur http://localhost:3000");
});
  1. Lancer le serveur :
npm start
  1. Tester la connexion en envoyant une requête POST à http://localhost:3000/login avec un corps JSON :
{
"username": "admin",
"password": "password"
}

Vérifier si :

  • Un serveur Express est mis en place avec une route /login qui génère un token JWT.
  • Les utilisateurs sont stockés en mémoire avec des mots de passe hashés.
  • La connexion est testée avec une requête POST et un token est retourné.

Étape 4 : Mise en place de l'autorisation par rôles

Une fois authentifié, un utilisateur doit avoir accès à certaines routes en fonction de son rôle.

Instructions :

  1. Ajouter un middleware pour vérifier le token et extraire les informations de l'utilisateur :
const verifyToken = (req, res, next) => {
const token = req.headers["authorization"];
if (!token) return res.status(403).json({ error: "Accès interdit" });

jwt.verify(token, process.env.JWT_SECRET, (err, user) => {
if (err) return res.status(401).json({ error: "Token invalide" });
req.user = user;
next();
});
};
  1. Ajouter un middleware pour restreindre l'accès à certaines routes en fonction du rôle :
const authorizeRole = (role) => {
return (req, res, next) => {
if (req.user.role !== role) {
return res.status(403).json({ error: "Accès refusé" });
}
next();
};
};
  1. Ajouter une route protégée accessible uniquement aux administrateurs :
app.get("/admin", verifyToken, authorizeRole("admin"), (req, res) => {
res.json({ message: "Bienvenue, administrateur !" });
});
  1. Tester l'accès avec un token valide et un rôle correct :
  • Se connecter avec un utilisateur admin et récupérer le token.
  • Ajouter le token dans les headers de la requête GET à http://localhost:3000/admin.

Vérifier si :

  • Un middleware verifyToken est ajouté pour sécuriser les routes.
  • Un middleware authorizeRole est créé pour limiter l'accès en fonction du rôle.
  • Une route /admin est protégée et accessible uniquement aux administrateurs.

Étape 5 : Ajout d'une route publique et d'une route protégée

Une API doit contenir des routes accessibles à tous et d'autres protégées.

Instructions :

  1. Ajouter une route publique :
app.get("/", (req, res) => {
res.json({ message: "Bienvenue sur l'API publique" });
});
  1. Ajouter une route protégée accessible à tous les utilisateurs authentifiés :
app.get("/profile", verifyToken, (req, res) => {
res.json({ message: "Profil utilisateur", user: req.user });
});
  1. Tester l'accès :
    • GET http://localhost:3000/ (accessible sans token).
    • GET http://localhost:3000/profile (nécessite un token).

Vérifier si :

  • Une route publique / est ajoutée pour tous les utilisateurs.
  • Une route protégée /profile est accessible uniquement aux utilisateurs authentifiés.

Conclusion

Ce TP permet de comprendre comment authentifier et autoriser l'accès aux ressources d'une API en utilisant JWT et Express.js.

Les améliorations possibles :

  • Ajouter une base de données pour stocker les utilisateurs.
  • Implémenter une expiration automatique des tokens avec un système de rafraîchissement.
  • Ajouter un système de permissions plus détaillé.

L'API est maintenant sécurisée et prête à être utilisée dans un projet plus avancé.