feat(core): Add MFA (#4767)

https://linear.app/n8n/issue/ADO-947/sync-branch-with-master-and-fix-fe-e2e-tets

---------

Co-authored-by: कारतोफ्फेलस्क्रिप्ट™ <aditya@netroy.in>
This commit is contained in:
Ricardo Espinoza
2023-08-23 22:59:16 -04:00
committed by GitHub
parent a01c3fbc19
commit 2b7ba6fdf1
61 changed files with 2301 additions and 105 deletions

View File

@@ -30,6 +30,7 @@ import { RESPONSE_ERROR_MESSAGES } from '@/constants';
import { TokenExpiredError } from 'jsonwebtoken';
import type { JwtPayload } from '@/services/jwt.service';
import { JwtService } from '@/services/jwt.service';
import type { MfaService } from '@/Mfa/mfa.service';
@RestController()
export class PasswordResetController {
@@ -47,18 +48,22 @@ export class PasswordResetController {
private readonly userService: UserService;
private readonly mfaService: MfaService;
constructor({
config,
logger,
externalHooks,
internalHooks,
mailer,
mfaService,
}: {
config: Config;
logger: ILogger;
externalHooks: IExternalHooksClass;
internalHooks: IInternalHooksClass;
mailer: UserManagementMailer;
mfaService: MfaService;
}) {
this.config = config;
this.logger = logger;
@@ -67,6 +72,7 @@ export class PasswordResetController {
this.mailer = mailer;
this.jwtService = Container.get(JwtService);
this.userService = Container.get(UserService);
this.mfaService = mfaService;
}
/**
@@ -150,7 +156,11 @@ export class PasswordResetController {
},
);
const url = this.userService.generatePasswordResetUrl(baseUrl, resetPasswordToken);
const url = this.userService.generatePasswordResetUrl(
baseUrl,
resetPasswordToken,
user.mfaEnabled,
);
try {
await this.mailer.passwordReset({
@@ -233,7 +243,7 @@ export class PasswordResetController {
*/
@Post('/change-password')
async changePassword(req: PasswordResetRequest.NewPassword, res: Response) {
const { token: resetPasswordToken, password } = req.body;
const { token: resetPasswordToken, password, mfaToken } = req.body;
if (!resetPasswordToken || !password) {
this.logger.debug(
@@ -264,6 +274,16 @@ export class PasswordResetController {
throw new NotFoundError('');
}
if (user.mfaEnabled) {
if (!mfaToken) throw new BadRequestError('If MFA enabled, mfaToken is required.');
const { decryptedSecret: secret } = await this.mfaService.getSecretAndRecoveryCodes(user.id);
const validToken = this.mfaService.totp.verifySecret({ secret, token: mfaToken });
if (!validToken) throw new BadRequestError('Invalid MFA token.');
}
const passwordHash = await hashPassword(validPassword);
await this.userService.update(user.id, { password: passwordHash });