Configuration for the attesto_phoenix authorization-server layer.
This is the single source of truth consumed by every controller and plug in
the library. It reads the host's configuration (from a host-chosen
otp_app/config key), validates the required keys, applies neutral defaults,
and derives the Attesto.Config the protocol layer needs.
Build one with new/1 (from a keyword list or map) or from_otp_app/2 (to
read Application.get_env/2). Validation raises ArgumentError on a missing
required key so misconfiguration fails fast at boot.
Keys
Required
:issuer- issuer URL (string) used as the JWTiss, the discovery issuer, and the base for endpoint URLs.:keystore- module implementingAttesto.Keystoreproviding the signing key and the verification keys published via JWKS. Use a static keystore or a host KMS/HSM/Vault-backed implementation; per-keyalgmetadata is supported by the core keystore behaviour.:repo-Ecto.Repomodule used by the Ecto-backed code, refresh, nonce, and replay stores.:load_client-(client_id -> {:ok, client} | {:error, :not_found} | {:error, :revoked}). Resolves an OAuth client. The host owns the client registry and revocation policy.:verify_client_secret-(client, presented_secret -> boolean). Constant-time client-secret verification (e.g. viaAttesto.SecureCompare). The host owns secret hashing.:load_principal-(subject_id -> {:ok, principal} | {:error, :not_found}). Resolves the subject/principal during protected-resource authentication.
Optional callbacks
:authorize_scope-(client, requested_scope -> {:ok, granted_scope} | {:error, :invalid_scope}). Validates/narrows requested scope usingAttesto.Scopealgebra. Defaults to "subset of:scopes_supported".:on_event-(%AttestoPhoenix.Event{} -> any). Audit/telemetry hook. No-op by default; the library never stores events itself.:send_error-(conn, status, body_map -> conn). Optional transport hook used byAttestoPhoenix.OAuthErrorto serialize OAuth/OIDC errors into the host's API envelope while preserving the RFC status, challenge, and cache-control semantics.:no_store-(conn -> conn). Optional transport hook used byAttestoPhoenix.OAuthErrorto apply no-store headers.:www_authenticate-(conn, challenge_string -> conn). Optional transport hook used byAttestoPhoenix.OAuthErrorto write theWWW-Authenticatechallenge header.:basic_realm- realm string for token-endpoint Basic auth challenges. Default"OAuth".:htu-(conn -> canonical_url_string). Overrides how the DPoPhtuis computed behind proxies. Defaults to derivation from:trusted_proxies.:cert_der-(conn -> der_binary | nil). Extracts the client mTLS certificate DER. Required only when:mtls_enabled.:register_client-(metadata -> {:ok, client} | {:error, reason}). Persists a dynamically registered client. Required only when:registration_enabled.:unregister_client-(client -> :ok | {:ok, client} | {:error, reason}). Deletes a dynamically registered client for registration management cleanup (RFC 7592). Optional; when unset, DELETE requests to the registration management endpoint fail closed.:client_registration_access_token_hash-(client -> String.t() | nil). Extracts the stored hash of the registration access token issued with a dynamic client (RFC 7592). Optional; when unset, DELETE requests fail closed.:principal_kinds- non-empty list ofAttesto.PrincipalKindvalues or a zero-arity callback returning that list, passed into the core token configuration.:build_principal-(client, subject, scope -> map). Builds the principal map passed toAttesto.Token.mint/3.:build_userinfo_claims-(subject, granted_scopes, requested_claims -> claims_map). Produces the claim values the UserInfo endpoint (OpenID Connect Core §5.3) returns for the authenticated subject. The host owns the claim source (its user store); the library owns only the scope-to-claim shaping (OpenID Connect Core §5.4) and the guarantee thatsubis present (OpenID Connect Core §5.3.2).granted_scopesis the list of scopes on the access token;requested_claimsis the per-claim request map from the OpenID Connectclaimsparameter (%{}when none). Required only when the UserInfo endpoint is mounted.:client_id-(client -> String.t()). Extracts the OAuth client identifier from the host's client struct.:client_jwks-(client -> jwks). Returns the client's trusted public JWK Set forprivate_key_jwtclient authentication. Required only for clients that authenticate withprivate_key_jwt.:client_redirect_uris-(client -> [String.t()]). Returns the client's registered redirect URIs (RFC 6749 §3.1.2.2). The authorization endpoint exact-matches the requestredirect_uriagainst this set (RFC 6749 §3.1.2.3); a client exposing none rejects every authorization request (fail closed).:authenticate_resource_owner-(conn, request, auth_opts -> {:authenticated, subject} | {:halt, conn} | {:none} | {:error, :login_required | :consent_required | :interaction_required}). Establishes the resource owner for an authorization request (RFC 6749 §3.1, OIDC Core §3.1.2.3). Returns{:authenticated, subject}once a resource owner is known (a map carrying at least:subject, the OIDCsub, and optionally:auth_time,:acr,:amr),{:halt, conn}to take over the connection (e.g. redirect to a host login page that re-enters the authorization endpoint),{:none}when no subject can be established without UI, or an{:error, _}classifying why interaction is required (OIDC Core §3.1.2.6).auth_optsis a map carrying the OIDC Core §3.1.2.1prompt/max_agedirectives the host must honour::prompt,:force_reauth(prompt=login),:interactive(falseforprompt=none, forbidding UI), and:max_age. The host owns all login UI; the library only invokes this hook. Required only when the authorization endpoint is mounted.:consent-(conn, request, subject -> {:consented, subject} | {:halt, conn} | {:denied, reason}). Obtains the resource owner's consent for an authorization request (RFC 6749 §4.1.1). Returns{:consented, subject}to proceed (the returned subject may carry consent-derived claims),{:halt, conn}to take over the connection (e.g. render a consent screen that re-enters the authorization endpoint), or{:denied, reason}to refuse (reported to the client asaccess_denied, RFC 6749 §4.1.2.1). When unset, consent is implicitly granted for the authenticated subject.:client_public?-(client -> boolean()). Returns whether a client may authenticate without a secret and rely on PKCE.:client_requires_mtls?-(client -> boolean()). Returns whether a client requires mTLS-bound token issuance.:issue_refresh_token?-(client, granted_scope -> boolean()). Returns whether the authorization-code grant should issue an initial refresh token (RFC 6749 §6). When unset, the token controller issues one iff the granted scope containsoffline_access(OIDC Core §11) and a:refresh_storeis configured.:code_store- module implementingAttesto.CodeStore.:refresh_store- module implementingAttesto.RefreshStore.:par_store- module implementingAttestoPhoenix.PARStore.:grant_types_supported- grant types advertised/accepted by dynamic client registration.:token_endpoint_auth_methods_supported- client authentication methods advertised/accepted by dynamic client registration.
Optional values (with defaults)
:audience- default access-token audience (string or list).:scopes_supported- list of supported scope strings (concrete and wildcard) advertised in discovery and used as the default scope catalog. For an OpenID Provider the reservedopenidscope (OpenID Connect Core §3.1.2.1) is added to the OpenID Provider Metadata automatically by the core builder; it need not be listed here.:authorization_endpoint- absolute URL of the host-owned authorization endpoint (RFC 6749 §3.1 / OpenID Connect Discovery §3). The authorization endpoint runs the host's login/consent UI, so the library does not mount it; the host supplies the URL where it serves it. Advertised in the OpenID Provider Metadata; omitted when unset.:userinfo_endpoint- absolute URL of the host-owned UserInfo endpoint (OpenID Connect Core §5.3). The host owns the claim source, so the library does not mount it; the host supplies the URL. Advertised in the OpenID Provider Metadata; omitted when unset.:claims_supported- list of claim names the host's UserInfo endpoint and ID Tokens can return (OpenID Connect Discovery §3). Advertised in the OpenID Provider Metadata; omitted when unset.:claims_parameter_supported- whether the provider accepts the OpenID Connectclaimsrequest parameter (OpenID Connect Discovery §3 / OpenID Connect Core §5.5). Defaultfalse: the authorization endpoint does not consume aclaimsparameter unless the host wires it, so the provider does not claim support for it. Advertised in the OpenID Provider Metadata only when set totrue(the core builder treats absence asfalseper OpenID Connect Discovery §3).:acr_values_supported- list of Authentication Context Class Reference values the provider can satisfy (OpenID Connect Discovery §3 / OpenID Connect Core §2). Advertised only when the host configures a non-empty list; omitted otherwise.:ui_locales_supported- list of BCP47 (RFC 5646) language tags the provider's UI supports (OpenID Connect Discovery §3). Advertised only when the host configures a non-empty list; omitted otherwise.:require_nonce- require the OpenID Connectnonceparameter on OpenID Connect Authentication Requests (OpenID Connect Core §3.1.2.1). Defaultfalse. Whentrue, the authorization endpoint passesrequire_nonce: truetoAttesto.AuthorizationRequest.validate/2for a request whose scope containsopenid, so a missingnonceon an OIDC request is rejected with a redirectableinvalid_requesterror. A non-OpenID OAuth 2.0 request is never affected (RFC 6749 keeps the authorization code at SHOULD, never requiring anonce). The host sets this per its own OpenID Provider policy.:require_https- enforce HTTPS on the endpoints. Defaulttrue.:trusted_proxies- list of trusted proxy CIDRs/IPs controlling whetherX-Forwarded-*headers are honored. Default[](no forwarded trust).:access_token_ttl- access-token lifetime, seconds. Default900.:refresh_token_ttl- refresh-token lifetime, seconds. Default1_209_600.:authorization_code_ttl- authorization-code lifetime, seconds. Default60.:dpop_enabled- enable DPoP sender-constraint support. Defaulttrue.:dpop_nonce_required- require server-issued DPoP nonces. Defaultfalse.:mtls_enabled- enable mTLS (RFC 8705)cnfbinding. Defaultfalse.:registration_enabled- enable/oauth/register. Defaultfalse.:replay_check- DPoPjtireplay check (module or{module, fun}). Defaults to the single-node ETS replay cache.:nonce_store-Attesto.DPoP.NonceStoreimplementation. Defaults to the single-node ETS nonce store.:sweep_interval_ms- interval forAttestoPhoenix.Store.Sweeper. The sweeper is not started if unset.:table_prefix- optional Ecto schema/table prefix for the generated tables.
Summary
Functions
Invokes the host's :build_userinfo_claims callback for the authenticated
subject and returns the raw claims map it produces.
Reads the config for otp_app under key (default AttestoPhoenix) from the
application environment and builds a validated config.
Builds and validates a config from a keyword list or map.
Derives the Attesto.Config consumed by the protocol layer from this config.
Types
@type t() :: %AttestoPhoenix.Config{ access_token_ttl: pos_integer(), acr_values_supported: [String.t()], audience: String.t() | [String.t()] | nil, authenticate_resource_owner: callback() | nil, authorization_code_ttl: pos_integer(), authorization_endpoint: String.t() | nil, authorize_scope: callback() | nil, basic_realm: String.t(), build_principal: callback() | nil, build_userinfo_claims: callback() | nil, cert_der: callback() | nil, claims_parameter_supported: boolean(), claims_supported: [String.t()], client_id: callback() | nil, client_jwks: callback() | nil, client_public?: callback() | nil, client_redirect_uris: callback() | nil, client_registration_access_token_hash: callback() | nil, client_requires_mtls?: callback() | nil, code_store: module() | nil, consent: callback() | nil, dpop_enabled: boolean(), dpop_nonce_required: boolean(), grant_types_supported: [String.t()] | nil, htu: callback() | nil, issue_refresh_token?: callback() | nil, issuer: String.t(), keystore: module(), load_client: callback(), load_principal: callback(), mtls_enabled: boolean(), no_store: callback() | nil, nonce_store: module() | nil, on_event: callback() | nil, par_store: module() | nil, par_ttl: pos_integer(), principal_kinds: [Attesto.PrincipalKind.t()] | callback() | nil, refresh_store: module() | nil, refresh_token_ttl: pos_integer(), register_client: callback() | nil, registration_enabled: boolean(), replay_check: callback() | module() | nil, repo: module(), require_https: boolean(), require_nonce: boolean(), require_pkce: boolean(), scopes_supported: [String.t()], send_error: callback() | nil, sweep_interval_ms: pos_integer() | nil, table_prefix: String.t() | nil, token_endpoint_auth_methods_supported: [String.t()] | nil, trusted_proxies: [String.t()], ui_locales_supported: [String.t()], unregister_client: callback() | nil, userinfo_endpoint: String.t() | nil, verify_client_secret: callback(), www_authenticate: callback() | nil }
Functions
Invokes the host's :build_userinfo_claims callback for the authenticated
subject and returns the raw claims map it produces.
The callback is applied with [subject, granted_scopes, requested_claims]
(see the :build_userinfo_claims key documentation). It is the claim source
for the UserInfo endpoint (OpenID Connect Core §5.3); the host owns the claim
values, the controller owns the scope-to-claim shaping. Raises
ArgumentError when the host has not configured the callback, so a mounted
UserInfo endpoint cannot silently return an empty document.
Reads the config for otp_app under key (default AttestoPhoenix) from the
application environment and builds a validated config.
Builds and validates a config from a keyword list or map.
Raises ArgumentError if a required key is missing or if a dependent key is
absent for an enabled feature (e.g. :register_client when
:registration_enabled, or :cert_der when :mtls_enabled).
@spec to_attesto_config( t(), keyword() ) :: Attesto.Config.t()
Derives the Attesto.Config consumed by the protocol layer from this config.
The protocol layer owns only the claim-level policy (:issuer, :audience,
:keystore, the principal kinds, and the default access-token lifetime). The
refresh/code TTLs and the DPoP/mTLS feature toggles are read directly from
this struct by the controllers and plugs, so they are not duplicated into the
Attesto.Config.
Pass principal_kinds: (a non-empty list of Attesto.PrincipalKind) and any
other Attesto.Config.new/1 option as extra to complete the protocol
config; they are merged over the values derived here.