This guide explores how to implement 'Sign-In with Ethereum' using the ERC-4361 specification, which enables Ethereum account authentication for off-chain services through a standardized message format.
This approach offers a self-custodial alternative to traditional centralized identity providers that rely on email and password credentials. For Web3 applications, it enhances user experiences and empowers individuals with greater control over their digital identities.
How Sign In With Ethereum Works
The authentication process follows a specific sequence to ensure security and reliability.
The Authentication Flow
- Message Presentation: The wallet displays a plaintext message to the user for signing. This message contains crucial details, including the user's Ethereum address, the domain requesting the signature, a chain identifier, a nonce for replay protection, and an issued-at timestamp.
- User Signature: The user signs this message using the ERC-191 signed data standard, creating a cryptographic signature that proves ownership of the address without exposing their private key.
- Signature Verification: The relying party (e.g., a website or application backend) receives the signature and verifies its authenticity against the original message content.
- Optional Data: Additional fields, such as an expiration time or specific resources being requested, can be incorporated to add more context and security to the sign-in request.
- Data Fetching: Upon successful verification, the service may fetch additional on-chain data associated with the Ethereum address, such as token balances or NFT ownership, to personalize the user experience.
Building a Sign-In Demo
Let's examine the core components required to create a functional 'Sign-In with Ethereum' demo. It is critical to only use test wallets without real funds when experimenting.
Constructing the Sign-In Message
The message follows a structured, human-readable format. This clarity is a key security feature, as it allows users to see exactly what they are approving.
${domain} wants you to sign in with your Ethereum account:
${address}
${statement}
URI: ${uri}
Version: ${version}
Chain ID: ${chainId}
Nonce: ${nonce}
Issued At: ${issuedAt}
Expiration Time: ${expirationTime}
Not Before: ${notBefore}
Request ID: ${requestId}
Resources:
- ${resources[0]}
- ${resources[1]}Implementing the Client-Side Code
Using a library like ethers.js, you can request a connection and a signature from the user's wallet.
const provider = new ethers.providers.Web3Provider(window.ethereum,'any');
const connectAndSign = async () => {
await provider.send("eth_requestAccounts", []);
const signer = provider.getSigner()
const domain = 'example.com';
const userAddress = await signer.getAddress();
const statement = 'I accept the terms and wish to sign in with Ethereum';
const uri = 'https://example.com/login';
const version = 1;
const chainId = 1;
const nonce = Math.round(Math.random() * 9999999);
const issuedAt = Math.floor(Date.now() / 1000);
const message = `${domain} wants you to sign in with your Ethereum account:
${userAddress}
${statement}
URI: ${uri}
Version: ${version}
Chain ID: ${chainId}
Nonce: ${nonce}
Issued At: ${issuedAt}`;
let flatSignature = await signer.signMessage(message);
let soliditySignature = ethers.utils.splitSignature(flatSignature);
// Send the signature to your backend for verification
}This code produces two signature formats: a flat string (flatSignature) and a structured object (soliditySignature) containing the r, s, and v components, which are useful for on-chain verification.
Verifying the Signature
After obtaining a signature, the relying party must verify it to authenticate the user. This can be done on a backend server or on-chain via a smart contract.
Backend Verification
A centralized server can verify the signature using cryptographic libraries. This method is common but introduces a single point of trust. Developers can use available web3 libraries in languages like JavaScript or Python to recover the signer's address from the signature and confirm it matches the claimed address.
๐ Explore more strategies for backend authentication
On-Chain Smart Contract Verification
A more decentralized approach uses a smart contract to handle verification. This removes the need to trust a central server.
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
contract SignatureVerifier {
function verifySignature(bytes32 _hashedMessage, uint8 _v, bytes32 _r, bytes32 _s) public pure returns (address) {
bytes memory prefix = "\x19Ethereum Signed Message:\n32";
bytes32 prefixedHashMessage = keccak256(abi.encodePacked(prefix, _hashedMessage));
address signer = ecrecover(prefixedHashMessage, _v, _r, _s);
return signer;
}
}The contract's verifySignature function uses the ecrecover opcode to return the address that signed the original message. The service can then compare this returned address to the one provided by the user.
Advantages and Considerations
Like any technology, 'Sign-In with Ethereum' presents a mix of benefits and challenges that developers and users should understand.
Key Advantages
- User Control: It empowers users with true self-sovereign identity, removing reliance on centralized platforms that can deactivate accounts or suffer data breaches.
- Enhanced Security: Cryptographic signatures provide a robust security model, proven by securing billions of dollars in digital assets on the blockchain.
- Standardization: The ERC-4361 specification creates a consistent, interoperable standard that improves user experience across different wallets and services.
- Simplified Experience: For users, it can mean fewer passwords to remember and a more seamless flow between Web3 applications.
Important Considerations
- Technical Complexity: Implementing secure and correct signature verification requires a solid understanding of cryptography and smart contract development.
- Adoption Hurdles: As a newer standard, widespread support is still growing, and it is primarily used within the Web3 ecosystem.
- User Responsibility: The self-custody model means users are solely responsible for safeguarding their private keys. Lost keys equate to lost access with no centralized recovery option.
- Phishing Risks: Users must remain vigilant and carefully check every message they sign, as malicious sites can create deceptive signing requests.
Frequently Asked Questions
What is the ERC-4361 specification?
ERC-4361, also known as 'Sign-In with Ethereum,' is a standard that defines a secure, self-custodial method for users to authenticate into web services using their Ethereum wallet and a cryptographic signature instead of a traditional username and password.
Is signing a login message risky?
Signing a well-formatted, plaintext message as defined by ERC-4361 is generally considered low-risk because it does not grant spending permissions for your assets. However, you should always scrutinize the message content and only sign requests from trusted websites. Never use a wallet containing significant funds for testing.
Can I use this with any Ethereum wallet?
Most modern Web3 wallets like MetaMask, WalletConnect, and others support the message signing functionality required for ERC-4361. The wallet's role is to present the message clearly to the user and generate the signature.
How do I handle user sessions after they sign in?
After successful signature verification, your backend server typically generates a traditional session token (like a JWT) or a cookie. This token is then used to manage the user's authenticated session, just like in a conventional web application.
What is the purpose of the nonce in the message?
The nonce is a random value included in the signing request to prevent replay attacks. It ensures that a captured signature cannot be reused maliciously at a later time to impersonate the user.
Where can I find verified libraries to implement this?
It is best practice to use well-audited, community-tested libraries. The Ethereum ecosystem offers resources for various programming languages to help developers implement both the message generation and signature verification steps correctly. ๐ Get advanced methods for secure implementation