OAuth 2.0 et autorisation JWT en Rust : guide pas à pas pour débutants

Formateur Python en ligne pour débutants

Apprenez Python facilement sans surcharge théorique. Résolvez des tâches pratiques avec vérification automatique et écrivez du code directement dans le navigateur.

Start Course

Introduction

\n

Le développement web moderne est impensable sans une authentification et une autorisation sécurisées des utilisateurs. Les deux normes les plus populaires — OAuth 2.0 et JWT (JSON Web Tokens) — sont devenues des solutions industrielles de facto. Si vous programmez en Rust, vous avez de la chance : ce langage offre non seulement les plus hautes performances, mais aussi des bibliothèques puissantes pour implémenter ces protocoles.

\n

Dans cet article, nous allons voir ce que sont OAuth 2.0 et JWT, comment ils fonctionnent ensemble, et écrire un exemple simple mais fonctionnel d'autorisation en Rust en utilisant le framework actix-web et la crate jsonwebtoken. L'article est destiné aux débutants, nous allons donc procéder étape par étape — de la théorie au code.

\n\n

Que sont OAuth 2.0 et JWT ?

\n

Avant d'écrire du code, il est important de comprendre les concepts. OAuth 2.0 est un protocole d'autorisation qui permet aux applications d'obtenir un accès limité aux ressources d'un utilisateur sans lui transmettre son mot de passe. Il est utilisé lorsque vous vous connectez à un site via Google, GitHub ou VK.

\n

JWT est un format de jeton (JSON Object Signing and Encryption) qui contient des assertions (claims) sur l'utilisateur. Le jeton est signé par le serveur, donc le client ne peut pas le falsifier. Dans le contexte d'OAuth 2.0, JWT est souvent utilisé comme access token (jeton d'accès).

\n

Scénario typique : l'utilisateur saisit son identifiant et son mot de passe, le serveur les vérifie, génère un jeton JWT et le renvoie au client. Le client envoie ce jeton dans l'en-tête Authorization: Bearer <token> à chaque requête. Le serveur vérifie la signature et extrait les données de l'utilisateur.

\n\n

Configuration du projet en Rust

\n

Créons un nouveau projet :

\n
cargo new rust_oauth_jwt\ncd rust_oauth_jwt
\n

Ajoutons les dépendances nécessaires dans Cargo.toml :

\n
[dependencies]\nactix-web = "4"\nactix-rt = "2"\nserde = { version = "1", features = ["derive"] }\nserde_json = "1"\njsonwebtoken = "9"\nchrono = { version = "0.4", features = ["serde"] }\ndotenv = "0.15"\nenv_logger = "0.11"\nlog = "0.4"
\n

Créons un fichier .env à la racine du projet pour stocker la clé secrète :

\n
JWT_SECRET=my_super_secret_key_change_in_production\nJWT_EXPIRATION_HOURS=24
\n

Chargeons les variables d'environnement dans main.rs :

\n
use dotenv::dotenv;\nuse std::env;\n\nfn main() {\n    dotenv().ok();\n    env_logger::init();\n    // ... reste du code\n}
\n\n

Création de la structure utilisateur et du jeton JWT

\n

Définissons d'abord le modèle utilisateur et la structure des claims pour JWT. Créons un fichier models.rs :

\n
use serde::{Deserialize, Serialize};\n\n#[derive(Debug, Serialize, Deserialize, Clone)]\npub struct User {\n    pub id: i32,\n    pub username: String,\n    pub password_hash: String, // Dans un vrai projet, stocker le hash, pas le mot de passe !\n}\n\n#[derive(Debug, Serialize, Deserialize)]\npub struct Claims {\n    pub sub: String,       // subject (id utilisateur)\n    pub username: String,\n    pub exp: usize,        // timestamp d'expiration\n}
\n

Créons maintenant un utilitaire pour travailler avec JWT dans jwt_utils.rs :

\n
use chrono::{Duration, Utc};\nuse jsonwebtoken::{decode, encode, DecodingKey, EncodingKey, Header, Validation};\nuse crate::models::Claims;\n\npub fn create_jwt(user_id: &str, username: &str) -> Result<String, jsonwebtoken::errors::Error> {\n    let secret = std::env::var("JWT_SECRET").expect("JWT_SECRET must be set");\n    let expiration_hours: i64 = std::env::var("JWT_EXPIRATION_HOURS")\n        .unwrap_or_else(|_| "24".to_string())\n        .parse()\n        .unwrap();\n\n    let expiration = Utc::now()\n        .checked_add_signed(Duration::hours(expiration_hours))\n        .expect("valid timestamp")\n        .timestamp() as usize;\n\n    let claims = Claims {\n        sub: user_id.to_owned(),\n        username: username.to_owned(),\n                

Blogs

Book Recommendations