Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Tutorial 12: JWT and Cookie Sessions 🔑

Cookie sessions and bearer JWTs are transport choices with different operational trade-offs. Neither is automatically “for web” or “for mobile,” and neither removes the need for TLS, authorization, rotation, expiry, and revocation design.


Step 1: Generate JWT middleware

cargo rullst make:jwt

This creates src/middlewares/jwt_middleware.rs, registers its module, and adds the direct jsonwebtoken, chrono, and serde dependencies when missing.

Configure a high-entropy secret plus exact issuer and audience values:

export JWT_SECRET="$(openssl rand -base64 48)"
export JWT_ISSUER="https://identity.example.test"
export JWT_AUDIENCE="rullst-api"

The generated HS256 validator requires sub, iss, aud, iat, and exp, checks expiry, and rejects secrets shorter than 32 bytes, with fewer than 12 distinct characters, or starting with mock_. It reads these variables with std::env::var on each request, so they must be set in the process environment: a value only in .env is not seen, and while one is missing a request that carries a bearer token receives 500. Keep the secret out of source control and logs.

The same file provides generate_token(user_id), which issues a token with these settings that expires after 24 hours. Call it after your own credential check, never for an unauthenticated caller.


Step 2: Protect a route group

use rullst::{Router, routing::get};
use rullst::web::axum::middleware;
use crate::middlewares::jwt_middleware::jwt_middleware;

pub fn protected_routes() -> Router {
    Router::new()
        .route("/profile", get(user_profile))
        .route("/orders", get(user_orders))
        .layer(middleware::from_fn(jwt_middleware))
}

Valid claims are inserted into request extensions. The handler must still map sub to a current account and enforce resource/tenant authorization. For long-lived systems, design key rotation and immediate revocation rather than assuming expiry alone is sufficient.


Choosing deliberately

  • An HttpOnly, Secure, appropriately SameSite cookie reduces direct token reads by browser JavaScript. It does not neutralize XSS: injected code can still act as the user, and cookie requests need CSRF protection.
  • A bearer token is convenient for interoperable API clients, but storage in a browser or native app is an application security decision. A stolen bearer token can be replayed until rejected or expired.
  • Cookie and JWT strategies can coexist at different boundaries, but keep one authoritative identity/session lifecycle and test logout/revocation behavior.