Wallet sign-in
Let people sign in and sign up by signing a message with an Ethereum or Solana wallet in their browser.
For an app whose customers already live in a crypto wallet, a password is one more thing to make up. Wallet sign-in lets them sign in by signing a message with the wallet in their browser: Sign-In with Ethereum (EIP-4361), or its Solana counterpart. A wallet nobody has seen before signs up with a name and an email, as a new phone number does. It all runs on your hosted sign-in page.
Step by step, with screenshots: Set up wallet sign-in.
Before you start
Wallet sign-in needs a plan that includes it. Without one, the console shows
the method locked and adding it is refused with a 402.
Setting it up
- Open User Management → Access → Authentication → Methods, choose Add auth method, then Wallet.
- Choose the Ethereum networks a wallet may sign in from: Ethereum, Polygon, Base, Arbitrum One and Optimism are listed by name, and Other chain IDs takes any other EIP-155 chain ID. Only Ethereum mainnet (chain ID 1) is allowed until you change it.
- Turn Solana on if Solana wallets may sign in. It is off by default.
- Tick the method on your client in the Settings tab.
A method has to allow at least one Ethereum network or Solana, or no wallet could sign in, and saving it is refused.
Which wallets work
Injected wallets with plain keys. For Ethereum, the EIP-1193 provider a browser wallet injects as window.ethereum; for Solana, a Phantom-style window.solana. The address is the key that signs.
Two kinds of wallet do not work yet, on purpose:
- WalletConnect, the QR code that connects a phone wallet to a desktop page. It needs a project ID registered by each app owner.
- Smart-contract wallets (ERC-1271). Checking their signatures needs a call to the chain, and BuildBase checks every signature offline.
What people see
The sign-in page offers the wallet, and asks it to sign a message the page builds from a one-time nonce. Nothing is sent to the chain and nothing costs gas. A wallet already linked to an account signs straight in.
- A new wallet is asked for a name and an email after signing. The email starts unverified: a verification code goes to it, as after any sign-up, so wallet sign-up needs a connected email sender. An address that already has an account here is refused, with the same sentence whether it is taken or not allowed. Linking the wallet there would hand the account to whoever holds the wallet; its owner can connect the wallet from their own Security screen instead.
- Sign-up rules apply. Closed sign-up, who can sign up, disposable addresses and your plan's end-user limit all hold for a new wallet.
- The second step still applies. A wallet sign-in is one factor,
swkin the token'samr, so a person with an authenticator app is asked for its code after signing, as after a password.
What is checked
A signed message is believed only when all of these hold:
- it names your sign-in page's domain and address, so a message signed for another site is refused
- its nonce is one BuildBase issued for this sign-in, less than 5 minutes ago, and it is spent on first use
- its network is one the method allows. Solana has no network setting: a message naming mainnet, devnet or testnet is accepted
- its times hold: issued recently, not expired, not dated in the future
- the signature belongs to the address in the message, recovered with secp256k1 for Ethereum and checked with ed25519 for Solana
A wallet is then a connected account: provider ethereum with the EIP-55 checksum address, or solana with the base58 address.
Connecting a wallet from your app
A signed-in person can add a wallet to the account they already have. While your client offers wallet sign-in, the Connected accounts section of the SDK's Security screen shows Connect an Ethereum wallet and Connect a Solana wallet. Connected wallets are listed with a short address, count as a way in, and emit user.account_linked. Hide only the connect buttons with ui.settings.security.wallets: false.
Without the screen, connectWallet runs the whole exchange: it gets a nonce, asks an Ethereum wallet on another network to switch to an allowed one, has the wallet sign, and links the result.
import { connectWallet, WalletFlowError } from '@buildbase/sdk/react';
try {
const account = await connectWallet('ethereum', { api }); // api: a WorkspaceApi
console.log(account.detail); // the short address
} catch (error) {
// no_wallet, rejected, wallet_busy, wrong_network, no_account, wallet_failed
if (error instanceof WalletFlowError) console.warn(error.code);
else throw error;
}Connect from your app's own page. A wallet shows the person which site is asking for the signature, and warns when the message names a different one. So a link's message names your app, not the sign-in page, and the request has to come from one of your allowed app origins: the origin of one of your clients' redirect URLs. From anywhere else it is refused with 403 origin_not_allowed.
| Refusal | When |
|---|---|
409 link_conflict | The wallet is connected to another account here |
409 guest_account | A guest creates an account before connecting a wallet |
401 nonce_expired | The nonce lapsed or was used. Start again. The SDK does not sign anyone out for it |
400 chain_not_allowed | The method does not take that kind of wallet, or that network |
400 invalid_message | The message was not made for this page, or its times are off |
400 invalid_signature | The signature is not the address's |
403 origin_not_allowed | The request did not come from one of your allowed app origins |
403 method_unavailable | Your client offers no wallet sign-in |
If you call the auth API yourself
The hosted page does this for you. Its three steps, with the sign-in page's state header:
| Route | Body | Answers |
|---|---|---|
POST /api/v1/auth/wallet/nonce | { chain }, ethereum or solana | { nonce, domain, uri, issuedAt, chainIds } |
POST /api/v1/auth/wallet/verify | { chain, message, signature } | A sign-in, or { signupRequired, signupToken } for a new wallet |
POST /api/v1/auth/wallet/register | { signupToken, name, email, turnstileToken? } | A sign-in, as phone sign-up answers |
/verify refuses with 400 invalid_signature, invalid_message or chain_not_allowed, and 401 nonce_expired. /register answers as phone sign-up does, from 409 email_unavailable to 401 signup_expired.
Next steps
- Connected Accounts - wallets beside social sign-ins, and removing one.
- Phone Sign-in - the other sign-up that asks for an email after the fact.
- Two-step sign-in - the second step after a wallet signature.