I'm making a dapp that will make calls to a Node.js server. I expect the user to have MetaMask installed and I want to verify that he is the real owner of the current address on MetaMask (i.e. accounts[0]).

This is the user flow I am trying to implement:

  1. User loads up my dapp frontend in his browser.
  2. The browser gets accounts[0] from Web3/MetaMask.
  3. The frontend requests some data specific to accounts[0] from my Node.js API.
  4. On the Node.js server, I need to verify that the request is coming from someone who actually owns the private keys to the address accounts[0]. If this is valid, then I respond with the specific data.

I spent a long time looking at different signing functions with Web3, and I ended up very confused. There is:

  • web3.eth.sign - there's no recover counterpart to this and MetaMask doesn't pop up asking the user to sign something.
  • web3.eth.personal.sign - this one requires a password, I don't want to ask the user to enter his password, shouldn't MetaMask do this?
  • web3.eth.accounts.sign - this seems more like a hashing function than what I need.

I have a feeling that none of the three above functions are what I need. Can someone provide some guidance on how to approach this?

  • I think the solution isn't working anymore. At least I don't get the right public address. Could you please help me finding out, how it has to be done? – Florian Pircher Jul 19 '18 at 18:03

I think web3.eth.sign is what you want, but note that it expects a 32-byte string (usually a hash of a message).

This worked for me:

web3.eth.sign(web3.eth.defaultAccount, web3.sha3('test'), function (err, signature) {
  console.log(signature);  // But maybe do some error checking. :-)

Then on the server, using ethereumjs-util:

const util = require('ethereumjs-util');
const sig = util.fromRpcSig('<signature from front end>');
const publicKey = util.ecrecover(util.sha3('test'), sig.v, sig.r, sig.s);
const address = util.pubToAddress(publicKey).toString('hex');

You said "MetaMask doesn't pop up asking the user to sign something," but it's supposed to. If it's still not working, please share the code you're using to call web3.eth.sign.


Note that in web3.js 1.0, it's web3.util.sha3 instead of web3.sha3.

  • Thanks, I'm going to try this out. If it works, I'll accept your answer. – adrianmc Jan 8 '18 at 4:58
  • this was very helpful, thank you. I had to make two changes to make it work though (as I am using Web3 1.0): (1) web3.utils.sha3 instead of web3.sha3, (2) fromRpcSig(), I needed to pass in the whole thing with the leading 0x. If you add this in an edit, I will accept your answer. – adrianmc Jan 8 '18 at 5:28
  • 1
    Thanks, the leading 0x is indeed needed (regardless of which web3.js version you're using). I edited the answer. – smarx Jan 8 '18 at 5:32

The MetaMask's own web3, still 0.2 asof May-2018, sign syntax is --

web3.personal.sign(web3.toHex("message to sign"), accounts[0], 
                   function(err, res) {
    // whatever

If you replace MetaMask's web3 with web3.js 1.0.0 (beta), the syntax to use is --

window.web3 = new Web3(web3.currentProvider);
web3.eth.personal.sign('message to sign', accounts[0])
.then(signature => {
    // whatever

In both cases, MetaMask will popup the sign notification

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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