The x402 http-message-signatures extension: identity through RFC 9421.
Implements the
http-message-signatures extension.
A network that authenticates paying agents with HTTP Message
Signatures tells clients, in PaymentRequired.extensions, where to
register their signature agent, which algorithms it accepts and which
signature tag values it validates:
%{
"http-message-signatures" => %{
"info" => %{
"registrationUrl" => "https://network.example.com/signature-agents",
"signatureSchemes" => ["ed25519"],
"tags" => ["web-bot-auth"]
},
"schema" => %{...}
}
}The client hosts its keys at /.well-known/http-message-signatures-directory
(X402.HTTPSignature.directory/1, X402.Plug.HTTPSignatureDirectory),
registers the directory URL at registrationUrl, and signs its
requests with X402.HTTPSignature.sign/3 using one of the advertised
algorithms and tags. Servers may sign their responses the same way to
give PAYMENT-REQUIRED and PAYMENT-RESPONSE integrity.
schema may be omitted from the advertisement to keep the header
small (extension/1 with include_schema: false); decode/1 accepts
both shapes.
Summary
Functions
Decodes the extension from a PaymentRequired map (or its
extensions map).
Builds the server-side advertisement for PaymentRequired.extensions.
Returns the extension key on the wire.
Returns the JSON schema the server advertises for the extension.
Validates the value advertised under the extension key and extracts its info.
Types
Functions
Decodes the extension from a PaymentRequired map (or its
extensions map).
Returns {:ok, nil} when the extension is absent.
Examples
iex> extension = X402.Extensions.HTTPMessageSignatures.extension(
...> registration_url: "https://network.example.com/agents", signature_schemes: ["ed25519"], include_schema: false)
iex> payment_required = %{"accepts" => [], "extensions" => %{"http-message-signatures" => extension}}
iex> X402.Extensions.HTTPMessageSignatures.decode(payment_required)
{:ok, %{registration_url: "https://network.example.com/agents", signature_schemes: ["ed25519"], tags: []}}
iex> X402.Extensions.HTTPMessageSignatures.decode(%{"accepts" => []})
{:ok, nil}
iex> X402.Extensions.HTTPMessageSignatures.decode(%{"extensions" => %{"http-message-signatures" => %{"info" => %{}}}})
{:error, {:missing_field, "registrationUrl"}}
Builds the server-side advertisement for PaymentRequired.extensions.
Raises NimbleOptions.ValidationError for invalid options: a malformed
declaration is a configuration error.
Options
:registration_url- Required. Where signature agents register their key directory (info.registrationUrl).:signature_schemes- Required. Accepted algorithms (info.signatureSchemes), at least one, for example["ed25519"].:tags- Accepted signature tags (info.tags), for example["web-bot-auth"]. The default value is[].:include_schema(boolean/0) - Whether to advertise the JSON schema alongsideinfo. The default value istrue.
Examples
iex> extension = X402.Extensions.HTTPMessageSignatures.extension(
...> registration_url: "https://network.example.com/signature-agents",
...> signature_schemes: ["ed25519", "ecdsa-p256-sha256", "rsa-pss-sha512"],
...> tags: ["web-bot-auth", "agent-browser-auth"]
...> )
iex> extension["info"]
%{
"registrationUrl" => "https://network.example.com/signature-agents",
"signatureSchemes" => ["ed25519", "ecdsa-p256-sha256", "rsa-pss-sha512"],
"tags" => ["web-bot-auth", "agent-browser-auth"]
}
iex> extension["schema"] == X402.Extensions.HTTPMessageSignatures.schema()
true
iex> X402.Extensions.HTTPMessageSignatures.extension(
...> registration_url: "https://network.example.com/signature-agents",
...> signature_schemes: ["ed25519"],
...> include_schema: false
...> )
%{"info" => %{"registrationUrl" => "https://network.example.com/signature-agents", "signatureSchemes" => ["ed25519"], "tags" => []}}
@spec extension_key() :: String.t()
Returns the extension key on the wire.
Examples
iex> X402.Extensions.HTTPMessageSignatures.extension_key()
"http-message-signatures"
@spec schema() :: map()
Returns the JSON schema the server advertises for the extension.
Examples
iex> schema = X402.Extensions.HTTPMessageSignatures.schema()
iex> schema["required"]
["registrationUrl", "signatureSchemes"]
iex> schema["properties"]["registrationUrl"]["format"]
"uri"
Validates the value advertised under the extension key and extracts its info.
Accepts the %{"info" => ...} envelope with or without schema, or a
bare info map. tags defaults to [].
Examples
iex> extension = X402.Extensions.HTTPMessageSignatures.extension(
...> registration_url: "https://network.example.com/agents", signature_schemes: ["ed25519"], tags: ["web-bot-auth"])
iex> X402.Extensions.HTTPMessageSignatures.validate(extension)
{:ok, %{registration_url: "https://network.example.com/agents", signature_schemes: ["ed25519"], tags: ["web-bot-auth"]}}
iex> X402.Extensions.HTTPMessageSignatures.validate(%{"info" => %{"registrationUrl" => "https://n.example/r"}})
{:error, {:missing_field, "signatureSchemes"}}
iex> X402.Extensions.HTTPMessageSignatures.validate(%{"info" => %{"registrationUrl" => "https://n.example/r", "signatureSchemes" => "ed25519"}})
{:error, :invalid_http_message_signatures_extension}
iex> X402.Extensions.HTTPMessageSignatures.validate("nope")
{:error, :invalid_http_message_signatures_extension}