MPC Snap: Integrating Multi-Factor Authentication Into MetaMask
A Sozu House Hackathon Snap built in Bogotá
MetaMask Snaps is the roadmap to making MetaMask the most extensible wallet in the world. As a developer, you can bring your features and APIs to MetaMask in totally new ways. Web3 developers are the core of this growth and this series aims to showcase the novel MetaMask Snaps being built today.
Snap Repo: https://github.com/Web3Auth/openlogin-snap/
Why did you build it?
At Web3Auth, we’ve always believed that multi-factor authentication is the best way to secure crypto assets, and is much better than seed phrases. When we saw at ETHBogota that the latest build of MetaMask Snaps allowed MetaMask Snaps to provide external key management capabilities, we decided to challenge ourselves to integrate MPC tech into MetaMask at the Sozu House hackathon.
Can you walk us through the technical implementation?
Our goal was to allow a new user to login and set up 2FA to access their account in MetaMask. Subsequently, under the hood, whenever they try to sign a transaction, our MPC SDK does a threshold ECDSA signature. This is done by splitting a private key into two parts: a local share that’s stored in the snap and one that’s stored on a signing server. Over several communication rounds, the signing server and the snap should be able to jointly sign an Ethereum transaction and have it be confirmed on the Goerli network. Unlike seed phrases, this setup does not have a single point of failure that leads to irreversible loss of keys: if the user’s laptop is hacked, or if the signing server is hacked, the user does not lose their private key.
Like every other snap, our snap consists of two parts, the actual snap package, and a page that loads / interacts with the snap. Our ideal user flow was to keep all key management interactions within the Metamask extension’s UI, but due to limitations in permissions we could not do that. Instead, the user authentication happens outside of the snap, and then once that is complete, we migrate the state into the snap. Subsequently, we hook into the RPC engine of Metamask and respond to transaction requests by using our MPC SDK and the migrated state in the snap to generate threshold ECDSA signatures on the transactions. Surprisingly, we were done with the state migration part within the first few hours of the hackathon and were just left with the signing functionality.
The rest of the process can be further split into two major tasks:
Repurposing existing Web3 providers with our MPC SDK to handle the incoming transaction requests
Getting the MPC SDKs running in the MetaMask Snaps SES environment
If you’re familiar with how MetaMask works, or how other wallets function, you’ll know that signing an Ethereum transaction requires much more work than just using a private key to sign a message. A lot of work goes into forming the message object that is to be signed. Each transaction follows a particular format that’s specific to each chain and transaction type, has legacy behaviors that need to be supported for backward compatibility, has to get information about the current gas prices, has to calculate the gas to be used / gas limits, and also has to track the account nonce, etc. Thankfully, most existing Web3 providers already do these things out of the box. However, in order to get our threshold signature library to work well with these SDKs we had to dig deep into every single module and replace the eth-sig-util library’s signing functions with our own threshold signature library signing functions, which took a significant amount of time. In addition, because our MPC SDK was chain-agnostic, and didn’t account for Ethereum specific checks, we also had to reverse engineer and reimplement checks from the eth-sig-util libraries which we had replaced. For example, EIP-2 specifies that the s-value in an ECDSA signature must not be larger than n/2 + 1, where n is the order of the secp256k1 elliptic curve, in order to prevent transaction malleability attacks, which requires ECDSA signing libraries to do a “flip” of the signature if its s-value falls outside of this range.
Another challenge we faced was getting our MPC SDKs to run in the MetaMask Snaps environment. Our MPC SDK uses wasm (compiled from rust) to run efficiently in the browser. Due to the multiple rounds of communication between the SDK and the signing server, it also uses Socket.IO to communicate to avoid the overhead of HTTP headers. While attempting to load our wasm code into the snap, we ran into some strange issues with mixed argument types in the wasm methods. Unfortunately, wasm debugging tools are still very lacking, with error messages being unhelpful and uninformative (eg. “Uncaught RuntimeError: unreachable”). In the end, we hacked our way around this limitation by accepting all mixed inputs arguments as strings, and parsing them as integers / byte arrays within the wasm code itself. Another problem we encountered was that socket.io did not work in the MetaMask Snaps environment. Socket.IO is a separate library / specification built on top of normal WebSockets that handles things like connection upgrading, dropped connections, fallbacks to long polling, etc. which are very helpful when using WebSockets in production environments. Unfortunately, we could not get Socket.IO’s libraries to run in the Metamask Snap environment. As such, we had to fallback to normal WebSockets, and also rewrite the signing server to use default WebSockets instead of socket.io, and fallback to a polling strategy if the websocket had issues.
Can you tell us a little bit about yourself and your team?
Zhen and I are the cofounders of Web3Auth, and our company specializes in providing intuitive, secure, and non-custodial key management solutions. We believe that good security must be easy to use and invisible, or it will not get adopted widely. That is why we are huge proponents of using multi-factor authentication for private keys.
When were you first introduced to MetaMask Snaps and what was your experience like?
We tried Metamask Snaps about a year ago, but unfortunately, it didn’t have the capabilities to allow us to extend the private key management functions within MetaMask back then. With the introduction of SIP-2 recently (thank you Olaf!), which allows MetaMask Snaps to extend MetaMask’s keyring, we’ve been very excited to see what’s possible! While building our snap, I also explored the SES system that MetaMask Snaps is based on, and I think it’s a very solid foundation for MetaMask to act as a platform for developers to add functionality through MetaMask Snaps.
What makes MetaMask Snaps different from other wallets?
The extensibility! I think MetaMask Snaps provides a secure interface for developers to experiment and augment MetaMask without compromising the security of the MetaMask extension itself. Unlike other types of projects, wallets require high standards of security, which means merging in new functionality from third-parties is always a long, arduous process of code review and audits, which stifles innovation. MetaMask Snaps allows the MetaMask team to continue building a secure product, while still allowing external developers to experiment and extend its functionality.
Tell us about what building Snaps with MetaMask is like for you and your team?
I think MetaMask Snaps is definitely the way forward, allowing people to build functionality while still keeping the base MetaMask extension secure. There are a few kinks now but improvements are coming every day but I think it’s very exciting overall.
What does MetaMask Snaps mean to you?
I hope that adding multi-factor authentication capabilities to MetaMask through MetaMask Snaps helps the millions of mainstream users on MetaMask to more securely manage their accounts and prevents them from falling victim to scams and hacks. MFA is already doing this today for billions of Web2 users, and we want to bring those secure best practices to Web3.
What opportunities do you see with MetaMask Snaps and the possibilities it unlocks for the Web3 space?
In general, I think MetaMask Snaps represents a realistic way for MetaMask to open up its codebase for others to contribute to it without compromising security.
Any advice you would like to share with developers keen to try MetaMask Snaps?
Just try it! It may seem a little new but it’s been very approachable so far and the support for it is great on the ConsenSys discord channel for MetaMask Snaps. Lots of amazing devs are working on it and if you want to be part of the conversation in shaping MetaMask Snaps, now is the time to be building on it and giving feedback!
Building with MetaMask Snaps
To get started with MetaMask Snaps:
- Checkout the developer docs
- Install MetaMask Flask
- Check out a MetaMask Snaps guide
- Stay connected with us on Twitter, GitHub discussions, and Discord
Keep an eye out for our team at the next hackathon in an area near you! Happy BUIDLing ⚒️
Disclaimer: MetaMask Snaps are generally developed by third parties other the ConsenSys Software. Use of third-party-developed MetaMask Snaps is done at your own discretion and risk and with agreement that you will solely be responsible for any loss or damage that results from such activities. ConsenSys makes no express or implied warranty, whether oral or written, regarding any third-party-developed MetaMask Snaps and disclaims all liability for third-party developed MetaMask Snaps. Use of blockchain-related software carries risks, and you assume them in full when using MetaMask Snaps.
Keep reading our latest stories
releases, security news, and more