Skip to content

Commit

Permalink
Merge pull request #17893 from mozilla/fxa-10583
Browse files Browse the repository at this point in the history
feat(tests): Fix 2FA inline setup flow
  • Loading branch information
vbudhram authored Oct 30, 2024
2 parents 7c4d789 + 1b5f4cd commit fcb2242
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 4 deletions.
5 changes: 5 additions & 0 deletions packages/functional-tests/pages/relier.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,11 @@ export class RelierPage extends BaseLayout {
return this.page.waitForURL(`${this.target.paymentsServerUrl}/**`);
}

async clickRequire2FA() {
await this.page.getByText('Sign In (Require 2FA)').click();
return this.page.waitForURL(`${this.target.contentServerUrl}/**`);
}

async getUrl() {
const expectedPathRegExp = new RegExp(
`\\/subscriptions\\/products\\/${this.target.subscriptionConfig.product}\\?plan=${this.target.subscriptionConfig.plan}&service=(\\w+)`,
Expand Down
66 changes: 65 additions & 1 deletion packages/functional-tests/tests/oauth/totp.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,70 @@ test.describe('severity-1 #smoke', () => {

expect(await relier.isLoggedIn()).toBe(true);
});

test('can setup TOTP inline and login', async ({
target,
pages: { page, relier, settings, signin, totp },
testAccountTracker,
}) => {
const credentials = await testAccountTracker.signUp();

await relier.goto();
await relier.clickRequire2FA();
await signin.fillOutEmailFirstForm(credentials.email);
await signin.fillOutPasswordForm(credentials.password);

await page.waitForURL(/inline_totp_setup/);

await expect(
signin.page.getByText('Enable two-step authentication')
).toBeVisible();

await signin.page.getByRole('button', { name: 'Continue' }).click();

await expect(
signin.page.getByText('Scan authentication code')
).toBeVisible();

await signin.page
.getByRole('button', { name: 'Can’t scan code?' })
.click();

await expect(signin.page.getByText('Enter code manually')).toBeVisible();

const secret = (
await signin.page.getByTestId('manual-code').innerText()
)?.replace(/\s/g, '');
const code = await getCode(secret);

await signin.page
.getByRole('textbox', { name: 'Authentication code' })
.fill(code);

await signin.page.getByRole('button', { name: 'Ready' }).click();

await page.waitForURL(/inline_recovery_setup/);

const codesRaw = await signin.page.getByTestId('datablock').innerText();
const recoveryCodes = codesRaw.trim().split(/\s+/);

await signin.page.getByRole('button', { name: 'Continue' }).click();

await expect(
signin.page.getByText('Confirm backup authentication code')
).toBeVisible();

await signin.page
.getByRole('textbox', { name: 'Backup authentication code' })
.fill(recoveryCodes[0]);

await signin.page.getByRole('button', { name: 'Confirm' }).click();

expect(await relier.isLoggedIn()).toBe(true);

await settings.goto();
await settings.disconnectTotp();
});
});
});

Expand All @@ -87,7 +151,7 @@ async function signInAccount(
await signin.fillOutEmailFirstForm(credentials.email);
await signin.fillOutPasswordForm(credentials.password);

//Verify logged in on Settings page
// Verify logged in on Settings page
await expect(settings.settingsHeading).toBeVisible();

return credentials;
Expand Down
12 changes: 9 additions & 3 deletions packages/fxa-settings/src/pages/InlineTotpSetup/container.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import { RouteComponentProps, useLocation } from '@reach/router';
import { useNavigateWithQuery as useNavigate } from '../../lib/hooks/useNavigateWithQuery';
import LoadingSpinner from 'fxa-react/components/LoadingSpinner';
import { useCallback, useEffect, useState } from 'react';
import { useCallback, useEffect, useState, useRef } from 'react';
import InlineTotpSetup from '.';
import { MozServices } from '../../lib/types';
import { OAuthIntegration, useSession } from '../../models';
Expand Down Expand Up @@ -46,6 +46,7 @@ export const InlineTotpSetupContainer = ({
const metricsContext = queryParamsToMetricsContext(
flowQueryParams as unknown as Record<string, string>
);
const isTotpCreating = useRef(false);

const [createTotp] = useMutation<{ createTotp: TotpToken }>(
CREATE_TOTP_MUTATION
Expand Down Expand Up @@ -81,14 +82,19 @@ export const InlineTotpSetupContainer = ({
const verified = await session.isSessionVerified();
setSessionVerified(verified);
})();
}, [session, sessionVerified]);
}, [session, sessionVerified, setSessionVerified]);

// Determine if a totp needs to be setup, and if so trigger setup.
useEffect(() => {
if (totp !== undefined || totpStatus?.account.totp.verified === true) {
if (
totp !== undefined ||
totpStatus?.account.totp.verified === true ||
isTotpCreating.current
) {
return;
}
(async () => {
isTotpCreating.current = true;
const totpResp = await createTotp({
variables: {
input: {
Expand Down

0 comments on commit fcb2242

Please sign in to comment.