y-pakorn / flutter_web3

Web3 Ethereum, Etherjs and Wallet Connect wrapper for Flutter Web.
Other
134 stars 47 forks source link

Sign and verify personal message via Metamask #17

Closed cryptobys-rami closed 2 years ago

cryptobys-rami commented 2 years ago

First of all thanks v much for this library!

Is your feature request related to a problem? Please describe. Feature request to help verifying that a user owns a certain wallet

Describe the solution you'd like I would like to know if its possible to sign a Metamask personal message which can then be verified server side in order to link off chain user accounts with Wallets. Ideally i would like it to make a signature which does not trigger the Metamask 'card blanche' warning below.

ED323FE7-030F-42F4-A9E3-E45510B4DA3A_4_5005_c

y-pakorn commented 2 years ago

Thank you!

If you’re referring to manually signing the message, the package already has the functionality to do that.

Please look for the signMessage method on Signer class. (Accessible by using ‘getSigner’ in Provider class.) And to verify the message, please use EthUtils.verifyMessage method.

About the warning message, I'm pretty sure It's from the wallet part, and it would be impossible to suppress that from the package.

I hope you understand the risk of manually signing the message as well.

This is a simple example of how to implement that.

final sig = await provider!.getSigner().signMessage('hehe');
final acc = EthUtils.verifyMessage('hehe', sig);
print(acc);
cryptobys-rami commented 2 years ago

Thank you very much for your prompt response.

I am referring to the 'personal.sign' spec (https://github.com/ethereum/go-ethereum/pull/2940), an example of which is here: https://medium.com/hackernoon/writing-for-blockchain-wallet-signature-request-messages-6ede721160d5

where this web3 code snippet:

handleSignMessage = ({ publicAddress, nonce }) => {
    return new Promise((resolve, reject) =>
      web3.personal.sign(
        web3.fromUtf8(`I am signing my one-time nonce: ${nonce}`),
        publicAddress,
        (err, signature) => {
          if (err) return reject(err);
          return resolve({ publicAddress, signature });
        }
      )
    );
  };

Seems to result in a warning-free Metamask notification - which makes sense because it is prefixed with some text that makes it impossible to sign transactions with the same signature.

The purpose of personal sign is to avoid open ended signatures. Reference below from Metamask docs:

https://docs.metamask.io/guide/signing-data.html#a-brief-history

In particular, the method eth_sign is an open-ended signing method that allows signing an arbitrary hash, which means it can be used to sign transactions, or any other data, making it a dangerous phishing risk.

For this reason, we make this method show the most frightening possible message to the user, and generally discourage using this method in production. However, some applications (usually admin panels internal to teams) use this method for the sake of its ease of use, and so we have continued to support it for the sake of not breaking the workflows of active projects.

Eventually, the personal_sign spec (opens new window)was proposed, which added a prefix to the data so it could not impersonate transactions. We also made this method able to display human readable text when UTF-8 encoded, making it a popular choice for site logins.

This would be very useful as it would allow users to verify their wallet with a site, with minimal security risk, and no need for smart contract interaction (which costs money).

Thanks very much.

y-pakorn commented 2 years ago

Yes, signMessage in ethers is prob personal_sign in ethereum. I tested and there's no warning.

If you want to sign a transaction, use signTransaction instead.

cryptobys-rami commented 2 years ago

Image 02-11-2021 at 12 08

I can confirm this works! Nice work.