Overview
Sign In With Ethereum (SIWE, EIP-4361) lets users authenticate using an Ethereum wallet they already own. Instead of a password or OTP, the user signs a structured message with their private key, proving ownership of an address. This approach is ideal when:- Your users already have Ethereum wallets (Base App, Base Account, MetaMask, Phantom, hardware wallets, etc.)
- You want a crypto-native sign-in experience without email or phone number requirements
- You need to tie the user wallet identity to an on-chain address
On first sign-in, CDP can automatically create an embedded user wallet and associate it with the user’s Ethereum address. Control this with the
ethereum.createOnLogin config option ("eoa" or "smart"). To disable automatic creation and provision wallets manually, omit createOnLogin from your config.How it works
SIWE authentication is a two-step flow:Step 1: Initiate — request a challenge message
Step 1: Initiate — request a challenge message
Your app calls
signInWithSiwe with the user’s Ethereum address and context about your application. CDP returns a standards-compliant EIP-4361 message containing a cryptographic nonce, expiration time, and the parameters you provided. Present this message to the user’s wallet for signing.Step 2: Verify — submit the signature
Step 2: Verify — submit the signature
After the user signs the message with their wallet, call
verifySiweSignature with the flowId from step 1 and the resulting signature. CDP verifies the signature on-chain, and on success, returns an authenticated user with a user wallet.Security features
Security features
- Cryptographic proof: Authentication requires a valid signature from the private key controlling the address — no credential sharing
- Replay protection: Each challenge contains a unique nonce and an expiration time
- Domain binding: The
domainfield ties the signed message to your application, preventing cross-site replay attacks - Rate limiting: Protection against brute force attempts
Choosing an integration path
| Path | Best for | Packages |
|---|---|---|
| Sign in with Base | Base ecosystem apps that want a one-line “Continue with Base” button | @coinbase/cdp-react, @base-org/account |
| React hooks (custom wallet) | Apps that already have a wallet connector (wagmi, viem, etc.) | @coinbase/cdp-hooks |
| Non-React | Vanilla JS/TS or non-React frameworks | @coinbase/cdp-core |
Sign in with Base
Sign in with Base is a first-party SIWE auth method for CDP React apps. Add"siwe:base" to your authMethods config and CDP renders a Continue with Base button that handles wallet connection, message signing, and verification automatically.
Under the hood, CDP uses the Base Account SDK (@base-org/account) to connect the user’s Base Account, then runs the standard SIWE flow (signInWithSiwe → personal_sign → verifySiweSignature). The chainId is read from the connected wallet at sign-in time.
Unlike OAuth providers, no CDP Portal configuration is required — enable the method in your app config and install the Base Account SDK.
Prerequisites
Install the CDP React packages and the Base Account SDK:Enable in your config
Add"siwe:base" to the authMethods array in your CDPReactProvider config. Set appName — it is passed to the Base Account SDK during wallet connection.
authMethods value uses the siwe:<provider> format. Today the supported provider is "base" (i.e. "siwe:base"). See the AuthMethod and SIWE_METHODS type references for details.
Combining with other auth methods
siwe:base works alongside email, SMS, and OAuth methods in the same authMethods array. Users pick their preferred method from the sign-in UI:
SignInAuthMethodButtons renders a button for each. Filter methods on individual components with the authMethods prop if needed.
SDK integration
React hooks (custom wallet)
UseuseSignInWithSiwe and useVerifySiweSignature from @coinbase/cdp-hooks when you manage wallet connection yourself (for example, with wagmi or viem). You are responsible for obtaining the user’s address, connecting their wallet, and signing the challenge message.
The
chainId must match the network the user’s wallet is on. Common values: 8453 (Base mainnet), 84532 (Base Sepolia), 1 (Ethereum mainnet). When using Sign in with Base, CDP reads the chain ID from the connected Base wallet automatically.Non-React
For vanilla JavaScript/TypeScript or other frameworks, import directly from@coinbase/cdp-core:
Auth method linking
Users who are already signed in can link a SIWE address to their account so they can sign in with their external wallet later. UseuseLinkSiwe from @coinbase/cdp-hooks — it follows the same two-step challenge-and-verify flow as useSignInWithSiwe, but associates the address with the current session instead of creating a new one.
When a signed-in user clicks Continue with Base in CDP React, the SDK automatically calls linkSiwe instead of signInWithSiwe.
See Auth Method Linking for the full linking guide and LinkAuth component examples.
Parameters
signInWithSiwe options
| Parameter | Type | Required | Description |
|---|---|---|---|
address | string | Yes | ERC-55 checksummed Ethereum address of the user |
chainId | number | Yes | EIP-155 chain ID (e.g. 8453 for Base mainnet, 1 for Ethereum mainnet) |
domain | string | Yes | RFC 3986 authority of your app (e.g. "example.com") |
uri | string | Yes | RFC 3986 URI of the resource being accessed (e.g. "https://example.com") |
statement | string | No | Human-readable ASCII assertion shown to the user |
resources | string[] | No | Additional URIs the user acknowledges |
idempotencyKey | string | No | Safe retry key |
signInWithSiwe result
| Field | Type | Description |
|---|---|---|
message | string | EIP-4361-formatted message for the user to sign |
flowId | string | Pass this to verifySiweSignature |
nonce | string | Cryptographic nonce embedded in the message |
expirationTime | string | ISO 8601 timestamp after which the challenge expires |
verifySiweSignature options
| Parameter | Type | Required | Description |
|---|---|---|---|
flowId | string | Yes | The flowId returned by signInWithSiwe |
signature | string | Yes | ERC-191 hex signature (0x-prefixed) from the user’s wallet |
idempotencyKey | string | No | Safe retry key |
verifySiweSignature result
| Field | Type | Description |
|---|---|---|
user | User | The authenticated user object |
message | string | Confirmation message |
isNewUser | boolean | true if this is the user’s first sign-in |
Optional message fields
You can include astatement to display a human-readable action the user is approving, and resources to enumerate URIs the user acknowledges:
Troubleshooting
Sign in with Base requires @base-org/account
Sign in with Base requires @base-org/account
Install the Base Account SDK in your project:This package is an optional peer dependency of
@coinbase/cdp-react — it is only required when "siwe:base" is in your authMethods.Challenge expired or verification failed
Challenge expired or verification failed
The signed message must be submitted to
verifySiweSignature before the expirationTime returned in step 1. If the user takes too long to sign, or the page is left open, request a new challenge by calling signInWithSiwe again.User rejected the signature request
User rejected the signature request
If the user dismisses the wallet signing prompt, the flow fails gracefully. Prompt them to try again — each attempt creates a fresh challenge with a new nonce.
chainId mismatch
chainId mismatch
The
chainId passed to signInWithSiwe must match the network the user’s wallet is connected to. For custom wallet integrations, read the chain ID from the connected provider. For Sign in with Base, CDP handles this automatically.What to read next
- Authentication Methods: Overview of all authentication options
- Auth Method Linking: Link SIWE to email, SMS, or OAuth on the same wallet
- Implementation Guide: Step-by-step integration patterns with
AuthButtonand hooks - Session Management: Understand session lifecycle and token management