DEV Community

Cover image for Building a Web3 dApp authentication with React, Web3.js and Metamask
Ishan Manandhar
Ishan Manandhar

Posted on

Building a Web3 dApp authentication with React, Web3.js and Metamask

If you are reading this, I am sure you are passionate about the Web3 alike me. This article introduces a new login method to blockchain development: A one-click, cryptographically-secure login flow using the MetaMask extension, with all data stored on our own back end. It also keeps our data and cryptocurrency secure.

Here, we are going to build an authentication solution that allows a user to login with MetaMask.

What is Metamask?

MetaMask is a browser extension and app that describes itself as a crypto wallet and a gateway to blockchain apps. MetaMask is available as a browser extension that you can download and install and use it. MetaMask helps you manage your private key which controls your Ethereum address, and facilitates crypto transactions and interacting with blockchain apps.

Their public Ethereum address will be used as a unique identifier, and we will use the private key management tools that MetaMask exposes to provide a mechanism for a user to prove they own that particular address and thus have permission to login as that user.

Prerequisite

You need to first install the Metamask wallet Meta mask website in your browser. With this you will get access to a unique Ethtereum public address, with which you can start sending and receiving ether or tokens. But, we will keep this simple by just connecting to your metamask wallet and displaying the information.

Let's dive into our code

We will create a fresh react project and start with writing some functions we need to connect with Metamask wallet.

Let's start with detecting the current browser has window.ethereum API. MetaMask injects a global API into websites visited by its users at window.ethereum. This API allows websites to request users' Ethereum accounts, read data from blockchains the user is connected to, and suggest that the user sign messages and transactions.

window.ethereum
Enter fullscreen mode Exit fullscreen mode

This API allows websites to request user login, load data from blockchains the user has a connection to, and suggest the user sign messages and transactions. You can use this API to detect the user of a web3 browser.

window.web3
Enter fullscreen mode Exit fullscreen mode

window.web3 is injected by MetaMask only when viewing a page with http or https protocol. So, we will check this two API has been injected in the website.

  const detectCurrentProvider = () => {
    let provider;
    if (window.ethereum) {
      provider = window.ethereum;
    } else if (window.web3) {
      provider = window.web3.currentProvider;
    } else {
      console.log(
        'Non-Ethereum browser detected. You should consider trying MetaMask!'
      );
    }
    return provider;
  };
Enter fullscreen mode Exit fullscreen mode

With this function we will know whether we have the API available to us, if not we will just log the message for simplicity.

Let's install our Web3 package that we will be using. Web3.js is a JavaScript interface to the Ethereum blockchain.

-Get the latest block of the chain (web3.eth.getBlockNumber)
-Check the current active account on MetaMask (web3.eth.coinbase)
-Get the balance of any account (web3.eth.getBalance)
-Send transactions (web3.eth.sendTransaction)
-Sign messages with the private key of the current account (web3.personal.sign)

and many more

We will now create our next function which will communicate with the Web3.js library and get our required information.

  const onConnect = async () => {
    try {
      const currentProvider = detectCurrentProvider();
      if (currentProvider) {
        if (currentProvider !== window.ethereum) {
          console.log(
            'Non-Ethereum browser detected. You should consider trying MetaMask!'
          );
        }
        await currentProvider.request({ method: 'eth_requestAccounts' });
        const web3 = new Web3(currentProvider);
        const userAccount = await web3.eth.getAccounts();
        const chainId = await web3.eth.getChainId();
        const account = userAccount[0];
        let ethBalance = await web3.eth.getBalance(account); // Get wallet balance
        ethBalance = web3.utils.fromWei(ethBalance, 'ether'); //Convert balance to wei
        saveUserInfo(ethBalance, account, chainId);
        if (userAccount.length === 0) {
          console.log('Please connect to meta mask');
        }
      }
    } catch (err) {
      console.log(
        'There was an error fetching your accounts. Make sure your Ethereum client is configured correctly.'
      );
    }
  };
Enter fullscreen mode Exit fullscreen mode

In the code above we have an asynchronous function which will make a call to the Metamask and get the information we requested for. We want to get information about our account number, Chain ID, Account and ETH balance. You may also have noticed we really have not created a function to save our user information yet. Lets, create a function which saves the user account information to the localstorage so that we can persist and preserve the information unless disconnected.

  const saveUserInfo = (ethBalance, account, chainId) => {
    const userAccount = {
      account: account,
      balance: ethBalance,
      connectionid: chainId,
    };
    window.localStorage.setItem('userAccount', JSON.stringify(userAccount)); //user persisted data
    const userData = JSON.parse(localStorage.getItem('userAccount'));
    setUserInfo(userData);
    setIsConnected(true);
  };
Enter fullscreen mode Exit fullscreen mode

We create a function which takes the three parameters and saves the information to our localstorage so that we wont loose if user accidentally refreshed the browser. And, finally set our local state using useState inbuilt hook.

We are making a great progress as we wont loose the information even if we hit a refresh and this solves our data persistance temporarily. We now also need to add our functionality to remove the information stored from loacalstorage.

  const onDisconnect = () => {
    window.localStorage.removeItem('userAccount');
    setUserInfo({});
    setIsConnected(false);
  };
Enter fullscreen mode Exit fullscreen mode

In this above function we used the window global object supported in all the browsers today and removed the object stored inside it with key value stored. All the things are working fine but we have a problem.

We connect to Metamask and retrieve wallet information but if we refresh we will loose our information in the UI. In order to tackle this information we will use builtin react hook useEffect to check whether we have this object in the window global object or not.

  useEffect(() => {
    function checkConnectedWallet() {
      const userData = JSON.parse(localStorage.getItem('userAccount'));
      if (userData != null) {
        setUserInfo(userData);
        setIsConnected(true);
      }
    }
    checkConnectedWallet();
  }, []);
Enter fullscreen mode Exit fullscreen mode

Here we checked if we already have the key stored in localstorage and if yes we will set our local state and if we dont have this information we will return void.

We introduced in this article a one-click, cryptographically-secure login flow. We just scratched the surface there are more possibilities for us Web3 has to offer.

If you’re interested in what else MetaMask API can do, read the MetaMask docs page here.

Code repository link

Please feel free to comment and suggest code improvements.

Happy coding! 💻

Discussion (3)

Collapse
sulaimanu profile image
Sulaiman Abubakar

Please I want to know, which app should I used to edit or write the code?

Collapse
mberatsanli profile image
Melih • Edited on

You can use web storm or visual studio code
jetbrains.com/webstorm/
code.visualstudio.com/

Collapse
germavinsmoke profile image
GermaVinsmoke

In detectCurrentProvider function, why are we overriding the value given by window.ethereum with window.web3.currentProvider?