Multi-Factor(MFA) Authentication in React JS using firebase
Hasnain01-hub
Posted on January 1, 2023
So, in July 2022 Firebase added a new segment in authentication: MFA(Multi-Factor Authentication) which adds one more layer to authenticate users.
What is MFA?
It is a secure Authentication method which is also known as two-step authentication, which first registers the user by taking his email id and password, and in the following step, the user will get the 6-digit OTP in his phone no. After passing this step user will be authenticated and redirected to the home page.
Steps to integrate MFA in your React JS app:
1) First create your react app.
npx create-react-app mfaexample
2) Create a new firebase project and configure your firebase project with your react app.
3) In order to have two-step authentication, first enable any authentication provider from authentication>Sign-in-method
In this blog, I will cover sign-in with email and password option as the first authentication layer.
4) Enable the MFA option under authentication>Sign-in-method
in the below Advanced section of your firebase project.
5) Create a Register page in which the first step is to create users using sign-in with email and password. The email needs to be verified for setting the 2-factor auth. After doing this a verification link will be sent to the email address of the newly created user. Then click on the share link the email will be verified the user data will be encrypted and stored in the local storage and will be redirected to the OTP page.
Also check the spam folder of your email address to get the verification link.
Register.js
import Cryptr from "cryptr";
const cryptr = new Cryptr("myTotallySecretKey");
const registerWithEmailAndPassword = async (e) => {
e.preventDefault();
try {
//Form fields validation
if (email !== "" && password !== "" && phone !== "") {
if (phone.length < 10)
return alert("Please enter a valid phone number");
// Creating a new user with the email and password
const res = await auth.createUserWithEmailAndPassword(email, password);
const user = res.user;
//encrypt the user data
const encryptedphone = cryptr.encrypt(`+91${phone}`);
const encryptedpassword = cryptr.encrypt(password);
const userdata = {
email: email,
password: encryptedpassword,
phone: encryptedphone,
user: user,
};
//Sending an verification link to user's email id
await user
.sendEmailVerification({ url: "http://localhost:3000/otp" })
.then(async () => {
//Store the users details in localStorage
window.localStorage.setItem("user", JSON.stringify(userdata));
setEmail("");
setPassword("");
setPhone("");
alert("Email sent");
});
} else {
alert("Please fill all the fields");
}
} catch (err) {
console.error(err);
}
};
6) The user data will be fetched from local storage and, 6-digit OTP will be sent to the registered phone number.
OtpScreen.js
import {
multiFactor,
PhoneAuthProvider,
PhoneMultiFactorGenerator,
RecaptchaVerifier,
} from "firebase/auth";
import Cryptr from "cryptr";
React.useEffect(() => {
//get the user data from localStorage
const getdata = JSON.parse(window.localStorage.getItem("user"));
//if the data is null than redirect back to registerd page
if (getdata === null) {
history.push("/register");
}
sentotp(getdata);
}, []);
const sentotp = async (getdata) => {
/* A listener that triggers whenever the authentication state changes. */
auth.onAuthStateChanged(async (user) => {
/* Decrypting the phone number that was encrypted in the previous step. */
const decryptedphone = cryptr.decrypt(getdata.phone);
/* Creating a new recaptcha verifier. */
const recaptchaVerifier = new RecaptchaVerifier(
"2fa-captcha",
{ size: "invisible" },
auth
);
recaptchaVerifier.render();
await multiFactor(user)
.getSession()
.then(function (multiFactorSession) {
// Specify the phone number and pass the MFA session.
const phoneInfoOptions = {
phoneNumber: decryptedphone,
session: multiFactorSession,
};
const phoneAuthProvider = new PhoneAuthProvider(auth);
// Send SMS verification code.
return phoneAuthProvider.verifyPhoneNumber(
phoneInfoOptions,
recaptchaVerifier
);
})
.then(function (verificationId) {
setverification(verificationId);
});
});
};
7) Add this function in OtpScreen.js, which will trigger after submitting the OTP. After the OTP is verified it will Enroll the user in the multi-factor authentication and remove the user data from the local storage.
verifyotp function
const verifyotp = async (e) => {
e.preventDefault();
try {
//get the OTP from user nad pass in PhoneAuthProvider
const cred = PhoneAuthProvider.credential(verification, otp);
const multiFactorAssertion = PhoneMultiFactorGenerator.assertion(cred);
const user = auth.currentUser;
/* Enrolling the user in the multi-factor authentication. */
await user.multiFactor
.enroll(multiFactorAssertion, "phone number")
.then((enrollment) => {
history.push("/home");
});
/* Removing the user from localStorage. */
localStorage.removeItem("user");
} catch (err) {
alert("Invalid OTP");
console.log(err);
}
};
hurry! the signup part is completed.
Login Part
8) For the login part, pass the user credentials in signInWithEmailAndPassword function and store a boolean after successful login in local storage.
Login.js
const handleSubmit = async (e) => {
e.preventDefault();
try {
/* function that is provided by firebase to sign in a user with email and password. */
await auth.signInWithEmailAndPassword(email, password);
} catch (error) {
if (error.code === "auth/multi-factor-auth-required") {
// The user is enrolled in MFA, must be verified
window.resolver = error.resolver;
} else if (error.code === "auth/too-many-requests") {
window.resolver = error.resolver;
} else {
console.log(error.code, error.message);
alert("Invalid Credentials");
return;
}
}
// store a true boolean after successfully login
window.localStorage.setItem("approvedsignin", JSON.stringify(true));
history.push("/loginotp");
};
9) Fetch the data from local storage to check if the user has already completed the initial login step, 6-digit OTP will be sent to the registered phone number. Then pass the user entered OTP in PhoneAuthProvider function to verify it.
VerifyUser.js
import {
PhoneAuthProvider,
PhoneMultiFactorGenerator,
RecaptchaVerifier,
} from "firebase/auth";
React.useEffect(() => {
//check if the user has completed the first login step
const verfifer = JSON.parse(window.localStorage.getItem("approvedsignin"));
if (verfifer === true) {
sendOtp();
} else {
history.push("/");
}
}, []);
const sendOtp = async () => {
const recaptchaVerifier = new RecaptchaVerifier(
"2fa-captcha",
{ size: "invisible" },
auth
);
recaptchaVerifier.render();
const phoneOpts = {
multiFactorHint: window.resolver.hints[0],
session: window.resolver.session,
};
const phoneAuthProvider = new PhoneAuthProvider(auth);
await phoneAuthProvider
.verifyPhoneNumber(phoneOpts, recaptchaVerifier)
.then((verificationId) => {
setverification(verificationId);
});
};
//verify otp
const verify = async (e) => {
e.preventDefault();
try {
//pass the user endtered otp
const cred = PhoneAuthProvider.credential(verification, otp);
const multiFactorAssertion = PhoneMultiFactorGenerator.assertion(cred);
const credential = await window.resolver
.resolveSignIn(multiFactorAssertion)
.then((enrollment) => {
auth.onAuthStateChanged(async (users) => {
if (users) {
//Remove boolean from localStorage
window.localStorage.removeItem("approvedsignin");
history.push("/home");
}
});
});
} catch (err) {
alert("Invalid OTP");
console.log(err);
}
};
Note: Add this code in OtpScreen.js and VerifyUser.js file in JSX part
<div id="2fa-captcha" style={{ display: "flex", justifyContent: "center" }}></div>
Congratulations, the two-factor authentication is set up for your react js app 😎.
Connect Me!
Linkedin: linkedin.com/in/hasnain-sayyed-537164177/
GitHub: https://github.com/Hasnain01-hub
Source Code: https://github.com/Hasnain01-hub/MultiFactorAuthentication-React
Buy me a Coffee: https://www.buymeacoffee.com/hasnainsayyed
If this post finds you helpful, plz like and follow me for more such amazing upcoming content 😊
Posted on January 1, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.