0

Context:

My dapp has 3 components: frontend (React), backend (Solidity) and storage (Node+MongoDb). The storage is needed to store info that will be used in the future.

Frontend-Storage communication needs some sort of authentication to make sure that User 0xABC is adding/modifying data that belongs to user 0xABC.

Question:

Is it possible to use the fact that the user wallet is connected with the app to generate an authentication header that will be sent with all requests and verified by the storage?

Bonus points: with ether.js? :D

2
  • hi did you work this out ? I have got so far as getting the signed message client side, but not sure how verify storage side Mar 21, 2022 at 6:35
  • @NimChimpsky see the answer. Feel free to ask more. Mar 21, 2022 at 15:32

1 Answer 1

1

Backend

In the backend (in my case it's called storage), I've added the following endpoints:

  • /message-to-sign/:userAddress -> creates a message that the user will sign with their Metamask, containing the user address and an OTP to prevent replay attacks. When the OTP is generated, we save it locally, so when login is called, we verify the OTP for that address.
    const message = composeMessage(userAddress, otp);
    return res.json({ message });
  • /login -> The signed message is sent over and verified. If it matches, the user is authenticated.
    const message = composeMessage(userAddress, otp);
    const decodedAddress = utils.verifyMessage(message, signedMessage);

    if (userAddress !== decodedAddress) {
        return res.status(403).send("Invalid signature");
    }

    // generate JWT and send back
    const authToken = jwt.sign({ userAddress }, process.env.JWT_SECRET as string, { expiresIn: '2592000s' });
    res.cookie('token', authToken, { httpOnly: true, secure: true });
    return res.send();

Frontend

In the frontend we

  1. get the message to be signed
  2. get the user to sign it with their wallet
  3. send back the signed message
  4. if the request works, the user will be logged in

async function triggerLogin(walletAddress: string): Promise<void> {
    const message = await getLoginMessage(walletAddress); // call /message-to-sign/:userAddress
    const signedMessage = await signMessage(message);
    await login(walletAddress, signedMessage); // call /login
}

async function signMessage(message: string): Promise<any> {
    const ethers = require('ethers');
    const provider = new ethers.providers.Web3Provider((window as any).ethereum);
    const signer = provider.getSigner();
    const signature = await signer.signMessage(message);
    return signature;
}

I've taken inspiration from debank.com to create the login system.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.