Enable wallet authentication in the Privy
Dashboard before implementing this feature.
- React
- React Native
- Swift
- Android
- Flutter
To authenticate a user via an Ethereum wallet (SIWE) without Privy UIs, use the React SDK’s
Then, when you attempt to login with a Phantom Solana wallet, you will be prompted to indicate whether you are signing with a Ledger wallet,
which will initiate a separate SIWS flow wherein which a no-op transaction will be signed and used for verification.
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
Report incorrect code
Copy
Ask AI
generateSiweMessage({ address: string, chainId: `eip155:${number}`, disableSignup?: boolean }) => Promise<string>
Parameters
EIP-55 checksum-encoded wallet address performing the signing.
EIP-155 Chain ID to which the session is bound (in CAIP-2 format), e.g.
eip155:1.Whether to disable signup for this login flow.
Returns
A SIWE message that can be signed by the wallet.
Sign the SIWE message
Request an EIP-191personal_sign signature for the message returned by generateSiweMessage from the connected wallet.Report incorrect code
Copy
Ask AI
import { useWallets } from '@privy-io/react-auth';
const { wallets } = useWallets();
const signature = await wallets[0].sign(message);
Login with SIWE
Report incorrect code
Copy
Ask AI
loginWithSiwe({ signature: string, message: string, disableSignup?: boolean }) => Promise<User>
Parameters
The EIP-191 signature corresponding to the message.
The EIP-4361 message returned by
generateSiweMessage.Whether to disable signup for the login flow.
Returns
The authenticated user.
Usage
Report incorrect code
Copy
Ask AI
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 intouseLoginWithSiwe to run custom logic after a successful login, or to handle errors that occur during the flow.onComplete
Report incorrect code
Copy
Ask AI
onComplete?: (params: {
user: User;
isNewUser: boolean;
wasAlreadyAuthenticated: boolean;
loginMethod: LoginMethod | null;
loginAccount: LinkedAccountWithMetadata | null;
}) => void
Parameters
The user object corresponding to the authenticated user.
Whether the user is a new user or an existing user.
Whether the user was already authenticated when the flow ran.
The method used by the user to login (if applicable).
The account corresponding to the login method.
onError
Report incorrect code
Copy
Ask AI
onError?: (error: PrivyErrorCode) => void
Parameters
The error that occurred during the login flow.
Usage
Report incorrect code
Copy
Ask AI
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
Thestate variable returned from useLoginWithSiwe will always be one of the following values.Report incorrect code
Copy
Ask AI
type SiweFlowState =
| { status: 'initial' }
| { status: 'error'; error: Error | null }
| { status: 'generating-message' }
| { status: 'awaiting-signature' }
| { status: 'submitting-signature' }
| { status: 'done' };
Sign in with Ledger on Solana
Currently, Ledger Solana hardware wallets only support transaction signatures, not the message signatures required for Sign-In With Solana (SIWS) authentication. In order to authenticate with a Solana Ledger wallet, you must mount theuseSolanaLedgerPlugin hook inside your PrivyProvider.Critical: The
useSolanaLedgerPlugin hook must be placed inside a component that is
wrapped by PrivyProvider. If the hook is placed alongside or outside the PrivyProvider, it
will not function correctly.Report incorrect code
Copy
Ask AI
import {PrivyProvider} from '@privy-io/react-auth';
import {useSolanaLedgerPlugin} from '@privy-io/react-auth/solana';
function SolanaLedgerSetup() {
// This hook MUST be called inside a component wrapped by PrivyProvider
useSolanaLedgerPlugin();
return null;
}
export default function App() {
return (
<PrivyProvider appId="your-app-id" config={{...}}>
<SolanaLedgerSetup />
{/* Your app components */}
</PrivyProvider>
);
}
Resources
- Ethereum (SIWE)
- Solana (SIWS)
To authenticate a user via an Ethereum wallet (SIWE), use the React Native 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
Report incorrect code
Copy
Ask AI
generateSiweMessage({wallet: {chainId: string, address: string}, from: {domain: string, uri: string}}) => Promise<string>
Parameters
Returns
A SIWE message that can be signed by the wallet.
Usage
Report incorrect code
Copy
Ask AI
import {useLoginWithSiwe} from '@privy-io/expo';
export function LoginScreen() {
const [address, setAddress] = useState('');
const [message, setMessage] = useState('');
const {generateSiweMessage} = useLoginWithSiwe();
const handleGenerate = async () => {
const message = await generateSiweMessage({
from: {
domain: 'my-domain.com', // domain must be allowlisted in the Privy dashboard.
uri: 'https://my-domain.com',
},
wallet: {
// sepolia chainId with CAIP-2 prefix
chainId: `eip155:11155111`,
address,
},
});
setMessage(message);
};
return (
<View>
<TextInput
value={address}
onChangeText={setAddress}
placeholder="0x..."
inputMode="ascii-capable"
/>
<Button onPress={handleGenerate}>Generate Message</Button>
{Boolean(message) && <Text>{message}</Text>}
</View>
);
}
Sign the SIWE message
Then, request an EIP-191personal_sign signature for the message returned by generateSiweMessage, from a connected wallet.There are many ways to connect a wallet to a mobile app, a few good options are:
Login with SIWE
Report incorrect code
Copy
Ask AI
loginWithSiwe({signature: string, messageOverride?: string, disableSignup?: boolean}) => Promise<Result<PrivyUser>>
Parameters
The signature of the SIWE message, signed by the user’s wallet.
An optional override for the message that is signed.
If true, the user will not be automatically created if they do not exist in the Privy database.
Returns
A PrivyUser object containing the user’s information.
Usage
Report incorrect code
Copy
Ask AI
import {useLoginWithSiwe, usePrivy} from '@privy-io/expo';
export function LoginScreen() {
const [signature, setSignature] = useState('');
const {user} = usePrivy();
const {loginWithSiwe} = useLoginWithSiwe();
if (user) {
return (
<>
<Text>Logged In</Text>
<Text>{JSON.stringify(user, null, 2)}</Text>
</>
);
}
return (
<View>
<TextInput
value={signature}
onChangeText={setSignature}
placeholder="0x..."
inputMode="ascii-capable"
/>
<Button onPress={() => loginWithSiwe({signature})}>Login</Button>
</View>
);
}
Callbacks
You can optionally pass callbacks into theuseLoginWithSiwe hook to run custom logic after a message has been generated, after a successful login, or to handle errors that occur during the flow.onGenerateMessage
Report incorrect code
Copy
Ask AI
onGenerateMessage?: ((message: string) => void) | undefined
Parameters
The SIWE message that was generated.
onSuccess
Report incorrect code
Copy
Ask AI
onSuccess?: ((user: PrivyUser, isNewUser: boolean) => void) | undefined
Parameters
The user object corresponding to the authenticated user.
Whether the user is a new user or an existing user.
onError
Report incorrect code
Copy
Ask AI
onError?: (error: Error) => void
Parameters
The error that occurred during the login flow.
Usage
Report incorrect code
Copy
Ask AI
import {useLoginWithSiwe} from '@privy-io/expo';
export function LoginScreen() {
const {generateSiweMessage, loginWithSiwe} = useLoginWithSiwe({
onGenerateMessage(message) {
// show a toast, send analytics event, etc...
},
onSuccess(user, isNewUser) {
// show a toast, send analytics event, etc...
},
onError(error) {
// show a toast, update form errors, etc...
},
});
// ...
}
Tracking login flow state
Thestate variable returned from useLoginWithSiwe will always be one of the following values.Report incorrect code
Copy
Ask AI
type SiweFlowState =
| { status: "initial" }
| { status: "error"; error: Error | null }
| { status: "generating-message" }
| { status: "awaiting-signature" }
| { status: "submitting-signature" }
| { status: "done" };
To authenticate a user via a Solana wallet (SIWS), use the React Native SDK’s
useLoginWithSiws 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 SIWS message
Report incorrect code
Copy
Ask AI
generateMessage({wallet: {address: string}, from: {domain: string, uri: string}}) => Promise<{message: string}>
Parameters
Wallet object containing Solana wallet address.
Hide child attributes
Hide child attributes
The address of the wallet.
Returns
A SIWS message that can be signed by the wallet.
Usage
Report incorrect code
Copy
Ask AI
import {useLoginWithSiws} from '@privy-io/expo';
export function LoginScreen() {
const [address, setAddress] = useState('');
const [message, setMessage] = useState('');
const {generateMessage} = useLoginWithSiws();
const handleGenerate = async () => {
const {message} = await generateMessage({
from: {
domain: 'my-domain.com',
uri: 'https://my-domain.com',
},
wallet: {
address,
},
});
setMessage(message);
};
return (
<View>
<TextInput
value={address}
onChangeText={setAddress}
placeholder="0x..."
inputMode="ascii-capable"
/>
<Button onPress={handleGenerate}>Generate Message</Button>
{Boolean(message) && <Text>{message}</Text>}
</View>
);
}
Sign the SIWS message
Then, request a signature for themessage returned by generateMessage, from a connected wallet.Login with SIWS
Report incorrect code
Copy
Ask AI
login({signature: string, message: string, wallet: {walletClientType: string, connectorType: string}, disableSignup?: boolean}) => Promise<Result<PrivyUser>>
Parameters
The signature of the SIWS message, signed by the user’s wallet.
The original message that was signed.
If true, the user will not be automatically created if they do not exist in the Privy database.
Returns
A PrivyUser object containing the user’s information.
Usage
Report incorrect code
Copy
Ask AI
import {useLoginWithSiws, usePrivy} from '@privy-io/expo';
export function LoginScreen() {
const [signature, setSignature] = useState('');
const {user} = usePrivy();
const {login} = useLoginWithSiws();
if (user) {
return (
<>
<Text>Logged In</Text>
<Text>{JSON.stringify(user, null, 2)}</Text>
</>
);
}
return (
<View>
<TextInput
value={signature}
onChangeText={setSignature}
placeholder="0x..."
inputMode="ascii-capable"
/>
<Button
onPress={() =>
login({
signature,
message,
wallet: {
walletClientType,
connectorType,
},
})
}
>
Login
</Button>
</View>
);
}
Resources
- Ethereum (SIWE)
- Solana (SIWS)
To authenticate a user via an Ethereum wallet (SIWE), use the Privy client’s
siwe handler.Generate SIWE message
Report incorrect code
Copy
Ask AI
func generateMessage(params: SiweMessageParams) async throws -> String
Parameters
Set of parameters required to generate the message.
Returns
A SIWE message that can be signed by the wallet.
Usage
Report incorrect code
Copy
Ask AI
do {
let params = SiweMessageParams(
appDomain: "my-domain.com",
appUri: "https://my-domain.com",
chainId: "1",
walletAddress: "0x12345..."
)
let siweMessage = try await privy.siwe.generateMessage(params: params)
} catch {
// An error can be thrown if the network call to generate the message fails,
// or if invalid metadata was passed in.
}
Sign the SIWE message
Using the message returned bygenerateMessage, request an EIP-191 personal_sign signature from the user’s connected wallet. You should do this using the library your app uses to connect to external wallets (e.g. the MetaMask iOS SDK or WalletConnect).
Once the user successfully signs the message, pass it into login.Login with SIWE
Report incorrect code
Copy
Ask AI
func login(
message: String,
signature: String,
params: SiweMessageParams,
metadata: WalletLoginMetadata?
) async throws -> PrivyUser
Parameters
The message returned from “generateMessage”.
The signature of the SIWE message, signed by the user’s wallet.
The same SiweMessageParams passed into “generateMessage”.
(Optional) you can pass additional metadata that will be stored with the linked wallet.
Returns
The authenticated Privy user
Throws
An error if logging the user in is unsuccessful.Usage
Report incorrect code
Copy
Ask AI
do {
let params = SiweMessageParams(
appDomain: "my-domain.com",
appUri: "https://my-domain.com",
chainId: "1",
walletAddress: "0x12345..."
)
// Generate SIWE message
let siweMessage = try await privy.siwe.generateMessage(params: siweParams)
// Optional metadata
let metadata = WalletLoginMetadata(
walletClientType: WalletClientType.metamask,
connectorType: "wallet_connect"
)
// Login
try await privy.siwe.login(
message: siweMessage,
// the signature generated by the user's wallet
signature: signature,
params: siweParams,
metadata: metadata
)
} catch {
// error logging user in
}
To authenticate a user via a Solana wallet (SIWS), use the Privy client’s
siws handler.Generate SIWS message
Report incorrect code
Copy
Ask AI
func generateMessage(params: SiwsMessageParams) async throws -> String
Parameters
The parameters For building a Sign-In with Solana (SIWS) message.
Returns
A unique SIWS message as a string
Usage
Report incorrect code
Copy
Ask AI
do {
let params = SiwsMessageParams(
domain: "my-domain.com",
uri: "https://my-domain.com",
address: "insert-solana-wallet-address"
)
let message = try await privy.siws.generateMessage(params: params)
} catch {
// An error can be thrown if the network call to generate the message fails,
// or if invalid parameters are passed in.
print(error)
}
Sign the SIWS message
Using the message returned bygenerateMessage, request a signature for the message returned by generateMessage, from a connected wallet.
You should do this using the library your app uses to connect to external wallets (e.g. WalletConnect).
Once the user successfully signs the message, pass it into login.Login with SIWS
Report incorrect code
Copy
Ask AI
func login(
message: String,
signature: String,
metadata: WalletLoginMetadata?
) async throws -> PrivyUser
Parameters
The previously generated SIWS message.
The SIWS signature generated by the wallet.
(Optional) Additional metadata specifying wallet client and connector type.
Returns
The authenticated Privy user
Usage
Report incorrect code
Copy
Ask AI
do {
let params = SiwsMessageParams(
domain: "my-domain.com",
uri: "https://my-domain.com",
address: "insert-solana-wallet-address"
)
// Generate SIWS message
let message = try await privy.siws.generateMessage(params: params)
// Login
try await privy.siws.login(
message: message,
// the signature generated by the user's wallet
signature: signature
)
} catch {
// Failed to login with SIWS
print(error)
}
Linking a wallet with SIWS
If instead of logging in, you want to link a Solana wallet to an already authenticated user, you can use thelink method in the siws handler, having generated the SIWS message in the same way.Report incorrect code
Copy
Ask AI
func link(
message: String,
signature: String,
metadata: WalletLoginMetadata?
) async throws
- Ethereum (SIWE)
- Solana (SIWS)
To authenticate a user via an Ethereum wallet (SIWE), use the Privy client’s
siwe handler.Generate SIWE message
Report incorrect code
Copy
Ask AI
public suspend fun generateMessage(params: SiweMessageParams): Result<String>
Parameters
Set of parameters required to generate the message.
Returns
A result type encapsulating the SIWE message that can be signed by the wallet on success.
Usage
Report incorrect code
Copy
Ask AI
val params = SiweMessageParams(
appDomain = domain,
appUri = uri,
chainId = chainId,
walletAddress = walletAddress
)
privy.siwe.generateMessage(params = params).fold(
onSuccess = { message ->
// request an EIP-191 `personal_sign` signature on the message
},
onFailure = { e ->
// An error can be thrown if the network call to generate the message fails,
// or if invalid metadata was passed in.
}
)
Sign the SIWE message
Using the message returned bygenerateMessage, request an EIP-191 personal_sign signature from the user’s connected wallet. You should do this using the library your app uses to connect to external wallets (e.g. the MetaMask SDK or WalletConnect).
Once the user successfully signs the message, pass the signature into the login function.Login with SIWE
Report incorrect code
Copy
Ask AI
public suspend fun login(
message: String,
signature: String,
params: SiweMessageParams,
metadata: WalletLoginMetadata?,
): Result<PrivyUser>
Parameters
The message returned from “generateMessage”.
The signature of the SIWE message, signed by the user’s wallet.
The same SiweMessageParams passed into “generateMessage”.
(Optional) you can pass additional metadata that will be stored with the linked wallet.
Returns
A result type ecapsulating the PrivyUser on success.
Usage
Report incorrect code
Copy
Ask AI
val params = SiweMessageParams(
appDomain = domain,
appUri = uri,
chainId = chainId,
walletAddress = walletAddress
)
// optional metadata
val metadata = WalletLoginMetadata(walletClientType = walletClient, connectorType = connectorType)
privy.siwe.login(message, signature, params, metadata).fold(
onSuccess = { privyUser ->
// Login success
},
onFailure = { e ->
// Login failure, either due to invalid signature or network error
}
)
Want to link a wallet to an existing user? Check out the linking accounts documentation.
To authenticate a user via a Solana wallet (SIWS), use the Privy client’s
siws handler.Generate SIWS message
Report incorrect code
Copy
Ask AI
public suspend fun generateMessage(params: SiwsMessageParams): Result<String>
Parameters
Set of parameters required to generate the message.
Hide child attributes
Hide child attributes
Your app’s domain. e.g. “my-domain.com”
Your app’s URI. e.g. “https://my-domain.com”
The user’s Solana wallet address.
Returns
A result type encapsulating the SIWS message that can be signed by the wallet on success.
Usage
Report incorrect code
Copy
Ask AI
val params = SiwsMessageParams(
appDomain = domain,
appUri = uri,
walletAddress = walletAddress
)
privy.siws.generateMessage(params = params).fold(
onSuccess = { message ->
// request a Solana wallet signature on the message
},
onFailure = { e ->
// An error can be thrown if the network call to generate the message fails,
// or if invalid metadata was passed in.
}
)
Sign the SIWS message
Using the message returned bygenerateMessage, request a signature from the user’s connected Solana wallet. You should do this using the library your app uses to connect to external wallets (e.g. the Solana Mobile SDK or Phantom). Once the user successfully signs the message, pass the signature into the login function.Login with SIWS
Report incorrect code
Copy
Ask AI
public suspend fun login(
message: String,
signature: String,
params: SiwsMessageParams,
metadata: WalletLoginMetadata?,
): Result<PrivyUser>
Parameters
The message returned from “generateMessage”.
The signature of the SIWS message, signed by the user’s wallet.
The same SiwsMessageParams passed into “generateMessage”.
Returns
A result type encapsulating the PrivyUser on success.
Usage
Report incorrect code
Copy
Ask AI
val params = SiwsMessageParams(
appDomain = domain,
appUri = uri,
walletAddress = walletAddress
)
// optional metadata
val metadata = WalletLoginMetadata(
walletClientType = walletClient,
connectorType = connectorType
)
privy.siws.login(message, signature, params, metadata).fold(
onSuccess = { privyUser ->
// Login success
},
onFailure = { e ->
// Login failure, either due to invalid signature or network error
}
)
Want to link a wallet to an existing user? Check out the linking accounts documentation.
- Ethereum (SIWE)
- Solana (SIWS)
To authenticate a user via an Ethereum wallet (SIWE), use the Privy client’s
siwe handler.Generate SIWE message
Report incorrect code
Copy
Ask AI
Future<Result<String>> generateMessage(SiweMessageParams params)
Parameters
Set of parameters required to generate the message.
Returns
A result type encapsulating the SIWE message that can be signed by the wallet on success.
Usage
Report incorrect code
Copy
Ask AI
final params = SiweMessageParams(
appDomain: domain,
appUri: uri,
chainId: chainId,
walletAddress: walletAddress,
);
final result = await privy.siwe.generateMessage(params);
result.fold(
onSuccess: (message) {
// request an EIP-191 `personal_sign` signature on the message
},
onFailure: (error) {
// An error can be thrown if the network call to generate the message fails,
// or if invalid metadata was passed in.
},
);
Sign the SIWE message
Using the message returned bygenerateMessage, request an EIP-191 personal_sign signature from the user’s connected wallet.
You should do this using the library your app uses to connect to external wallets (e.g. the MetaMask SDK or WalletConnect). Once
the user successfully signs the message, pass the signature into the login function.Login with SIWE
Report incorrect code
Copy
Ask AI
Future<Result<PrivyUser>> login({
required String message,
required String signature,
required SiweMessageParams params,
WalletLoginMetadata? metadata,
})
Parameters
The message returned from “generateMessage”.
The signature of the SIWE message, signed by the user’s wallet.
The same SiweMessageParams passed into “generateMessage”.
(Optional) you can pass additional metadata that will be stored with the linked wallet.
Returns
A result type encapsulating the PrivyUser on success.
Usage
Report incorrect code
Copy
Ask AI
final params = SiweMessageParams(
appDomain: domain,
appUri: uri,
chainId: chainId,
walletAddress: walletAddress,
);
// optional metadata
final metadata = WalletLoginMetadata(
walletClientType: walletClient,
connectorType: connectorType,
);
final result = await privy.siwe.login(
message: message,
signature: signature,
params: params,
metadata: metadata,
);
result.fold(
onSuccess: (privyUser) {
// Login success
},
onFailure: (error) {
// Login failure, either due to invalid signature or network error
},
);
To authenticate a user via a Solana wallet (SIWS), use the Privy client’s
siws handler.Generate SIWS message
Report incorrect code
Copy
Ask AI
Future<Result<String>> generateMessage(SiwsMessageParams params)
Parameters
Set of parameters required to generate the message.
Hide child attributes
Hide child attributes
Your app’s domain. e.g. “my-domain.com”
Your app’s URI. e.g. “https://my-domain.com”
The user’s base58-encoded Solana wallet address.
Returns
A result type encapsulating the SIWS message that can be signed by the wallet on success.
Usage
Report incorrect code
Copy
Ask AI
final params = SiwsMessageParams(
appDomain: domain,
appUri: uri,
walletAddress: walletAddress,
);
final result = await privy.siws.generateMessage(params);
result.fold(
onSuccess: (message) {
// request a signature on the message using the Solana wallet
},
onFailure: (error) {
// An error can be thrown if the network call to generate the message fails,
// or if invalid metadata was passed in.
},
);
Sign the SIWS message
Using the message returned bygenerateMessage, request a signature from the user’s connected Solana wallet.
You should do this using the library your app uses to connect to Solana wallets (e.g. the Phantom SDK or Solana Mobile Wallet Adapter). Once
the user successfully signs the message, pass the signature into the login function.Login with SIWS
Report incorrect code
Copy
Ask AI
Future<Result<PrivyUser>> login({
required String message,
required String signature,
required SiwsMessageParams params,
WalletLoginMetadata? metadata,
})
Parameters
The message returned from “generateMessage”.
The signature of the SIWS message, signed by the user’s wallet.
The same SiwsMessageParams passed into “generateMessage”.
(Optional) you can pass additional metadata that will be stored with the linked wallet.
Returns
A result type encapsulating the PrivyUser on success.
Usage
Report incorrect code
Copy
Ask AI
final params = SiwsMessageParams(
appDomain: domain,
appUri: uri,
walletAddress: walletAddress,
);
// optional metadata
final metadata = WalletLoginMetadata(
walletClientType: walletClient,
connectorType: connectorType,
);
final result = await privy.siws.login(
message: message,
signature: signature,
params: params,
metadata: metadata,
);
result.fold(
onSuccess: (privyUser) {
// Login success
},
onFailure: (error) {
// Login failure, either due to invalid signature or network error
},
);

