misc: addressed remaining comments

This commit is contained in:
Sheen Capadngan
2024-11-16 03:15:39 +08:00
parent 774371a218
commit 682b552fdc
4 changed files with 19 additions and 15 deletions
+3
View File
@@ -0,0 +1,3 @@
import crypto from "node:crypto";
export const generateRecoveryCode = () => String(crypto.randomInt(10 ** 7, 10 ** 8 - 1));
+8 -9
View File
@@ -1,5 +1,3 @@
import crypto from "node:crypto";
import { authenticator } from "otplib"; import { authenticator } from "otplib";
import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors"; import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
@@ -7,6 +5,7 @@ import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/
import { TKmsServiceFactory } from "../kms/kms-service"; import { TKmsServiceFactory } from "../kms/kms-service";
import { TUserDALFactory } from "../user/user-dal"; import { TUserDALFactory } from "../user/user-dal";
import { TTotpConfigDALFactory } from "./totp-config-dal"; import { TTotpConfigDALFactory } from "./totp-config-dal";
import { generateRecoveryCode } from "./totp-fns";
import { import {
TCreateUserTotpRecoveryCodesDTO, TCreateUserTotpRecoveryCodesDTO,
TDeleteUserTotpConfigDTO, TDeleteUserTotpConfigDTO,
@@ -25,6 +24,8 @@ type TTotpServiceFactoryDep = {
export type TTotpServiceFactory = ReturnType<typeof totpServiceFactory>; export type TTotpServiceFactory = ReturnType<typeof totpServiceFactory>;
const MAX_RECOVERY_CODE_LIMIT = 10;
export const totpServiceFactory = ({ totpConfigDAL, kmsService, userDAL }: TTotpServiceFactoryDep) => { export const totpServiceFactory = ({ totpConfigDAL, kmsService, userDAL }: TTotpServiceFactoryDep) => {
const getUserTotpConfig = async ({ userId }: TGetUserTotpConfigDTO) => { const getUserTotpConfig = async ({ userId }: TGetUserTotpConfigDTO) => {
const totpConfig = await totpConfigDAL.findOne({ const totpConfig = await totpConfigDAL.findOne({
@@ -82,7 +83,7 @@ export const totpServiceFactory = ({ totpConfigDAL, kmsService, userDAL }: TTotp
// create new TOTP configuration // create new TOTP configuration
const secret = authenticator.generateSecret(); const secret = authenticator.generateSecret();
const encryptedSecret = encryptWithRoot(Buffer.from(secret)); const encryptedSecret = encryptWithRoot(Buffer.from(secret));
const recoveryCodes = Array.from({ length: 10 }).map(() => String(crypto.randomInt(10 ** 7, 10 ** 8 - 1))); const recoveryCodes = Array.from({ length: MAX_RECOVERY_CODE_LIMIT }).map(generateRecoveryCode);
const encryptedRecoveryCodes = encryptWithRoot(Buffer.from(recoveryCodes.join(","))); const encryptedRecoveryCodes = encryptWithRoot(Buffer.from(recoveryCodes.join(",")));
const newTotpConfig = await totpConfigDAL.create({ const newTotpConfig = await totpConfigDAL.create({
userId, userId,
@@ -241,16 +242,14 @@ export const totpServiceFactory = ({ totpConfigDAL, kmsService, userDAL }: TTotp
} }
const recoveryCodes = decryptWithRoot(totpConfig.encryptedRecoveryCodes).toString().split(","); const recoveryCodes = decryptWithRoot(totpConfig.encryptedRecoveryCodes).toString().split(",");
if (recoveryCodes.length >= 10) { if (recoveryCodes.length >= MAX_RECOVERY_CODE_LIMIT) {
throw new BadRequestError({ throw new BadRequestError({
message: "Cannot have more than 10 recovery codes at a time" message: `Cannot have more than ${MAX_RECOVERY_CODE_LIMIT} recovery codes at a time`
}); });
} }
const toGenerateCount = 10 - recoveryCodes.length; const toGenerateCount = MAX_RECOVERY_CODE_LIMIT - recoveryCodes.length;
const newRecoveryCodes = Array.from({ length: toGenerateCount }).map(() => const newRecoveryCodes = Array.from({ length: toGenerateCount }).map(generateRecoveryCode);
String(crypto.randomInt(10 ** 7, 10 ** 8 - 1))
);
const encryptedRecoveryCodes = encryptWithRoot(Buffer.from([...recoveryCodes, ...newRecoveryCodes].join(","))); const encryptedRecoveryCodes = encryptWithRoot(Buffer.from([...recoveryCodes, ...newRecoveryCodes].join(",")));
await totpConfigDAL.updateById(totpConfig.id, { await totpConfigDAL.updateById(totpConfig.id, {
@@ -91,10 +91,12 @@ export default function LoginPage() {
return; return;
} }
const { token, isMfaEnabled, mfaMethod } = await selectOrg.mutateAsync({ const { token, isMfaEnabled, mfaMethod } = await selectOrg
organizationId: organization.id, .mutateAsync({
userAgent: callbackPort ? UserAgentType.CLI : undefined organizationId: organization.id,
}); userAgent: callbackPort ? UserAgentType.CLI : undefined
})
.finally(() => setIsInitialOrgCheckLoading(false));
if (isMfaEnabled) { if (isMfaEnabled) {
SecurityClient.setMfaToken(token); SecurityClient.setMfaToken(token);
+2 -2
View File
@@ -114,7 +114,7 @@ export const Mfa = ({ successCallback, closeMfa, hideLogo, email, method }: Prop
return ( return (
<> <>
<div className="mb-6 text-center text-lg font-bold text-white"> <div className="mb-6 text-center text-lg font-bold text-white">
Your organization requires mobile authenticator to be configured. Your organization requires mobile authentication to be configured.
</div> </div>
<div className="mx-auto w-max pb-4 pt-4 md:mb-16 md:px-8"> <div className="mx-auto w-max pb-4 pt-4 md:mb-16 md:px-8">
<TotpRegistration <TotpRegistration
@@ -196,7 +196,7 @@ export const Mfa = ({ successCallback, closeMfa, hideLogo, email, method }: Prop
<div className="mt-2 flex flex-row justify-center text-sm text-bunker-400 "> <div className="mt-2 flex flex-row justify-center text-sm text-bunker-400 ">
<Link href="/verify-email"> <Link href="/verify-email">
<span className="cursor-pointer duration-200 hover:text-bunker-200 hover:underline hover:decoration-primary-700 hover:underline-offset-4"> <span className="cursor-pointer duration-200 hover:text-bunker-200 hover:underline hover:decoration-primary-700 hover:underline-offset-4">
No access to both codes? Reset your account Lost your recovery codes? Reset your account
</span> </span>
</Link> </Link>
</div> </div>