core:jwt
JSON Web Tokens (RFC 7519) in the JWS Compact Serialization (RFC 7515),
with HMAC-SHA256 (HS256, RFC 7518 §3.2) built in.
The key decides the algorithm, never the token. [verify] takes a key that
implements [JwsVerifier] and checks the signature with that algorithm,
so a header naming another one cannot redirect verification — the
algorithm-confusion class. alg is still read, and a token whose header
disagrees with the key is rejected.
An RSA or ECDSA key type defined elsewhere implements the same traits and works with the same entry points, with no change here to admit it.
Claims stay bytes: [verify] returns the payload for the caller to
deserialize with core:json, into its own struct or into
[RegisteredClaims]. [RegisteredClaims::validate_time] checks exp and
nbf against a time it is passed, so verifying needs no clock effect.
Synopsis
let key = Hs256Key::new(&"a 32-byte-or-longer shared secret");
let token = sign(&`{"sub":"joe","exp":1300819380}`, &key);
// Verification hands back the payload bytes; claims are yours to read.
let Ok(payload) = verify(&token, &key) else {
panic("the token this key just signed must verify");
};
let Ok(claims) = from_bytes::<RegisteredClaims, ByteList>(payload) else {
panic("the payload is JSON");
};
assert claims.sub == Option::Some("joe");
assert claims.validate_time(1300819379) matches { Ok(_) };
assert claims.validate_time(1300819380) matches { Err(JwtError::Expired) };
Functions
pub fn sign<K: JwsSigner, P: AsByteSlice>(payload: &P, key: &K) -> String
Sign payload into a compact JWS: base64url(header).base64url(payload).base64url(signature).
pub fn verify<K: JwsVerifier, S: AsStrSlice>(token: S, key: &K) -> Result<ByteList, JwtError>
Verify token with key and return its payload bytes.
Checks, in order: three canonical base64url segments, the key's signature
over header.payload, the header's alg against the key's, and that no
extension is marked critical. Claims are not read: checking exp is
[RegisteredClaims::validate_time]'s job, and the caller's to invoke.
Traits
pub trait JwsAlgorithm with ()
The alg a key speaks for. Both halves inherit it, so a key that signs
and verifies answers once.
fn alg(&self) -> String
The alg header value, e.g. "HS256".
pub trait JwsVerifier: JwsAlgorithm with ()
The verifying half of a JWS algorithm. Implement it on a key type to teach
[verify] an algorithm this module does not carry.
fn verify(&self, signing_input: &ByteList, signature: &ByteList) -> bool
Whether signature is this key's over signing_input (the token's
header.payload ASCII bytes). An implementation compares with
eq_constant_time, never ==.
pub trait JwsSigner: JwsAlgorithm with ()
The signing half of a JWS algorithm.
fn sign(&self, signing_input: &ByteList) -> ByteList
Structs
pub struct RegisteredClaims
The registered claims (RFC 7519 §4.1) this module knows how to check.
aud is absent: it is a string or an array of strings on the wire, so it
belongs to a caller's own claims struct until this module carries both.
iss: Option<String>
sub: Option<String>
exp: Option<i64>
nbf: Option<i64>
iat: Option<i64>
pub fn validate_time(&self, now: i64, leeway: i64 = 0) -> Result<(), JwtError>
Check exp and nbf against now (seconds since the Unix epoch),
tolerating leeway seconds of clock skew on either side. An absent
claim is not checked. leeway widens the window, so it is never
negative.
The time is an argument rather than a clock read, so verifying a token
gains no SystemClock effect. Pass Instant::now().seconds where you
do have one.
pub struct Hs256Key
A shared secret for HS256, signing and verifying with HMAC-SHA256.
Fields are private.
pub fn new<S: AsByteSlice>(secret: &S) -> Hs256Key
RFC 7518 §3.2 requires a key at least as long as the hash output. A shorter secret is brute-forcible, so it is refused rather than accepted.
impl JwsAlgorithm for Hs256Key
fn alg(&self) -> String
impl JwsSigner for Hs256Key
fn sign(&self, signing_input: &ByteList) -> ByteList
impl JwsVerifier for Hs256Key
fn verify(&self, signing_input: &ByteList, signature: &ByteList) -> bool
Enums
pub enum JwtError
Why a token was rejected.
Malformed
Not three .-separated segments, or a segment that is not canonical
base64url, or a header that is not JSON.
AlgMismatch
The header's alg is not the one the key verifies.
CriticalHeader
The header carries crit, which names extensions a recipient must
implement. RFC 7515 §4.1.11 requires rejecting the ones it does not,
and this module implements none.
InvalidSignature
The signature is not this key's over this token.
Expired
exp has passed.
NotYetValid
nbf has not arrived.
pub enum Crit
Whether the header carries crit, which is all [verify] asks: the member
is refused for being present, so its contents are never read. An Option
field would read an explicit null as the absent member it is not.
