Wado

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.

Absent

Present