import { Inject, Injectable } from '@nestjs/common'; import { Repository } from 'typeorm'; import { User } from '../user/user.entity'; import { TokenService } from 'src/modules/utility/services/token.service'; import { authenticator as totp } from 'otplib'; import { UserToken, UserTokenType } from './user-token.entity'; totp.options = { window: 2, }; @Injectable() export class UserTOTPService { constructor( @Inject('USER_TOKEN_REPOSITORY') private userTokenRepository: Repository, private token: TokenService, ) {} /** * Check if the user has TOTP enabled * @param user User object * @returns true if the user has TOTP enabled */ public async userHasTOTP(user: User): Promise { return !!(await this.getUserTOTP(user)); } /** * Get the TOTP token of a user * @param user User object * @returns TOTP token */ public async getUserTOTP(user: User): Promise { return this.userTokenRepository.findOne({ where: { user, type: UserTokenType.TOTP }, }); } public validateTOTP(secret: string, token: string): boolean { return totp.verify({ token, secret }); } public getTOTPURL(secret: string, username: string): string { return totp.keyuri(username, 'Icy Network', secret); } public createTOTPSecret(): string { return totp.generateSecret(); } public async activateTOTP(user: User, secret: string): Promise { const totp = new UserToken(); const recovery = new UserToken(); totp.user = user; totp.token = secret; totp.type = UserTokenType.TOTP; recovery.user = user; recovery.token = Array.from({ length: 8 }, () => this.token.generateString(8), ).join(' '); recovery.type = UserTokenType.RECOVERY; await this.userTokenRepository.save(totp); await this.userTokenRepository.save(recovery); return [totp, recovery]; } }