Chapitre 6.3 : Authentification de base
Temps d'étude : 1 heure
1. Authentification API : Le laissez-passer du Centre de Contrôle Mission
L'authentification est le processus de vérification de l'identité d'un utilisateur. Contrairement aux sites web avec sessions et cookies, les API stateless (sans état) utilisent généralement des jetons.
Le processus se déroule comme suit :
- L'utilisateur envoie son nom d'utilisateur et son mot de passe à un point d'extrémité dédié (par exemple,
/login
). - Le serveur les vérifie. Si tout est correct, il génère un jeton unique et chiffré (une longue chaîne de caractères) et le renvoie.
- Pour chaque requête ultérieure aux ressources protégées (par exemple,
POST /planets
), l'utilisateur doit joindre ce jeton dans l'en-têteAuthorization
. - Le serveur vérifie la validité du jeton et, s'il est correct, exécute la requête.
💡 Analogie spatiale :
- Nom d'utilisateur/mot de passe = Votre scan biométrique pour obtenir le laissez-passer.
- Jeton = Le laissez-passer électronique (carte d'identité) que vous recevez à l'entrée du Centre de Contrôle Mission.
- En-tête
Authorization: Bearer <jeton>
= Vous présentez votre laissez-passer au lecteur devant chaque porte sécurisée.- Points d'extrémité protégés (POST, PUT, DELETE) = Les portes menant à la salle des serveurs ou au pupitre de commande de lancement.
2. Authentification dans Laravel : Sanctum
Laravel offre une solution élégante pour l'authentification API — Laravel Sanctum. Il est idéal pour les SPA (applications monopages), les applications mobiles et les API simples basées sur des jetons.
Étape 1 : Installation et configuration de Sanctum
Sanctum est déjà installé dans une application Laravel standard, mais nous allons vérifier la configuration.
- Publication de la configuration (si ce n'est pas déjà fait) :
- Exécution des migrations (créera la table
personal_access_tokens
) : - Ajout du trait au modèle
User
: Ouvrezapp/Models/User.php
et assurez-vous qu'il utilise le traitHasApiTokens
.
Étape 2 : Création d'un point d'extrémité pour l'émission de jetons Nous avons besoin d'une route vers laquelle l'utilisateur enverra son nom d'utilisateur/mot de passe.
Ajoutez à routes/api.php
:
// routes/api.php
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
use App\Models\User;
use Illuminate\Validation\ValidationException;
Route::post('/login', function (Request $request) {
$request->validate([
'email' => 'required|email',
'password' => 'required',
]);
$user = User::where('email', $request->email)->first();
if (! $user || ! Hash::check($request->password, $user->password)) {
throw ValidationException::withMessages([
'email' => ['Les identifiants sont incorrects.'],
]);
}
// Retourne le jeton
return response()->json([
'token' => $user->createToken('api-token')->plainTextToken
]);
});
Pour les tests, vous pouvez créer un utilisateur via un seeder ou Tinker.
Étape 3 : Protection des routes
Maintenant, protégeons nos opérations CRUD. Modifions routes/api.php
:
// routes/api.php
use App\Http\Controllers\PlanetController;
// Route publique pour visualiser les planètes
Route::get('/planets', [PlanetController::class, 'index']);
Route::get('/planets/{planet}', [PlanetController::class, 'show']);
// Groupe de routes protégées
Route::middleware('auth:sanctum')->group(function () {
Route::post('/planets', [PlanetController::class, 'store']);
Route::put('/planets/{planet}', [PlanetController::class, 'update']);
Route::delete('/planets/{planet}', [PlanetController::class, 'destroy']);
// Route pour la déconnexion (suppression du jeton)
Route::post('/logout', function (Request $request) {
$request->user()->currentAccessToken()->delete();
return response()->json(['message' => 'Vous avez été déconnecté avec succès'], 200);
});
});
Le middleware auth:sanctum
vérifiera la présence d'un jeton valide dans l'en-tête Authorization
.
3. Authentification dans FastAPI : OAuth2 et JWT
FastAPI ne dispose pas de système d'authentification intégré, mais il offre de puissants outils pour sa mise en œuvre. La norme de facto est OAuth2 avec des jetons JWT.
Étape 1 : Installation des dépendances
python-jose
: pour la création et la vérification des jetons JWT.passlib
: pour le hachage et la vérification des mots de passe.python-multipart
: pour le traitement des données de formulaire (username
etpassword
).
Étape 2 : Création du module de sécurité (security.py
)
C'est une bonne pratique de déplacer toute la logique d'authentification dans un fichier séparé.
Créez le fichier security.py
:
# security.py
from fastapi import Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer
from jose import JWTError, jwt
from passlib.context import CryptContext
from datetime import datetime, timedelta, timezone
# --- Paramètres ---
SECRET_KEY = "your-super-secret-key-that-is-long-and-random" # ⚠️ Remplacez par votre propre clé !
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30
# --- Utilitaires ---
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/api/v1/login")
# --- Fonctions ---
def verify_password(plain_password, hashed_password):
return pwd_context.verify(plain_password, hashed_password)
def get_password_hash(password):
return pwd_context.hash(password)
def create_access_token(data: dict):
to_encode = data.copy()
expire = datetime.now(timezone.utc) + timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
to_encode.update({"exp": expire})
encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
return encoded_jwt
# --- Fonction de dépendance pour la vérification du jeton ---
def get_current_user(token: str = Depends(oauth2_scheme)):
credentials_exception = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Impossible de valider les identifiants",
headers={"WWW-Authenticate": "Bearer"},
)
try:
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
username: str = payload.get("sub")
if username is None:
raise credentials_exception
except JWTError:
raise credentials_exception
# Ici, vous pouvez retourner l'utilisateur depuis la BDD, pour l'instant, nous retournons juste le nom
return {"username": username}
Étape 3 : Intégration dans main.py
Maintenant, intégrons cela à notre application.
-
Créons un point d'extrémité
/login
:# main.py from fastapi.security import OAuth2PasswordRequestForm from fastapi import Depends, APIRouter from . import security # Importons notre module # ... votre code FastAPI ... router = APIRouter(prefix="/api/v1") @router.post("/login") def login_for_access_token(form_data: OAuth2PasswordRequestForm = Depends()): # Ici, la vérification de l'utilisateur dans la BDD doit avoir lieu # Pour l'exemple, nous avons un utilisateur de test is_user_valid = (form_data.username == "testuser" and security.verify_password("testpass", security.get_password_hash("testpass"))) if not is_user_valid: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Nom d'utilisateur ou mot de passe incorrect", ) access_token = security.create_access_token(data={"sub": form_data.username}) return {"access_token": access_token, "token_type": "bearer"} # ... app.include_router(router)
-
Protégeons les points d'extrémité :
Utilisons notre dépendance
get_current_user
.# main.py ou dans votre routeur de planètes @router.post("/planets", status_code=status.HTTP_201_CREATED) def create_planet( planet: PlanetCreate, current_user: dict = Depends(security.get_current_user) # <-- Protection ! ): # Logique de création de planète... print(f"L'utilisateur {current_user['username']} crée une planète.") # ... return new_planet # Protégeons également PUT et DELETE
4. Utilisation des jetons sur le frontend
Notre frontend doit maintenant d'abord obtenir le jeton, le stocker (par exemple, dans localStorage
) et le joindre à chaque requête protégée.
Exemple en JavaScript (fetch
) :
// 1. Connexion
async function login(email, password) {
const response = await fetch('http://localhost:8001/api/login', { // Adresse de l'API Laravel
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({email, password})
});
const data = await response.json();
if (data.token) {
localStorage.setItem('api_token', data.token); // Stocke le jeton
}
}
// 2. Effectue une requête protégée
async function createPlanet(planetData) {
const token = localStorage.getItem('api_token');
const response = await fetch('http://localhost:8001/api/planets', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}` // <--- Joindre le jeton !
},
body: JSON.stringify(planetData)
});
// ...
}
Quiz pour la consolidation
🚀 Résumé du chapitre :
Vous avez mis en place un "système de contrôle d'accès" sur vos API. Désormais, n'importe qui ne peut plus modifier votre "base de données galactique".
- ✅ Compris le principe de l'authentification basée sur les jetons.
- 🔐 Mis en œuvre l'émission de jetons et la protection des routes dans Laravel Sanctum.
- ⚙️ Configurée l'authentification basée sur OAuth2 et JWT dans FastAPI.
- 🛰️ Appris comment le frontend doit stocker et utiliser le jeton.
Vos API sont devenues non seulement fonctionnelles, mais aussi sécurisées. Cependant, pour que d'autres développeurs puissent les utiliser, ils ont besoin d'"instructions d'utilisation".
📌 Vérification :
- Essayez de faire une requête
POST
à/api/planets
(dans Laravel) ou/api/v1/planets
(dans FastAPI) sans jeton en utilisant Postman ou Insomnia. Vous devriez obtenir une erreur401 Unauthorized
.- Faites une requête à
/login
, obtenez un jeton, ajoutez-le à l'en-têteAuthorization
et répétez la requêtePOST
. Elle devrait s'exécuter avec succès.