Skip to main content
The Sign-In-With-X (SIWX) extension implements CAIP-122 for chain-agnostic wallet authentication. It allows clients to prove control of a wallet that previously paid for a resource, enabling access without requiring repurchase.

Overview

SIWX solves two key problems in x402:
  1. Repeat access to purchased content: Without SIWX, clients must pay every time they request a resource. With SIWX, sign in with your wallet to access content you’ve already paid for.
  2. Auth-only routes: Protect resources with wallet authentication alone, without requiring payment.
Key Features:
  • For Buyers: Sign in with your wallet to access content you’ve already paid for, or authenticate to access wallet-gated resources
  • For Sellers: Grant access to returning customers without requiring repayment, or create auth-only routes that require wallet signatures but no payment
  • Chain-Agnostic: Works with EVM (Ethereum, Base, etc.) and Solana wallets
  • Standards-Based: Built on CAIP-122, EIP-4361 (SIWE), and Sign-In-With-Solana

How It Works

  1. Server returns 402 with sign-in-with-x extension containing challenge parameters
  2. Client signs the CAIP-122 message with their wallet
  3. Client sends signed proof in SIGN-IN-WITH-X HTTP header
  4. Server verifies signature and grants access either because:
    • The route is auth-only (requires signature but no payment), or
    • The wallet has previously paid for the resource
This is a Server ↔ Client extension. The Facilitator is not involved in the authentication flow.

Server Usage

The easiest way to implement SIWX is registering the provided extension factories:
The server extension derives network from accepts, derives domain/uri from the configured origin and request path, refreshes nonce/timestamps per request, records successful payments, and validates SIWX proofs for declared HTTP routes. Auth-only routes (declared with accepts: []) grant access based on a valid SIWX signature alone, without requiring payment. This is useful for wallet-gated content that doesn’t need micropayments.

Configured origin (required)

