
I recently had to implement a two factor authentication on a project for my company and it was a whole new thing for me. Sure I had already used 2fa before but I had never implemented it.
What is 2fa ? Well we all know that passwords aren't really secure enough to avoid security breaches so...
2FA is an extra layer of security used to make sure that people trying to gain access to an online account are who they say they are. First, a user will enter their username and a password. Then, instead of immediately gaining access, they will be required to provide another piece of information.
In this case, I was asked to use the google authenticator app to generate a 2fa code that would be used to authenticate the user after the login step.
Here's the login flow for 2fa authentication:
First we have to create a unique secret for every user that turns on 2fa, but we'll also need a special otp authentication url that we'll be using later to create a QrCode. The otplib package is a good match, so let's install it.
yarn add otplib
We should also update the user entity.
typescriptexport class User {@Exclude()@Column({ type: DataType.STRING })twoFactorAuthenticationSecret: string;@Exclude()@Column({ type: DataType.STRING })twoFactorAuthenticationBackupCode: string;}
Then we create a method to generate the secret, backupCode and otpAuthUrl in the authentication service and return them. The TWO_FA_APP_NAME is the name that will appear in the google authenticator app.
typescriptasync generateTwoFactorAuthenticationSecret(user: UserEntity) {const secret = authenticator.generateSecret();const backupCode = StringUtils.random();const otpAuthUrl = authenticator.keyuri(user.email,configParams.TWO_FA_APP_NAME,secret,);await this.userService.set2FASecretAndBackupCode(secret,backupCode,+user.id,);return {secret,backupCode,otpAuthUrl,};}
We have to update the user with the secret and backupCode that has just been generated. Once again, this should all be in a database.
typescriptasync set2FASecretAndBackupCode(secret: string,backupCode: string,userId: number,) {const user = await this.userRepository.findOne({ where: { id: userId } });if (!user) {customThrowError('User not found', HttpStatus.BAD_REQUEST);}await this.userRepository.update({twoFactorAuthenticationSecret: secret,twoFactorAuthenticationBackupCode: backupCode,},{ where: { id: userId } },);}
Now, we can generate the QrCode that will be used to add our application to the google authenticator app.
yarn add qrcode
Let's add the generate method in the authentication service.
typescriptimport { toDataURL } from 'qrcode';async generateQrCodeDataURL(otpAuthUrl: string) {return toDataURL(otpAuthUrl);}
Now we need to offer the possiblity for the user to turn on the 2fa. So let's add another property to the user interface.
typescriptexport class User {@Column({ type: DataType.BOOLEAN, defaultValue: false })is2FAEnabled: boolean;}
Add the switch method in the users service
typescriptasync switchTwoFactorAuthentication(userId: number, status: boolean) {const user = await this.userRepository.findOne({ where: { id: userId } });if (!user) {customThrowError('User not found', HttpStatus.BAD_REQUEST);}await this.userRepository.update({ is2FAEnabled: status },{ where: { id: userId } },);}
Add the method that will verify the authentication code with the user's secret
typescriptasync authenticateWithTwoFactor(code: string, user: UserEntity) {const isCodeValid = await this.isTwoFactorAuthenticationCodeValid(code,user,);if (!isCodeValid) {customThrowError('Wrong authentication code', 401);}const accessToken = await this.authService.createSignInToken({id: user.id,email: user.email,isSecondFactorAuthenticated: user.is2FAEnabled,});return { accessToken, user };}
So first we'll do a POST request to log in with the user and password:

This will return the following:

Then, we need to get the QrCode to add our app to the google authenticator app

This will return a base64 data url which in turn will happen to be a QrCode

If you scan this with the Google Authenticator App it should add your app:

And then you should be able to call the authenticate route with the current code from the google authenticator app

Hey there! This piece will show you how to supercharge your page loading on NextJs with data caching. It's pretty cool, you can use
Beiryu
Contributor

Stress is not an inherent part of our lives, but a result of our inability to manage our minds, emotions, bodies, and energies effectively. It's not external situations, but our internal mechanisms that cause stress. A daily routine called 'inner engineering' can help reduce stress significantly. By understanding and managing ourselves better, we can avoid stress altogether.
Beiryu
Contributor