Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions packages/fxa-settings/src/lib/passkeys/signin-flow.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -128,11 +128,13 @@ const buildArgs = (
emails: [{ email: EMAIL, isPrimary: true, verified: true }],
totp: { exists: false, verified: false },
});
const sessionResendVerifyCode = jest.fn();

const authClient = {
beginPasskeyAuthentication,
completePasskeyAuthentication,
account,
sessionResendVerifyCode
} as jest.Mocked<PasskeySignInAuthClient>;
const integration = {
isSync: () => false,
Expand Down Expand Up @@ -231,6 +233,7 @@ describe('usePasskeySignIn', () => {
performNavigation: true,
isPasskeySession: true,
accountHasTotp: false,
authClient: args.authClient,
});
expect(storeAccountData).toHaveBeenCalledWith({
email: EMAIL,
Expand Down Expand Up @@ -281,6 +284,7 @@ describe('usePasskeySignIn', () => {
performNavigation: true,
isPasskeySession: true,
accountHasTotp: false,
authClient: args.authClient,
});
});

Expand Down
3 changes: 2 additions & 1 deletion packages/fxa-settings/src/lib/passkeys/signin-flow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ export type PasskeySignInIntegration = NavigationOptions['integration'];
/** Pick<> so tests can pass minimal mocks without `as any`. */
export type PasskeySignInAuthClient = Pick<
AuthClient,
'beginPasskeyAuthentication' | 'completePasskeyAuthentication' | 'account'
'beginPasskeyAuthentication' | 'completePasskeyAuthentication' | 'account' | 'sessionResendVerifyCode'
>;

/**
Expand Down Expand Up @@ -373,6 +373,7 @@ export function usePasskeySignIn({
performNavigation: true,
isPasskeySession: true,
accountHasTotp,
authClient
});

if (navError) {
Expand Down
2 changes: 2 additions & 0 deletions packages/fxa-settings/src/models/mocks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -147,10 +147,12 @@ export function mockAuthClient() {
if (typeof jest !== 'undefined') {
return {
sessionStatus: jest.fn().mockResolvedValue(mockSessionStatus),
sessionResendVerifyCode: jest.fn().mockResolvedValue(undefined),
} as unknown as AuthClient;
} else {
return {
sessionStatus: () => Promise.resolve(mockSessionStatus),
sessionResendVerifyCode: () => Promise.resolve(undefined),
} as unknown as AuthClient;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import {
RelierAccount,
useAuthClient,
useConfig,
useSession,
} from '../../models';

import { useCallback, useEffect, useState, useRef } from 'react';
Expand Down Expand Up @@ -58,7 +57,6 @@ const AuthorizationContainer = ({
const config = useConfig();
const location = useLocation();
const navigateWithQuery = useNavigateWithQuery();
const session = useSession();
const { finishOAuthFlowHandler, oAuthDataError } = useFinishOAuthFlowHandler(
authClient,
integration
Expand All @@ -76,12 +74,9 @@ const AuthorizationContainer = ({
relierAccount
);

const isOauthPromptNone = true;
const { data, error } = await cachedSignIn(
account?.sessionToken!,
authClient,
session,
isOauthPromptNone
);

if (error === AuthUiErrors.SESSION_EXPIRED) {
Expand Down Expand Up @@ -122,6 +117,7 @@ const AuthorizationContainer = ({
redirectTo: integration.data.redirectTo,
finishOAuthFlowHandler,
queryParams: location.search,
authClient
};

const { error: navError } = await handleNavigation(navigationOptions);
Expand Down Expand Up @@ -156,7 +152,6 @@ const AuthorizationContainer = ({
integration,
location.search,
navigateWithQuery,
session,
]);

useEffect(() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@ const SetPasswordContainer = ({
// users will see a "flash" of whatever page we navigate them to
// before the client closes the view. See FXA-11944
performNavigation: !integration.isFirefoxMobileClient(),
authClient
};

const { error } = await handleNavigation(navigationOptions);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { renderWithLocalizationProvider } from 'fxa-react/lib/test-utils/localiz
import ThirdPartyAuthCallback from '.';
import { AppContext } from '../../../models';
import { createAppContext, mockAppContext } from '../../../models/mocks';
import { useAccount } from '../../../models';
import { useAccount, useAuthClient } from '../../../models';
import { useFinishOAuthFlowHandler } from '../../../lib/oauth/hooks';
import {
handleNavigation,
Expand All @@ -23,17 +23,19 @@ import { MOCK_EMAIL, MOCK_SESSION_TOKEN } from '../../mocks';
import { LocationProvider } from '@reach/router';
import { GenericData } from '../../../lib/model-data';

jest.mock('../../../models', () => ({
...jest.requireActual('../../../models'),
useClientInfoState: jest.fn(),
useProductInfoState: jest.fn(),
useAccount: jest.fn(),
useAuthClient: () => {
return {
checkTotpTokenExists: jest.fn().mockResolvedValue({ verified: true }),
};
},
}));
jest.mock('../../../models', () => {
const mockAuthClient = {
checkTotpTokenExists: jest.fn().mockResolvedValue({ verified: true }),
sessionResendVerifyCode: jest.fn().mockResolvedValue(undefined),
};
return {
...jest.requireActual('../../../models'),
useClientInfoState: jest.fn(),
useProductInfoState: jest.fn(),
useAccount: jest.fn(),
useAuthClient: () => mockAuthClient,
};
});

jest.mock('@reach/router', () => ({
...jest.requireActual('@reach/router'),
Expand Down Expand Up @@ -165,6 +167,15 @@ describe('ThirdPartyAuthCallback component', () => {
mockHandleNavigation = jest.fn().mockResolvedValue({ error: null });
(handleNavigation as jest.Mock).mockReturnValue(mockHandleNavigation);
(ensureCanLinkAcountOrRedirect as jest.Mock).mockResolvedValue(true);
// useAuthClient returns a stable mock; restore its implementations here
// because afterEach's resetAllMocks() wipes them between tests.
const authClient = useAuthClient();
(authClient.checkTotpTokenExists as jest.Mock).mockResolvedValue({
verified: true,
});
(authClient.sessionResendVerifyCode as jest.Mock).mockResolvedValue(
undefined
);
mockNavigateWithQuery.mockClear();
mockCurrentAccount();
});
Expand Down Expand Up @@ -262,6 +273,7 @@ describe('ThirdPartyAuthCallback component', () => {
isSignInWithThirdPartyAuth: true,
queryParams: '?',
redirectTo: redirectTo,
authClient: useAuthClient(),
signinData: {
sessionToken: MOCK_SESSION_TOKEN,
uid: '123',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ const ThirdPartyAuthCallback = ({
// user sets a password and keys are available (see SetPassword/container.tsx).
handleFxaLogin: isFirefoxNonSync,
handleFxaOAuthLogin: isFirefoxNonSync,
authClient
};

const { error: navError } = await handleNavigation(navigationOptions);
Expand All @@ -144,6 +145,7 @@ const ThirdPartyAuthCallback = ({
navigateWithQuery,
webRedirectCheck,
ftlMsgResolver,
authClient
]
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ const SigninPasskeyFallbackContainer = ({
queryParams: location.search,
handleFxaLogin: true,
handleFxaOAuthLogin: true,
authClient
});
if (navError) {
GleanMetrics.passkeyEnterPassword.submitFrontendError({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,7 @@ const SigninPasswordlessCode = ({
integration.isFirefoxMobileClient() && isSessionVerified
),
isPasswordlessOtpSignin: true,
authClient
};

// For existing users signing into Sync (signin flow), show merge warning
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,14 @@ jest.mock('../../../lib/hooks/useNavigateWithQuery', () => ({
useNavigateWithQuery: () => mockNavigateWithQuery,
}));

const mockSessionResendVerifyCode = jest.fn().mockResolvedValue(undefined);
jest.mock('../../../models', () => ({
...jest.requireActual('../../../models'),
useAuthClient: () => ({
sessionResendVerifyCode: mockSessionResendVerifyCode,
}),
}));

jest.mock('../../../lib/glean', () => ({
__esModule: true,
default: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import { useCallback, useEffect, useState } from 'react';
import { RouteComponentProps, useLocation } from '@reach/router';
import { FtlMsg } from 'fxa-react/lib/utils';
import { isWebIntegration, useFtlMsgResolver } from '../../../models';
import { isWebIntegration, useAuthClient, useFtlMsgResolver } from '../../../models';
import { BackupCodesImage } from '../../../components/images';
import LinkExternal from 'fxa-react/components/LinkExternal';
import FormVerifyCode, {
Expand Down Expand Up @@ -55,6 +55,7 @@ const SigninRecoveryCode = ({
'Backup authentication code required'
);
const location = useLocation();
const authClient = useAuthClient();

const webRedirectCheck = useWebRedirect(integration.data.redirectTo);

Expand Down Expand Up @@ -109,6 +110,7 @@ const SigninRecoveryCode = ({
handleFxaLogin: true,
handleFxaOAuthLogin: true,
performNavigation: !integration.isFirefoxMobileClient(),
authClient
};

const { error } = await handleNavigation(navigationOptions);
Expand All @@ -129,6 +131,7 @@ const SigninRecoveryCode = ({
uid,
unwrapBKey,
ftlMsgResolver,
authClient
]);

const localizedInvalidCodeError = getLocalizedErrorMessage(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ const SigninRecoveryPhoneContainer = ({
handleFxaLogin: true,
handleFxaOAuthLogin: true,
performNavigation: !integration.isFirefoxMobileClient(),
authClient
};

await handleNavigation(navigationOptions);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { RouteComponentProps, useLocation } from '@reach/router';
import { FtlMsg } from 'fxa-react/lib/utils';
import {
isWebIntegration,
useAuthClient,
useFtlMsgResolver,
useSession,
} from '../../../models';
Expand Down Expand Up @@ -62,6 +63,7 @@ const SigninTokenCode = ({
const [resendCountdown, setResendCountdown] = useState<number>(0);
const { isThrottled, startThrottle } = useThrottle();

const authClient = useAuthClient();
const webRedirectCheck = useWebRedirect(integration.data.redirectTo);
const redirectTo =
isWebIntegration(integration) && webRedirectCheck?.isValid
Expand Down Expand Up @@ -186,6 +188,7 @@ const SigninTokenCode = ({
handleFxaLogin: false,
handleFxaOAuthLogin: true,
performNavigation: !integration.isFirefoxMobileClient(),
authClient
};

await GleanMetrics.isDone();
Expand Down Expand Up @@ -228,6 +231,7 @@ const SigninTokenCode = ({
showInlineRecoveryKeySetup,
onSessionVerified,
startThrottle,
authClient
]
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import React, { useEffect, useState } from 'react';
import { Link, RouteComponentProps, useLocation } from '@reach/router';
import { FtlMsg } from 'fxa-react/lib/utils';
import { useAlertBar, useFtlMsgResolver, useSession } from '../../../models';
import { useAlertBar, useAuthClient, useFtlMsgResolver, useSession } from '../../../models';
import { logViewEvent } from '../../../lib/metrics';
import { MozServices } from '../../../lib/types';
import firefox from '../../../lib/channels/firefox';
Expand Down Expand Up @@ -58,6 +58,7 @@ export const SigninTotpCode = ({
const session = useSession();
const isSessionAALUpgrade = signinState.isSessionAALUpgrade;
const alertBar = useAlertBar();
const authClient = useAuthClient();

const [bannerError, setBannerError] = useState<string>('');

Expand Down Expand Up @@ -176,6 +177,7 @@ export const SigninTotpCode = ({
handleFxaLogin: true,
handleFxaOAuthLogin: true,
performNavigation: !integration.isFirefoxMobileClient(),
authClient
};

const { error: navError } = await handleNavigation(navigationOptions);
Expand Down
26 changes: 15 additions & 11 deletions packages/fxa-settings/src/pages/Signin/SigninTotpCode/mocks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@
import { LocationProvider } from '@reach/router';
import { OAuthNativeServices } from '@fxa/accounts/oauth';
import {
AppContext,
IntegrationData,
IntegrationType,
RelierCmsInfo,
} from '../../../models';
import { mockAppContext } from '../../../models/mocks';
import { SigninTotpCode, SigninTotpCodeProps } from '.';
import {
MOCK_EMAIL,
Expand Down Expand Up @@ -100,16 +102,18 @@ export const Subject = ({
submitTotpCode = mockSubmitTotpCode,
}: Partial<SigninTotpCodeProps>) => {
return (
<LocationProvider>
<SigninTotpCode
{...{
finishOAuthFlowHandler,
integration,
serviceName,
signinState,
submitTotpCode,
}}
/>
</LocationProvider>
<AppContext.Provider value={mockAppContext()}>
<LocationProvider>
<SigninTotpCode
{...{
finishOAuthFlowHandler,
integration,
serviceName,
signinState,
submitTotpCode,
}}
/>
</LocationProvider>
</AppContext.Provider>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,14 @@ jest.mock('@reach/router', () => ({
navigate: jest.fn(),
}));

const mockSessionResendVerifyCode = jest.fn().mockResolvedValue(undefined);
jest.mock('../../../models', () => ({
...jest.requireActual('../../../models'),
useAuthClient: () => ({
sessionResendVerifyCode: mockSessionResendVerifyCode,
}),
}));

const email = MOCK_EMAIL;
const hasLinkedAccount = false;
const hasPassword = true;
Expand Down
Loading
Loading