Sign In With Ethereum

AppKit provides a simple solution for integrating with “Sign In With Ethereum” (SIWE), a form of authentication that enables users to control their digital identity with their Ethereum account. SIWE is a standard also known as EIP-4361.

One-Click Auth

One-Click Auth represents a key advancement within WalletConnect v2, streamlining the user authentication process in AppKit by enabling them to seamlessly connect with a wallet and sign a SIWE message with just one click.

Connecting a wallet, proving control of an address with an off-chain signature, authorizing specific actions. These are the kinds of authorizations that can be encoded as “ReCaps”. ReCaps are permissions for a specific website or dapp that can be compactly encoded as a long string in the message you sign and translated by any wallet into a straight-forward one-sentence summary. WalletConnect uses permissions expressed as ReCaps to enable a One-Click Authentication.

Pre-requisites

In order for SIWE to work, you need to have a backend to communicate with. This backend will be used to generate a nonce, verify messages and handle sessions. More info here

Configure your SIWE Client

// siweConfig.ts

import {
  createSIWEConfig,
  formatMessage,
  type SIWEVerifyMessageArgs,
  type SIWECreateMessageArgs,
} from "@reown/appkit-siwe-react-native";

export const siweConfig = createSIWEConfig({
  getNonce: async (): Promise<string> => {
    // The getNonce method functions as a safeguard
    // against spoofing, akin to a CSRF token.

    return await api.getNonce();
  },
  verifyMessage: async ({
    message,
    signature,
    cacao,
  }: SIWEVerifyMessageArgs): Promise<boolean> => {
    try {
      // This function ensures the message is valid,
      // has not been tampered with, and has been appropriately
      // signed by the wallet address.

      const isValid = await api.verifyMessage({ message, signature, cacao });

      return isValid;
    } catch (error) {
      return false;
    }
  },
  getSession: async (): Promise<SIWESession | null> => {
    // The backend session should store the associated address and chainId
    // and return it via the `getSession` method.

    const session = await api.getSession();
    if (!session) throw new Error("Failed to get session!");

    const { address, chainId } = session;

    return { address, chainId };
  },
  signOut: (): Promise<boolean> => {
    try {
      // The users session must be destroyed when calling `signOut`.

      await api.signOut();
      return true;
    } catch {
      return false;
    }
  },
  createMessage: ({ address, ...args }: SIWECreateMessageArgs): string => {
    // Method for generating an EIP-4361-compatible message.

    return formatMessage(args, address);
  },
  getMessageParams: () => {
    // Parameters to create the SIWE message internally.
    // More info in https://github.com/ChainAgnostic/CAIPs/blob/main/CAIPs/caip-222.method

    return {
      domain: "your domain",
      uri: "your uri",
      chains: [1, 137], // array of chain ids
      statement: "Please sign with your account",
      iat: new Date().toISOString(),
    };
  },
});

Initialize AppKit with your siweConfig

Add the siwe configuration in createAppKit initialization

import { siweConfig } from "./siweConfig.ts";

createAppKit({
  //..
  siweConfig,
});

SIWE Config reference

interface SIWEConfig {
  /** Required **/
  getNonce: () => Promise<string>
  createMessage: (args: SIWECreateMessageArgs) => string
  verifyMessage: (args: SIWEVerifyMessageArgs) => Promise<boolean>
  getSession: () => Promise<SIWESession | null>
  signOut: () => Promise<boolean>

  /** Required for One-Click Auth **/
  getMessageParams `() => Promise<{ domain: string, uri: string, chains: number[], statement: string }>


  /** Optional **/

  // Callback when user signs in
  onSignIn?: (session?: SIWESession) => void

  // Callback when user signs out
  onSignOut?: () => void

  // Defaults to true
  enabled?: boolean

  // In milliseconds, defaults to 5 minutes
  nonceRefetchIntervalMs?: number

  // In milliseconds, defaults to 5 minutes
  sessionRefetchIntervalMs?: number

  // Defaults to true
  signOutOnDisconnect?: boolean

  // Defaults to true
  signOutOnAccountChange?: boolean

  // Defaults to true
  signOutOnNetworkChange?: boolean
}

Exported functions

verifySignature

Verify a SIWE signature. Internally it calls your backend verification method.

import { verifySignature } from "@reown/appkit-siwe-react-native";

const isValid = await verifySignature({
  address,
  message,
  signature,
  chainId,
  projectId,
});

getChainIdFromMessage

Get the chain ID from the SIWE message.

import { getChainIdFromMessage } from "@reown/appkit-siwe-react-native";

const chainId = getChainIdFromMessage(message);

getAddressFromMessage

Get the address from the SIWE message.

import { getAddressFromMessage } from "@reown/appkit-siwe-react-native";

const address = getAddressFromMessage(message);