Introduction: Why Do You Need Authorization in Rust?
Modern web applications rarely do without an authentication and authorization system. Rust, thanks to its performance and memory safety, is becoming a popular choice for building high-load services. However, working with protocols like OAuth 2.0 and the JWT format can seem complex for beginners.
In this article, we will break down the basic concepts of OAuth 2.0 and JWT (JSON Web Token) and implement a simple authorization system in Rust using popular crates. You will learn how to protect your API endpoints and work with access tokens.
What are OAuth 2.0 and JWT?
Before writing code, it is important to understand the theory.
OAuth 2.0: Delegated Access Protocol
OAuth 2.0 is a protocol that allows an application to obtain limited access to a user's resources without transmitting their login and password. Imagine you want to log into a website via Google or GitHub — that is OAuth 2.0.
The main participants of the protocol:
- Resource Owner — the user who owns the data.
- Client — your Rust application that wants to gain access.
- Authorization Server — the server that issues tokens (e.g., Google).
- Resource Server — the API that provides data (e.g., Google Drive).
JWT: Compact Token Format
JWT (JSON Web Token) is an open standard for creating access tokens. The token consists of three parts separated by dots:
- Header — the token type and signing algorithm.
- Payload — the data (claims), e.g., user ID or role.
- Signature — a signature that guarantees the token has not been altered.
Example JWT: eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIn0.dozjgNrxP0G7n9G4W7N8Y7J8
JWT is convenient because it does not require storing sessions on the server — all the information is already embedded in the token.
Setting Up a Rust Project for Authorization
We will use the Actix Web framework and several helper crates. Create a new project:
cargo new rust-auth-examplecd rust-auth-exampleAdd dependencies to Cargo.toml:
[dependencies]actix-web = "4"actix-rt = "2"serde = { version = "1", features = ["derive"] }serde_json = "1"jsonwebtoken = "9"chrono = { version = "0.4", features = ["serde"] }dotenv = "0.15"env_logger = "0.10"bcrypt = "0.15"File Structure
For convenience, let's create a modular structure:
src/├── main.rs├── auth.rs├── models.rs└── handlers.rsIn .env, we will specify the secret key for JWT:
JWT_SECRET=supersecretkey12345DATABASE_URL=sqlite://users.dbImplementing JWT in Rust: From Generation to Verification
Let's start with the auth.rs module, which will handle JWT operations.
Structures for JWT
// auth.rsuse serde::{Deserialize, Serialize};use jsonwebtoken::{encode, decode, Header, Validation, EncodingKey, DecodingKey};use chrono::{Utc, Duration};
#[derive(Debug, Serialize, Deserialize)]pub struct Claims { pub sub: String, // User ID pub exp: usize, // Expiration time pub iat: usize, // Issued at time}
pub fn create_jwt(user_id: &str, secret: &str) -> Result<String, jsonwebtoken::errors::Error> { let now = Utc::now(); let expiration = now + Duration::hours(24); // token lives for 24 hours
let claims = Claims { sub: user_id.to_owned(), exp: expiration.timestamp() as usize, iat: now.timestamp() as usize, };
let token = encode( &Header::default(), &claims,\