libmacaroon 0.2.0

Macaroons (bearer credentials with contextual caveats) in pure Rust, with first-party and third-party caveats, WASM support, and cross-language interop.
Documentation
# libmacaroon Change Log

## Version 0.2.0 - 2026-04-23 (libmacaroon)

Ergonomics improvements from first-wave dogfooding feedback. Both
changes are breaking at call sites, but both are mechanical rewrites.

- **`Macaroon::create`** now takes `Option<&str>` for the location
  instead of `Option<String>`. A call site that was
  `Some("http://bank".to_string())` or `Some("http://bank".into())`
  becomes `Some("http://bank")`. The macaroon still owns its location
  (the crate does one `.to_string()` internally), but the allocation
  disappears from every call site.
- **`add_first_party_caveat`** and **`add_third_party_caveat`** now
  return `Result<&mut Self>` instead of `Result<()>`, so calls chain:

  ```rust
  let mut m = Macaroon::create(Some("loc"), &key, "id")?;
  m.add_first_party_caveat("account = 12345")?
      .add_first_party_caveat("user = alice")?;
  ```

  Same `?` count, fewer lines, reads as a pipeline. The cap checks that
  made the calls fallible in 0.1.0 are unchanged — this is only a
  return-type tweak.

## Version 0.1.0 - 2026-04-23 (libmacaroon)

