Skip to main content

Sign In With Ethereum

AppKit provides a simple solution for integrating with "Sign In With Ethereum" (SIWE), a new 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 Web3Modal 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.


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


This feature is currently in alpha. To get it, install @web3modal/wagmi-react-native@alpha or @web3modal/ethers-react-native@alpha following the steps here

Configure your SIWE Client

// siweConfig.ts

import {
type SIWEVerifyMessageArgs,
type SIWECreateMessageArgs
} from '@web3modal/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

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 Web3Modal with your siweConfig

Add the siwe configuration in createWeb3Modal initialization

import { siweConfig } from './siweConfig.ts'


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


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

import { verifySignature } from '@web3modal/siwe-react-native'

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


Get the chain ID from the SIWE message.

import { getChainIdFromMessage } from '@web3modal/siwe-react-native'

const chainId = getChainIdFromMessage(message)


Get the address from the SIWE message.

import { getAddressFromMessage } from '@web3modal/siwe-react-native'

const address = getAddressFromMessage(message)