Skip to content
Kausa Docs
Security

Wallet Standard Sign-In (SEP-0001)

How Kausa uses the Solana Wallet Standard Sign-In feature for phishing-resistant authentication.

What is SEP-0001?

SEP-0001 is the Solana Wallet Standard Sign-In feature (solana:signIn). It replaces the older approach of asking users to sign an arbitrary text message with a structured, domain-bound authentication flow.

When a wallet supports solana:signIn, it constructs a sign-in message internally and shows the requesting domain to the user. The wallet verifies the domain itself. If a phishing site at evil-clone.com requests sign-in for kausa.app, the wallet refuses before the user sees anything.

Why Kausa uses it

The previous hand-rolled SIWS flow relied on users reading the message text to confirm the domain. That works in theory, but most people sign without reading. SEP-0001 moves the domain check into the wallet UI so it happens automatically.

Kausa checks whether the connected wallet exposes the signIn feature. Phantom, Solflare, and Backpack all support it. Wallets that don't get the legacy flow instead. Either way, the session cookie is the same.

How it works

sequenceDiagram
    participant User
    participant Wallet
    participant Client as Kausa Client
    participant Server as Kausa Server

    User->>Client: Click "Connect Wallet"
    Client->>Wallet: Check for solana:signIn feature
    alt Wallet supports SEP-0001
        Client->>Server: GET /api/auth/siws/nonce
        Server-->>Client: nonce
        Client->>Wallet: signIn({ domain, nonce, chainId, ... })
        Wallet->>User: Show structured sign-in panel
        User->>Wallet: Approve
        Wallet-->>Client: { signedMessage, signature, account }
        Client->>Server: POST /api/auth/siws (type: "sep0001")
        Server->>Server: Verify domain, chainId, nonce, expiry, ed25519 sig
        Server-->>Client: Session cookie
    else Legacy flow
        Client->>Server: GET /api/auth/siws/nonce
        Server-->>Client: nonce
        Client->>Wallet: signMessage(text)
        Wallet->>User: Show raw message
        User->>Wallet: Approve
        Wallet-->>Client: signature
        Client->>Server: POST /api/auth/siws (type: "legacy")
        Server->>Server: Verify nonce, expiry, ed25519 sig
        Server-->>Client: Session cookie
    end

Server-side verification

The server checks five things for SEP-0001 payloads:

  1. Domain must match the production hostname or localhost:3700 in development. No wildcards, no regex. Literal string match only.
  2. Chain ID must match the SOLANA_NETWORK environment variable (devnet in current deployment).
  3. Nonce must be server-issued and unused. Consumed on first use (single-use enforcement via the database).
  4. Expiration: the expirationTime field must be in the future.
  5. Signature: Ed25519 over the signed message bytes, verified against the account's public key using tweetnacl.

If any check fails, the request is rejected. The nonce gets consumed before the signature check, so a failed signature attempt still burns the nonce.

Rollback

To disable SEP-0001 and go back to legacy-only:

  1. Remove the case "sep0001": branch in src/app/api/auth/siws/route.ts.
  2. Revert src/components/wallet-button.tsx to always use the signMessage path.

Wallets fall back to the legacy flow automatically when SEP-0001 isn't offered. The session cookie format is identical, so users won't notice.

References