Web3 Authentication with SIWE (Sign-In with Ethereum)
Passwords are dead. Learn how to let users log in with their crypto wallets securely using the SIWE standard.
Introduction
"Connect Wallet" buttons are everywhere, but simply connecting a wallet doesn't prove ownership for a backend session. Anyone can pretend to be `0x123...` in an HTTP request.
Sign-In with Ethereum (EIP-4361) standardizes how off-chain authentication works. It challenges the user to sign a specific message, proving they control the private key.
💡 Why This Matters: SIWE brings the security of public-key cryptography to standard Web2 user sessions.
How It Works
Client Implementation
Using `siwe` and `wagmi` in Next.js to generate and sign the message.
import { SiweMessage } from 'siwe';
import { useAccount, useSignMessage } from 'wagmi';
export function SignInButton() {
const { address, chainId } = useAccount();
const { signMessageAsync } = useSignMessage();
const signIn = async () => {
// 1. Get nonce from backend
const nonceRes = await fetch('/api/nonce');
const nonce = await nonceRes.text();
// 2. Create message
const message = new SiweMessage({
domain: window.location.host,
address,
statement: 'Sign in with Ethereum to the app.',
uri: window.location.origin,
version: '1',
chainId,
nonce,
});
// 3. Sign message
const signature = await signMessageAsync({
message: message.prepareMessage(),
});
// 4. Verify on backend
await fetch('/api/verify', {
method: 'POST',
body: JSON.stringify({ message, signature }),
});
};
return <button onClick={signIn}>Sign In with Wallet</button>;
}Backend Verification (Spring Boot)
The backend must verify that the signature matches the message and the address. It also checks the nonce to ensure freshness.
@PostMapping("/verify")
public ResponseEntity<?> verify(@RequestBody SiweRequest request) {
try {
// Use a library like 'siwe-java' or verify signature manually using Web3j
boolean isValid = siweService.verify(
request.getMessage(),
request.getSignature()
);
if (isValid) {
String address = extractAddress(request.getMessage());
// Create JWT Session for this address
String token = jwtService.createToken(address);
return ResponseEntity.ok(new AuthResponse(token));
}
return ResponseEntity.status(401).build();
} catch (Exception e) {
return ResponseEntity.status(401).body("Invalid signature");
}
}Security Analysis
SIWE is secure, but implementation details matter.
🔒 Nonce Validation
The nonce must be generated server-side and checked to prevent replay attacks.
🔒 Domain Checking
The message includes the domain to prevent phishing (signing a message on evil.com for good.com).
🔒 Expiration
The message should have an expiration time (`expirationTime` field).
🔒 HTTPS
Always serve your app over HTTPS to prevent MITM attacks on the signature transmission.
✅ SIWE Checklist
Before you ship:
Build Web3 Apps
Need to test your smart contract interactions? We have tools for that.
Related Topics
Conclusion
SIWE is a game-changer for user onboarding. It eliminates the need for passwords, reduces friction, and leverages the security of the blockchain.
By implementing SIWE correctly with backend verification, you open your application to the world of Web3 while maintaining robust security standards.