First release under the `libmacaroon` name. This is a fork of the
[`macaroon`](https://crates.io/crates/macaroon) crate
(`macaroon-rs/macaroon`, last released as 0.3.0 in Oct 2022), published
fresh on crates.io under the new name — so the version resets to 0.1.0
even though the internals continue from `macaroon 0.3.0` plus the
pre-release hardening below.

The API is source-compatible with `macaroon 0.3.0` in spirit, but many
signatures changed for the hardening work (see "API changes from
`macaroon 0.3.0`" below).

### API changes from `macaroon 0.3.0`

Several breaking changes from the original `macaroon 0.3.0` API.

**Security / interop fixes:**
- V2JSON decoder now accepts URL-safe base64 in `i64`, `v64`, `l64`, and
  `s64` fields, and emits URL-safe base64 without padding. Previously only
  standard-alphabet padded input was accepted, breaking interop with
  libmacaroons and pymacaroons which emit URL-safe unpadded by spec.
- `Macaroon::create`, `add_first_party_caveat`, `add_third_party_caveat` now
  enforce `MAX_FIELD_SIZE_BYTES` (65535) on every field at construction, so
  producer and consumer agree: a macaroon this crate serializes is always
  one this crate can parse back. V1's 4-hex-digit packet header could
  previously silently truncate oversized fields at serialize time.
- Migrated from unmaintained `xsalsa20poly1305 0.9` to `crypto_secretbox
  0.1` (RUSTSEC-2023-0037). Same algorithm and wire format.
- Dropped `rand 0.8` dependency (RUSTSEC-2026-0097) in favor of a direct
  `getrandom` call on native and WASM alike.
- `getrandom` failures now propagate as `MacaroonError::RngError` instead
  of panicking — important on WASM where a sandboxed iframe without
  `crypto.getRandomValues` would otherwise abort the module.
- V2JSON decoder now rejects per-caveat tokens that set both plain and
  `*64` variants of the same field (`i`/`i64`, `l`/`l64`, `v`/`v64`),
  mirroring the top-level exclusion check.

**API ergonomics:**
- `Verifier` is now `Send + Sync``satisfy_general` closures now require
  `Fn(&[u8]) -> bool + Send + Sync + 'static`.
- `MacaroonKey::generate_random()` returns `Result<MacaroonKey>`.
- `MacaroonKey` no longer implements `Deref<Target = [u8]>` — use
  `.as_ref()` to go through the explicit byte accessor.
- Added public constants `MAX_CAVEATS` and `MAX_FIELD_SIZE_BYTES`.
- Added `MacaroonError::FieldTooLarge { field, size }` and
  `MacaroonError::RngError`.

**Packaging / CI:**
- MSRV is now 1.71 (was claimed 1.56 but effective MSRV was higher
  because of transitive deps). 1.71 matches the real floor from
  `env_logger 0.11` and `quote 1.0.45`.
- `env_logger` and `time` dev-dependencies removed. `env_logger` was
  declared but never used; `time` backed a single test helper that
  now compares ISO-8601 timestamps lexicographically against a fixed
  reference (equivalent semantics with no dep).
- Cargo.lock is now committed so `cargo audit` runs reproducibly in CI
  and everyone gets the same dep set in local dev. Published crate is
  unaffected (Cargo excludes the lockfile from packaged libraries).
- CI modernized: `actions/checkout@v4`, `dtolnay/rust-toolchain@stable`,
  `Swatinem/rust-cache@v2`, separate jobs for `fmt`, `clippy -D warnings`,
  MSRV, WASM target, and `rustsec/audit-check`. All build steps now
  use `--locked`.

**Correctness polish:**
- V2JSON decoder rejects tokens whose top-level `v` field isn't `2`.
- V1/V2 binary format sniff in `Macaroon::deserialize_binary` now accepts
  only actual hex digits (`'0'..='9' | 'a'..='f' | 'A'..='F'`) as a V1
  first byte, rather than any uppercase letter.
- `Verifier::verify` rejects duplicate-identifier discharges explicitly
  instead of silently de-duplicating on HashMap insertion.

**API ergonomics (continued):**
- `Caveat::as_first_party` / `Caveat::as_third_party` accessors added so
  callers can skip the `match` when they already know or want to filter
  for a specific caveat kind.
- Example doctests in `lib.rs` and `README.md` rewritten in `?`-style
  instead of `match { Err(e) => panic!(..) }`.

**Testing:**
- Property-based tests under `tests/proptest_roundtrip.rs` cover
  round-trip of random macaroons through all three formats, correct-key
  verification, wrong-key rejection, and "never panic on arbitrary input"
  for the deserialization entry points.
- `cargo-fuzz` scaffolding under `fuzz/` with targets for
  `Macaroon::deserialize` and `Macaroon::deserialize_binary`. Out-of-band
  (nightly only); see `fuzz/README.md`.

Note: would increment to v0.4.0 if there are major changes.

## Version 0.3.0 - Oct 13, 2022 (macaroon)

This is a backwards-incompatible release with respect to serialized macaroon signatures, because the HMAC has changed. This version should have signatures interoperable with `libmacaroon-rs v0.1.x`, and with most popular Macaroon implementations in other languages.

- Revert HMAC back to SHA-256 (breaks signatures)
- Dependency updates
- Update Rust edition to 2021, and minimum required Rust version to v1.56
- Public API "flattened" (internal modules no longer exposed), and some internal cryptographic functions removed from API
- Fix several trivial panics deserializing tokens
- Flexible decoding of base64-encoded macaroons (either URL-safe base64 or "standard" base64)
- Refactor MacaroonError, MacaroonKey, and Macaroon::deserialize()

## Version 0.2.0 - Sep 24, 2021 (macaroon)

First release of [`macaroon`](https://crates.io/crates/macaroon) crate from the new [`macaroon-rs`](https://github.com/macaroon-rs) github organization.

Macaroon signatures created with this version are not compatible with prior releases, because of the HMAC change.

- Several refactors to code and API
- Dependencies updated
- Macaroon HMAC changed from SHA-256 to SHA-512-256

## Version 0.1.1 - Feb 22, 2017 (libmacaroon-rs)

- Coverage using [coveralls.io]https://coveralls.io/github/jacklund/libmacaroon-rs?branch=trunk
- Expanded coverage of unit tests
- Bug fix for version 1 deserialization

## Version 0.1.0 - Feb 20, 2017 (libmacaroon-rs)

Initial commit. Functionality:

- Macaroons with first- and third-party caveats
- Serialization/Deserialization using [libmacaroons]https://github.com/rescrv/libmacaroons version 1, 2, and 2J (JSON) formats
- Verification of first-party caveats using either exact string comparison or submitted verification function
- Verification of third-party caveats using discharge macaroons