OAuth 2.0 and JWT Authorization in Rust: A Step-by-Step Guide for Beginners

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Introduction

\n

Modern web development is unthinkable without secure user authentication and authorization. The two most popular standards — OAuth 2.0 and JWT (JSON Web Tokens) — have become de facto industry solutions. If you are writing in Rust, you are in luck: this language offers not only the highest performance but also powerful libraries for implementing these protocols.

\n

In this article, we will break down what OAuth 2.0 and JWT are, how they work together, and write a simple but working authorization example in Rust using the actix-web framework and the jsonwebtoken crate. The article is aimed at beginners, so we will go step by step — from theory to code.

\n\n

What are OAuth 2.0 and JWT?

\n

Before writing code, it is important to understand the concepts. OAuth 2.0 is an authorization protocol that allows applications to obtain limited access to user resources without transmitting their password. It is used when you log into a website via Google, GitHub, or VK.

\n

JWT is a token format (JSON Object Signing and Encryption) that contains claims about the user. The token is signed by the server, so the client cannot forge it. In the context of OAuth 2.0, JWT is often used as an access token.

\n

A typical scenario: the user enters a login and password, the server verifies them, generates a JWT token, and returns it to the client. The client sends this token in the Authorization: Bearer <token> header with each request. The server verifies the signature and extracts the user data.

\n\n

Setting Up a Rust Project

\n

Let's create a new project:

\n
cargo new rust_oauth_jwt\ncd rust_oauth_jwt
\n

Add the necessary dependencies to 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

Create a .env file in the project root to store the secret key:

\n
JWT_SECRET=my_super_secret_key_change_in_production\nJWT_EXPIRATION_HOURS=24
\n

Load the environment variables in main.rs:

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

Creating the User Structure and JWT Token

\n

First, let's define the user model and the claims structure for JWT. Create a file 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, // In a real project, store the hash, not the password!\n}\n\n#[derive(Debug, Serialize, Deserialize)]\npub struct Claims {\n    pub sub: String,       // subject (user id)\n    pub username: String,\n    pub exp: usize,        // expiration timestamp\n}
\n

Now create a utility for working with JWT in 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