mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-09-22 13:39:35 +00:00
fix: included mfa login flow
This commit is contained in:
@@ -49,12 +49,20 @@ export default function InitialLoginStep({
|
||||
})
|
||||
|
||||
if (isCliLoginSuccessful && isCliLoginSuccessful.success) {
|
||||
|
||||
if (isCliLoginSuccessful.mfaEnabled) {
|
||||
// case: login requires MFA step
|
||||
setStep(2);
|
||||
setIsLoading(false);
|
||||
return;
|
||||
}
|
||||
// case: login was successful
|
||||
const cliUrl = `http://localhost:${callbackPort}`
|
||||
|
||||
//send request to server endpoint
|
||||
const instance = axios.create()
|
||||
const cliResp = await instance.post(cliUrl,{...isCliLoginSuccessful.loginResponse,email,password})
|
||||
const cliResp = await instance.post(cliUrl,{...isCliLoginSuccessful.loginResponse})
|
||||
console.log(cliResp)
|
||||
|
||||
//cli page
|
||||
router.push("/cli-redirect");
|
||||
|
||||
@@ -3,8 +3,10 @@ import React, { useState } from 'react';
|
||||
import ReactCodeInput from 'react-code-input';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useRouter } from 'next/router';
|
||||
import axios from "axios"
|
||||
|
||||
import attemptLoginMfa from '@app/components/utilities/attemptLoginMfa';
|
||||
import attemptCliLoginMfa from '@app/components/utilities/attemptCliLoginMfa'
|
||||
import { useSendMfaToken } from '@app/hooks/api/auth';
|
||||
|
||||
import Error from '../basic/Error';
|
||||
@@ -76,17 +78,43 @@ export default function MFAStep({
|
||||
}
|
||||
|
||||
setIsLoading(true);
|
||||
const isLoginSuccessful = await attemptLoginMfa({
|
||||
email,
|
||||
password,
|
||||
providerAuthToken,
|
||||
mfaToken: mfaCode
|
||||
});
|
||||
const queryParams = new URLSearchParams(location.search)
|
||||
if (queryParams){
|
||||
const callbackPort = queryParams.get("callback_port")
|
||||
|
||||
if (isLoginSuccessful) {
|
||||
setIsLoading(false);
|
||||
router.push(`/dashboard/${localStorage.getItem('projectData.id')}`);
|
||||
//attemptCliLogin
|
||||
const isCliLoginSuccessful = await attemptCliLoginMfa({
|
||||
email,
|
||||
password,
|
||||
providerAuthToken,
|
||||
mfaToken: mfaCode
|
||||
})
|
||||
|
||||
if (isCliLoginSuccessful && isCliLoginSuccessful.success){
|
||||
// case: login was successful
|
||||
const cliUrl = `http://localhost:${callbackPort}`
|
||||
|
||||
//send request to server endpoint
|
||||
const instance = axios.create()
|
||||
const cliResp = await instance.post(cliUrl,{...isCliLoginSuccessful.loginResponse,email})
|
||||
|
||||
//cli page
|
||||
router.push("/cli-redirect");
|
||||
}
|
||||
}else{
|
||||
const isLoginSuccessful = await attemptLoginMfa({
|
||||
email,
|
||||
password,
|
||||
providerAuthToken,
|
||||
mfaToken: mfaCode
|
||||
});
|
||||
|
||||
if (isLoginSuccessful) {
|
||||
setIsLoading(false);
|
||||
router.push(`/dashboard/${localStorage.getItem('projectData.id')}`);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (err) {
|
||||
const error = err as VerifyMfaTokenError;
|
||||
|
||||
|
||||
@@ -15,23 +15,11 @@ import SecurityClient from './SecurityClient';
|
||||
const client = new jsrp.client();
|
||||
|
||||
interface IsCliLoginSuccessful {
|
||||
loginResponse: {
|
||||
loginOneResponse: {
|
||||
serverPublicKey: string;
|
||||
salt: string;
|
||||
};
|
||||
loginTwoResponse: {
|
||||
mfaEnabled: boolean;
|
||||
token: string;
|
||||
encryptionVersion?: number;
|
||||
protectedKey?: string;
|
||||
protectedKeyIV?: string;
|
||||
protectedKeyTag?: string;
|
||||
publicKey?: string;
|
||||
encryptedPrivateKey?: string;
|
||||
iv?: string;
|
||||
tag?: string;
|
||||
};
|
||||
mfaEnabled: boolean;
|
||||
loginResponse?: {
|
||||
email: string;
|
||||
privateKey: string;
|
||||
JTWToken: string;
|
||||
};
|
||||
success: boolean;
|
||||
}
|
||||
@@ -92,27 +80,54 @@ const attemptLogin = async (
|
||||
providerAuthToken,
|
||||
}
|
||||
);
|
||||
if (mfaEnabled) {
|
||||
// case: MFA is enabled
|
||||
|
||||
resolve({
|
||||
loginResponse: {
|
||||
loginOneResponse: { serverPublicKey, salt },
|
||||
loginTwoResponse: {
|
||||
mfaEnabled,
|
||||
encryptionVersion,
|
||||
protectedKey,
|
||||
protectedKeyIV,
|
||||
protectedKeyTag,
|
||||
token,
|
||||
publicKey,
|
||||
encryptedPrivateKey,
|
||||
iv,
|
||||
tag
|
||||
}
|
||||
},
|
||||
success: true
|
||||
})
|
||||
// set temporary (MFA) JWT token
|
||||
SecurityClient.setMfaToken(token);
|
||||
|
||||
resolve({
|
||||
mfaEnabled,
|
||||
success: true
|
||||
});
|
||||
} else if (
|
||||
!mfaEnabled &&
|
||||
encryptionVersion &&
|
||||
encryptedPrivateKey &&
|
||||
iv &&
|
||||
tag &&
|
||||
token
|
||||
) {
|
||||
// case: MFA is not enabled
|
||||
|
||||
// unset provider auth token in case it was used
|
||||
SecurityClient.setProviderAuthToken('');
|
||||
// set JWT token
|
||||
SecurityClient.setToken(token);
|
||||
|
||||
const privateKey = await KeyService.decryptPrivateKey({
|
||||
encryptionVersion,
|
||||
encryptedPrivateKey,
|
||||
iv,
|
||||
tag,
|
||||
password,
|
||||
salt,
|
||||
protectedKey,
|
||||
protectedKeyIV,
|
||||
protectedKeyTag
|
||||
});
|
||||
|
||||
resolve({
|
||||
mfaEnabled: false,
|
||||
loginResponse: {
|
||||
email: email,
|
||||
privateKey: privateKey,
|
||||
JTWToken: token
|
||||
},
|
||||
success: true
|
||||
})
|
||||
|
||||
}
|
||||
} catch (err) {
|
||||
reject(err);
|
||||
}
|
||||
|
||||
122
frontend/src/components/utilities/attemptCliLoginMfa.ts
Normal file
122
frontend/src/components/utilities/attemptCliLoginMfa.ts
Normal file
@@ -0,0 +1,122 @@
|
||||
/* eslint-disable prefer-destructuring */
|
||||
import jsrp from 'jsrp';
|
||||
|
||||
import login1 from '@app/pages/api/auth/Login1';
|
||||
import verifyMfaToken from '@app/pages/api/auth/verifyMfaToken';
|
||||
import getOrganizations from '@app/pages/api/organization/getOrgs';
|
||||
import getOrganizationUserProjects from '@app/pages/api/organization/GetOrgUserProjects';
|
||||
import KeyService from '@app/services/KeyService';
|
||||
|
||||
import { saveTokenToLocalStorage } from './saveTokenToLocalStorage';
|
||||
import SecurityClient from './SecurityClient';
|
||||
|
||||
// eslint-disable-next-line new-cap
|
||||
const client = new jsrp.client();
|
||||
|
||||
interface isMfaLoginSuccessful {
|
||||
success: boolean;
|
||||
loginResponse:{
|
||||
privateKey: string;
|
||||
JTWToken: string;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether or not MFA-login is successful for user with email [email]
|
||||
* and MFA token [mfaToken]
|
||||
* @param {Object} obj
|
||||
* @param {String} obj.email - email of user
|
||||
* @param {String} obj.mfaToken - MFA code/token
|
||||
*/
|
||||
const attemptLoginMfa = async ({
|
||||
email,
|
||||
password,
|
||||
providerAuthToken,
|
||||
mfaToken
|
||||
}: {
|
||||
email: string;
|
||||
password: string;
|
||||
providerAuthToken?: string,
|
||||
mfaToken: string;
|
||||
}): Promise<isMfaLoginSuccessful> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
client.init({
|
||||
username: email,
|
||||
password
|
||||
}, async () => {
|
||||
try {
|
||||
const clientPublicKey = client.getPublicKey();
|
||||
const { salt } = await login1({
|
||||
email,
|
||||
clientPublicKey,
|
||||
providerAuthToken,
|
||||
});
|
||||
|
||||
const {
|
||||
encryptionVersion,
|
||||
protectedKey,
|
||||
protectedKeyIV,
|
||||
protectedKeyTag,
|
||||
token,
|
||||
publicKey,
|
||||
encryptedPrivateKey,
|
||||
iv,
|
||||
tag
|
||||
} = await verifyMfaToken({
|
||||
email,
|
||||
mfaToken
|
||||
});
|
||||
|
||||
// unset temporary (MFA) JWT token and set JWT token
|
||||
SecurityClient.setMfaToken('');
|
||||
SecurityClient.setToken(token);
|
||||
SecurityClient.setProviderAuthToken('');
|
||||
|
||||
const privateKey = await KeyService.decryptPrivateKey({
|
||||
encryptionVersion,
|
||||
encryptedPrivateKey,
|
||||
iv,
|
||||
tag,
|
||||
password,
|
||||
salt,
|
||||
protectedKey,
|
||||
protectedKeyIV,
|
||||
protectedKeyTag
|
||||
});
|
||||
|
||||
saveTokenToLocalStorage({
|
||||
publicKey,
|
||||
encryptedPrivateKey,
|
||||
iv,
|
||||
tag,
|
||||
privateKey
|
||||
});
|
||||
|
||||
// TODO: in the future - move this logic elsewhere
|
||||
// because this function is about logging the user in
|
||||
// and not initializing the login details
|
||||
const userOrgs = await getOrganizations();
|
||||
const orgId = userOrgs[0]._id;
|
||||
localStorage.setItem('orgData.id', orgId);
|
||||
|
||||
const orgUserProjects = await getOrganizationUserProjects({
|
||||
orgId
|
||||
});
|
||||
localStorage.setItem('projectData.id', orgUserProjects[0]._id);
|
||||
|
||||
resolve({
|
||||
success: true,
|
||||
loginResponse:{
|
||||
privateKey: privateKey,
|
||||
JTWToken: token
|
||||
}
|
||||
});
|
||||
} catch (err) {
|
||||
reject(err);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export default attemptLoginMfa;
|
||||
Reference in New Issue
Block a user