X402.Extensions.SIWX.Challenge (X402 v0.9.0)

Copy Markdown View Source

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.

Summary

Types

A normalized supportedChains entry.

Functions

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

Formats a DateTime as ISO 8601 UTC with millisecond precision.

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

Returns the JSON schema advertised under schema.

Types

supported_chain()

@type supported_chain() :: %{required(String.t()) => String.t()}

A normalized supportedChains entry.

Functions

build(opts)

(since 0.9.0)
@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 (String.t/0) - Required. The server's public host (info.domain), e.g. "api.example.com".

  • :uri (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 String.t/0) - URIs associated with the request (info.resources); omitted when empty. The default value is [].

  • :version (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 (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(datetime)

(since 0.9.0)
@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)
@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)
@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"]