Wallet Authorization Development (Sign-In with Ethereum)
Sign-In with Ethereum (SIWE, EIP-4361) is an authentication standard where a user signs a message with their Ethereum wallet to log into a dApp. No passwords, no email — only cryptographic signature. This is wallet-based auth, which has become the standard in Web3.
How SIWE Works
- Backend generates message with nonce
- Frontend shows message to user for signing
- User signs via MetaMask/WalletConnect
- Backend verifies signature → extracts address → creates session
Standard SIWE message format:
app.example.com wants you to sign in with your Ethereum account:
0xYourAddress
Sign in to Example App
URI: https://app.example.com
Version: 1
Chain ID: 1
Nonce: abc123def456
Issued At: 2024-01-15T10:30:00.000Z
Expiration Time: 2024-01-15T11:30:00.000Z
Backend Implementation (Node.js)
import { SiweMessage, generateNonce } from 'siwe';
import { ethers } from 'ethers';
// 1. Generate nonce
app.get('/api/nonce', (req, res) => {
const nonce = generateNonce();
req.session.nonce = nonce;
res.json({ nonce });
});
// 2. Verify signature
app.post('/api/verify', async (req, res) => {
const { message, signature } = req.body;
const siweMessage = new SiweMessage(message);
try {
const fields = await siweMessage.verify({
signature,
nonce: req.session.nonce,
domain: 'app.example.com'
});
// Signature is valid — user owns this address
req.session.user = {
address: fields.data.address,
chainId: fields.data.chainId
};
res.json({ success: true, address: fields.data.address });
} catch (error) {
res.status(401).json({ error: 'Invalid signature' });
}
});
Frontend Implementation with wagmi
import { useSignMessage, useAccount } from 'wagmi';
import { SiweMessage } from 'siwe';
function SignInButton() {
const { address } = useAccount();
const { signMessageAsync } = useSignMessage();
const handleSignIn = async () => {
// Get nonce from backend
const { nonce } = await fetch('/api/nonce').then(r => r.json());
// Create SIWE message
const message = new SiweMessage({
domain: window.location.host,
address,
statement: 'Sign in to Example App',
uri: window.location.origin,
version: '1',
chainId: 1,
nonce,
});
// Sign
const signature = await signMessageAsync({
message: message.prepareMessage()
});
// Verify on backend
await fetch('/api/verify', {
method: 'POST',
body: JSON.stringify({ message: message.prepareMessage(), signature })
});
};
return <button onClick={handleSignIn}>Sign In with Ethereum</button>;
}
Attack Prevention
Replay attacks: each nonce is used once. Backend invalidates nonce after verification.
Phishing: message must contain correct domain. User signs for your domain specifically, not another.
Session management: after SIWE, a regular HTTP session is created (JWT, session cookie). SIWE is only for initial auth, not each request.
SIWE is the gold standard for Web3 auth. Integration with existing backend — 1-3 days.







