# `X402.Extensions.HTTPMessageSignatures`
[🔗](https://github.com/cardotrejos/x402/blob/v0.9.0/lib/x402/extensions/http_message_signatures.ex#L1)

The x402 `http-message-signatures` extension: identity through RFC 9421.

Implements the
[http-message-signatures extension](https://github.com/x402-foundation/x402/blob/main/specs/extensions/http-message-signatures.md).
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.

# `error`

```elixir
@type error() ::
  :invalid_http_message_signatures_extension | {:missing_field, String.t()}
```

# `info`

```elixir
@type info() :: %{
  registration_url: String.t(),
  signature_schemes: [String.t()],
  tags: [String.t()]
}
```

The decoded extension info.

# `decode`
*since 0.9.0* 

```elixir
@spec decode(term()) :: {:ok, info() | nil} | {:error, error()}
```

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"}}

# `extension`
*since 0.9.0* 

```elixir
@spec extension(keyword()) :: map()
```

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` (`t:boolean/0`) - Whether to advertise the JSON schema alongside `info`. The default value is `true`.

## 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" => []}}

# `extension_key`
*since 0.9.0* 

```elixir
@spec extension_key() :: String.t()
```

Returns the extension key on the wire.

## Examples

    iex> X402.Extensions.HTTPMessageSignatures.extension_key()
    "http-message-signatures"

# `schema`
*since 0.9.0* 

```elixir
@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"

# `validate`
*since 0.9.0* 

```elixir
@spec validate(term()) :: {:ok, info()} | {:error, error()}
```

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}

---

*Consult [api-reference.md](api-reference.md) for complete listing*
