Arkena Docs

Sign-In with Canton

SIWC is Arkena's authentication primitive. The shape mirrors EIP-4361 with Canton-native fields.

Sign-In with Canton (SIWC) is the equivalent of Sign-In with Ethereum. The backend issues a structured message with a nonce, the user signs it with their wallet, and the backend issues a JWT scoped to the user's party. You attach the JWT on subsequent calls.

SIWC is not a transaction signature. It does not authorise on-chain state changes — those still go through prepare/execute with a separate signature.

The flow

Code
// 1. Backend issues a challenge
const { message, nonce } = await fetch("/api/auth/nonce", {
  method: "POST",
  body: JSON.stringify({ partyId: party }),
  headers: { "Content-Type": "application/json" },
}).then((r) => r.json());

// 2. Wallet signs the message
const { signature } = await provider.request({
  method: "canton_signMessage",
  params: { message },
});

// 3. Backend verifies and returns a JWT
const { token, expiresAt } = await fetch("/api/auth/verify", {
  method: "POST",
  body: JSON.stringify({ partyId: party, signature, nonce }),
  headers: { "Content-Type": "application/json" },
}).then((r) => r.json());

// 4. Use the token
await fetch("/api/wallet/balance/" + party, {
  headers: { Authorization: `Bearer ${token}` },
});

The structured message

The challenge message follows EIP-4361 conventions, adapted for Canton. Example:

Code
arkena.io wants you to sign in with your Canton party:
alice::1220abc...

URI: https://arkena.io
Version: 1
Network: devnet
Nonce: 9b2d0c4f81a3...
Issued At: 2026-05-04T16:00:00Z
Expiration Time: 2026-05-04T16:10:00Z
Statement: I accept the Arkena Terms of Service.

Fields:

  • Domain (first line) — the host the user is signing in to. Lets the user verify they're signing for the right site.
  • Party — the party ID this signature claims to be from.
  • URI, Version, Network — boilerplate.
  • Nonce — single-use, server-generated. Prevents replay.
  • Issued At, Expiration Time — challenge lifetime, 10 minutes from issue.
  • Statement — optional human-readable claim shown in the wallet prompt.

The wallet renders this as a sign-in card with the domain and party prominent. The user clicks once.

The JWT

The backend's response carries:

  • token — the JWT to attach as Authorization: Bearer <token>.
  • expiresAt — Unix timestamp.

The token is scoped to the single partyId it was issued for. Trying to use it for another party returns 403 Forbidden. To inspect the party attached to a token at any time, call GET /api/auth/me.

Rotation

When expiresAt is approaching (say, within 5 minutes), request a fresh challenge and re-sign. Rotation is silent — the wallet doesn't show a prompt for already-trusted origins on the same party.

To explicitly end a session, call POST /api/auth/logout.

What SIWC is not

  • Not a transaction signature. SIWC signatures don't authorise on-ledger state changes.
  • Not a permission grant. Connecting (canton_connect) and signing in are separate. A user can be connected without ever signing in; signing in implies connection.
  • Not persistent across browsers. Each browser instance has its own JWT lifecycle.

What's next