Sign-In with Ethereum (SIWE) is an open standard for Web3 authentication that lets users prove ownership of an Ethereum wallet address to authenticate with web applications — without username/password credentials or reliance on OAuth providers. In 2026, SIWE is the foundation of decentralised identity in web3 applications and is increasingly adopted for enterprise use cases requiring verifiable, user-controlled identity.
What Is Sign-In with Ethereum?
Sign-In with Ethereum (EIP-4361) enables users to authenticate with web applications by signing a structured plaintext message with their Ethereum wallet private key. The application verifies the signature cryptographically to confirm the user controls the wallet address — without any shared secret, database lookup, or third-party OAuth provider. The signed message includes the application domain, the wallet address, a nonce (preventing replay attacks), and an expiry time, making it a complete, secure authentication credential.
How SIWE Authentication Works
Implementing SIWE in a Web Application
// Backend: Generate SIWE message (Node.js + siwe package)
import { SiweMessage } from 'siwe';
app.get('/auth/nonce', (req, res) => {
req.session.nonce = generateNonce();
res.json({ nonce: req.session.nonce });
});
app.post('/auth/verify', async (req, res) => {
const { message, signature } = req.body;
const siweMessage = new SiweMessage(message);
try {
const fields = await siweMessage.verify({ signature });
if (fields.data.nonce !== req.session.nonce) {
return res.status(422).json({ error: 'Invalid nonce' });
}
// Authentication successful — create session
req.session.siwe = fields.data;
req.session.address = fields.data.address;
res.json({ ok: true, address: fields.data.address });
} catch (e) {
res.status(401).json({ error: 'Signature verification failed' });
}
});
// Frontend: Request signature (viem + wagmi)
import { useSignMessage } from 'wagmi';
import { SiweMessage } from 'siwe';
async function handleSignIn(address, chainId) {
const nonceRes = await fetch('/auth/nonce');
const { nonce } = await nonceRes.json();
const message = new SiweMessage({
domain: window.location.host,
address,
statement: 'Sign in with Ethereum to MyApp.',
uri: window.location.origin,
version: '1',
chainId,
nonce,
});
const signature = await signMessageAsync({
message: message.prepareMessage()
});
await fetch('/auth/verify', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ message: message.prepareMessage(), signature }),
});
}
Libraries and Frameworks
| Library | Ecosystem | Use Case |
|---|---|---|
| siwe (npm: siwe) | Node.js / Browser | Core SIWE message creation and verification — use in any framework |
| wagmi | React | React hooks for wallet connection and signing; integrates with SIWE |
| viem | TypeScript | Low-level Ethereum client; used by wagmi under the hood |
| NextAuth.js + SIWE | Next.js | SIWE as a NextAuth provider for session management |
| WalletConnect | Multi-platform | QR-code wallet connection for mobile wallets; supports SIWE signing |
| RainbowKit | React | Wallet connection UI component with built-in SIWE support |
SIWE in Enterprise Contexts
Security Considerations
SIWE is only as secure as its implementation. Critical requirements: always include the domain in the message and verify it server-side to prevent phishing (an attacker tricking a user into signing a message for a different domain); always generate server-side nonces and verify them are single-use to prevent replay attacks; set short expiry times on SIWE messages (5 minutes maximum for sign-in); and use HTTPS exclusively — SIWE over HTTP is vulnerable to MITM attacks that can substitute the message or intercept the signature.