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
Concept | Définition |
---|---|
Authentification | Vérifie que l'utilisateur est bien celui qu'il prétend être. |
Autorisation | Dé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
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 :
- Ouvrir un terminal et naviguer vers un dossier de travail.
- Créer un nouveau dossier pour le projet :
mkdir secure-api
cd secure-api
- Initialiser un projet Node.js :
npm init -y
- Installer les dépendances nécessaires :
npm install express jsonwebtoken bcryptjs dotenv cors
- 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 :
- Créer un fichier
.env
à la racine du projet :
touch .env
- Ajouter la clé secrète dans ce fichier :
JWT_SECRET=supersecretkey
- 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 :
- 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");
});
- Lancer le serveur :
npm start
- 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 :
- 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();
});
};
- 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();
};
};
- Ajouter une route protégée accessible uniquement aux administrateurs :
app.get("/admin", verifyToken, authorizeRole("admin"), (req, res) => {
res.json({ message: "Bienvenue, administrateur !" });
});
- 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 :
- Ajouter une route publique :
app.get("/", (req, res) => {
res.json({ message: "Bienvenue sur l'API publique" });
});
- 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 });
});
- 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é.