Sign In With Ethereum (SiWe)
Sign In With Ethereum (SiWe) describes the process for Ethereum accounts to authenticate with non-EVM systems. Completing an authentication attempt requires the user account to request a random challenge, sign that challenge, and then submit that challenge back to the server to validate the user owns the EOA they've presented to the mesh server
Externally Owned Accounts (EOA's)
- An EOA (Externally Owned Account) is your personal Ethereum wallet account, controlled by your private key. Think of it like your digital identity on Ethereum - it can hold funds and interact with blockchain applications. When you sign in with Ethereum, you're proving you own this account by signing a message with your private key.
Authentication Reference
The Mesh SDK authentication flow consists of four main functions that handle requesting challenges, signing messages, and managing authentication state using Sign in With Ethereum (SiWE).
SDK Methods
Function | Description |
---|---|
requestNonce | Requests a random challenge from the server to be signed by the user account |
invalidateNonce | Rotates the random challenge to expire the active challenge |
signMessageSiWe | Signs the SiWE message locally with the configured private key |
authenticateSiWe | Submits the signed SiWE message to the Mesh API gateway |
requestNonce
Requests a random challenge from the server that will be signed by the user's Ethereum account.
const response = await mesh.enforcer.requestNonce({
account_address: mesh.getSigner.account.address
});
Parameters
Parameter | Type | Description |
---|---|---|
account_address | string | The Ethereum address requesting authentication |
Response
{
"nonce": "string" // The random challenge to be signed
}
invalidateNonce
Rotates the random challenge, forcing users to sign a new nonce for authentication.
const response = await mesh.enforcer.invalidateNonce({
account_address: mesh.signer.account.address
});
Parameters
Parameter | Type | Description |
---|---|---|
account_address | string | The Ethereum address associated with the nonce to invalidate |
Response
{
"success": true // Indicates if nonce was successfully invalidated
}
signMessageSiWe
Signs the SiWE message locally using the configured private key.
const siweMessageParams = {
domain: 'localhost',
address: mesh.getSigner.account.address,
uri: 'http://0.0.0.0',
version: '1',
chainId: 1,
nonce: nonce,
issuedAt: new Date().toISOString(),
};
const { message, signature } = await mesh.signSiWeMessage(siweMessageParams);
Parameters
Parameter | Type | Description |
---|---|---|
domain | string | Domain requesting the signature |
address | string | User's Ethereum address |
uri | string | URI of the application |
version | '1' | SiWE version |
chainId | number | Chain ID of the network |
nonce | string | Challenge from requestNonce |
issuedAt | string | ISO timestamp |
Response
{
"message": "string", // Formatted SiWE message
"signature": "string" // Message signature
}
authenticateSiWe
Submits the signed SiWE message to the Mesh API gateway for authentication.
const response = await mesh.enforcer.authenticateSiWe({
message,
signature
});
Parameters
Parameter | Type | Description |
---|---|---|
message | string | Signed SiWE message |
signature | string | Message signature |
Authentication Example
Authentication Flow Steps
- Request a nonce challenge using
requestNonce
- Sign the challenge using
signMessageSiWe
- Submit signed message using
authenticateSiWe
- Optionally invalidate the nonce using
invalidateNonce
import { initializeMeshSDK, MeshSDKConfig } from "./util";
import { sepolia } from 'viem/chains';
// API KEY not required after SiWe session established
const API_KEY = "";
async function logResponse(method: string, response: any) {
console.log(`Method: ${method}`);
console.log(`Response Payload: ${JSON.stringify(response, null, 2)}\n`);
}
async function main() {
const config: MeshSDKConfig = {
apiKey: API_KEY,
chain: sepolia
};
try {
const mesh = await initializeMeshSDK(config);
let response: any = await mesh.enforcer.requestNonce({ account_address: mesh.getSigner.account.address });
await logResponse('requestNonce', response);
const nonce = response.nonce;
const siweMessageParams = {
domain: 'localhost',
address: mesh.getSigner.account.address,
uri: 'http://0.0.0.0',
version: '1' as '1',
chainId: 1,
nonce: nonce,
issuedAt: new Date().toISOString(),
};
const { message, signature } = await mesh.signSiWeMessage(siweMessageParams);
response = await mesh.enforcer.authenticateSiWe({ message, signature });
await logResponse('authorizeSiWe', response.token);
// Invalidate the challenge so that the same challenge cannot be used again
response = await mesh.enforcer.invalidateNonce({ account_address: mesh.signer.account.address });
await logResponse('invalidateNonce', response);
} catch (error) {
console.error("An error occurred:", error);
}
}
main().catch((error) => {
console.error("Unhandled error in main function:", error);
process.exit(1);
});
Important Notes
- API key is not required once a SiWE session is established
- Nonces should be invalidated after use to prevent replay attacks
- Authentication tokens should be stored securely
- Implement proper error handling around each authentication step
Updated 6 days ago