Expand description
Authentication plugins for the Churust web framework.
This crate provides ready-made authentication schemes — Bearer tokens,
HTTP Basic credentials, and Jwt bearer tokens — plus the
Principal<P> extractor used to require and read the authenticated user
inside a handler.
§Authentication vs. authorization
An auth plugin only authenticates: it inspects the incoming Authorization
header, verifies the credentials, and — on success — inserts a principal
value (your own user/claims type) into the call’s extensions. It never
rejects a request on its own. If the credentials are missing or invalid, the
plugin simply leaves no principal behind.
Authorization — actually requiring a logged-in user — is type-driven and
explicit: a handler asks for a Principal<P> argument. When a principal of
type P is present the handler runs; when it is absent the extractor returns
401 Unauthorized with a WWW-Authenticate: Bearer challenge before the
handler body is ever entered. Routes that do not ask for a Principal stay
public.
All schemes are constructed through the Auth namespace and installed with
AppBuilder::install. The principal type
P you choose is what later handlers extract; only one principal type can be
resolved per call (the most recently inserted value of a given type wins).
§Comparing credentials
Bearer and Basic do not check the credential themselves — they decode
the header and hand you the value, and your closure decides. That makes the
comparison yours to get right, and == on a String is the wrong tool: it
returns as soon as it meets a differing byte, so how long it took reveals how
much of a guess was correct. Enough requests recovers the secret a byte at a
time.
Use churust_core::secure_compare, which compares in constant time:
use churust_core::secure_compare;
secure_compare(&token, "the-expected-token").then_some(User)Every example below does this, so copying one does not copy a timing leak.
A username is not a secret — it identifies rather than authenticates, and is
often visible anyway — so comparing it with == is fine; the password or
token beside it is what needs the constant-time path.
Jwt needs none of this: it verifies an HMAC signature through
jsonwebtoken, which already compares in constant time.
§Example
Protect a route with bearer-token auth. The /me handler only runs when a
valid token resolved to a User principal; otherwise the Principal
extractor short-circuits with 401.
use churust_core::{secure_compare, Churust, TestClient};
use churust_auth::{Auth, Principal};
#[derive(Clone)]
struct User {
name: String,
}
let app = Churust::server()
.install(Auth::bearer(|token: String| async move {
// `secure_compare`, not `==`: see "Comparing credentials" below.
secure_compare(&token, "s3cret").then(|| User { name: "ana".into() })
}))
.routing(|r| {
r.get("/me", |Principal(u): Principal<User>| async move {
format!("hello {}", u.name)
});
})
.build();
// A valid token reaches the handler.
let ok = TestClient::new(app.clone())
.get("/me")
.header("authorization", "Bearer s3cret")
.send()
.await;
assert_eq!(ok.status().as_u16(), 200);
assert_eq!(ok.text(), "hello ana");
// No credentials => 401 with a challenge, handler never runs.
let denied = TestClient::new(app).get("/me").send().await;
assert_eq!(denied.status().as_u16(), 401);
assert_eq!(denied.header("www-authenticate"), Some("Bearer"));Structs§
- Auth
- Constructor namespace for the authentication scheme plugins.
- Basic
- HTTP Basic authentication plugin produced by
Auth::basic. - Bearer
- Bearer-token authentication plugin produced by
Auth::bearer. - Jwt
- JWT bearer-token authentication plugin produced by
Auth::jwt_hs256. - Principal
- Handler argument that requires and yields the authenticated principal of
type
P.