Skip to main content

Module hmac_auth

Module hmac_auth 

Source
Expand description

AWS-style canonical request signed with SHA-256, replay-bounded by an X-Date tolerance window. See hmac_auth::HmacAuthLayer. HMAC-signed request authentication for service-to-service traffic.

AWS-style: each request carries X-Date and an Authorization header containing a key id + HMAC-SHA256 over the canonical request. The shared key is never transmitted, and replay attacks are bounded by a configurable X-Date tolerance window.

Distinct from crate::api_keys (bearer-token style — the key itself rides the wire on every call): pick HMAC when callers can sign and you don’t trust the channel; pick bearer when TLS is enough and you want the simplest possible client.

§Wire format

POST /webhooks/incoming HTTP/1.1
X-Date: 2026-05-02T12:34:56Z
Authorization: HMAC-SHA256 keyId=k_abc,signature=<base64>

§Canonical request (what gets signed)

<UPPER-METHOD>\n
<PATH>\n
<SORTED-QUERY-STRING>\n
<X-DATE>\n
<HEX-SHA256(BODY)>

Sorted query so ?b=2&a=1 and ?a=1&b=2 produce the same signature. Body is hashed (SHA-256 hex) — saves the verifier from buffering and re-hashing inside HMAC.

§Quick start

use rustango::hmac_auth::{HmacAuthLayer, KeyResolver};
use tower::ServiceBuilder;
use std::sync::Arc;

// Lookup function: key id -> Some(secret) or None for unknown.
let resolver: KeyResolver = Arc::new(|key_id: &str| {
    if key_id == "k_abc" { Some(b"shared-secret-bytes".to_vec()) } else { None }
});

let inner = axum::Router::new().route("/webhooks/incoming", post(handle));
let app = ServiceBuilder::new()
    .layer(HmacAuthLayer::new(resolver))
    .service(inner);

§Signing on the client side

Use [sign_request] to build the Authorization header value that this layer will accept.

Structs§

HmacAuthLayer
HmacAuthService

Functions§

sign_now
Convenience: pick now() as the date and return both headers (the date + the authorization). Date is RFC 3339 / ISO 8601.
sign_request
Build the Authorization header value for a request signed with secret for key id key_id. Caller is responsible for setting X-Date to a matching RFC 3339 timestamp.

Type Aliases§

KeyResolver
Closure that maps key_idOption<secret>. Implementors typically look up the key in a DB / cache. Returning None rejects the request with 401.