Sign in with Ethereum

SIWE User Authentication using an Ethereum Address

Moon allows an app to authenticate a user using SIWE (Sign in with Ethereum). The process to implement SIWE into a Moon app involves the following steps:

Step 1: Fetch a Nonce from Moon

The first step in the SIWE authentication process is to obtain a nonce by calling the getSIWENonce method from the MoonSDK class and passing the user's Ethereum address. Moon will then generate a nonce, associate it with the user's session, and return it.

What is a nonce?

A nonce is a random number that can be used just once in a cryptographic communication. It is used here to prevent replay attacks, where an attacker might try to re-send a previous request to gain unauthorized access.

Here is an example of how to retrieve a nonce from Moon using the MoonSDK class:

import { MoonSDK } from '@moonup/moon-sdk';

const sdk = new MoonSDK();

const nonce = await sdk.getSIWENonce(USER_ADDRESS_HERE);

Step 2: User Signs a Message

Once the nonce is fetched, the user is prompted to sign a message. This message includes the nonce and other details about the sign-in request, structured according to the SIWE format. This step is crucial for verifying the user's control over the Ethereum address without exposing any sensitive information like private keys.

Here is an example of how to have a user sign a message with the nonce using the signMessage function from @wagmi/core and the SiweMessage class from siwe:

import { signMessage } from '@wagmi/core';
import { SiweMessage } from 'siwe';

const message = new SiweMessage({
    domain: window.location.host,
    address: address,
    statement: 'Sign in with Ethereum to the app.',
    uri: window.location.origin,
    version: '1',
    chainId: 1,
    nonce: nonce,
});
const signedMessage = await signMessage({
    message: message.prepareMessage(),
});

How does this code prompt the user?

The above example uses the signMessage function to prompt the user to connect their wallet through their wallet connector (like MetaMask or Coinbase Wallet) and then have the user "sign the message" which logs them into the app!

Step 3: Verify the SIWE Signature

After obtaining the signed message, the next step is to verify the SIWE signature using the verifySIWESignature method from the MoonSDK class. This method takes the user's Ethereum address, the signed message, the nonce, and the message as parameters.

Here is an example of how to verify the SIWE signature using the MoonSDK class:

const siweResponse = await sdk.verifySIWESignature(userAddress, signedMessage, nonce, message);

Step 4: Handle Final Response from Moon’s Server

If a user has been successfully authorized, setting the access token on an active MoonSDK instance will finish authenticating the user!

if (siweResponse.ok) {
    // Verification was successful
    moon.setAccessToken(siweResponse.access_token, siweResponse.refresh_token);
    console.log('Verification successful!');
} else {
    // Verification failed
    console.error('Verification failed!');
}

Full code for example:

Put together, below is an entire code sample for how to authenticate a user using SIWE:

import { signMessage } from '@wagmi/core';
import { SiweMessage } from 'siwe';
import { MoonSDK } from '@moonup/moon-sdk';

const sdk = new MoonSDK();

const MoonSIWE = async (address) => {
    const userAddress = address ? address : '';

    try {
        // 1. Get a nonce from the server using MoonSDK
        const nonce = await sdk.getSIWENonce(userAddress);

        // 2. Ask the user to sign a message
        const message = new SiweMessage({
            domain: window.location.host,
            address: userAddress,
            statement: 'Sign in with Ethereum to the app.',
            uri: window.location.origin,
            version: '1',
            chainId: 1,
            nonce: nonce,
        });
        const signedMessage = await signMessage({
            message: message.prepareMessage(),
        });

        // 3. Verify the SIWE signature using MoonSDK
        const siweResponse = await sdk.verifySIWESignature(userAddress, signedMessage, nonce, message);

        // 4. Handle final response from Moon’s server
        if (siweResponse.ok) {
            // Verification was successful
            moon.setAccessToken(siweResponse.access_token, siweResponse.refresh_token);
            console.log('Verification successful!');
        } else {
            // Verification failed
            console.error('Verification failed!');
        }
    } catch (err) {
        console.error('An error occurred:', err);
    }
}

In this documentation page, the new methods getSIWENonce and verifySIWESignature from the MoonSDK class are used to handle the SIWE (Sign-In with Ethereum) flow. The getSIWENonce method retrieves a nonce from the server, and the verifySIWESignature method verifies the SIWE signature using the provided address, signed message, nonce, and message.

Last updated