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, appropriatelySameSitecookie 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.