import { Injectable } from '@nestjs/common' ; import { ConfigService } from '@nestjs/config' ; import User from '../../authorization/entity/user.entity' ; import { authenticator } from 'otplib' ; import * as speakeasy from 'speakeasy' ; import { GeneratedSecret } from 'speakeasy' ; @ Injectable ( ) export class TwoFactorAuthService { private authenticator = authenticator ; constructor ( private configService ) { this . authenticator . options = { step : Number ( configService . get ( 'OTP_STEP' ) ) , window : Number ( configService . get ( 'OTP_WINDOW' ) ) , } ; } generateOTP ( keyString ) { const secret = ( keyString + this . configService . get ( 'OTP_SECRET' ) ) as string ; return this . authenticator . generate ( secret ) ; } validate2FAOTP ( token , secret ) { return speakeasy . totp . verify ( { secret , encoding : 'base32' , token , } ) ; } validateOTP ( token , keyString ) { const secret = ( keyString + this . configService . get ( 'OTP_SECRET' ) ) as string ; return this . authenticator . verify ( { token , secret } ) ; } public async generateTotpSecret ( user ) { const twoFASecret = speakeasy . generateSecret ( { length : 20 } ) ; const otpUri = this . authenticator . keyuri ( user . id , < string > this . configService . get ( '2FA_APP_NAME' ) , twoFASecret . base32 ) ; return { secret : twoFASecret , otpUri , } ; } public verify2FACode ( code , user ) { if ( user . twoFASecret ) { const secret = JSON . parse ( user . twoFASecret ) as GeneratedSecret ; return this . validate2FAOTP ( code , secret . base32 ) ; } } }