# docs.login.xyz llms.txt > Offering resources and guidance for integrating Sign-In with Ethereum, enhancing user control over digital identities in web applications, while promoting best practices and supporting community involvement within the Ethereum ecosystem. > Updated at: 22:35 05/28/25 Sign-In with EthereumYour Keys, Your IdentifierNextQuickstart GuideLast updated 3 years ago Your Keys, Your Identifier Last updated 3 years ago Sign-In with Ethereum is a new form of authentication that enables users to control their digital identity with their Ethereum account and ENS profile instead of relying on a traditional intermediary. Already used throughout Web3, this effort standardizes the method with best practices and makes it easy to adopt securely. To hop right in, check out our Quickstart Guide. Sign-in With Ethereum was a standard built collaboratively with the greater Ethereum community. For more information on the EIP, check out the following page: For more information on Sign-In with Ethereum and its related benefits to both the Web3 ecosystem and Web2 services, check out the following page: đ» Login.xyz - Check out the Sign-In with Ethereum home page for more information about supporters, and recent activity. đŸ Discord - Join the #sign-in-with-ethereum channel in the Spruce Discord Server for additional support. đ Blog - Check out the latest updates on Sign-In with Ethereum posted on the Spruce blog. We host a Sign-In with Ethereum community where we discuss relevant updates, new libraries, additional integrations, and more. If you're interested in contributing to Sign-In with Ethereum, we encourage that you join the calls by filling in this form. # Quickstart Guide Sign-In with EthereumQuickstart GuideThis guide will show how to implement Sign-In with Ethereum (SIWE) in a client-server JavaScript web application.PreviousSign-In with EthereumNextCreating SIWE MessagesLast updated 3 years ago This guide will show how to implement Sign-In with Ethereum (SIWE) in a client-server JavaScript web application. Requirements An Ethereum account in the installed MetaMask wallet The repository for this tutorial can be found here: # Creating SIWE Messages Sign-In with EthereumQuickstart GuideCreating SIWE MessagesThis section describes how to generate Sign-In with Ethereum messages and print them to the console.PreviousQuickstart GuideNextImplement the FrontendLast updated 2 years ago This section describes how to generate Sign-In with Ethereum messages and print them to the console. Last updated 2 years ago A completed version of this part can be found in the example repository (). Creating SIWE messages in JavaScript is straightforward when using the siwe library in npm. To begin, create a new project called siwe-print. siwe siwe-print mkdir siwe-print && cd siwe-print/ yarn init --yes yarn add siwe ethers mkdir src/ We can then write the following into ./src/index.js: ./src/index.js const siwe = require('siwe'); const domain = "localhost"; const origin = "https://localhost/login"; function createSiweMessage (address, statement) { const siweMessage = new siwe.SiweMessage({ domain, address, statement, uri: origin, version: '1', chainId: '1' }); return siweMessage.prepareMessage(); } console.log(createSiweMessage( "0x6Ee9894c677EFa1c56392e5E7533DE76004C8D94", "This is a test statement." )); Now run the example: node src/index.js You should see output similar to the following message, with different values for the Nonce and Issued At fields: localhost wants you to sign in with your Ethereum account: 0x6Ee9894c677EFa1c56392e5E7533DE76004C8D94 This is a test statement. URI: https://localhost/login Version: 1 Chain ID: 1 Nonce: oNCEHm5jzQU2WvuBB Issued At: 2022-01-28T23:28:16.013Z To learn about all the available fields in a SiweMessage, check out the information in EIP-4361 SiweMessage The fields we are most interested in for the purposes of this guide are address and statement. address is the Ethereum address which the user is signing in with, and the statement as this will describe to the user what action we wish to perform on their behalf. address statement Often, as in this example, we don't need to do any manipulation of the message, so we can immediately convert it into the textual representation that the user will sign. 00_print # Implement the Backend Sign-In with EthereumQuickstart GuideImplement the BackendHere we learn how to build the backend server to handle the user's submission using Express.js.PreviousImplement the FrontendNextConnect the FrontendLast updated 2 years ago 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 (). 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: /nonce to generate the nonce for the interaction via GET request. /nonce GET /verify to verify the submitted SIWE message and signature via POST request. /verify POST While this simple example does not check the nonce during verification, all production implementations should, as demonstrated in the final section . 1. Setup the project directory: mkdir siwe-backend && cd siwe-backend/ yarn add cors express siwe ethers 2. Make sure that the package.json type is module like the following: package.json type module { "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: src/index.js 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 start In 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: 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"}' 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: verify 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. In other applications, you may wish to do further verification on other fields in the message, for example asserting that the authority matches the expected domain, or checking that the named address has the authority to access the named URI. A small example of this is shown later where the nonce attribute is used to track that a given address has signed the message given by the server. # Connect the Frontend Sign-In with EthereumQuickstart GuideConnect the FrontendHere we learn how to update the frontend to send the signed messages to the server.PreviousImplement the BackendNextImplement SessionsLast updated 2 years ago Here we learn how to update the frontend to send the signed messages to the server. A completed version of the updated frontend can be found here (). This example uses the to print messages, so it should be actively monitored. 1. Revisit the siwe-frontend directory, stop any running servers, and update src/index.js: siwe-frontend src/index.js: import { BrowserProvider } from 'ethers'; import { SiweMessage } from 'siwe'; const domain = window.location.host; const origin = window.location.origin; const provider = new BrowserProvider(window.ethereum); const BACKEND_ADDR = "http://localhost:3000"; async function createSiweMessage(address, statement) { const res = await fetch(`${BACKEND_ADDR}/nonce`); const message = new SiweMessage({ domain, address, statement, uri: origin, version: '1', chainId: '1', nonce: await res.text() }); return message.prepareMessage(); function connectWallet() { provider.send('eth_requestAccounts', []) .catch(() => console.log('user rejected request')); let message = null; let signature = null; async function signInWithEthereum() { const signer = await provider.getSigner(); message = await createSiweMessage( await signer.address, 'Sign in with Ethereum to the app.' ); console.log(message); signature = await signer.signMessage(message); console.log(signature); async function sendForVerification() { const res = await fetch(`${BACKEND_ADDR}/verify`, { method: "POST", headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ message, signature }), console.log(await res.text()); const connectWalletBtn = document.getElementById('connectWalletBtn'); const siweBtn = document.getElementById('siweBtn'); const verifyBtn = document.getElementById('verifyBtn'); connectWalletBtn.onclick = connectWallet; siweBtn.onclick = signInWithEthereum; verifyBtn.onclick = sendForVerification; 2. Update src/index.html: src/index.html