A Phoenix/Ecto OAuth 2.0 / OIDC authorization-server and
resource-server layer built on top of Attesto.
Attesto is transport-agnostic: it implements the pure, effect-free
protocol primitives - JWT mint/verify with per-key algorithm metadata
(RFC 7519), DPoP sender-constraint proofs (RFC 9449), mutual-TLS binding
(RFC 8705), PKCE (RFC 7636), the JWK Set (RFC 7517), authorization-server
metadata (RFC 8414), private_key_jwt client assertions (RFC 7523),
signed request objects (RFC 9101), and the scope grant-form algebra. It
deliberately carries no HTTP, no persistence, and no identity model.
attesto_phoenix adds exactly the two things a running server needs and
the core leaves out: a transport (HTTP endpoints and protected-resource
plugs) and persistence (Ecto-backed implementations of the core store
behaviours). Everything that is inherently application policy stays the
host's, supplied through a small set of neutral configuration callbacks.
The split
The library keeps the same boundary Attesto draws - protocol versus
policy - and adds a third concern, transport:
Protocol (core).
Attesto.Token,Attesto.DPoP,Attesto.MTLS,Attesto.PKCE,Attesto.Scope,Attesto.JWKS,Attesto.ClientAssertion,Attesto.RequestObject, andAttesto.Discovery. Pure functions over bytes and claims. This layer is reused verbatim; this package adds no crypto and forwards every protocol decision to it.Transport (here). Controllers behind a router macro that mount authorization, token, pushed-authorization-request (RFC 9126), revocation (RFC 7009), discovery (RFC 8414), JWK Set (RFC 7517), UserInfo, and optional dynamic-registration (RFC 7591) endpoints, plus protected-resource plugs that verify a Bearer/DPoP access token and enforce its sender-constraint binding. The controllers and plugs use the core OAuth-error /
WWW-Authenticatehelpers so every failure is an RFC 6749 §5.2 / RFC 6750 §3 response, never a silent reject.Persistence (here). Ecto schemas that implement the core store behaviours for authorization codes and refresh tokens, and - for clustered correctness - DPoP nonces and proof
jtireplay records. Migration scaffolding is amixgenerator that writes the migration into the host application; this package owns no migration of its own.Policy (host application). The client registry and its revocation rule, client-secret hashing, the subject/principal model, the scope catalog, signing keys, and the audit log. These are injected as the callbacks documented on
AttestoPhoenix.Config, so the library never hardcodes one application's identity model.
Configuration
All behaviour is centralized in AttestoPhoenix.Config. It is the single
source of truth read by every controller and plug: it validates the
required keys at build time (raising ArgumentError so misconfiguration
fails closed at boot), applies neutral defaults, and derives the
Attesto.Config the protocol layer runs against via
AttestoPhoenix.Config.to_attesto_config/2.
Anything that is application policy is a callback rather than a baked-in assumption, named in OAuth terms:
- client lookup ->
:load_client - client-secret verification ->
:verify_client_secret - client public keys ->
:client_jwks - subject/principal resolution ->
:load_principal - scope catalog / narrowing ->
:scopes_supportedand/or:authorize_scope - audit / telemetry ->
:on_event(optional, no-op by default) - dynamic client persistence ->
:register_client(only when registration is enabled) - mTLS certificate extraction ->
:cert_der(only when mTLS is enabled) - HTTPS / proxy trust ->
:require_httpsand:trusted_proxies
See AttestoPhoenix.Config for the full key reference and the default
for each value.
Mounting the routes
AttestoPhoenix.Router provides the attesto_routes/1 macro, which
mounts the authorization-server endpoints under a scope the host chooses.
Discovery and the JWK Set are public; the token and revocation endpoints
authenticate the client via the :load_client / :verify_client_secret
callbacks.
Entry points
AttestoPhoenix.Config- the validated configuration every controller and plug reads, and the derivation of the protocolAttesto.Config.AttestoPhoenix.Router- theattesto_routes/1macro that mounts the HTTP surface.
See the README for the supply/own breakdown, the router and plug usage,
and the migration generator.