78 lines
1.6 KiB
TypeScript
78 lines
1.6 KiB
TypeScript
import { PrismaService } from '@/src/core/prisma/prisma.service'
|
|
import { EnableTotpInput } from '@/src/module/auth/totp/inputs/enable-totp.input'
|
|
import { BadRequestException, Injectable } from '@nestjs/common'
|
|
import { User } from '@prisma/generated'
|
|
import { encode } from 'hi-base32'
|
|
import { randomBytes } from 'node:crypto'
|
|
import { TOTP } from 'otpauth'
|
|
import * as QRCode from 'qrcode'
|
|
|
|
@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
|
|
}
|
|
}
|