Secure Authentication in Rust: Building a Protected Backend

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

Why Rust is the Ideal Choice for Authentication?

\n\n

If you are writing a web application in Rust, you are already one step ahead in terms of security. Rust does not forgive memory errors, meaning many vulnerabilities (buffer overflow, use-after-free) are eliminated at compile time. But the compiler will not protect you from poor authentication logic. That is what this is about — how to build secure authentication in Rust using modern practices.

\n\n

In this article, we will assemble a full-fledged authentication module: password hashing via bcrypt, JWT token issuance, middleware verification in Actix-web, and protection against common attacks. The code will be functional — feel free to use it in your project.

\n\n

1. Password Hashing: Why bcrypt and Not SHA-256?

\n\n

Many beginners think: "I'll use SHA-256, it's fast and reliable." And that is a fatal mistake. SHA is a cryptographic hash function, but it is too fast. An attacker can brute-force billions of combinations per second. We need a slow function, specifically designed for passwords.

\n\n

bcrypt is the gold standard. It includes a salt and a cost factor, which can be increased as computing power grows.

\n\n

Adding bcrypt to Cargo.toml

\n
[dependencies]\nbcrypt = "0.15"
\n\n

Hashing a Password

\n
use bcrypt::{hash, verify, DEFAULT_COST};\n\nfn hash_password(plain: &str) -> Result<String, bcrypt::BcryptError> {\n    hash(plain, DEFAULT_COST) // cost = 12 (default)\n}\n\nfn verify_password(plain: &str, hashed: &str) -> bool {\n    verify(plain, hashed).unwrap_or(false)\n}
\n\n

Note: verify extracts the salt from the hash itself. You do not need to store the salt separately — it is already inside the hash string. This is safe and convenient.

\n\n

2. JWT Tokens: How Not to Fail Authentication

\n\n

After the user enters their password, we must issue them a "pass". JWT (JSON Web Token) is a compact and self-contained format. But the devil is in the details:

\n\n
    \n
  • Do not store the secret key in code. Use environment variables.
  • \n
  • Set a short expiration time. Access token — 15–30 minutes, refresh token — 7–30 days.
  • \n
  • Do not insert sensitive data into the payload. Only the user ID and role.
  • \n
\n\n

Implementing JWT with jsonwebtoken

\n\n
use jsonwebtoken::{encode, decode, Header, Validation, EncodingKey, DecodingKey};\nuse serde::{Serialize, Deserialize};\nuse chrono::{Utc, Duration};\n\n#[derive(Debug, Serialize, Deserialize)]\npub struct Claims {\n    pub sub: String,       // User ID\n    pub exp: usize,        // expiration timestamp\n    pub iat: usize,        // issued at\n}\n\npub fn create_token(user_id: &str, secret: &str) -> Result<String, jsonwebtoken::errors::Error> {\n    let now = Utc::now();\n    let claims = Claims {\n        sub: user_id.to_owned(),\n        exp: (now + Duration::minutes(30)).timestamp() as usize,\n        iat: now.timestamp() as usize,\n    };\n    encode(&Header::default(), &claims, &EncodingKey::from_secret(secret.as_bytes()))\n}\n\npub fn validate_token(token: &str, secret: &str) -> Result<Claims, jsonwebtoken::errors::Error> {\n    let token_data = decode::<Claims>(\n        token,\n        &DecodingKey::from_secret(secret.as_bytes()),\n        &Validation::default(),\n    )?;\n    Ok(token_data.claims)\n}
\n\n

3. Putting It All Together: Actix-web + Middleware

\n\n

Now let's combine hashing and JWT into a full-fledged web application using Actix-web. We'll write middleware that checks the token on every protected request.

\n\n

Project Structure

\n
src/\n├── main.rs\n├── auth.rs       # hashing, JWT\n├── middleware.rs # token verification\n└── handlers.rs   #                

Blogs

Book Recommendations