logo

SIWE Authentication

Prepare and complete the manual SIWE flow for protected Namefi API operations

SIWE (EIP-4361) gives you a session token after one ERC-191 personal_sign signature. Use it for protected operations that do not require EIP-712.

At the moment, production SIWE has a known server-side issue in prepareSiweMessage caused by an invalid URI value. If you see a 500 INTERNAL_SERVER_ERROR during message preparation on production, that is a backend bug rather than a signing-flow bug in your client or tooling.

Flow

  1. GET /v-next/siwe/allowed-chains
  2. GET /v-next/siwe/nonce?signerAddress=...
  3. GET /v-next/siwe/message?signerAddress=...&nonce=...&chainId=...
  4. Sign messageString with personal_sign
  5. POST /v-next/siwe/verify
  6. Send the returned token as x-namefi-siwe-token

Step 1: Allowed chains

const allowedChains = await fetch(
  'https://api.namefi.io/v-next/siwe/allowed-chains',
).then((r) => r.json());

console.log(allowedChains);
// Production: [1, 8453]
// Development: [11155111, 46630]

Pick one of the returned chain IDs for the SIWE message.

Step 2: Get a nonce

const { valid, nonce } = await fetch(
  'https://api.namefi.io/v-next/siwe/nonce?signerAddress=0xYourAddress',
).then((r) => r.json());

if (!valid) {
  throw new Error('Failed to get nonce');
}

The nonce is single-use and expires after 5 minutes.

Step 3: Prepare the message

const { valid, message, messageString } = await fetch(
  `https://api.namefi.io/v-next/siwe/message?signerAddress=0xYourAddress&nonce=${nonce}&chainId=1`,
).then((r) => r.json());

if (!valid) {
  throw new Error('Failed to prepare SIWE message');
}

message is the structured payload you send back to /siwe/verify.

messageString is the exact ERC-191 string you sign with personal_sign.

Step 4: Sign with ERC-191

The signature step is external to the API and external to the namefi-api skill.

viem

const signature = await account.signMessage({ message: messageString });

WalletConnect

const signature = await provider.request({
  method: 'personal_sign',
  params: [messageString, '0xYourAddress'],
});

ethers.js

const signature = await signer.signMessage(messageString);

Step 5: Verify and get a token

const verifyResult = await fetch('https://api.namefi.io/v-next/siwe/verify', {
  method: 'POST',
  headers: { 'content-type': 'application/json' },
  body: JSON.stringify({
    address: '0xYourAddress',
    message,
    signature,
  }),
}).then((r) => r.json());

if (!verifyResult.valid) {
  throw new Error(`Verification failed: ${verifyResult.error}`);
}

const token = verifyResult.token;

Successful responses also include session details such as chainId, createdAt, and maxAgeSeconds.

Step 6: Call the protected operation

const domains = await fetch('https://api.namefi.io/v-next/user/domains', {
  headers: {
    'x-namefi-siwe-token': token,
  },
}).then((r) => r.json());

Session details

PropertyValue
Nonce lifetime5 minutes (300 seconds)
Token lifetime12 hours (43200 seconds)
Token headerx-namefi-siwe-token
Signing methodERC-191 personal_sign

Using the skill helpers

The public namefi-skills repo exposes two useful prep flows:

Full operation-aware prep

bun .rulesync/skills/namefi-api/scripts/prepare-auth-request.ts \
  --env dev \
  --operationId getUserDomains \
  --signer-address 0xYourAddress

That returns:

  • allowed chains
  • selected chain ID
  • nonce
  • structured SIWE message
  • messageString
  • a verify request template
  • a final request template with x-namefi-siwe-token

SIWE-only helper

bun .rulesync/skills/namefi-api/scripts/prepare-siwe-message.ts \
  --env dev \
  --signer-address 0xYourAddress

Use either output with any external signer or MCP that can do personal_sign.

For the combined operation-aware entrypoint that also handles EIP-712 and no-auth cases, see Prepare Auth Requests.

Client-library behavior

When you use @namefi/api-client with type: 'EIP712', the client performs this SIWE flow automatically for siwe-required and siwe-optional operations.

For raw integrations, the flow stays the same: prepare the message, sign it externally, verify it, then reuse the token until it expires.

On this page