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 at 18:03
up vote 3 down vote accepted

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.

EDIT

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 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 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 at 5:32

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

console.log(web3.version.api);
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);
console.log(web3.version);
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 acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

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