mirror of
https://github.com/documenso/documenso.git
synced 2026-10-03 10:13:52 +10:00
Selecting password auth failed with a generic "Unauthorized" for users who signed up via OAuth or passkey, with no way to set one. Detect the missing password and email the existing reset link from the signing dialog and security settings. Require a 2FA code and rate limit update-password.
64 lines
1.7 KiB
TypeScript
64 lines
1.7 KiB
TypeScript
import { prisma } from '@documenso/prisma';
|
|
import { type User, UserSecurityAuditLogType } from '@prisma/client';
|
|
|
|
import { AppError, AppErrorCode } from '../../errors/app-error';
|
|
import type { RequestMetadata } from '../../universal/extract-request-metadata';
|
|
import { getBackupCodes } from './get-backup-code';
|
|
import { verifyTwoFactorAuthenticationToken } from './verify-2fa-token';
|
|
|
|
type EnableTwoFactorAuthenticationOptions = {
|
|
user: Pick<User, 'id' | 'email' | 'twoFactorEnabled' | 'twoFactorSecret'>;
|
|
code: string;
|
|
requestMetadata?: RequestMetadata;
|
|
};
|
|
|
|
export const enableTwoFactorAuthentication = async ({
|
|
user,
|
|
code,
|
|
requestMetadata,
|
|
}: EnableTwoFactorAuthenticationOptions) => {
|
|
if (user.twoFactorEnabled) {
|
|
throw new AppError('TWO_FACTOR_ALREADY_ENABLED');
|
|
}
|
|
|
|
if (!user.twoFactorSecret) {
|
|
throw new AppError(AppErrorCode.TWO_FACTOR_SETUP_REQUIRED);
|
|
}
|
|
|
|
const isValidToken = await verifyTwoFactorAuthenticationToken({ user, totpCode: code });
|
|
|
|
if (!isValidToken) {
|
|
throw new AppError(AppErrorCode.INCORRECT_TWO_FACTOR_CODE);
|
|
}
|
|
|
|
let recoveryCodes: string[] = [];
|
|
|
|
await prisma.$transaction(async (tx) => {
|
|
const updatedUser = await tx.user.update({
|
|
where: {
|
|
id: user.id,
|
|
},
|
|
data: {
|
|
twoFactorEnabled: true,
|
|
},
|
|
});
|
|
|
|
recoveryCodes = getBackupCodes({ user: updatedUser }) ?? [];
|
|
|
|
if (recoveryCodes.length === 0) {
|
|
throw new AppError('MISSING_BACKUP_CODE');
|
|
}
|
|
|
|
await tx.userSecurityAuditLog.create({
|
|
data: {
|
|
userId: user.id,
|
|
type: UserSecurityAuditLogType.AUTH_2FA_ENABLE,
|
|
userAgent: requestMetadata?.userAgent,
|
|
ipAddress: requestMetadata?.ipAddress,
|
|
},
|
|
});
|
|
});
|
|
|
|
return { recoveryCodes };
|
|
};
|