mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-09-22 13:39:35 +00:00
feat: srp handover for admin and minor bug fix in mfa
This commit is contained in:
@@ -79,6 +79,7 @@ export const registerAdminRouter = async (server: FastifyZodProvider) => {
|
||||
schema: {
|
||||
body: z.object({
|
||||
email: z.string().email().trim(),
|
||||
password: z.string().trim(),
|
||||
firstName: z.string().trim(),
|
||||
lastName: z.string().trim().optional(),
|
||||
protectedKey: z.string().trim(),
|
||||
|
||||
@@ -570,20 +570,21 @@ export const authLoginServiceFactory = ({
|
||||
? decodedProviderToken.orgId
|
||||
: undefined;
|
||||
|
||||
const user = await userDAL.findUserEncKeyByUsername({
|
||||
const userEnc = await userDAL.findUserEncKeyByUsername({
|
||||
username: email
|
||||
});
|
||||
if (!user) throw new BadRequestError({ message: "Invalid token" });
|
||||
if (!user.serverEncryptedPrivateKey) throw new BadRequestError({ message: "Private key handoff needs to be done" });
|
||||
if (!userEnc) throw new BadRequestError({ message: "Invalid token" });
|
||||
if (!userEnc.serverEncryptedPrivateKey)
|
||||
throw new BadRequestError({ message: "Private key handoff needs to be done" });
|
||||
// send multi factor auth token if they it enabled
|
||||
if (user.isMfaEnabled && user.email) {
|
||||
enforceUserLockStatus(Boolean(user.isLocked), user.temporaryLockDateEnd);
|
||||
if (userEnc.isMfaEnabled && userEnc.email) {
|
||||
enforceUserLockStatus(Boolean(userEnc.isLocked), userEnc.temporaryLockDateEnd);
|
||||
|
||||
const mfaToken = jwt.sign(
|
||||
{
|
||||
authMethod,
|
||||
authTokenType: AuthTokenType.MFA_TOKEN,
|
||||
userId: user.userId
|
||||
userId: userEnc.userId
|
||||
},
|
||||
appCfg.AUTH_SECRET,
|
||||
{
|
||||
@@ -592,22 +593,22 @@ export const authLoginServiceFactory = ({
|
||||
);
|
||||
|
||||
await sendUserMfaCode({
|
||||
userId: user.id,
|
||||
email: user.email
|
||||
userId: userEnc.userId,
|
||||
email: userEnc.email
|
||||
});
|
||||
|
||||
return { isMfaEnabled: true, token: mfaToken } as const;
|
||||
}
|
||||
|
||||
const token = await generateUserTokens({
|
||||
user: { ...user, id: user.userId },
|
||||
user: { ...userEnc, id: userEnc.userId },
|
||||
ip,
|
||||
userAgent,
|
||||
authMethod,
|
||||
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 { TKeyStoreFactory } from "@app/keystore/keystore";
|
||||
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 { TAuthLoginFactory } from "../auth/auth-login-service";
|
||||
@@ -77,6 +81,7 @@ export const superAdminServiceFactory = ({
|
||||
firstName,
|
||||
salt,
|
||||
email,
|
||||
password,
|
||||
verifier,
|
||||
publicKey,
|
||||
protectedKey,
|
||||
@@ -92,6 +97,17 @@ export const superAdminServiceFactory = ({
|
||||
const existingUser = await userDAL.findOne({ email });
|
||||
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 newUser = await userDAL.create(
|
||||
{
|
||||
@@ -119,7 +135,12 @@ export const superAdminServiceFactory = ({
|
||||
iv: encryptedPrivateKeyIV,
|
||||
tag: encryptedPrivateKeyTag,
|
||||
verifier,
|
||||
userId: newUser.id
|
||||
userId: newUser.id,
|
||||
password: hashedPassword,
|
||||
serverEncryptedPrivateKey: ciphertext,
|
||||
serverEncryptedPrivateKeyIV: iv,
|
||||
serverEncryptedPrivateKeyTag: tag,
|
||||
serverEncryptedPrivateKeyEncoding: encoding
|
||||
},
|
||||
tx
|
||||
);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export type TAdminSignUpDTO = {
|
||||
email: string;
|
||||
password: string;
|
||||
publicKey: string;
|
||||
salt: string;
|
||||
lastName?: string;
|
||||
|
||||
@@ -10,6 +10,7 @@ export type TServerConfig = {
|
||||
|
||||
export type TCreateAdminUserDTO = {
|
||||
email: string;
|
||||
password: string;
|
||||
firstName: string;
|
||||
lastName?: string;
|
||||
protectedKey: string;
|
||||
|
||||
@@ -60,48 +60,45 @@ export const MFAStep = ({ email, password, providerAuthToken }: Props) => {
|
||||
// They don't have password
|
||||
const handleLoginMfaOauth = async (callbackPort: string, organizationId?: string) => {
|
||||
setIsLoading(true);
|
||||
if (callbackPort) {
|
||||
// attemptCliLogin
|
||||
const { token } = await verifyMfaToken({
|
||||
email,
|
||||
mfaCode
|
||||
});
|
||||
//
|
||||
// unset temporary (MFA) JWT token and set JWT token
|
||||
SecurityClient.setMfaToken("");
|
||||
SecurityClient.setToken(token);
|
||||
SecurityClient.setProviderAuthToken("");
|
||||
const privateKey = await fetchMyPrivateKey();
|
||||
localStorage.setItem("PRIVATE_KEY", privateKey);
|
||||
const { token } = await verifyMfaToken({
|
||||
email,
|
||||
mfaCode
|
||||
});
|
||||
//
|
||||
// unset temporary (MFA) JWT token and set JWT token
|
||||
SecurityClient.setMfaToken("");
|
||||
SecurityClient.setToken(token);
|
||||
SecurityClient.setProviderAuthToken("");
|
||||
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
|
||||
if (organizationId) {
|
||||
const { token: newJwtToken } = await selectOrganization({ organizationId });
|
||||
if (callbackPort) {
|
||||
const cliUrl = `http://127.0.0.1:${callbackPort}/`;
|
||||
const instance = axios.create();
|
||||
await instance.post(cliUrl, {
|
||||
email,
|
||||
privateKey,
|
||||
JTWToken: newJwtToken
|
||||
});
|
||||
}
|
||||
await navigateUserToOrg(router, organizationId);
|
||||
// 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) {
|
||||
const { token: newJwtToken } = await selectOrganization({ organizationId });
|
||||
if (callbackPort) {
|
||||
const cliUrl = `http://127.0.0.1:${callbackPort}/`;
|
||||
const instance = axios.create();
|
||||
await instance.post(cliUrl, {
|
||||
email,
|
||||
privateKey,
|
||||
JTWToken: newJwtToken
|
||||
});
|
||||
}
|
||||
// case: no organization ID is present -- navigate to the select org page IF the user has any orgs
|
||||
// if the user has no orgs, navigate to the create org page
|
||||
else {
|
||||
const userOrgs = await fetchOrganizations();
|
||||
await navigateUserToOrg(router, organizationId);
|
||||
}
|
||||
// case: no organization ID is present -- navigate to the select org page IF the user has any orgs
|
||||
// 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
|
||||
if (userOrgs.length > 0) {
|
||||
navigateUserToSelectOrg(router, callbackPort);
|
||||
}
|
||||
// case: no orgs found, so we navigate the user to create an org
|
||||
// cli login will fail in this case
|
||||
else {
|
||||
await navigateUserToOrg(router);
|
||||
}
|
||||
// case: user has orgs, so we navigate the user to select an org
|
||||
if (userOrgs.length > 0) {
|
||||
navigateUserToSelectOrg(router, callbackPort);
|
||||
}
|
||||
// case: no orgs found, so we navigate the user to create an org
|
||||
// cli login will fail in this case
|
||||
else {
|
||||
await navigateUserToOrg(router);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -73,6 +73,7 @@ export const SignUpPage = () => {
|
||||
const { privateKey, ...userPass } = await generateUserPassKey(email, password);
|
||||
const res = await createAdminUser({
|
||||
email,
|
||||
password,
|
||||
firstName,
|
||||
lastName,
|
||||
...userPass
|
||||
|
||||
Reference in New Issue
Block a user