Skip to main content

Web3Auth Single Factor Auth without Redirect

web@web3auth/single-factor-auth@web3auth/no-modalcustom authenticationreactethereumWeb3Auth Team | October 7, 2022

Introduction

Adding Web3Auth Plug and Play SDKs to your application is a great way to authenticate users and reconstruct their private key, and to reconstruct the key, Web3Auth SDK redirects users to a Web3Auth hosted screen (i.e. http://app.openlogin.com). This flow is great for most use cases. Still, sometimes you may want to customize the authentication flow such that you can have more control over the UI and UX of the authentication process (i.e. to avoid the redirection to openlogin hosted screens). This guide will show you how to use one such flow to authenticate users without redirecting to a Web3Auth-hosted screen.

So before we get started, let's look at the general authentication flow in web3auth.

When a user logs in with Web3Auth, a user's account can be in two states:

  • Account Without MFA: When the User logs in with only social login and key is secured by web3auth network.
  • Account With MFA: When the User enables MFA by adding other shares like password, backup share, device share, etc., to the existing account.

By default, to reconstruct the key in both states, web3auth SDK redirects the user to http://app.openlogin.com. Where all the computation to reconstruct the key is done. The advantage of this approach is that it makes it easy for applications to integrate web3auth SDK without having to worry about the key reconstruction process. But sometimes, applications want to reconstruct the key in their application context, where the authentication flow described in this guide comes in.

Currently, there are two ways to achieve this:

  • First is using the Core Kit approach where the application itself handles both states of the user's accounts.
  • Second approach is a hybrid approach where the application handles the user's account state without MFA, and the account state with MFA is handled by web3auth SDK by redirecting to openlogin.

In this guide, we will be looking at the second approach. If you are curious about the first approach, you can look at our Core Kit docs here.

note

We will refer this approach as Single Factor Auth in the upcoming sections.

Live Demo: https://w3a.link/sfa-example

Quick Start

If you want to check what the result of this guide will look like, you can clone the example project from our GitHub repo and run it as described below.

npx degit Web3Auth/web3auth-core-kit-examples/single-factor-auth-web/react-evm-sfa-example w3a-sfa-react-demo && cd w3a-sfa-react-demo && npm install && npm run start

How it works?

Web3Auth Core - Single Factor Auth

Refer to the flow chart above to understand how the Single Factor Auth works:

  • The first step for the application is to get the user authenticated by social login and get the user's idToken.
  • Once the social login is successful, depending on if the user has enabled Multi Factor Authentication(MFA) or not, the key is generated.
  • If the user has not enabled MFA, we use the @web3auth/single-factor-auth sdk to retrieve the private key shares from the Web3Auth auth network nodes and reconstruct the private key.
  • If the user has enabled MFA, the private key can be reconstructed simply via Web3Auth Login using Web3Auth SDK by redirecting the user to app.openlogin.com.

Also, it is up to the application whether they want to give the option to enable MFA in the application. If you don't want to provide the option to enable MFA in your application. You can skip the parts of this guide where we explain enabling MFA in the application. But the good part is that this is future compatible. If you want to enable MFA in your application in the future, you can do it easily by adding a few lines of code.

Prerequisites

  • You should know how Web3Auth works. You can read about it here.
  • You should know how to create a basic application on Web3Auth by following this quck-start.
  • We will be using react.js in this app, so basic knowledge of react is expected to follow the guide.
  • A Firebase account to be used as Federated Identity Provider.
  • A Google Developer account to be used as Identity provider for Firebase.

Setup

Setup your JWT provider

  • Single Factor Auth will work with any JWT provider. For this particular guide we will use Firebase as our JWT provider.
  • You can follow the Firebase guide to setup Firebase.

Setup your Web3Auth Dashboard

  • Create a Project from the Project Section of the Web3Auth Developer Dashboard.

    Plug n Play Project Creation on Web3Auth Dashboard

    • Enter your desired Project name.

    • Select the Product you want to use. For this guide, we'll be using the Plug n Play product.

    • Select the Platform type you want to use. For this guide, we'll be using the Web Application as the platform.

    • Select the Web3Auth Network as Sapphire Devnet. We recommend creating a project in the sapphire_devnet or tesnet network during development. While moving to a production environment, make sure to convert your project to sapphire_mainnet or any of the legacy mainnet network mainnet, aqua, or cyan network. Otherwise, you'll end up losing users and keys.

    • Select the blockchain(s) you'll be building this project on. For interoperability with Torus Wallets, you have the option of allowing the user's private key to be used in other applications using Torus Wallets (EVM, Solana, XRPL & Casper).

    • Finally, once you create the project, you have the option to whitelist your URLs for the project. Please whitelist the domains where your project will be hosted.

      Plug n Play Project - Whitelist

Below are the steps to setup Firebase as JWT Provider. The same steps can be followed for any other JWT provider.

  • Create a Verifier from the Custom Auth Section of the Web3Auth Developer Dashboard with following configuration:

    • Choose a name of your choice for the verifier identifier. eg. w3a-firebase-verifier
    • Select environment: testnet, mainnet, aqua, or cyan as per your requirement.
    • Select Custom from the Login Provider.
    • Select Sub for the JWT Verifier ID.
    • Enter https://www.googleapis.com/service_accounts/v1/jwk/securetoken@system.gserviceaccount.com as the JWK endpoint for Firebase's idToken.
    • JWT validation fields:
      • iss: https://securetoken.google.com/<firebase-project-id>.
      • aud: <firebase-project-id>
    • Click on Create button to create your verifier. It may take up to 10 minutes to deploy verifier on testnet. You'll receive an email once it's complete.

    Custom Authentication - Create Firebase Verifier

  • You will require the verifierName of the newly created verifier and clientId of the Plug and Play Project.

Setting up react project.

  • You can simply setup react project using create-react-app.

    Webpack 5 polyfill issues

    If you are using create-react-app version >= 5 you may run into issues building. Those issues can be resolved by following the troubleshooting guide given here

Dependencies

You will need to install the following dependencies in order to get started with the project. If you only want to add login without mfa flow to your application you can skip installation of dependencies required for login with mfa.

Dependencies Required for login without mfa:-

@web3auth/node-sdk

This is our new node sdk which will help you to integrate login without mfa flow in your application.

npm install --save @web3auth/node-sdk

Dependencies Required only login with mfa:-

@web3auth/no-modal

This is the main Core package that contains the Web3Auth SDK.

npm install --save @web3auth/no-modal
@web3auth/openlogin-adapter

For using Custom Authentication, we need to use the OpenLogin Adapter, where we can initialise the authentication details.

npm install --save @web3auth/openlogin-adapter
@web3auth/base

Since we're using typescript, we need the @web3auth/base package to provide types of the different variables we'll be using throughout the app building process. This reduces errors to a very large extent.

npm install --save @web3auth/base

Web3 Libraries to interact with the blockchain

web3 / ethers

According to your preference, you can choose to install the web3 or ethers libraries, to talk to the EVM compatible blockchains under the hood. For Solana, @solana/web3.js is the recommended choice.

We'll be having code snippets from web3 and ethers in the documentation, so you can choose the one that suits you the best.

npm install --save web3

Implementing User Login.

Doing user login with Single Factor Auth consists of following steps:

  • Authenticate user with social login provider.
  • Checking if user's mfa is enabled.
  • If mfa is not enabled, then login with @web3auth/single-factor-auth sdk.
  • If mfa is enabled, then login with @web3auth/no-modal sdk.

Authenticate user with social login provider.

In this guide, we are using firebase to authenticate the user's Identity and get the idToken. You can use any other social login provider as well.

Following is the code snippet to authenticate a user with firebase and get the idToken.

// Import the functions you need from the SDK
import { initializeApp } from "firebase/app";

// Your web app's Firebase configuration
const firebaseConfig = {
apiKey: "AIzaSyDEfyUmXDhgGWibRUro2EBoX8-TtBKMYyA",
authDomain: "web3auth-x-firebase-demo-e3332.firebaseapp.com",
projectId: "web3auth-x-firebase-demo-e3332",
storageBucket: "web3auth-x-firebase-demo-e3332.appspot.com",
messagingSenderId: "108145034076",
appId: "1:108145034076:web:3ff4c0088ec4c311b17799",
};

// Initialize Firebase
const app = initializeApp(firebaseConfig);
import { GoogleAuthProvider, getAuth, signInWithPopup, UserCredential, User } from "firebase/auth";

const signInWithGoogle = async (): Promise<UserCredential> => {
try {
const app = initializeApp(firebaseConfig);
const auth = getAuth(app);
const googleProvider = new GoogleAuthProvider();
const res = await signInWithPopup(auth, googleProvider);
return res;
} catch (err) {
console.error(err);
throw err;
}
};

// Calling the signInWithGoogle function to get the idToken

const loginRes = await signInWithGoogle();
const idToken = await loginRes.user.getIdToken(true); // this idToken will be passed to web3auth

Checking if user's MFA is enabled and implementing login.

Now we have idToken and login response from firebase, which will give the user's detail. We will use these two fields to check whether a user has already enabled MFA or not.

The check can be made by calling the connect method of @web3auth/single-factor-auth. This method will throw an error if the user has already enabled MFA. Note that this function takes idToken as a parameter which is the idToken we got from Firebase.

info

Ensure that the idToken passed to the connect() is a newly issued Token(JWT).

import { Web3Auth } from "@web3auth/single-factor-auth";
import { CHAIN_NAMESPACES } from "@web3auth/base";

const idToken = "YOUR_ID_TOKEN"; // idToken from firebase login response or from your own backend.

const chainConfig = {
chainNamespace: CHAIN_NAMESPACES.EIP155, // SOLANA, OTHER
chainId: "0x1",
rpcTarget: "https://rpc.ankr.com/eth",
// Avoid using public rpcTarget in production.
// Use services like Infura, Quicknode etc
displayName: "Ethereum Mainnet",
blockExplorer: "https://etherscan.io",
ticker: "ETH",
tickerName: "Ethereum",
};
const web3auth = new Web3Auth({
clientId, // Get your Client ID from the Web3Auth Dashboard
chainConfig,
// Available networks are "testnet", "mainnet", "sapphire_mainnet", "aqua".
web3AuthNetwork: "sapphire_mainnet",
});

await web3auth.init();

const web3AuthProvider = await web3auth?.connect({
verifier: "YOUR_VERIFIER_NAME", // verifier that you created earlier from web3auth dashboard.
verifierId: loginRes.user.uid, // user's id from firebase login response or this can be any unique user's id like email etc.
idToken,
});

At this point, your application has successfully authenticated the user with web3auth, and you can use the web3AuthProvider to interact with the blockchain.

caution

Note if you will use the above code snippet. The connect() method will throw an error if the user has already enabled MFA. If MFA is enabled, use the flow described in the next section.

Now, if you want to know how to login with MFA or upgrade your user's account to MFA, you can refer to the next section. Otherwise, you can skip to the section here to learn how to use blockchain providers.

Implementing login with MFA.

If the user's MFA is enabled, we can simply login with @web3auth/no-modal SDK.

Here are the steps required to login with MFA:

Instantiate Web3Auth sdk

import { Web3AuthNoModal } from "@web3auth/no-modal";
import { CHAIN_NAMESPACES } from "@web3auth/base";

const chainConfig = {
chainNamespace: CHAIN_NAMESPACES.EIP155, // SOLANA, OTHER
chainId: "0x1",
rpcTarget: "https://rpc.ankr.com/eth",
// Avoid using public rpcTarget in production.
// Use services like Infura, Quicknode etc
displayName: "Ethereum Mainnet",
blockExplorer: "https://etherscan.io",
ticker: "ETH",
tickerName: "Ethereum",
};
const web3auth = new Web3AuthNoModal({
clientId, // Get your Client ID from the Web3Auth Dashboard
chainConfig,
});

Initialising the Openlogin Adapter

const openloginAdapter = new OpenloginAdapter({
adapterSettings: {
network: "sapphire_mainnet",
loginConfig: {
jwt: {
name: "Web3Auth One Key Login Flow",
verifier: "YOUR_VERIFIER_NAME", // verifier that you created earlier from web3auth dashboard.
typeOfLogin: "jwt",
clientId,
},
},
},
});

You must pass your Web3Auth clientId in the adapterSettings object and your Custom Auth verifierName in the loginConfig object. To learn more about openloginAdapterParams refer to docs here.

Initialising the Web3Auth SDK

await web3auth.init();

Login with MFA

const idToken = "YOUR_ID_TOKEN"; // idToken from firebase login response or from your own backend.
// if NO, login with web3auth
const web3AuthProvider = await web3auth.connectTo(WALLET_ADAPTERS.OPENLOGIN, {
loginProvider: "jwt",
extraLoginOptions: {
id_token: idToken,
verifierIdField: "sub",
},
});

When connecting using Web3Auth SDK, the connectTo function takes the arguments for the adapter you want to connect to and the options for the login. A few important things to note here is the idToken and verifierIdField option in the extraLoginOptions object. The idToken is received from Firebase SDK, and verifierIdField is the field's name in idToken, which contains the value of the user's id. This field is also defined while creating a verifier in web3auth's dashboard.

Once the connectTo function resolves, it will return a blockchain provider the same as the one we got in login without MFA. We will use this blockchain provider to fetch the user's balance from the blockchain.

NOTE

Once enabled by the User, MFA cannot be disabled.

How to enable Multi Factor Authentication(MFA) for your users

So far, we have seen how to login with MFA and without MFA. But there might be a case in your application where you want the user to start without MFA for easy onboarding. Later, you want your users to enable MFA to enhance security.

The process to enable MFA is quite simple, as described in the code snippet below. Still, the caveat is once the user enables MFA, the user won't be able to disable it. So make sure your login screen is flexible enough to cater both with and without MFA flows if you are giving this option in your application.

The following code snippet depicts how to enable MFA for users while using Firebase as the JWT provider.

import { WALLET_ADAPTERS } from "@web3auth/base";
import { initializeApp } from "firebase/app";
import { getAuth } from "firebase/auth";
const firebaseConfig = {
//...Your firebase config
};
const app = initializeApp(firebaseConfig);

// initialize web3auth sdk here as described in login with mfa step

// ...
// ...
// ...

const enableMfa = async () => {
const auth = getAuth(app);
const user = auth.currentUser;
// user
const idToken = await user.getIdToken(true);

// web3auth instance must be initialized before calling this function
// as decribed in login with mfa flow above
const web3AuthProvider = await web3auth.connectTo(WALLET_ADAPTERS.OPENLOGIN, {
loginProvider: "jwt",
extraLoginOptions: {
id_token: idToken,
verifierIdField: "sub",
domain: window.location.origin,
},
mfaLevel: "optional",
});
return web3AuthProvider;
};
NOTE

Once enabled by the User, MFA cannot be disabled.

Get the User Profile

Depending upon if the user has enabled MFA or not, we can get the user profile from the provider.

const user = await web3auth.getUserInfo();
console.log("User info", user);

Depending if the user has enabled MFA or not the functions to get the user profile will be different. If the user has enabled MFA, you can use the getUserInfo function to get the user profile.

Logout

Logging out your user is as simple as calling the logout function.

await web3auth.logout();

Interacting with Blockchain

So if you have completed this far, it means that you have successfully authenticated your user. Now, you can use the provider returned by Web3Auth as web3auth.provider to interact with your blockchain. You can use the Provider SDKs to perform RPC Calls to your blockchain.

Web3Auth is chain agnostic, ie. depending on whatever blockchain or layer-2 you use, Web3Auth can easily support that. Web3Auth has native providers for EVM and Solana blockchains and for others, you can get the private key in the user scope and make RPC calls. For standardising the type of provider, Web3Auth Base provides a IProvider from which you can create your own provider.

  • Ethereum Provider gives you the capability of making RPC calls to the EVM compatible blockchains.
  • Solana Provider gives you the capability of making RPC calls to the Solana blockchain.
  • XRPL Provider gives you the capability of making RPC calls to the XRPL blockchain.
  • If you want to use any other chain except Solana or EVM chains, for ex: Starknet, you can specify the value of chainNamespace field as other in the Web3Auth SDK Constructor. Refer to: Using other blockchains

Get User Accounts

const getAccounts = async () => {
if (!provider) {
console.log("provider not initialized yet");
return;
}
const web3 = new Web3(provider as any);
const userAccounts = await web3.eth.getAccounts();
console.log(userAccounts);
};

View User Balance

const getBalance = async () => {
if (!provider) {
console.log("provider not initialized yet");
return;
}
const web3 = new Web3(provider as any);
const accounts = await web3.eth.getAccounts();
const balance = await web3.eth.getBalance(accounts[0]);
console.log(web3.utils.fromWei(balance));
};

Sign Message

const signMessage = async () => {
if (!provider) {
console.log("provider not initialized yet");
return;
}
const web3 = new Web3(provider as any);
// Get user's Ethereum public address
const account = (await web3.eth.getAccounts())[0];

// Message
const message = "Hello MPC, Bye Bye SeedPhrase";

const typedMessage = [
{
type: "string",
name: "message",
value: message,
},
];
const params = [JSON.stringify(typedMessage), account];
const method = "eth_signTypedData";

const signedMessage = await this.provider.request({
method,
params,
});
console.log(signedMessage);
};

Sign Transaction

const signTransaction = async () => {
if (!provider) {
console.log("provider not initialized yet");
return;
}
const web3 = new Web3(provider as any);
const accounts = await web3.eth.getAccounts();

const txRes = await web3.eth.signTransaction({
from: accounts[0],
to: accounts[0],
value: web3.utils.toWei("0.0001"),
chainId: 1, // change it to your specific chain id.
});
console.log(txRes.transactionHash);
};

Send Transaction

const sendTransaction = async () => {
if (!provider) {
console.log("provider not initialized yet");
return;
}
const web3 = new Web3(provider as any);
const accounts = await web3.eth.getAccounts();

const txRes = await web3.eth.sendTransaction({
from: accounts[0],
to: accounts[0],
value: web3.utils.toWei("0.0001"),
chainId: 1, // change it to your specific chain id.
});
console.log(txRes.transactionHash);
};

Example code

The code for the application we developed in this guide can be found in the Web3Auth Single Factor Auth Example. Check it out and try running it locally yourself!

Questions?

Ask us on Web3Auth's Community Portal