SIWX proofs bind to the public origin configured when creating the server extension or request hook. Challenge issuance and validation both use this value — never request headers such as Host, X-Forwarded-Host, or X-Forwarded-Proto.
Behind a TLS-terminating reverse proxy, set origin to the browser-visible URL (for example https://api.example.com), not the upstream listener address (for example http://127.0.0.1:4021). The request path is rebased onto the configured origin for each challenge.

Smart Wallet Support (EIP-1271 / EIP-6492)

By default, only EOA (Externally Owned Account) signatures are verified. To support smart contract wallets (like Coinbase Smart Wallet, Safe, etc.), pass a verifier via verifyOptions:
This enables:
  • EIP-1271: Verification of deployed smart contract wallets
  • EIP-6492: Verification of counterfactual (not-yet-deployed) wallets
Note: Smart wallet verification requires RPC calls, while EOA verification is purely local.

Using Hook Adapters Directly

If you need more control over the integration, you can use the individual hook adapter functions instead of createSIWxResourceServerExtension. This is useful when you want to attach SIWX behavior to an existing server setup:
Similarly, on the client side you can use createSIWxClientHook for a single signer, or attach it directly to an HTTP client:

Manual Usage (Advanced)

For custom implementations, you can use the low-level functions directly:

Client Usage

The easiest way to use SIWX as a client is with the provided client extension:
The client extension automatically:
  • Detects SIWX support in 402 responses
  • Matches your wallet’s chain with server’s supportedChains
  • Signs and sends the authentication proof
  • Falls back to payment if SIWX auth fails

Manual Usage (Advanced)

For custom implementations:

Multi-Chain Support

Servers can support multiple chains (e.g., both EVM and Solana) by including multiple entries in supportedChains:
Clients match their wallet’s chainId against supportedChains and use the first matching entry. The same nonce is shared across all chains, preventing replay attacks when authenticating with different wallets.

Supported Chains

EVM (Ethereum, Base, Polygon, etc.)

  • Chain ID Format: eip155:* (e.g., eip155:8453 for Base)
  • Signature Type: eip191
  • Signature Schemes:
    • eip191 (EOA - default)
    • eip1271 (smart contract wallet)
    • eip6492 (counterfactual wallet)
  • Message Format: EIP-4361 (SIWE)

Solana

  • Chain ID Format: solana:* (e.g., solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp for mainnet)
  • Signature Type: ed25519
  • Signature Scheme: siws
  • Message Format: Sign-In With Solana

API Reference

declareSIWxExtension(options?)

Creates the extension declaration for servers to include in PaymentRequired. Domain and URI are derived from the configured origin passed to the server extension factory (createSIWxResourceServerExtension in TypeScript/Python, CreateResourceServerExtension in Go). Do not set domain or resource_uri/ResourceURI in route declarations.
Go: DeclareExtension(DeclareOptions{Statement, Version, Networks, ExpirationSeconds}) Python: declare_siwx_extension(DeclareSIWxOptions(...)) Note for auth-only routes: When using accepts: [], the network parameter cannot be inferred from payment requirements and must be provided explicitly.

createSIWxResourceServerExtension(options) / CreateResourceServerExtension / create_siwx_resource_server_extension

Creates the server extension that enriches SIWX challenges, records successful payments, and verifies HTTP SIWX proofs for declared routes. Requires origin / Origin. Internally uses settle and request hooks. Go: CreateResourceServerExtension(ServerOptions{Storage, Origin, VerifyOptions, OnEvent}) Python: create_siwx_resource_server_extension(CreateSIWxHookOptions(storage=..., origin=...))

createSIWxSettleHook(options)

Creates an onAfterSettle hook that records payments for SIWX. Use this when you want to attach SIWX payment recording to an existing x402ResourceServer without registering the full extension.

createSIWxRequestHook(options)

Creates an onProtectedRequest hook that validates SIWX proofs on incoming HTTP requests. For paid routes, grants access when the signature is valid and the address has paid. For auth-only routes (accepts: []), grants access on a valid signature alone.

createSIWxClientHook(signer)

Creates an onPaymentRequired hook for a single wallet signer. Matches the signer type (EVM or Solana) to a compatible chain in the server’s supportedChains and signs the SIWX challenge.

createSIWxClientExtension({ signers })

Creates the client extension that signs compatible SIWX challenges before falling back to payment. Accepts multiple signers (tried in order until one succeeds).

parseSIWxHeader(header)

Parses a base64-encoded SIGN-IN-WITH-X header into a payload object.

validateSIWxMessage(payload, expectedOrigin, options?) / ValidateMessage / validate_siwx_message

Validates message fields (expiry, domain binding, nonce, etc.).
Go: ValidateMessage(payload, *url.URL, ValidationOptions) Python: validate_siwx_message(message, expected_origin: str, options=None)

verifySIWxSignature(payload, options?)

Verifies the cryptographic signature and recovers the signer address.

createSIWxPayload(serverInfo, signer, requestUrl)

Client helper that creates and signs a complete payload. requestUrl is the final URL of the 402 response (after redirects) and is required — signing is refused when the challenge domain or uri origin does not match that URL’s origin.

encodeSIWxHeader(payload)

Encodes a payload as base64 for the SIGN-IN-WITH-X header.

SIWxHookEvent

Event type emitted by SIWX hooks when onEvent is configured. Useful for logging and debugging:

Storage Interface

Implement SIWxStorage to track which wallets have paid:
The package includes InMemorySIWxStorage for development. For production, implement persistent storage (database, Redis, etc.). Optionally implement hasUsedNonce and recordNonce to prevent replay attacks where an intercepted SIWX header could be reused. Both methods must be implemented together — implementing only one will throw an error at startup.

Security Considerations

  • Origin Binding: The configured origin prevents signature reuse across different services. Validation compares message.domain and the origin of message.uri against this operator-defined value, not request headers.
  • Nonce Uniqueness: Each challenge MUST have a unique nonce to prevent replay attacks
  • Temporal Bounds: The issuedAt, expirationTime, and notBefore fields constrain signature validity windows
  • Chain-Specific Verification: Signatures are verified using chain-appropriate algorithms, preventing cross-chain signature reuse
  • Smart Wallet Support: EIP-1271 and EIP-6492 verification requires an RPC call to the wallet contract

Troubleshooting

Signature Verification Fails

Problem: verifySIWxSignature returns { isValid: false }. Check invalidReason for the specific failure code (e.g., invalid_siwx_signature, invalid_siwx_unsupported_chain). Solutions:
  • Ensure the message was signed with the correct wallet
  • Check that the signature scheme matches the wallet type
  • For smart wallets, enable evmVerifier option with a viem public client
  • Verify the chain ID matches between client and server

Message Validation Fails

Problem: validateSIWxMessage returns { isValid: false }. Check invalidReason for the specific failure code (e.g., invalid_siwx_expired, invalid_siwx_domain_mismatch). Solutions:
  • Check that issuedAt is recent (within maxAge, default 5 minutes)
  • Verify expirationTime hasn’t passed
  • Ensure domain matches the configured origin host
  • Confirm message.uri origin exactly matches the configured origin

Client Hook Not Working

Problem: SIWX authentication not being attempted. Solutions:
  • Verify server is declaring SIWX extension in 402 response
  • Check that client’s wallet chain matches one of the supportedChains
  • Ensure signer is properly configured for the wallet type