This guide shows you how to ask the connected wallet for a signature, and how to turn a signature into a signed-in session on your server. A connected wallet or a signature on its own doesn’t sign anyone in; About Wallet Connections explains why.
Sign a Message#
Call signMessage() from useAccount() with a non-empty message:
"use client"
import { useAccount } from "@dogeos/dogeos-sdk"
export function SignMessageButton() {
const { address, signMessage } = useAccount()
async function onSign() {
if (!address || !signMessage) return
const signature = await signMessage({ message: "Welcome to DogeOS" })
console.log(signature)
}
return (
<button disabled={!address} onClick={onSign}>
Sign message
</button>
)
}On EVM chains the wallet signs the message exactly as you pass it. Signing works on DogeOS Chikyū; it doesn’t need Ethereum mainnet. If the user rejects the request, signMessage() rejects with a wallet error; see Handle Wallet Errors.
Sign Users In With Their Wallet#
-
When the user chooses to sign in, ask your server for a challenge. The server creates a fresh, single-use message that names the account, chain, domain and URI, stores it with an expiry, and returns it.
-
Have the wallet sign the server’s message exactly as returned:
import type { UseAccount } from "@dogeos/dogeos-sdk" type SignMessage = NonNullable<UseAccount["signMessage"]> export async function signChallenge(signMessage: SignMessage, message: string) { const signature = await signMessage({ message }) return { message, signature } }Don’t change the message, rebuild it, or add your own nonce to it.
-
Send the message and signature to your server. The server checks the signature, that the message is the one it issued, that it hasn’t expired or been used, and that its account, chain, domain and URI match. Then it marks the challenge used and creates the session.
-
Before you show the user as signed in, read the current account from
useAccount()again. If it changed while the server was verifying, sign the old account out through your normal account-switch flow.
Let the SDK Build the Sign-In Message#
If your server issues Sign-In with Ethereum parameters rather than a whole message, pass them to signInWithWallet():
const signature = await signInWithWallet(signInParams)On EVM chains the SDK builds a standard Sign-In with Ethereum (SIWE) message bound to the connected address and chain; any address or chain you pass must match them. signInWithWallet() returns only the signature, so your server has to rebuild the same message to verify it: pass every field, including scheme and issuedAt, from your server, and rebuild the message there with a SIWE library such as viem’s createSiweMessage. Otherwise the SDK fills in scheme, version, uri and issuedAt itself, and your rebuilt message won’t match. If you’d rather not rebuild it, use the exact-message flow above. See SignInParams.
On Solana and Dogecoin, use the exact-message flow with signMessage().
Called without parameters, signInWithWallet() signs a demonstration message with a random nonce made in the browser. That’s useful for trying the flow, but it doesn’t sign anyone in to your server.
Sign Users In Inside MyDoge#
Use the same wallet sign-in. DogeOS email and social sign-in (useDogeOSAuth().login()) isn’t available inside the MyDoge app and rejects with DogeOSHostError. See Sign Users In Inside MyDoge.