icynet-auth-server/src/modules/features/two-factor/two-factor.controller.ts

94 lines
2.6 KiB
TypeScript
Raw Normal View History

2022-03-09 20:03:29 +00:00
import { Body, Controller, Get, Post, Req, Res, Session } from '@nestjs/common';
import { Request, Response } from 'express';
import { SessionData } from 'express-session';
import { UserTOTPService } from 'src/modules/objects/user-token/user-totp-token.service';
2022-03-09 20:03:29 +00:00
import { FormUtilityService } from 'src/modules/utility/services/form-utility.service';
import { QRCodeService } from 'src/modules/utility/services/qr-code.service';
import { TokenService } from 'src/modules/utility/services/token.service';
2022-03-16 18:37:50 +00:00
@Controller('/account/two-factor')
2022-03-09 20:03:29 +00:00
export class TwoFactorController {
constructor(
private totp: UserTOTPService,
2022-03-09 20:03:29 +00:00
private qr: QRCodeService,
private token: TokenService,
private form: FormUtilityService,
) {}
2022-03-16 18:37:50 +00:00
@Get('activate')
2022-03-09 20:03:29 +00:00
public async twoFAStatus(
@Session() session: SessionData,
@Req() req: Request,
@Res() res: Response,
) {
const twoFA = await this.totp.getUserTOTP(req.user);
2022-03-09 20:03:29 +00:00
let secret: string;
if (!twoFA) {
if (session.challenge) {
const challenge = await this.token.decryptChallenge(session.challenge);
if (challenge.type === 'totp') {
secret = challenge.secret;
}
}
if (!secret) {
secret = this.totp.createTOTPSecret();
2022-03-09 20:03:29 +00:00
const challenge = { type: 'totp', secret };
session.challenge = await this.token.encryptChallenge(challenge);
}
const url = this.totp.getTOTPURL(secret, req.user.username);
2022-03-09 20:03:29 +00:00
const qrcode = await this.qr.createQRDataURI(url);
res.render('two-factor/activate', {
2022-03-20 14:50:12 +00:00
...this.form.populateTemplate(req),
2022-03-09 20:03:29 +00:00
qrcode,
});
return;
}
res.redirect('/');
}
2022-03-16 18:37:50 +00:00
@Post('activate')
2022-03-09 20:03:29 +00:00
public async twoFAActivate(
@Session() session: SessionData,
@Body() body: { code: string },
@Req() req: Request,
@Res() res: Response,
) {
let secret: string;
try {
if (!session.challenge || !body.code) {
throw new Error('Invalid request');
}
const challenge = await this.token.decryptChallenge(session.challenge);
secret = challenge.secret;
if (challenge.type !== 'totp' || !secret) {
throw new Error('Invalid request');
}
const verify = this.totp.validateTOTP(secret, body.code);
2022-03-09 20:03:29 +00:00
if (!verify) {
throw new Error('Invalid code! Try again.');
}
} catch (e: any) {
req.flash('message', {
error: true,
text: e.message,
});
res.redirect('/two-factor');
return;
}
// TODO: show the recovery tokens to the user
await this.totp.activateTOTP(req.user, secret);
2022-03-09 20:03:29 +00:00
session.challenge = null;
res.redirect('/');
}
}