Phoenix-friendly protected-resource authentication.
This plug is a thin integration layer over Attesto.Plug.Authenticate. The
core plug owns the protocol work: parsing Bearer/DPoP credentials, verifying
the JWT access token, enforcing DPoP and mTLS sender-constraint bindings, and
rendering RFC 6750 / RFC 9449 failures. This wrapper derives the core options
from AttestoPhoenix.Config, resolves the verified subject through the
host's :load_principal callback, and assigns neutral values for downstream
Phoenix code.
Defaults:
:claims_key-:attesto_claims:principal_key-:attesto_principal:context_key-:attesto_context
The context assign is a map with :subject, :client_id, :scope, :claims,
:cnf, and :principal. It is deliberately protocol-shaped; application
policy such as accounts, roles, audit actors, and error envelopes belongs in
the host application.
Configuration resolution is request-aware. When
conn.private[:attesto_phoenix_config] exists, that validated
%AttestoPhoenix.Config{} is authoritative and the plug's :config and
:otp_app options are ignored. When the request-private value is absent, the
plug uses its :config option, then its :otp_app option, and finally the
library's configured :otp_app. A malformed request-private value raises
instead of falling back to global application configuration. Mount
AttestoPhoenix.Plug.PutConfig when a route should use a request-specific
profile.
RFC 9728 challenge discovery is selected through
AttestoPhoenix.Config.resource_metadata_url/3: an explicit per-plug
:resource_metadata value (including nil) takes precedence on every error
path and skips the resolver. A non-nil per-plug value is static Plug
configuration and is validated by init/1, which Phoenix runs at compile
time by default. Otherwise, the existing static
:resource_metadata URL remains the default, while an optional
:resource_metadata_resolver can choose a URL or omit it for each request.
Invalid runtime resolver results are omitted. A resolver exception is not
rescued and aborts the request, because resolvers are trusted host
configuration rather than untrusted request input.