Skip to content

[bug] SIWX prompts users to sign message even if the network is not supported #5527

@hsunami10

Description

@hsunami10

Link to minimal reproducible example

https://drive.google.com/file/d/17iU7UQ8VL5oRjzI9TrIQrstPNQKJVuVt/view?usp=sharing

Steps to Reproduce

  1. enable siwx on AppKit - for my example, I only have BSC testnet in my networks field
  2. connect a wallet that does not support BSC testnet - I used Phantom

My code is below:

siwx.ts

import { ConstantsUtil } from "@reown/appkit-common";
import {
  DefaultSIWX,
  InformalMessenger,
  type SIWXSession,
  SIWXVerifier,
} from "@reown/appkit-siwx";
import { AppKitNetwork, bscTestnet } from "@reown/appkit/networks";
import { isNil } from "es-toolkit";
import { toast } from "sonner";

import { createBrowserClient } from "@/lib/supabase/browser";

export const siwxNetworks = [bscTestnet] as const satisfies [
  AppKitNetwork,
  ...AppKitNetwork[],
];

class SIWXEip155Verifier extends SIWXVerifier {
  public readonly chainNamespace = ConstantsUtil.CHAIN.EVM;

  public override shouldVerify(session: SIWXSession): boolean {
    const parts = session.data.chainId.split(":");

    if (parts[0] !== this.chainNamespace) {
      return false;
    }

    // const numericChainId = parseInt(parts.slice(1).join(":"));
    const numericChainId = parseInt(parts[1]);

    const isValidChainId = siwxNetworks.some(
      (network) => network.id === numericChainId,
    );

    if (!isValidChainId) {
      const validChainNames = siwxNetworks
        .map((network) => network.name)
        .join(", ");
      toast.error("Error Signing Message", {
        id: "invalid-chain",
        description: `Your wallet must support the following network(s): ${validChainNames}. Please add it to your wallet, click "Cancel", then refresh the page.`,
        duration: Infinity,
        dismissible: true,
        closeButton: true,
        action: {
          label: "Refresh",
          onClick: () => window.location.reload(),
        },
      });
    }

    return isValidChainId;
  }

  public async verify(session: SIWXSession): Promise<boolean> {
    try {
      const response = await fetch("/api/v1/siwx/verify", {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
        },
        body: JSON.stringify({
          message: session.message,
          signature: session.signature,
          address: session.data.accountAddress,
        }),
      });

      if (!response.ok) {
        return false;
      }

      const result = await response.json();

      if (isNil(result) || typeof result !== "object") {
        return false;
      }

      if (
        "access_token" in result &&
        typeof result.access_token === "string" &&
        "refresh_token" in result &&
        typeof result.refresh_token === "string"
      ) {
        const supabase = createBrowserClient();
        const { error } = await supabase.auth.setSession({
          access_token: result.access_token,
          refresh_token: result.refresh_token,
        });

        if (error) {
          console.error("Error setting session:", error);
          return false;
        }

        return true;
      }

      return false;
    } catch (error) {
      console.error("SIWX verification error:", error);
      return false;
    }
  }
}

// Create the SIWX configuration
export const siwxConfig = new DefaultSIWX({
  messenger: new InformalMessenger({
    domain: process.env.NODE_ENV === "production" ? "twin.fun" : "localhost",
    uri:
      process.env.NODE_ENV === "production"
        ? "https://www.twin.fun"
        : "http://localhost:3000",
    getNonce: async () => Date.now().toString(),
  }),

  verifiers: [new SIWXEip155Verifier()],
  required: true,
});

AppKitProvider.tsx

import { WagmiAdapter } from "@reown/appkit-adapter-wagmi";
import { AppKitNetwork } from "@reown/appkit/networks";
import { createAppKit } from "@reown/appkit/react";
import { WagmiProvider } from "wagmi";

import { siwxConfig, siwxNetworks } from "@/config/siwx";

const projectId = "xxxxxxxxxxxxxxxxxx";

const networks = [...siwxNetworks] as const satisfies [
  AppKitNetwork,
  ...AppKitNetwork[],
];

const wagmiAdapter = new WagmiAdapter({
  networks,
  projectId,
  ssr: true,
});

createAppKit({
  adapters: [wagmiAdapter],
  networks,
  projectId,
  siwx: siwxConfig,
});

export function AppKitProvider({ children }: { children: React.ReactNode }) {
  return (
    <WagmiProvider config={wagmiAdapter.wagmiConfig}>{children}</WagmiProvider>
  );
}

Summary

Bug 1

If you look closely, the Switch Network dialog flashes briefly before the App needs to connect your wallet dialog. Even though the network is unsupported, it still prompts the user to sign the message. This shouldn't happen - it doesn't make any sense to still ask the user to sign the message when the network isn't supported.

Bug 2

Without my custom shouldVerify implementation, it automatically assigns another chain (one that isn't supported by my app) to the SIWX message. Then, it allows the user to sign the message - and they would be logged in on the wrong network. This happens even if I set createAppKitallowUnsupportedChain: false.

I was able to prevent this with overriding shouldVerify and manually checking the chain ID. But, this should definitely not happen. Otherwise, users who are able to log in with a network that my app doesn't support will not be able to make transactions anyways.

Bug 3

If you follow these steps:

  1. connect a wallet that does not support your app's networks
  2. click "Cancel" on the App needs to connect your wallet dialog
  3. refresh the page

This causes the Switch Network dialog to pop-up on load, and I'm unable to close out of it. The x and Disconnect buttons do not work. The only way to close out of it is to click into one one of the network names - and exit out of that.

List of related npm package versions

{
    "@reown/appkit": "^1.8.11",
    "@reown/appkit-adapter-wagmi": "^1.8.11",
    "@reown/appkit-siwe": "^1.8.11",
    "@reown/appkit-siwx": "^1.8.11",
    "viem": "^2.38.3",
    "wagmi": "^2.18.1",
}

Node.js Version

v24.4.0

Package Manager

npm@11.4.2

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions