Expand description
Verify auth-service tokens in a consuming app (issue #11).
Six ventures each writing their own JWT validation is six chances to forget the audience check. This crate does it once.
let auth = AuthClient::new(http, clock, "https://auth.acme.example", "client-kontinuum");
async fn handler(Authenticated(claims): Authenticated) -> String { claims.sub }What it checks, in this order: the header names ES256 and a
kid we hold; the signature verifies against that key; iss equals
the configured issuer; aud equals this client’s id; exp has not
passed and nbf/iat are not implausibly far in the future. Every
one of those is a way a token from somewhere else gets accepted if it
is skipped, so none of them is optional and none is configurable.
Algorithm confusion is refused structurally. The header’s alg
must be exactly ES256. none and the HMAC families are rejected
before any key is looked up, so a token asking to be verified with
the public key as an HMAC secret never reaches verification.
wasm-safe. Time comes from the harness Clock port and HTTP from
its HttpClient port, so this compiles for wasm32-unknown-unknown
where std::time and chrono::Utc::now both panic.
§cratefield-auth-client
Verify auth-service tokens in a consuming app: JWKS fetch and cache, ES256 verification with the audience check that hand-rolled verifiers forget, and an axum extractor. Issue #11.
§Freshness and roles (issue #912)
Verification proves who signed a token and that it was minted for this client. It does not prove that the person just proved who they are. A relying party that is about to approve something (a payment, an offer, a change of ownership) must check that itself:
if !claims.stepped_up_within(120, now) {
// ask the person to sign in again, with prompt=login and max_age=0
}Payment, signing and other irreversible gates must use
stepped_up_within, not authenticated_within. authenticated_within
only checks the age of the last sign-in, whatever method it used. A
federated (sso, federated) or password sign-in passes it, and the
password was never proven fresh. stepped_up_within also requires a passkey
or an email link in amr. The token’s acr is urn:cratefield:stepup when
the session spent a step-up transaction; that is a fact about the session,
and a relying party may check it too, but the age and method check above is
the one that decides.
auth_time is epoch seconds of the person’s last real sign-in ceremony. A
refresh never moves it, so a long-lived session does not look fresh. roles
lists the account’s roles in the issuing instance (owner, admin,
member) when the client was registered to receive them, and is empty
otherwise. Only access tokens verify here: a token whose typ is not
at+jwt (an ID token, for one) is refused.
MIT. Part of the Cratefield harness.
Structs§
- Auth
Client - Verifies tokens issued by one auth instance, for one registered client.
- Auth
State - Router state carrying the verifier.
- Authenticated
- A verified caller.
- Claims
- The claims a verified access token carries.
- Jwks
Cache - The cached key set and the times that govern refetching.
Enums§
- Verify
Error - Why a token was refused.
Constants§
- ALGORITHM
- The only signature algorithm this client accepts.
- JWKS_
MIN_ REFETCH_ SECS - The shortest gap between two forced refetches triggered by an unknown
kid. Without it, a stream of tokens naming keys that do not exist would let anyone drive unbounded traffic at the auth service. - JWKS_
TTL_ SECS - How long a fetched key set is trusted before it is refetched.
- LEEWAY_
SECS - Clock skew tolerated on
exp,nbfandiat. - UNAUTHENTICATED
- The one refusal this crate ever returns.
Functions§
- from_
config - The
Authport a deployment gets from its environment (issue #153). - token_
fingerprint - The SHA-256 of a token, for logging a token without logging it.