88 lines
2.3 KiB
TypeScript
88 lines
2.3 KiB
TypeScript
import { InjectRepository } from '@nestjs/typeorm';
|
|
import { UserTokenEntity } from '../database/entities/user-token.entity';
|
|
import { authenticator as totp } from 'otplib';
|
|
import { Repository } from 'typeorm';
|
|
import { Injectable } from '@nestjs/common';
|
|
import { UserEntity } from '../database/entities/user.entity';
|
|
import { UserTokenType, generateString } from '@freeblox/shared';
|
|
|
|
totp.options = {
|
|
window: 2,
|
|
};
|
|
|
|
@Injectable()
|
|
export class OTPService {
|
|
constructor(
|
|
@InjectRepository(UserTokenEntity)
|
|
private readonly userTokenRepository: Repository<UserTokenEntity>,
|
|
) {}
|
|
|
|
/**
|
|
* Check if the user has TOTP enabled
|
|
* @param user User object
|
|
* @returns true if the user has TOTP enabled
|
|
*/
|
|
public async userHasTOTP(user: UserEntity): Promise<boolean> {
|
|
return !!(await this.getUserTOTP(user));
|
|
}
|
|
|
|
/**
|
|
* Get the TOTP token of a user
|
|
* @param user User object
|
|
* @returns TOTP token
|
|
*/
|
|
public async getUserTOTP(user: UserEntity): Promise<UserTokenEntity> {
|
|
return this.userTokenRepository.findOne({
|
|
where: { user: { id: user.id }, type: UserTokenType.TOTP },
|
|
relations: ['user'],
|
|
});
|
|
}
|
|
|
|
public validateTOTP(secret: string, token: string): boolean {
|
|
return totp.verify({ token, secret });
|
|
}
|
|
|
|
public getTOTPURL(secret: string, username: string): string {
|
|
return totp.keyuri(username, 'Freeblox', secret);
|
|
}
|
|
|
|
public createTOTPSecret(): string {
|
|
return totp.generateSecret();
|
|
}
|
|
|
|
public async activateTOTP(
|
|
user: UserEntity,
|
|
secret: string,
|
|
): Promise<UserTokenEntity[]> {
|
|
const totp = new UserTokenEntity();
|
|
const recovery = new UserTokenEntity();
|
|
|
|
totp.user = user;
|
|
totp.token = secret;
|
|
totp.type = UserTokenType.TOTP;
|
|
|
|
recovery.user = user;
|
|
recovery.token = Array.from({ length: 8 }, () => generateString(8)).join(
|
|
' ',
|
|
);
|
|
recovery.type = UserTokenType.RECOVERY;
|
|
|
|
await this.userTokenRepository.save(totp);
|
|
await this.userTokenRepository.save(recovery);
|
|
|
|
return [totp, recovery];
|
|
}
|
|
|
|
public async deactivateTOTP(token: UserTokenEntity): Promise<void> {
|
|
if (!token) {
|
|
return;
|
|
}
|
|
|
|
await this.userTokenRepository.delete({
|
|
type: UserTokenType.RECOVERY,
|
|
user: { id: token.user.id },
|
|
});
|
|
await this.userTokenRepository.remove(token);
|
|
}
|
|
}
|