# `AshOnetime.Token`
[🔗](https://github.com/baselabs/ash_onetime/blob/v1.5.0/lib/ash_onetime/token.ex#L1)

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.

## 1.x compatibility (window-bounded)

Within 1.x, a reader verifies tokens minted by any earlier 1.x writer while those
tokens remain inside their acceptance window (`max_age` + the `skew:` option) — the
obligation expires with each token's own window, nothing is frozen forever. New
envelope forms are additive; a breaking format change waits out the longest
configured window (or verifies both forms through the transition). The rule is
recorded as ADR-0008 in the repository's `docs/adr/` directory.

# `algorithm`

```elixir
@type algorithm() :: :hmac_sha256 | :ed25519
```

# `result`

```elixir
@type result(value) :: {:ok, value} | {:error, AshOnetime.Error.t()}
```

# `t`

```elixir
@type t() :: %AshOnetime.Token{
  algorithm: algorithm(),
  expires_at: DateTime.t() | nil,
  issued_at: DateTime.t(),
  key: binary(),
  key_id: binary(),
  namespace: binary()
}
```

# `mint`

```elixir
@spec mint(binary(), keyword()) :: result(t())
```

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.

# `sign`

```elixir
@spec sign(t(), module(), term()) :: result(binary())
```

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.

# `verify`

```elixir
@spec verify(binary(), module(), keyword()) :: result(t())
```

Verifies a wire token against expected algorithm and namespace, returning the bound token.

`options` MUST supply `:algorithm`, `:namespace`, and `:max_age` from outside the token
(`:algorithm` and `:namespace` are replay-binding expectations, not token-supplied facts),
and MAY supply `:skew` (acceptance-window clock skew, default 0) 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{}}`.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
