diff --git a/dashboard/src/components/modals/TwoFactorAuth.js b/dashboard/src/components/modals/TwoFactorAuth.js index abbef36556..201ae63a8d 100644 --- a/dashboard/src/components/modals/TwoFactorAuth.js +++ b/dashboard/src/components/modals/TwoFactorAuth.js @@ -23,7 +23,7 @@ class TwoFactorAuthModal extends Component { profileSettings: { data }, generateTwoFactorQRCode, } = this.props; - generateTwoFactorQRCode(data.id); + generateTwoFactorQRCode(data.id || data._id); window.addEventListener('keydown', this.handleKeyBoard); } @@ -72,7 +72,7 @@ class TwoFactorAuthModal extends Component { verifyTwoFactorAuthToken, profileSettings, } = this.props; - values.userId = profileSettings.data.id; + values.userId = profileSettings.data.id || profileSettings.data._id; verifyTwoFactorAuthToken(values).then(response => { setTwoFactorAuth(response.data.twoFactorAuthEnabled); this.props.closeThisDialog(); @@ -221,19 +221,73 @@ class TwoFactorAuthModal extends Component { {qrCode.data .otpauth_url ? ( - + <> + +
+ + You + can + also + add + the + QR + code + below + directly + on + Google + Auhenticator + app + or + Authy + +
+ +
+ + QR + Code: + + + {' '} + { + qrCode.data.otpauth_url.split( + 'secret=' + )[1] + } + +
+ ) : ( )} diff --git a/dashboard/src/components/profileSettings/Profile.js b/dashboard/src/components/profileSettings/Profile.js index d347854e4b..0257fafea3 100755 --- a/dashboard/src/components/profileSettings/Profile.js +++ b/dashboard/src/components/profileSettings/Profile.js @@ -1272,6 +1272,7 @@ export class ProfileSetting extends Component { style={{ marginTop: '10px', }} + id="twoFactorLabel" > { + const otp = speakeasy.totp({ + secret: token.trim(), + encoding: 'base32', + }); + return otp; +}; + +describe('TwoFactor Authentication API', () => { + const operationTimeOut = init.timeout; + beforeAll(async done => { + jest.setTimeout(360000); + browser = await puppeteer.launch(utils.puppeteerLaunchConfig); + page = await browser.newPage(); + await page.setUserAgent(utils.agent); + + const user = { + email: email, + password: password, + }; + //user login + await init.registerUser(user, page); + await init.addProject(page, projectName); + + done(); + }); + + afterAll(async done => { + browser.close(); + done(); + }); + + test( + 'Should throw an error when invalid otp token is passed', + async done => { + await page.goto(utils.DASHBOARD_URL, { + waitUntil: ['networkidle2'], + }); + + await init.pageWaitForSelector(page, '#profile-menu'); + await init.pageClick(page, '#profile-menu'); + await init.pageWaitForSelector(page, '#userProfile'); + await init.pageClick(page, '#userProfile'); + await init.pageWaitForSelector(page, '#profileSettings', { + visible: true, + timeout: init.timeout, + }); + + await init.pageWaitForSelector(page, '#twoFactorLabel'); + await init.pageClick(page, '#twoFactorLabel'); + + await init.pageWaitForSelector(page, '#nextFormButton'); + await init.pageClick(page, '#nextFormButton'); + await init.pageWaitForSelector(page, '#token'); + await init.pageType(page, '#token', '432424'); + await init.pageWaitForSelector(page, '#enableTwoFactorAuthButton'); + await init.pageClick(page, '#enableTwoFactorAuthButton'); + + const message = await init.page$Eval( + page, + '#modal-message', + element => element.innerHTML + ); + expect(message).equal('Invalid token.'); + done(); + }, + operationTimeOut + ); + + test( + 'Should enable twoFactor authentication', + async done => { + await page.goto(utils.DASHBOARD_URL, { + waitUntil: ['networkidle2'], + }); + + await init.pageWaitForSelector(page, '#profile-menu'); + await init.pageClick(page, '#profile-menu'); + await init.pageWaitForSelector(page, '#userProfile'); + await init.pageClick(page, '#userProfile'); + await init.pageWaitForSelector(page, '#profileSettings', { + visible: true, + timeout: init.timeout, + }); + + await init.pageWaitForSelector(page, '#twoFactorLabel'); + await init.pageClick(page, '#twoFactorLabel'); + + await init.pageWaitForSelector(page, '#otpath-url'); + token = await init.page$Eval( + page, + '#otpath-url', + element => element.innerHTML + ); + const otp = await generateOtp(token); + await init.pageWaitForSelector(page, '#nextFormButton'); + await init.pageClick(page, '#nextFormButton'); + await init.pageWaitForSelector(page, '#token'); + await init.pageType(page, '#token', otp.toString()); + await init.pageWaitForSelector(page, '#enableTwoFactorAuthButton'); + await init.pageClick(page, '#enableTwoFactorAuthButton'); + const isVisible = await init.isElementOnPage( + page, + '#modal-message' + ); + expect(isVisible).equal(false); + await init.saasLogout(page); + done(); + }, + operationTimeOut + ); + test( + 'Should ask a user with two factor enabled when they are about to login again', + async done => { + await page.goto(utils.ACCOUNTS_URL + '/login', { + waitUntil: 'networkidle2', + }); + await init.pageWaitForSelector(page, '#login-button'); + await init.pageClick(page, 'input[name=email]'); + await init.pageType(page, 'input[name=email]', email); + await init.pageClick(page, 'input[name=password]'); + await init.pageType(page, 'input[name=password]', password); + await init.pageClick(page, 'button[type=submit]'); + await init.pageWaitForSelector(page, '.message', { + visible: true, + timeout: init.timeout, + }); + + const message = await init.page$Eval( + page, + '.message', + element => element.innerHTML + ); + expect(message).equal('Enter your auth token below to login.'); + done(); + }, + operationTimeOut + ); + + test( + 'Should throw an error when invalid otp token is passed during login', + async done => { + await page.goto(utils.ACCOUNTS_URL + '/login', { + waitUntil: 'networkidle2', + }); + await init.pageWaitForSelector(page, '#login-button'); + await init.pageClick(page, 'input[name=email]'); + await init.pageType(page, 'input[name=email]', email); + await init.pageClick(page, 'input[name=password]'); + await init.pageType(page, 'input[name=password]', password); + await init.pageClick(page, 'button[type=submit]'); + + await init.pageWaitForSelector(page, '#token'); + await init.pageType(page, '#token', '432224'); + await init.pageWaitForSelector(page, 'button[type=submit]'); + await init.pageClick(page, 'button[type=submit]'); + + const message = await init.page$Eval( + page, + '.title span', + element => element.innerHTML + ); + expect(message).equal('Invalid token.'); + done(); + }, + operationTimeOut + ); + test( + 'Should successfully login when valid otp token is passed during login', + async done => { + await page.goto(utils.ACCOUNTS_URL + '/login', { + waitUntil: 'networkidle2', + }); + await init.pageWaitForSelector(page, '#login-button'); + await init.pageClick(page, 'input[name=email]'); + await init.pageType(page, 'input[name=email]', email); + await init.pageClick(page, 'input[name=password]'); + await init.pageType(page, 'input[name=password]', password); + await init.pageClick(page, 'button[type=submit]'); + + const otp = generateOtp(); + await init.pageWaitForSelector(page, '#token'); + await init.pageType(page, '#token', otp.toString()); + await init.pageWaitForSelector(page, 'button[type=submit]'); + await init.pageClick(page, 'button[type=submit]'); + await init.pageWaitForSelector(page, '#home', { + visible: true, + timeout: init.timeout, + }); + done(); + }, + operationTimeOut + ); +});