fix: included mfa login flow

This commit is contained in:
quinton11
2023-06-16 12:58:00 +00:00
parent 9e9129dd02
commit dd0fdea19f
7 changed files with 449 additions and 254 deletions

View File

@@ -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");

View File

@@ -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;

View File

@@ -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);
}

View 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;