Implement the Backend
Here we learn how to build the backend server to handle the user's submission using Express.js.
A completed version of this part can be found here (02_backend). This example uses only uses the command line in the terminal to print messages, no monitoring of the browser console log is necessary.
The backend server gives the frontend a nonce to include in the SIWE message and also verifies the submission. As such, this basic example only provides two corresponding endpoints:
/nonceto generate the nonce for the interaction viaGETrequest./verifyto verify the submitted SIWE message and signature viaPOSTrequest.
While this simple example does not check the nonce during verification, all production implementations should, as demonstrated in the final section Using Sessions.
1. Setup the project directory:
mkdir siwe-backend && cd siwe-backend/
yarn init --yes
mkdir src/
yarn add cors express siwe ethers2. Make sure that the package.json type is module like the following:
{
"name": "backend",
"version": "1.0.0",
"main": "index.js",
"type": "module",
"license": "MIT",
"scripts": {
"start": "node src/index.js"
},
"dependencies": {
"siwe": "^2.1.4",
"cors": "^2.8.5",
"ethers": "^6.3.0",
"express": "^4.18.2"
}
}3. Populate src/index.js with the following:
import cors from 'cors';
import express from 'express';
import { generateNonce, SiweMessage } from 'siwe';
const app = express();
app.use(express.json());
app.use(cors());
app.get('/nonce', function (_, res) {
res.setHeader('Content-Type', 'text/plain');
res.send(generateNonce());
});
app.post('/verify', async function (req, res) {
const { message, signature } = req.body;
const siweMessage = new SiweMessage(message);
try {
await siweMessage.verify({ signature });
res.send(true);
} catch {
res.send(false);
}
});
app.listen(3000);4. You can run the server with the following command.
yarn startIn a new terminal window, test the /nonce endpoint to make sure the backend is working:
curl 'http://localhost:3000/nonce'In the same new terminal window, test the /verify endpoint use the following, and ensure the response is true:
curl 'http://localhost:3000/verify' \
-H 'Content-Type: application/json' \
--data-raw '{"message":"localhost:8080 wants you to sign in with your Ethereum account:\n0x9D85ca56217D2bb651b00f15e694EB7E713637D4\n\nSign in with Ethereum to the app.\n\nURI: http://localhost:8080\nVersion: 1\nChain ID: 1\nNonce: spAsCWHwxsQzLcMzi\nIssued At: 2022-01-29T03:22:26.716Z","signature":"0xe117ad63b517e7b6823e472bf42691c28a4663801c6ad37f7249a1fe56aa54b35bfce93b1e9fa82da7d55bbf0d75ca497843b0702b9dfb7ca9d9c6edb25574c51c"}'Note on Verifying Messages
We can verify the received SIWE message by parsing it back into a SiweMessage object (the constructor handles this), assigning the received signature to it and calling the verify method:
message.verify({ signature })message.verify({ signature }) in the above snippet makes sure that the given signature is correct for the message, ensuring that the Ethereum address within the message produced the matching signature.
Last updated
