AttestoClient

Hex.pmHexdocs.pmLicense: MITOpenID Certified

OpenID Certified

AttestoClient is OpenID Certified as a Relying Party library to the Basic, Config, and Dynamic OP profiles, run against the OpenID Foundation's conformance suite.

Run a secure OpenID Connect Authorization Code + PKCE exchange, refresh and revoke tokens, build RP-Initiated Logout requests, and build or verify the OAuth and OpenID Connect wire artifacts an Elixir client needs: private_key_jwt, signed authorization request objects (JAR), strict ID Token verification, JARM, ID-JAG/EMA assertions, signed introspection and UserInfo, and discovery/JWKS.

Use it when you are writing a relying party or OAuth client that needs secure protocol mechanics without delegating application policy:

AttestoClient is the client-side counterpart to attesto. attesto verifies client artifacts and issues server artifacts with the authorization server's keystore; AttestoClient builds artifacts signed with the client's own key and verifies the server artifacts a client receives.

On the server side of the same family, attesto_phoenix is the batteries-included Phoenix/Ecto authorization server built on attesto, and attesto_mcp protects a Model Context Protocol server as an OAuth resource server.

It does not make authorization decisions or own application sessions. Its included ETS store retains only short-lived protocol correlation data, and its refresh coordinator retains no token set after a flight completes. The host chooses its durable/distributed store, atomically persists rotation results, maps verified identities to authorization, and applies session-retention policy. DPoP proof generation for outgoing requests is req_dpop's job.

What it provides

Example

key = JOSE.JWK.generate_key({:ec, "P-256"})
{:ok, assertion} =
AttestoClient.ClientAssertion.build(key,
client_id: "my-client",
audience: "https://op.example.com"
)
# Submit it at the token / PAR / introspection endpoint:
# client_assertion_type = AttestoClient.ClientAssertion.assertion_type()
# client_assertion = assertion

For a full authorization flow, store setup, callback handling, refresh, revocation, and logout, see the Authorization Code guide.

For inbound JWT access-token verification backed by a remote issuer, including key rotation, stale-key behavior, scope checks, and Plug wiring, see the Remote issuer resource-server guide.

Assurance

Build-side artifacts carry cross-language parity tests where practical: they are checked against an independent, non-Elixir reference implementation (e.g. PyJWT or Node's native crypto implementation), so correctness does not rest only on this library and attesto agreeing with each other. This includes exact and legacy Ed25519/Ed448 signatures plus the curve-dependent OIDC hash construction. The mirror modules also carry in-family interop tests against the corresponding attesto server-side issuer or verifier. External-reference tests skip cleanly when their toolchain is absent, so they never block a plain mix test.

For release confidence, run them explicitly against a Python with the reference libraries installed (the system Python is usually PEP-668 externally managed, so use a venv):

python3 -m venv .venv
.venv/bin/pip install "pyjwt[crypto]"
ATTESTO_CLIENT_PYTHON=.venv/bin/python ATTESTO_PATH=1 mix test

When ATTESTO_CLIENT_PYTHON is unset the harness falls back to python3 on the PATH.

Status

A stable 2.x release: the public API follows semantic versioning — minor and patch releases are backward-compatible, and breaking changes wait for a new major version. Pin to ~> 2.2.

Requirements

AttestoClient requires Elixir 1.18 or later, Attesto 1.3 or later, and JOSE 1.11.12 or later within the JOSE 1.x line. The JOSE range keeps the patched security floor while allowing native OTP SHA-3 and Ed448 improvements in later compatible releases. Both this package and attesto use Elixir's built-in JSON module, so lowering only this package's declared floor would not create a working older-Elixir installation.

Ed448 verification and OIDC hash claims additionally require JOSE to have a working Curve448 and SHAKE256 backend. That may come from supported native OTP crypto in a later JOSE 1.x release, an installed backend, or JOSE's cryptographic fallback. Operations fail closed when the required capability is unavailable.