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

Server-side Sign-In-With-X challenge advertisements.

A challenge is the value a server places under
`PaymentRequired.extensions["sign-in-with-x"]`: the CAIP-122 message
metadata (`info`), the chains it accepts proofs from (`supportedChains`),
and the JSON schema of the proof (`schema`). `build/1` produces one with a
fresh nonce and timestamps; `X402.Extensions.SIWX.challenge/1` is the
public entry point.

# `supported_chain`

```elixir
@type supported_chain() :: %{required(String.t()) =&gt; String.t()}
```

A normalized `supportedChains` entry.

# `build`
*since 0.9.0* 

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

Builds the `sign-in-with-x` advertisement map.

Raises `NimbleOptions.ValidationError` for invalid options (programmer
error). Timestamps are rendered in ISO 8601 with millisecond precision.

## Options

* `:domain` (`t:String.t/0`) - Required. The server's public host (`info.domain`), e.g. `"api.example.com"`.

* `:uri` (`t:String.t/0`) - Required. The URI the proof is bound to (`info.uri`), e.g. `"https://api.example.com"`.

* `:supported_chains` - Required. Chains proofs are accepted from: a list of maps or keyword lists with
  `:chain_id` (CAIP-2, `eip155:*` or `solana:*`), an optional `:type`
  (`"eip191"` for `eip155`, `"ed25519"` for `solana`; derived from the
  chain when omitted), and an optional `:signature_scheme` hint
  (`"eip191"`, `"eip1271"`, `"eip6492"`, or `"siws"`).

* `:statement` - Human-readable purpose shown by the wallet (`info.statement`). The default value is `nil`.

* `:resources` (list of `t:String.t/0`) - URIs associated with the request (`info.resources`); omitted when empty. The default value is `[]`.

* `:version` (`t:String.t/0`) - CAIP-122 version. Always `"1"`. The default value is `"1"`.

* `:nonce` - Explicit nonce; a fresh `generate_nonce/0` value when omitted. The default value is `nil`.

* `:issued_at` - Challenge creation time; `DateTime.utc_now/0` when omitted. The default value is `nil`.

* `:expiration_seconds` (`t:pos_integer/0`) - Seconds after `issued_at` at which the challenge expires (`info.expirationTime`). The default value is `300`.

* `:not_before` - Optional `info.notBefore`. The default value is `nil`.

* `:request_id` - Optional correlation id (`info.requestId`). The default value is `nil`.

## Examples

    iex> challenge = X402.Extensions.SIWX.Challenge.build(
    ...>   domain: "api.example.com",
    ...>   uri: "https://api.example.com",
    ...>   supported_chains: [%{chain_id: "eip155:8453"}],
    ...>   nonce: "a1b2c3d4e5f67890a1b2c3d4e5f67890",
    ...>   issued_at: ~U[2024-01-15 10:30:00Z]
    ...> )
    iex> challenge["info"]
    %{
      "domain" => "api.example.com",
      "uri" => "https://api.example.com",
      "version" => "1",
      "nonce" => "a1b2c3d4e5f67890a1b2c3d4e5f67890",
      "issuedAt" => "2024-01-15T10:30:00.000Z",
      "expirationTime" => "2024-01-15T10:35:00.000Z"
    }
    iex> challenge["supportedChains"]
    [%{"chainId" => "eip155:8453", "type" => "eip191"}]

# `format_datetime`
*since 0.9.0* 

```elixir
@spec format_datetime(DateTime.t()) :: String.t()
```

Formats a `DateTime` as ISO 8601 UTC with millisecond precision.

## Examples

    iex> X402.Extensions.SIWX.Challenge.format_datetime(~U[2024-01-15 10:30:00Z])
    "2024-01-15T10:30:00.000Z"

    iex> X402.Extensions.SIWX.Challenge.format_datetime(~U[2024-01-15 10:30:00.123456Z])
    "2024-01-15T10:30:00.123Z"

# `generate_nonce`
*since 0.9.0* 

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

Generates a nonce: 32 lowercase hex characters from 16 random bytes.

## Examples

    iex> nonce = X402.Extensions.SIWX.Challenge.generate_nonce()
    iex> Regex.match?(~r/\A[a-f0-9]{32}\z/, nonce)
    true

# `schema`
*since 0.9.0* 

```elixir
@spec schema() :: map()
```

Returns the JSON schema advertised under `schema`.

## Examples

    iex> X402.Extensions.SIWX.Challenge.schema()["required"]
    ["domain", "address", "uri", "version", "chainId", "type", "nonce", "issuedAt", "signature"]

---

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