feat: srp handover for admin and minor bug fix in mfa

This commit is contained in:
=
2024-06-12 19:25:06 +05:30
parent f3ea7b3dfd
commit 736f067178
7 changed files with 73 additions and 50 deletions
@@ -79,6 +79,7 @@ export const registerAdminRouter = async (server: FastifyZodProvider) => {
schema: { schema: {
body: z.object({ body: z.object({
email: z.string().email().trim(), email: z.string().email().trim(),
password: z.string().trim(),
firstName: z.string().trim(), firstName: z.string().trim(),
lastName: z.string().trim().optional(), lastName: z.string().trim().optional(),
protectedKey: z.string().trim(), protectedKey: z.string().trim(),
+11 -10
View File
@@ -570,20 +570,21 @@ export const authLoginServiceFactory = ({
? decodedProviderToken.orgId ? decodedProviderToken.orgId
: undefined; : undefined;
const user = await userDAL.findUserEncKeyByUsername({ const userEnc = await userDAL.findUserEncKeyByUsername({
username: email username: email
}); });
if (!user) throw new BadRequestError({ message: "Invalid token" }); if (!userEnc) throw new BadRequestError({ message: "Invalid token" });
if (!user.serverEncryptedPrivateKey) throw new BadRequestError({ message: "Private key handoff needs to be done" }); if (!userEnc.serverEncryptedPrivateKey)
throw new BadRequestError({ message: "Private key handoff needs to be done" });
// send multi factor auth token if they it enabled // send multi factor auth token if they it enabled
if (user.isMfaEnabled && user.email) { if (userEnc.isMfaEnabled && userEnc.email) {
enforceUserLockStatus(Boolean(user.isLocked), user.temporaryLockDateEnd); enforceUserLockStatus(Boolean(userEnc.isLocked), userEnc.temporaryLockDateEnd);
const mfaToken = jwt.sign( const mfaToken = jwt.sign(
{ {
authMethod, authMethod,
authTokenType: AuthTokenType.MFA_TOKEN, authTokenType: AuthTokenType.MFA_TOKEN,
userId: user.userId userId: userEnc.userId
}, },
appCfg.AUTH_SECRET, appCfg.AUTH_SECRET,
{ {
@@ -592,22 +593,22 @@ export const authLoginServiceFactory = ({
); );
await sendUserMfaCode({ await sendUserMfaCode({
userId: user.id, userId: userEnc.userId,
email: user.email email: userEnc.email
}); });
return { isMfaEnabled: true, token: mfaToken } as const; return { isMfaEnabled: true, token: mfaToken } as const;
} }
const token = await generateUserTokens({ const token = await generateUserTokens({
user: { ...user, id: user.userId }, user: { ...userEnc, id: userEnc.userId },
ip, ip,
userAgent, userAgent,
authMethod, authMethod,
organizationId organizationId
}); });
return { token, isMfaEnabled: false, user } as const; return { token, isMfaEnabled: false, user: userEnc } as const;
}; };
/* /*
@@ -1,6 +1,10 @@
import bcrypt from "bcrypt";
import { TSuperAdmin, TSuperAdminUpdate } from "@app/db/schemas"; import { TSuperAdmin, TSuperAdminUpdate } from "@app/db/schemas";
import { TKeyStoreFactory } from "@app/keystore/keystore"; import { TKeyStoreFactory } from "@app/keystore/keystore";
import { getConfig } from "@app/lib/config/env"; import { getConfig } from "@app/lib/config/env";
import { infisicalSymmetricEncypt } from "@app/lib/crypto/encryption";
import { getUserPrivateKey } from "@app/lib/crypto/srp";
import { BadRequestError } from "@app/lib/errors"; import { BadRequestError } from "@app/lib/errors";
import { TAuthLoginFactory } from "../auth/auth-login-service"; import { TAuthLoginFactory } from "../auth/auth-login-service";
@@ -77,6 +81,7 @@ export const superAdminServiceFactory = ({
firstName, firstName,
salt, salt,
email, email,
password,
verifier, verifier,
publicKey, publicKey,
protectedKey, protectedKey,
@@ -92,6 +97,17 @@ export const superAdminServiceFactory = ({
const existingUser = await userDAL.findOne({ email }); const existingUser = await userDAL.findOne({ email });
if (existingUser) throw new BadRequestError({ name: "Admin sign up", message: "User already exist" }); if (existingUser) throw new BadRequestError({ name: "Admin sign up", message: "User already exist" });
const privateKey = await getUserPrivateKey(password, {
salt,
protectedKey,
protectedKeyIV,
protectedKeyTag,
encryptedPrivateKey,
iv: encryptedPrivateKeyIV,
tag: encryptedPrivateKeyTag
});
const hashedPassword = await bcrypt.hash(password, appCfg.BCRYPT_SALT_ROUND);
const { iv, tag, ciphertext, encoding } = infisicalSymmetricEncypt(privateKey);
const userInfo = await userDAL.transaction(async (tx) => { const userInfo = await userDAL.transaction(async (tx) => {
const newUser = await userDAL.create( const newUser = await userDAL.create(
{ {
@@ -119,7 +135,12 @@ export const superAdminServiceFactory = ({
iv: encryptedPrivateKeyIV, iv: encryptedPrivateKeyIV,
tag: encryptedPrivateKeyTag, tag: encryptedPrivateKeyTag,
verifier, verifier,
userId: newUser.id userId: newUser.id,
password: hashedPassword,
serverEncryptedPrivateKey: ciphertext,
serverEncryptedPrivateKeyIV: iv,
serverEncryptedPrivateKeyTag: tag,
serverEncryptedPrivateKeyEncoding: encoding
}, },
tx tx
); );
@@ -1,5 +1,6 @@
export type TAdminSignUpDTO = { export type TAdminSignUpDTO = {
email: string; email: string;
password: string;
publicKey: string; publicKey: string;
salt: string; salt: string;
lastName?: string; lastName?: string;
+1
View File
@@ -10,6 +10,7 @@ export type TServerConfig = {
export type TCreateAdminUserDTO = { export type TCreateAdminUserDTO = {
email: string; email: string;
password: string;
firstName: string; firstName: string;
lastName?: string; lastName?: string;
protectedKey: string; protectedKey: string;
@@ -60,48 +60,45 @@ export const MFAStep = ({ email, password, providerAuthToken }: Props) => {
// They don't have password // They don't have password
const handleLoginMfaOauth = async (callbackPort: string, organizationId?: string) => { const handleLoginMfaOauth = async (callbackPort: string, organizationId?: string) => {
setIsLoading(true); setIsLoading(true);
if (callbackPort) { const { token } = await verifyMfaToken({
// attemptCliLogin email,
const { token } = await verifyMfaToken({ mfaCode
email, });
mfaCode //
}); // unset temporary (MFA) JWT token and set JWT token
// SecurityClient.setMfaToken("");
// unset temporary (MFA) JWT token and set JWT token SecurityClient.setToken(token);
SecurityClient.setMfaToken(""); SecurityClient.setProviderAuthToken("");
SecurityClient.setToken(token); const privateKey = await fetchMyPrivateKey();
SecurityClient.setProviderAuthToken(""); localStorage.setItem("PRIVATE_KEY", privateKey);
const privateKey = await fetchMyPrivateKey();
localStorage.setItem("PRIVATE_KEY", privateKey);
// case: organization ID is present from the provider auth token -- select the org and use the new jwt token in the CLI, then navigate to the org // case: organization ID is present from the provider auth token -- select the org and use the new jwt token in the CLI, then navigate to the org
if (organizationId) { if (organizationId) {
const { token: newJwtToken } = await selectOrganization({ organizationId }); const { token: newJwtToken } = await selectOrganization({ organizationId });
if (callbackPort) { if (callbackPort) {
const cliUrl = `http://127.0.0.1:${callbackPort}/`; const cliUrl = `http://127.0.0.1:${callbackPort}/`;
const instance = axios.create(); const instance = axios.create();
await instance.post(cliUrl, { await instance.post(cliUrl, {
email, email,
privateKey, privateKey,
JTWToken: newJwtToken JTWToken: newJwtToken
}); });
}
await navigateUserToOrg(router, organizationId);
} }
// case: no organization ID is present -- navigate to the select org page IF the user has any orgs await navigateUserToOrg(router, organizationId);
// if the user has no orgs, navigate to the create org page }
else { // case: no organization ID is present -- navigate to the select org page IF the user has any orgs
const userOrgs = await fetchOrganizations(); // if the user has no orgs, navigate to the create org page
else {
const userOrgs = await fetchOrganizations();
// case: user has orgs, so we navigate the user to select an org // case: user has orgs, so we navigate the user to select an org
if (userOrgs.length > 0) { if (userOrgs.length > 0) {
navigateUserToSelectOrg(router, callbackPort); navigateUserToSelectOrg(router, callbackPort);
} }
// case: no orgs found, so we navigate the user to create an org // case: no orgs found, so we navigate the user to create an org
// cli login will fail in this case // cli login will fail in this case
else { else {
await navigateUserToOrg(router); await navigateUserToOrg(router);
}
} }
} }
}; };
@@ -73,6 +73,7 @@ export const SignUpPage = () => {
const { privateKey, ...userPass } = await generateUserPassKey(email, password); const { privateKey, ...userPass } = await generateUserPassKey(email, password);
const res = await createAdminUser({ const res = await createAdminUser({
email, email,
password,
firstName, firstName,
lastName, lastName,
...userPass ...userPass