Skip to content

Authentication

The auth Cargo feature adds BasicAuthLayer and JwtLayer middleware, plus standalone helpers for building and verifying HS256 JWTs.

[dependencies]
rust-web-server = { version = "17", features = ["auth"] }

The feature pulls in hmac and sha2 (RustCrypto) as additional dependencies. No other HTTP or JWT crates are required.

HTTP Basic Auth

BasicAuthLayer validates Authorization: Basic <base64> credentials against a caller-supplied closure. It issues 401 Unauthorized with a WWW-Authenticate: Basic realm="Protected" challenge when the header is absent or malformed.

use rust_web_server::app::App;
use rust_web_server::auth::BasicAuthLayer;
use rust_web_server::core::New;
let app = App::new()
.wrap(BasicAuthLayer::new(|user, pass| {
user == "admin" && pass == "s3cret"
}));

The closure signature is Fn(&str, &str) -> bool + Send + Sync + 'static. Passwords containing : are handled correctly — only the first : splits username from password, per RFC 7617.

Credential check outcomes:

ConditionResponse
Authorization header absent or malformed401 + WWW-Authenticate challenge
Header present but closure returns false401 (no challenge)
Closure returns trueRequest forwarded to next layer

JWT — HS256

JwtLayer verifies Authorization: Bearer <token> JWTs signed with HMAC-SHA256.

use rust_web_server::app::App;
use rust_web_server::auth::JwtLayer;
use rust_web_server::core::New;
let app = App::new()
.wrap(JwtLayer::new(b"my-signing-secret"));

Tokens with a past exp claim are rejected. Any token that fails format validation, algorithm check, or signature verification returns 401 Unauthorized.

Building tokens

Use build_jwt in a login handler to issue a signed token:

use rust_web_server::auth::build_jwt;
fn login_handler(/* ... */) -> String {
let claims = r#"{"sub":"42","role":"admin","exp":9999999999}"#;
let token = build_jwt(claims, b"my-signing-secret");
// return token to client
token
}

build_jwt(claims_json, secret) always produces an HS256 JWT with the alg and typ header set. The claims_json argument is any valid JSON object string.

Verifying tokens and reading claims

Call verify_jwt directly inside a handler when you also need the decoded claims. The verification is cheap (~1 µs) so double-calling is fine.

use rust_web_server::auth::{verify_jwt, extract_bearer_token};
use rust_web_server::request::Request;
use rust_web_server::response::Response;
fn protected_handler(req: &Request) -> Response {
let token = extract_bearer_token(req).expect("JwtLayer already validated this");
let claims = verify_jwt(&token, b"my-signing-secret").unwrap();
println!("subject: {:?}", claims.sub);
println!("expires: {:?}", claims.exp);
// parse custom claims from claims.raw (raw JSON string)
Response::new()
}

Claims struct

FieldTypeDescription
subOption<String>Subject (user ID), if present
expOption<u64>Expiration as Unix seconds, if present
rawStringRaw UTF-8 JSON payload — inspect for custom claims

claims.is_valid_at(now_secs) returns true when the token is not yet expired. Returns true when exp is absent (no expiry set).

extract_bearer_token

Extracts the raw token string from Authorization: Bearer <token>. Returns None when the header is absent or does not start with Bearer .

use rust_web_server::auth::extract_bearer_token;
if let Some(token) = extract_bearer_token(&request) {
// token is the raw JWT string
}

Login handler pattern

A typical login flow: verify credentials, issue a JWT, return it to the client.

use rust_web_server::auth::build_jwt;
use rust_web_server::request::Request;
use rust_web_server::response::Response;
use rust_web_server::body::form_urlencoded::FormUrlEncoded;
use std::time::{SystemTime, UNIX_EPOCH};
fn login(req: &Request) -> Response {
let form = FormUrlEncoded::parse(&req.body);
let username = form.get("username").unwrap_or("");
let password = form.get("password").unwrap_or("");
if username != "admin" || password != "s3cret" {
let mut r = Response::new();
r.status_code = 401;
return r;
}
let exp = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_secs() + 3600; // 1 hour
let claims = format!(r#"{{"sub":"{username}","exp":{exp}}}"#);
let token = build_jwt(&claims, b"my-signing-secret");
let mut r = Response::new();
// set body to JSON with token
r
}