For users who already have wallets, Privy supports signing in with Ethereum (SIWE) or Solana (SIWS). With this flow, users who are already onchain can bring their existing wallet to your app, verify ownership of assets, and take onchain actions.
Enable wallet authentication in the Privy Dashboard before implementing this feature.
To authenticate a user via an Ethereum wallet (SIWE) without Privy UIs, use the React SDK’s useLoginWithSiwe hook.
In order to use Privy’s login with wallet flow, users must actively have a wallet connected to your app from which you can request signatures.

Generate SIWE message

generateSiweMessage({ address: string, chainId: `eip155:${number}`, disableSignup?: boolean }) => Promise<string>

Parameters

address
string
required
EIP-55 checksum-encoded wallet address performing the signing.
chainId
`eip155:${number}`
required
EIP-155 Chain ID to which the session is bound (in CAIP-2 format), e.g. eip155:1.
disableSignup
boolean
Whether to disable signup for this login flow.

Returns

message
string
A SIWE message that can be signed by the wallet.

Sign the SIWE message

Request an EIP-191 personal_sign signature for the message returned by generateSiweMessage from the connected wallet.
import { useWallets } from '@privy-io/react-auth';

const { wallets } = useWallets();

const signature = await wallets[0].sign(message);

Login with SIWE

loginWithSiwe({ signature: string, message: string, disableSignup?: boolean }) => Promise<User>

Parameters

signature
string
required
The EIP-191 signature corresponding to the message.
message
string
required
The EIP-4361 message returned by generateSiweMessage.
disableSignup
boolean
Whether to disable signup for the login flow.

Returns

User
User
The authenticated user.

Usage

import { useLoginWithSiwe, useWallets } from '@privy-io/react-auth';

export function LoginWithWalletButton() {
  const { generateSiweMessage, loginWithSiwe } = useLoginWithSiwe();
  const { wallets } = useWallets();

  const handleLogin = async () => {
    if (!wallets?.length) return;
    const activeWallet = wallets[0];

    const message = await generateSiweMessage({
      address: activeWallet.address,
      chainId: 'eip155:1',
    });

    const signature = await activeWallet.sign(message);
    await loginWithSiwe({ signature, message });
  };

  return (
    <button onClick={handleLogin}>Log in with wallet</button>
  );
}

Callbacks

You can optionally pass callbacks into useLoginWithSiwe to run custom logic after a successful login, or to handle errors that occur during the flow.

onComplete

onComplete?: (params: {
  user: User;
  isNewUser: boolean;
  wasAlreadyAuthenticated: boolean;
  loginMethod: LoginMethod | null;
  loginAccount: LinkedAccountWithMetadata | null;
}) => void

Parameters

user
User
The user object corresponding to the authenticated user.
isNewUser
boolean
Whether the user is a new user or an existing user.
wasAlreadyAuthenticated
boolean
Whether the user was already authenticated when the flow ran.
loginMethod
LoginMethod | null
The method used by the user to login (if applicable).
loginAccount
LinkedAccountWithMetadata | null
The account corresponding to the login method.

onError

onError?: (error: PrivyErrorCode) => void

Parameters

error
PrivyErrorCode
The error that occurred during the login flow.

Usage

import { useLoginWithSiwe } from '@privy-io/react-auth';

export function SiweWithCallbacks() {
  const { generateSiweMessage, loginWithSiwe } = useLoginWithSiwe({
    onComplete: ({
      user,
      isNewUser,
      wasAlreadyAuthenticated,
      loginMethod,
      loginAccount,
    }) => {
       // show a toast, update form errors, etc...
    },
    onError: (error) => {
       // show a toast, update form errors, etc...
    },
  });

  // ... use generateSiweMessage and loginWithSiwe as shown above
  return null;
}

Tracking login flow state

The state variable returned from useLoginWithSiwe will always be one of the following values.
type SiweFlowState =
  | { status: 'initial' }
  | { status: 'error'; error: Error | null }
  | { status: 'generating-message' }
  | { status: 'awaiting-signature' }
  | { status: 'submitting-signature' }
  | { status: 'done' };

Resources