Authentication Extensions

Copy Markdown View Source

Payment authorization and application authentication are separate checks. The authentication extensions announce a server's expectations; they do not grant access or automatically enforce those expectations.

Authentication hints

auth-hints associates credential methods with zero-based indexes in the accepts array of a payment-required response:

alias X402.Extensions.AuthHints

method =
  AuthHints.oauth2(
    token_type: "Bearer",
    authorization_server: "https://auth.example.com",
    token_endpoint: "https://auth.example.com/token"
  )

auth_requirements = [[accept_indexes: [0], methods: [method]]]
extension = AuthHints.extension(auth_requirements)

Add {X402.Extensions.AuthHints.Adapter, auth_requirements: auth_requirements} to the gate's :extensions option to advertise these hints. The adapter omits indexes outside the current requirements. AuthHints.sign_in_with_x/0 builds a SIWX method hint instead.

Your application acquires and validates OAuth tokens or DPoP proofs. A SIWX hint does not configure the gate's :siwx verifier or storage. Validate credentials before serving the protected resource, regardless of whether a client acknowledges the hints. Treat advertised URLs as untrusted metadata, not permission to fetch arbitrary hosts or send credentials to them.

HTTP message signatures

X402.Extensions.HTTPMessageSignatures.Adapter advertises registration and signature preferences through the same gate option:

signature_extension =
  {X402.Extensions.HTTPMessageSignatures.Adapter,
   registration_url: "https://api.example.com/signature-agents",
   signature_schemes: ["ed25519"],
   tags: ["web-bot-auth"]}

Use X402.HTTPSignature to sign messages and explicitly require the fields your application depends on. This example generates a temporary demonstration key, signs a request, and verifies it using only its public key:

alias X402.HTTPSignature
alias X402.HTTPSignature.Key

{:ok, signing_key} = Key.generate("ed25519")
{:ok, public_key} = signing_key |> Key.to_jwk() |> Key.from_jwk()

message = %{
  method: "GET",
  url: "https://api.example.com/resource?view=summary",
  headers: []
}

covered = ["@method", "@target-uri"]

{:ok, signature_headers} =
  HTTPSignature.sign(message, signing_key,
    components: covered,
    tag: "web-bot-auth",
    ttl: 60,
    nonce: true
  )

signed_message = %{message | headers: message.headers ++ signature_headers}

{:ok, verified} =
  HTTPSignature.verify(signed_message,
    keys: [public_key],
    algorithms: ["ed25519"],
    tag: "web-bot-auth",
    required_components: covered,
    required_params: ["created", "expires", "nonce"],
    max_age: 60
  )

In production, load signing keys from your application's key store. Supply trusted verification keys or a trusted resolver; a keyid is only a lookup hint, not proof that a key belongs to an authorized caller. Reconstruct the original target URI carefully behind proxies. Include the payment header and other security-relevant fields in both signing and required coverage.

Time checks limit signature age but do not prevent replay. Atomically record and reject reused verified.params["nonce"] values under the trusted signer identity until expiry. For body integrity, compute and verify a content digest yourself, and require its header to be covered.

Public key directory

X402.Plug.HTTPSignatureDirectory serves /.well-known/http-message-signatures-directory with public JWKs, response signatures, and a cache lifetime. Configure keys: [signing_key], or a zero-arity function returning current signing keys for rotation. Private material signs the response but is not published in the directory.

Directory publication does not establish caller trust. Verification never fetches directories automatically. Your application controls registration, origin validation, caching, and key revocation.

Supported profile

Supported algorithms are Ed25519, ECDSA P-256/SHA-256, and RSA-PSS/SHA-512. Requests and responses support ordinary fields, derived components, signature parameters, and request-bound response components through ;req.

This is not all of RFC 9421: HMAC, RSA-v1.5, P-384, the sf, key, bs, and tr component parameters, and Accept-Signature negotiation are not implemented. Neither the adapter nor the directory Plug automatically verifies incoming requests. See X402.HTTPSignature for the full supported profile.