Mints, signs, and verifies bounded self-identifying canonical tokens.
The signed body binds the algorithm, key identifier, namespace, keyed-effect key, issuance instant, and optional expiry instant. Verification requires an expected algorithm and namespace supplied outside the token.
Summary
Functions
Mints a token struct from a key and a keyword of binding options.
Signs a minted token into a self-identifying wire string under the resolved key material.
Verifies a wire token against expected algorithm and namespace, returning the bound token.
Types
@type algorithm() :: :hmac_sha256 | :ed25519
@type result(value) :: {:ok, value} | {:error, AshOnetime.Error.t()}
@type t() :: %AshOnetime.Token{ algorithm: algorithm(), expires_at: DateTime.t() | nil, issued_at: DateTime.t(), key: binary(), key_id: binary(), namespace: binary() }
Functions
Mints a token struct from a key and a keyword of binding options.
The options carry the security-relevant fields the signed body will bind: :algorithm
(:hmac_sha256 or :ed25519), :key_id (the resolver-scoped key identifier, ≤128 bytes),
:namespace (the caller-supplied namespace, ≤128 bytes), :issued_at (a DateTime,
defaulting to Clock.now/0), and optional :expires_at (a DateTime strictly after
issued_at). Every field is validated before the struct is built; an invalid option or a
non-binary key returns {:error, %AshOnetime.Error{}} without minting.
Signs a minted token into a self-identifying wire string under the resolved key material.
resolver is a module implementing the key-resolution callback (resolve/4 for :sign),
invoked as resolver.resolve(:sign, token, resolver_context) to obtain the signing
material. The body is canonical-encoded, signed by the token's algorithm (HMAC or
Ed25519), and wrapped in a base64url envelope prefixed ash_onetime.. The signature
binds exactly the canonical body bytes — the algorithm, key identifier, namespace, key,
issuance instant, and expiry.
Verifies a wire token against expected algorithm and namespace, returning the bound token.
options MUST supply :algorithm and :namespace from outside the token (these are
replay-binding expectations, not token-supplied facts), and MAY supply :max_age,
:clock_skew, and :resolver_context. The body is re-derived, the expected algorithm and
namespace are bound against the token's, the window is validated, and the signature is
verified under the resolved key material (resolver.resolve(:verify, token, context)).
Every field of the decoded body is re-validated before the signature check, so a tampered
body cannot reach verification. Returns {:ok, token} or {:error, %AshOnetime.Error{}}.