2025-07-30 05:23:26 +03:00

80 lines
1.7 KiB
TypeScript

import { randomBytes } from 'node:crypto';
import { BadRequestException, Injectable } from '@nestjs/common';
import { encode } from 'hi-base32';
import { TOTP } from 'otpauth';
import * as QRCode from 'qrcode';
import { PrismaService } from '@/src/core/prisma/prisma.service';
import { EnableTotpInput } from '@/src/module/auth/totp/inputs/enable-totp.input';
import { User } from '@prisma/generated';
@Injectable()
export class TotpService {
constructor(
private readonly prismaService: PrismaService,
) {
}
async generate(user: User) {
const secret = encode(randomBytes(15))
.replace(/=/g, '')
.substring(0, 24);
const totp = new TOTP({
issuer: 'TeaStream',
label: user.email,
algorithm: 'SHA-1',
digits: 6,
secret,
});
const qrcodeUrl = await QRCode.toDataURL(totp.toString());
return {
qrcodeUrl,
secret,
};
}
async enable(user: User, input: EnableTotpInput) {
const { pin, secret } = input;
const totp = new TOTP({
issuer: 'TeaStream',
label: user.email,
algorithm: 'SHA-1',
digits: 6,
secret,
});
const delta = totp.validate({ token: pin });
if (delta === null) {
throw new BadRequestException('Невереый код');
}
await this.prismaService.user.update({
where: { id: user.id },
data: {
isTotpEnabled: true,
totpSecret: secret,
},
});
return true;
}
async disable(user: User) {
await this.prismaService.user.update({
where: { id: user.id },
data: {
isTotpEnabled: false,
totpSecret: null,
},
});
return true;
}
}