Hex.pm Hexdocs.pm License: MIT

Build and verify the OAuth and OpenID Connect wire artifacts an Elixir client needs when your app already owns the HTTP flow: private_key_jwt, signed authorization request objects (JAR), ID Token verification, JARM verification, ID-JAG/EMA assertions, PKCE generation, signed introspection and UserInfo verification, and discovery/JWKS fetching.

Use it when you are writing a relying party or OAuth client that needs the cryptographic pieces without adopting a full redirect/session/token framework:

  • Authenticate to token, PAR, or introspection endpoints with private_key_jwt.
  • Send signed authorization requests for JAR/FAPI deployments.
  • Verify ID Tokens, signed authorization responses returned through JARM, signed introspection responses, and signed UserInfo responses.
  • Build ID-JAG/EMA identity assertions for the JWT-bearer grant.
  • Generate S256 PKCE verifier/challenge pairs.
  • Fetch authorization-server metadata and JWKS with issuer validation.

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.

It is not a full OAuth client framework: no flow orchestrator, token store, or session handling. It produces and checks the cryptographic wire-format artifacts a FAPI client needs and leaves HTTP orchestration to the host. 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

Assurance

Build-side artifacts carry cross-language parity tests where practical: they are checked against an independent, non-Elixir reference implementation (e.g. PyJWT), so correctness does not rest only on this library and attesto agreeing with each other. The mirror modules also carry in-family interop tests against the corresponding attesto server-side issuer or verifier. The Python parity tests skip cleanly when the reference 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 0.x release: pre-1.0, API may change between minor versions. Pin to ~> 0.6.