Expand description
§broadcast-auth
Shared multi-scheme HTTP and RTSP authentication: a client that answers WWW-Authenticate challenges, and a server that issues and verifies them (Basic, Digest, Bearer, reverse-proxy Forwarded, signed URLs).
§What it does / what it does not
Does: one Credentials model (Basic/Digest user and password, or a Bearer token); a client side (Authenticator, respond) that turns a server challenge into an Authorization value and keeps Digest’s nonce count advancing across a session; and a server side (server::Verifier) that renders the WWW-Authenticate challenge for a 401 and verifies an incoming RequestContext (method, URI, body, headers, peer address). Secrets are wiped on drop and compared in constant time.
Does not: do any IO (sockets, header framing, TLS), store credentials or parse config, or implement OAuth flows. Digest offers qop=auth with MD5 only (the shape every camera and player in the workspace uses); auth-int and SHA-256 Digest are not offered by the verifier. Each client or server owns its transport: rtsp-runtime, hls-runtime and multimux call into this crate.
§Standards supported
| Standard | Edition | Scope | Status | Notes |
|---|---|---|---|---|
| RFC 7617 HTTP Basic | RFC 7617 (not vendored, unverified here) | Client response, server verify (constant-time) | Full | Challenge parsing delegated to http-auth |
| RFC 7616 HTTP Digest | RFC 7616 (not vendored, unverified here) | Client response (MD5, qop=auth, advancing nc), server verify incl. §3.4.4 username* and claimed-uri check | Partial | MD5 and qop=auth only. Server nonces are signed (HMAC-SHA256), expire after DIGEST_NONCE_LIFETIME (3600 s) and carry a 64-value anti-replay window per (nonce, cnonce), capped at DIGEST_NC_TRACK_CAP |
| RFC 6750 Bearer token | RFC 6750 (not vendored, unverified here) | Authorization: Bearer <token> | Full | No challenge round-trip |
| RFC 7235 HTTP authentication framework | RFC 7235 (not vendored, unverified here) | WWW-Authenticate / Authorization header handling | Full | Via http-auth’s ChallengeParser |
| RFC 2326 RTSP 1.0 §14 and §16 | RFC 2326 (vendored specs/rfc2326_rtsp.txt) | RTSP reuses Basic and Digest; only the request URI differs | Full | Used by rtsp-runtime |
| Forwarded (reverse-proxy trust) | Workspace scheme, not a standard | Trusts a configured header (default convention X-Forwarded-User) | Full, server only | Safe only behind a proxy that strips client-supplied copies |
Signed URLs (HMAC-SHA256 exp/kid/sig, optional ip) | Workspace scheme, CDN-style, not a standard | Mint and verify, key rotation across kids, optional IP scope, constant-time compare | Full, server only | signed_url::SignedUrlKeySet |
§Features
None. The crate has no Cargo features, is std, and has #![forbid(unsafe_code)].
§Quick start
A loopback of the whole flow: an origin issues a Digest challenge, a client answers it, the origin verifies.
use broadcast_auth::{AuthResult, Authenticator, Credentials, RequestContext, Verifier};
let creds = Credentials::new("admin", "12345");
let verifier = Verifier::new(creds.clone(), "cameras");
// 401 response: the server renders the challenge.
let challenge = verifier.challenge();
assert!(challenge.starts_with("Digest "));
// Client answers it and keeps the Authenticator for later requests.
let mut client = Authenticator::from_challenge(&challenge, creds).unwrap();
let authorization = client
.authorization(&RequestContext::new("DESCRIBE", "rtsp://cam/stream"))
.unwrap();
// Server verifies the retried request.
let headers = [("Authorization", authorization.as_str())];
let ctx = RequestContext::new("DESCRIBE", "rtsp://cam/stream").with_headers(&headers);
assert_eq!(verifier.verify(&ctx), AuthResult::Ok);
// A wrong password is refused.
let mut bad = Authenticator::from_challenge(&challenge, Credentials::new("admin", "nope")).unwrap();
let wrong = bad.authorization(&RequestContext::new("DESCRIBE", "rtsp://cam/stream")).unwrap();
let headers = [("Authorization", wrong.as_str())];
let ctx = RequestContext::new("DESCRIBE", "rtsp://cam/stream").with_headers(&headers);
assert_eq!(verifier.verify(&ctx), AuthResult::Unauthorized);Signed URLs, with no Authorization header at all:
use broadcast_auth::{AuthResult, RequestContext, SignedUrlKeySet, Verifier};
let keys = SignedUrlKeySet::new([("key-1".to_string(), vec![0u8; 32])]).unwrap();
let query = keys.sign("key-1", "/stream/media.m3u8", 4_000_000_000, None).unwrap();
let verifier = Verifier::signed_url(keys);
let uri = format!("/stream/media.m3u8?{query}");
assert_eq!(verifier.verify(&RequestContext::new("GET", &uri)), AuthResult::Ok);Runnable programs: cargo run -p broadcast-auth --example client_respond and --example server_verify.
§What is implemented
- Client:
Credentials::new(answers whichever of Basic or Digest the challenge advertises) andCredentials::bearer;Authenticator::from_challengeplusauthorizationfor session reuse; one-shotrespond. - Server:
Verifier::new(scheme chosen by theCredentialsvariant: Basic, Digest or Bearer),Verifier::forwarded,Verifier::signed_url,challenge/challenge_for,verifyreturningAuthResult,with_clockandwith_digest_nc_capacityfor tests and tuning. RequestContext: method, URI, optional body and headers (case-insensitiveheaderlookup), peer address.- Security properties:
Credentials,Verifier, nonce keys andSignedUrlKeySetzeroize on drop; comparisons usesubtle::ConstantTimeEq; Digest nonces are stateless-signed and replay-windowed; asecret_debug_guardtest forbids derivedDebugover secrets.
§Design notes
Delegates challenge parsing and Digest hashing to http-auth; types are #[non_exhaustive] where they may grow; errors are structured (Error); a lexical dehandroll_guard test keeps hand-rolled HTTP or base64 out of the source.
§Related crates
Consumed by rtsp-runtime (re-exports Credentials), hls-runtime (client adapter) and multimux (input adapters and the shared output-auth gate). It depends on no other workspace crate.
§MSRV and licence
MSRV 1.95.0. Licensed under MIT OR Apache-2.0.
Re-exports§
pub use signed_url::SignedUrlKeySet;
Modules§
- signed_
url - HMAC-signed URL access control (issue #747) — CDN-style, short-lived, tamper-proof query-string tokens that gate a media egress route without the caller carrying a credential header at all.
Structs§
- Authenticator
- Negotiates a challenge once, then answers every subsequent request in the
session (RFC 7235
WWW-Authenticate/Authorization; RFC 2326 §14 for RTSP; RFC 6750 for Bearer). - Request
Context - The request fields the Digest response hash covers (RFC 7616 §3.4.1 / RFC
2326 §14): the method, the request URI, and — for
qop=auth-int— the body. Also carries the request’s headers and transport peer address, so a server-sidecrate::Verifierscheme can see beyond theAuthorizationheader — e.g. a reverse-proxy forwarded-auth scheme readingX-Forwarded-User/X-Forwarded-For(issue #663 extensibility wave part 1). Client-side use (crate::respond/crate::Authenticator) needs neither field;Self::newdefaults both to empty/None. - Verifier
- Challenges + verifies incoming requests against one configured
Credentials(RFC 7235 origin-side auth) — see the module docs.
Enums§
- Auth
Result - The outcome of
Verifier::verify. - Credentials
- Credentials for one of the supported auth schemes.
- Error
- Errors produced by challenge parsing / response computation.
Constants§
- DIGEST_
NC_ TRACK_ CAP - Default number of
(nonce, cnonce)pairs a DigestVerifiertracks (seeVerifier::with_digest_nc_capacity). Each tracked pair costs about 128 bytes (a 32-byte key hash stored twice, 28 bytes of counters, and hash-map/B-tree overhead), so the default is roughly 8 MiB at most. - DIGEST_
NONCE_ LIFETIME - How long an issued Digest nonce is accepted, measured from issue (RFC 7616
§5.4 leaves the lifetime to the server). An older nonce is refused, and
Verifier::challenge_foranswers it withstale=trueso the client retries silently with a fresh nonce. - NC_
WINDOW - Width of the per-pair
ncanti-replay window: how far below the highest acceptednca not-yet-seenncis still accepted (RFC 4303 §3.4.3).
Functions§
- respond
- One-shot challenge->response: computes the
Authorizationvalue for a single request without keeping anAuthenticatoraround.
Type Aliases§
- Result
- Result alias for the crate’s fallible operations.