mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-14 14:34:09 +00:00
misc: addressed comments 1
This commit is contained in:
@@ -525,7 +525,8 @@ export const registerRoutes = async (
|
||||
tokenService,
|
||||
smtpService,
|
||||
authDAL,
|
||||
userDAL
|
||||
userDAL,
|
||||
totpConfigDAL
|
||||
});
|
||||
|
||||
const projectBotService = projectBotServiceFactory({ permissionService, projectBotDAL, projectDAL });
|
||||
|
||||
@@ -196,7 +196,7 @@ export const registerUserRouter = async (server: FastifyZodProvider) => {
|
||||
method: "DELETE",
|
||||
url: "/me/totp",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
handler: async (req) => {
|
||||
@@ -254,4 +254,18 @@ export const registerUserRouter = async (server: FastifyZodProvider) => {
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
method: "POST",
|
||||
url: "/me/totp/recovery-codes",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
handler: async (req) => {
|
||||
return server.services.totp.createUserTotpRecoveryCodes({
|
||||
userId: req.permission.id
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
@@ -2,7 +2,7 @@ import jwt from "jsonwebtoken";
|
||||
import { z } from "zod";
|
||||
|
||||
import { getConfig } from "@app/lib/config/env";
|
||||
import { NotFoundError } from "@app/lib/errors";
|
||||
import { BadRequestError, NotFoundError } from "@app/lib/errors";
|
||||
import { mfaRateLimit } from "@app/server/config/rateLimiter";
|
||||
import { AuthModeMfaJwtTokenPayload, AuthTokenType, MfaMethod } from "@app/services/auth/auth-type";
|
||||
|
||||
@@ -73,7 +73,7 @@ export const registerMfaRouter = async (server: FastifyZodProvider) => {
|
||||
isVerified: Boolean(totpConfig)
|
||||
};
|
||||
} catch (error) {
|
||||
if (error instanceof NotFoundError) {
|
||||
if (error instanceof NotFoundError || error instanceof BadRequestError) {
|
||||
return { isVerified: false };
|
||||
}
|
||||
|
||||
|
||||
@@ -361,9 +361,9 @@ export const authLoginServiceFactory = ({
|
||||
}
|
||||
|
||||
const shouldCheckMfa = selectedOrg.enforceMfa || user.isMfaEnabled;
|
||||
const orgMfaMethod = selectedOrg.enforceMfa ? selectedOrg.selectedMfaMethod : undefined;
|
||||
const userMfaMethod = user.isMfaEnabled ? user.selectedMfaMethod : undefined;
|
||||
const mfaMethod = orgMfaMethod ?? userMfaMethod ?? MfaMethod.EMAIL;
|
||||
const orgMfaMethod = selectedOrg.enforceMfa ? selectedOrg.selectedMfaMethod ?? MfaMethod.EMAIL : undefined;
|
||||
const userMfaMethod = user.isMfaEnabled ? user.selectedMfaMethod ?? MfaMethod.EMAIL : undefined;
|
||||
const mfaMethod = orgMfaMethod ?? userMfaMethod;
|
||||
|
||||
if (shouldCheckMfa && (!decodedToken.isMfaVerified || decodedToken.mfaMethod !== mfaMethod)) {
|
||||
enforceUserLockStatus(Boolean(user.isLocked), user.temporaryLockDateEnd);
|
||||
|
||||
@@ -8,6 +8,7 @@ import { generateSrpServerKey, srpCheckClientProof } from "@app/lib/crypto";
|
||||
import { TAuthTokenServiceFactory } from "../auth-token/auth-token-service";
|
||||
import { TokenType } from "../auth-token/auth-token-types";
|
||||
import { SmtpTemplates, TSmtpService } from "../smtp/smtp-service";
|
||||
import { TTotpConfigDALFactory } from "../totp/totp-config-dal";
|
||||
import { TUserDALFactory } from "../user/user-dal";
|
||||
import { TAuthDALFactory } from "./auth-dal";
|
||||
import { TChangePasswordDTO, TCreateBackupPrivateKeyDTO, TResetPasswordViaBackupKeyDTO } from "./auth-password-type";
|
||||
@@ -18,6 +19,7 @@ type TAuthPasswordServiceFactoryDep = {
|
||||
userDAL: TUserDALFactory;
|
||||
tokenService: TAuthTokenServiceFactory;
|
||||
smtpService: TSmtpService;
|
||||
totpConfigDAL: Pick<TTotpConfigDALFactory, "delete">;
|
||||
};
|
||||
|
||||
export type TAuthPasswordFactory = ReturnType<typeof authPaswordServiceFactory>;
|
||||
@@ -25,7 +27,8 @@ export const authPaswordServiceFactory = ({
|
||||
authDAL,
|
||||
userDAL,
|
||||
tokenService,
|
||||
smtpService
|
||||
smtpService,
|
||||
totpConfigDAL
|
||||
}: TAuthPasswordServiceFactoryDep) => {
|
||||
/*
|
||||
* Pre setup for pass change with srp protocol
|
||||
@@ -185,6 +188,12 @@ export const authPaswordServiceFactory = ({
|
||||
temporaryLockDateEnd: null,
|
||||
consecutiveFailedMfaAttempts: 0
|
||||
});
|
||||
|
||||
/* we reset the mobile authenticator configs of the user
|
||||
because we want this to be one of the recovery modes from account lockout */
|
||||
await totpConfigDAL.delete({
|
||||
userId
|
||||
});
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
@@ -8,6 +8,7 @@ import { TKmsServiceFactory } from "../kms/kms-service";
|
||||
import { TUserDALFactory } from "../user/user-dal";
|
||||
import { TTotpConfigDALFactory } from "./totp-config-dal";
|
||||
import {
|
||||
TCreateUserTotpRecoveryCodesDTO,
|
||||
TDeleteUserTotpConfigDTO,
|
||||
TGetUserTotpConfigDTO,
|
||||
TRegisterUserTotpDTO,
|
||||
@@ -27,8 +28,7 @@ export type TTotpServiceFactory = ReturnType<typeof totpServiceFactory>;
|
||||
export const totpServiceFactory = ({ totpConfigDAL, kmsService, userDAL }: TTotpServiceFactoryDep) => {
|
||||
const getUserTotpConfig = async ({ userId }: TGetUserTotpConfigDTO) => {
|
||||
const totpConfig = await totpConfigDAL.findOne({
|
||||
userId,
|
||||
isVerified: true
|
||||
userId
|
||||
});
|
||||
|
||||
if (!totpConfig) {
|
||||
@@ -37,6 +37,12 @@ export const totpServiceFactory = ({ totpConfigDAL, kmsService, userDAL }: TTotp
|
||||
});
|
||||
}
|
||||
|
||||
if (!totpConfig.isVerified) {
|
||||
throw new BadRequestError({
|
||||
message: "TOTP configuration has not been verified"
|
||||
});
|
||||
}
|
||||
|
||||
const decryptWithRoot = kmsService.decryptWithRootKey();
|
||||
const recoveryCodes = decryptWithRoot(totpConfig.encryptedRecoveryCodes).toString().split(",");
|
||||
|
||||
@@ -102,8 +108,7 @@ export const totpServiceFactory = ({ totpConfigDAL, kmsService, userDAL }: TTotp
|
||||
|
||||
const verifyUserTotpConfig = async ({ userId, totp }: TVerifyUserTotpConfigDTO) => {
|
||||
const totpConfig = await totpConfigDAL.findOne({
|
||||
userId,
|
||||
isVerified: false
|
||||
userId
|
||||
});
|
||||
|
||||
if (!totpConfig) {
|
||||
@@ -112,6 +117,12 @@ export const totpServiceFactory = ({ totpConfigDAL, kmsService, userDAL }: TTotp
|
||||
});
|
||||
}
|
||||
|
||||
if (totpConfig.isVerified) {
|
||||
throw new BadRequestError({
|
||||
message: "TOTP configuration has already been verified"
|
||||
});
|
||||
}
|
||||
|
||||
const decryptWithRoot = kmsService.decryptWithRootKey();
|
||||
const secret = decryptWithRoot(totpConfig.encryptedSecret).toString();
|
||||
const isValid = authenticator.verify({
|
||||
@@ -132,8 +143,7 @@ export const totpServiceFactory = ({ totpConfigDAL, kmsService, userDAL }: TTotp
|
||||
|
||||
const verifyUserTotp = async ({ userId, totp }: TVerifyUserTotpDTO) => {
|
||||
const totpConfig = await totpConfigDAL.findOne({
|
||||
userId,
|
||||
isVerified: true
|
||||
userId
|
||||
});
|
||||
|
||||
if (!totpConfig) {
|
||||
@@ -142,6 +152,12 @@ export const totpServiceFactory = ({ totpConfigDAL, kmsService, userDAL }: TTotp
|
||||
});
|
||||
}
|
||||
|
||||
if (!totpConfig.isVerified) {
|
||||
throw new BadRequestError({
|
||||
message: "TOTP configuration has not been verified"
|
||||
});
|
||||
}
|
||||
|
||||
const decryptWithRoot = kmsService.decryptWithRootKey();
|
||||
const secret = decryptWithRoot(totpConfig.encryptedSecret).toString();
|
||||
const isValid = authenticator.verify({
|
||||
@@ -158,8 +174,7 @@ export const totpServiceFactory = ({ totpConfigDAL, kmsService, userDAL }: TTotp
|
||||
|
||||
const verifyWithUserRecoveryCode = async ({ userId, recoveryCode }: TVerifyWithUserRecoveryCodeDTO) => {
|
||||
const totpConfig = await totpConfigDAL.findOne({
|
||||
userId,
|
||||
isVerified: true
|
||||
userId
|
||||
});
|
||||
|
||||
if (!totpConfig) {
|
||||
@@ -168,6 +183,12 @@ export const totpServiceFactory = ({ totpConfigDAL, kmsService, userDAL }: TTotp
|
||||
});
|
||||
}
|
||||
|
||||
if (!totpConfig.isVerified) {
|
||||
throw new BadRequestError({
|
||||
message: "TOTP configuration has not been verified"
|
||||
});
|
||||
}
|
||||
|
||||
const decryptWithRoot = kmsService.decryptWithRootKey();
|
||||
const encryptWithRoot = kmsService.encryptWithRootKey();
|
||||
|
||||
@@ -188,8 +209,7 @@ export const totpServiceFactory = ({ totpConfigDAL, kmsService, userDAL }: TTotp
|
||||
|
||||
const deleteUserTotpConfig = async ({ userId }: TDeleteUserTotpConfigDTO) => {
|
||||
const totpConfig = await totpConfigDAL.findOne({
|
||||
userId,
|
||||
isVerified: true
|
||||
userId
|
||||
});
|
||||
|
||||
if (!totpConfig) {
|
||||
@@ -201,12 +221,51 @@ export const totpServiceFactory = ({ totpConfigDAL, kmsService, userDAL }: TTotp
|
||||
await totpConfigDAL.deleteById(totpConfig.id);
|
||||
};
|
||||
|
||||
const createUserTotpRecoveryCodes = async ({ userId }: TCreateUserTotpRecoveryCodesDTO) => {
|
||||
const decryptWithRoot = kmsService.decryptWithRootKey();
|
||||
const encryptWithRoot = kmsService.encryptWithRootKey();
|
||||
|
||||
return totpConfigDAL.transaction(async (tx) => {
|
||||
const totpConfig = await totpConfigDAL.findOne(
|
||||
{
|
||||
userId,
|
||||
isVerified: true
|
||||
},
|
||||
tx
|
||||
);
|
||||
|
||||
if (!totpConfig) {
|
||||
throw new NotFoundError({
|
||||
message: "Valid TOTP configuration not found"
|
||||
});
|
||||
}
|
||||
|
||||
const recoveryCodes = decryptWithRoot(totpConfig.encryptedRecoveryCodes).toString().split(",");
|
||||
if (recoveryCodes.length >= 10) {
|
||||
throw new BadRequestError({
|
||||
message: "Cannot have more than 10 recovery codes at a time"
|
||||
});
|
||||
}
|
||||
|
||||
const toGenerateCount = 10 - recoveryCodes.length;
|
||||
const newRecoveryCodes = Array.from({ length: toGenerateCount }).map(() =>
|
||||
String(crypto.randomInt(10 ** 7, 10 ** 8 - 1))
|
||||
);
|
||||
const encryptedRecoveryCodes = encryptWithRoot(Buffer.from([...recoveryCodes, ...newRecoveryCodes].join(",")));
|
||||
|
||||
await totpConfigDAL.updateById(totpConfig.id, {
|
||||
encryptedRecoveryCodes
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
registerUserTotp,
|
||||
verifyUserTotpConfig,
|
||||
getUserTotpConfig,
|
||||
verifyUserTotp,
|
||||
verifyWithUserRecoveryCode,
|
||||
deleteUserTotpConfig
|
||||
deleteUserTotpConfig,
|
||||
createUserTotpRecoveryCodes
|
||||
};
|
||||
};
|
||||
|
||||
@@ -24,3 +24,7 @@ export type TVerifyWithUserRecoveryCodeDTO = {
|
||||
export type TDeleteUserTotpConfigDTO = {
|
||||
userId: string;
|
||||
};
|
||||
|
||||
export type TCreateUserTotpRecoveryCodesDTO = {
|
||||
userId: string;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user