Skip to content

Commit da0f117

Browse files
authored
Merge pull request #20093 from mozilla/FXA-12951-2
chore(fxa): Show cached login for third party auth
2 parents b94584b + b78e360 commit da0f117

8 files changed

Lines changed: 174 additions & 94 deletions

File tree

packages/fxa-settings/src/components/App/index.test.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -548,6 +548,7 @@ describe('SettingsRoutes', () => {
548548
(useIntegration as jest.Mock).mockReturnValue({
549549
isSync: () => true,
550550
isFirefoxClientServiceRelay: () => false,
551+
isFirefoxDesktopClient: () => true,
551552
getCmsInfo: jest.fn(),
552553
getLegalTerms: jest.fn(),
553554
data: {

packages/fxa-settings/src/components/App/index.tsx

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,10 @@ export const App = ({
195195
// Determine if user is actually signed in
196196
const [isSignedIn, setIsSignedIn] = useState<boolean | undefined>(undefined);
197197

198+
// Track whether the user is signed into Firefox Desktop via WebChannel
199+
const [isSignedIntoFirefoxDesktop, setIsSignedIntoFirefoxDesktop] =
200+
useState(false);
201+
198202
// Track current page's split layout state to prevent visual flashing during navigation.
199203
// This state is updated by AppLayout and read by the Suspense fallback to preserve
200204
// the origin page's layout until the destination page loads.
@@ -222,6 +226,10 @@ export const App = ({
222226
userFromBrowser.sessionToken
223227
);
224228
if (isValidSession) {
229+
setIsSignedIntoFirefoxDesktop(
230+
!!userFromBrowser?.sessionToken &&
231+
integration.isFirefoxDesktopClient()
232+
);
225233
const cachedUser = getAccountByUid(userFromBrowser.uid);
226234
// Refresh the token without switching the "current" account.
227235
persistAccount(
@@ -384,6 +392,7 @@ export const App = ({
384392
isSignedIn,
385393
integration,
386394
flowQueryParams: updatedFlowQueryParams,
395+
isSignedIntoFirefoxDesktop,
387396
setCurrentSplitLayout,
388397
}}
389398
path="/*"
@@ -458,11 +467,13 @@ const AuthAndAccountSetupRoutes = ({
458467
isSignedIn,
459468
integration,
460469
flowQueryParams,
470+
isSignedIntoFirefoxDesktop,
461471
setCurrentSplitLayout,
462472
}: {
463473
isSignedIn: boolean;
464474
integration: Integration;
465475
flowQueryParams: QueryParams;
476+
isSignedIntoFirefoxDesktop: boolean;
466477
setCurrentSplitLayout: (value: boolean) => void;
467478
} & RouteComponentProps) => {
468479
const localAccount = currentAccount();
@@ -589,6 +600,7 @@ const AuthAndAccountSetupRoutes = ({
589600
serviceName,
590601
flowQueryParams,
591602
useFxAStatusResult,
603+
isSignedIntoFirefoxDesktop,
592604
setCurrentSplitLayout,
593605
}}
594606
/>
@@ -599,6 +611,7 @@ const AuthAndAccountSetupRoutes = ({
599611
serviceName,
600612
flowQueryParams,
601613
useFxAStatusResult,
614+
isSignedIntoFirefoxDesktop,
602615
setCurrentSplitLayout,
603616
}}
604617
/>
@@ -609,6 +622,7 @@ const AuthAndAccountSetupRoutes = ({
609622
serviceName,
610623
flowQueryParams,
611624
useFxAStatusResult,
625+
isSignedIntoFirefoxDesktop,
612626
setCurrentSplitLayout,
613627
}}
614628
/>
@@ -619,6 +633,7 @@ const AuthAndAccountSetupRoutes = ({
619633
serviceName,
620634
flowQueryParams,
621635
useFxAStatusResult,
636+
isSignedIntoFirefoxDesktop,
622637
setCurrentSplitLayout,
623638
}}
624639
/>

packages/fxa-settings/src/pages/Signin/container.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,12 +139,14 @@ const SigninContainer = ({
139139
serviceName,
140140
flowQueryParams,
141141
useFxAStatusResult,
142+
isSignedIntoFirefoxDesktop = false,
142143
setCurrentSplitLayout,
143144
}: {
144145
integration: Integration;
145146
serviceName: MozServices;
146147
flowQueryParams?: QueryParams;
147148
useFxAStatusResult: UseFxAStatusResult;
149+
isSignedIntoFirefoxDesktop?: boolean;
148150
setCurrentSplitLayout?: (value: boolean) => void;
149151
} & RouteComponentProps) => {
150152
const config = useConfig();
@@ -629,6 +631,7 @@ const SigninContainer = ({
629631
localizedSuccessBannerDescription,
630632
flowQueryParams,
631633
useFxAStatusResult,
634+
isSignedIntoFirefoxDesktop,
632635
setCurrentSplitLayout,
633636
}}
634637
/>

packages/fxa-settings/src/pages/Signin/index.stories.tsx

Lines changed: 14 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -147,36 +147,26 @@ export const SignInRelayNoPasswordlessSupport = storyWithProps({
147147
supportsKeysOptionalLogin: false,
148148
});
149149

150-
export const SignInRelayWithPasswordlessSupport = storyWithProps({
150+
export const SignInNonSyncWithBrowserPasswordlessSupport = storyWithProps({
151151
integration: createMockSigninOAuthNativeIntegration({
152-
service: OAuthNativeServices.Relay,
152+
service: OAuthNativeServices.SmartWindow,
153153
isSync: false,
154154
}),
155155
supportsKeysOptionalLogin: true,
156156
});
157157

158-
export const CachedSignInRelayWithPasswordlessSupport = storyWithProps({
159-
sessionToken: MOCK_SESSION_TOKEN,
160-
integration: createMockSigninOAuthNativeIntegration({
161-
service: OAuthNativeServices.Relay,
162-
isSync: false,
163-
}),
164-
supportsKeysOptionalLogin: true,
165-
});
158+
export const CachedSignInNonSyncWithBrowserPasswordlessSupport = storyWithProps(
159+
{
160+
sessionToken: MOCK_SESSION_TOKEN,
161+
integration: createMockSigninOAuthNativeIntegration({
162+
service: OAuthNativeServices.SmartWindow,
163+
isSync: false,
164+
}),
165+
supportsKeysOptionalLogin: true,
166+
}
167+
);
166168

167-
export const CachedSignInSmartWindowWithPasswordlessSupport = storyWithProps({
169+
export const CachedSignInSignedIntoFirefoxDesktop = storyWithProps({
168170
sessionToken: MOCK_SESSION_TOKEN,
169-
integration: createMockSigninOAuthNativeIntegration({
170-
service: OAuthNativeServices.SmartWindow,
171-
isSync: false,
172-
}),
173-
supportsKeysOptionalLogin: true,
174-
});
175-
176-
export const SignInSmartWindowWithPasswordlessSupport = storyWithProps({
177-
integration: createMockSigninOAuthNativeIntegration({
178-
service: OAuthNativeServices.SmartWindow,
179-
isSync: false,
180-
}),
181-
supportsKeysOptionalLogin: true,
171+
isSignedIntoFirefoxDesktop: true,
182172
});

packages/fxa-settings/src/pages/Signin/index.test.tsx

Lines changed: 102 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,16 @@ function thirdPartyAuthRendered() {
173173
name: /Continue with Apple/,
174174
});
175175
}
176+
177+
function thirdPartyAuthNotRendered() {
178+
expect(
179+
screen.queryByRole('button', { name: /Continue with Google/ })
180+
).not.toBeInTheDocument();
181+
expect(
182+
screen.queryByRole('button', { name: /Continue with Apple/ })
183+
).not.toBeInTheDocument();
184+
}
185+
176186
function signInButtonAndSeparatorRendered() {
177187
screen.getByRole('button', { name: 'Sign in' });
178188
screen.getByText('or');
@@ -257,12 +267,7 @@ describe('Signin component', () => {
257267
// to `enterPasswordAndSubmit()` because it's wrapped in a `setTimeout`
258268
expect(hardNavigateSpy).toHaveBeenCalled();
259269

260-
expect(
261-
screen.queryByRole('button', { name: /Continue with Google/ })
262-
).not.toBeInTheDocument();
263-
expect(
264-
screen.queryByRole('button', { name: /Continue with Apple/ })
265-
).not.toBeInTheDocument();
270+
thirdPartyAuthNotRendered();
266271
expect(GleanMetrics.login.view).toHaveBeenCalledWith({
267272
event: { thirdPartyLinks: false },
268273
});
@@ -278,12 +283,7 @@ describe('Signin component', () => {
278283
supportsKeysOptionalLogin: false,
279284
});
280285

281-
expect(
282-
screen.queryByRole('button', { name: /Continue with Google/ })
283-
).not.toBeInTheDocument();
284-
expect(
285-
screen.queryByRole('button', { name: /Continue with Apple/ })
286-
).not.toBeInTheDocument();
286+
thirdPartyAuthNotRendered();
287287
expect(GleanMetrics.login.view).toHaveBeenCalledWith({
288288
event: { thirdPartyLinks: false },
289289
});
@@ -968,7 +968,9 @@ describe('Signin component', () => {
968968
thirdPartyAuthRendered();
969969
privacyAndTermsRendered();
970970
differentAccountLinkRendered();
971-
resetPasswordLinkRendered();
971+
expect(
972+
screen.queryByRole('link', { name: 'Forgot password?' })
973+
).not.toBeInTheDocument();
972974

973975
passwordInputNotRendered();
974976
});
@@ -984,7 +986,6 @@ describe('Signin component', () => {
984986
expect(
985987
screen.queryByRole('link', { name: 'Forgot password?' })
986988
).not.toBeInTheDocument();
987-
expect(screen.queryByText('Or')).not.toBeInTheDocument();
988989
expect(
989990
screen.queryByRole('button', { name: 'Sign in' })
990991
).not.toBeInTheDocument();
@@ -1013,16 +1014,18 @@ describe('Signin component', () => {
10131014

10141015
expect(GleanMetrics.cachedLogin.view).toHaveBeenCalledTimes(1);
10151016
expect(GleanMetrics.cachedLogin.view).toHaveBeenCalledWith({
1016-
event: { thirdPartyLinks: true },
1017+
event: { thirdPartyLinks: false },
10171018
});
10181019
signInHeaderRendered();
10191020
avatarAndEmailRendered();
1020-
thirdPartyAuthRendered();
1021-
signInButtonAndSeparatorRendered();
1021+
screen.getByRole('button', { name: 'Sign in' });
10221022
privacyAndTermsRendered();
1023-
resetPasswordLinkRendered();
10241023
differentAccountLinkRendered();
1025-
1024+
thirdPartyAuthNotRendered();
1025+
expect(
1026+
screen.queryByRole('link', { name: 'Forgot password?' })
1027+
).not.toBeInTheDocument();
1028+
expect(screen.queryByText('or')).not.toBeInTheDocument();
10261029
passwordInputNotRendered();
10271030
});
10281031

@@ -1039,7 +1042,7 @@ describe('Signin component', () => {
10391042

10401043
passwordInputNotRendered();
10411044
expect(GleanMetrics.cachedLogin.view).toHaveBeenCalledWith({
1042-
event: { thirdPartyLinks: true },
1045+
event: { thirdPartyLinks: false },
10431046
});
10441047
});
10451048

@@ -1056,7 +1059,7 @@ describe('Signin component', () => {
10561059

10571060
passwordInputNotRendered();
10581061
expect(GleanMetrics.cachedLogin.view).toHaveBeenCalledWith({
1059-
event: { thirdPartyLinks: true },
1062+
event: { thirdPartyLinks: false },
10601063
});
10611064
});
10621065

@@ -1101,19 +1104,6 @@ describe('Signin component', () => {
11011104
});
11021105
});
11031106

1104-
it('emits an event on forgot password link click', async () => {
1105-
renderWithLocalizationProvider(
1106-
<Subject sessionToken={MOCK_SESSION_TOKEN} />
1107-
);
1108-
1109-
fireEvent.click(screen.getByText('Forgot password?'));
1110-
await waitFor(() => {
1111-
expect(GleanMetrics.cachedLogin.forgotPassword).toHaveBeenCalledTimes(
1112-
1
1113-
);
1114-
});
1115-
});
1116-
11171107
describe('successful submission', () => {
11181108
it('submits and emits metrics', async () => {
11191109
const cachedSigninHandler = jest
@@ -1447,16 +1437,19 @@ describe('Signin component', () => {
14471437

14481438
signInHeaderRendered();
14491439
avatarAndEmailRendered();
1450-
signInButtonAndSeparatorRendered();
1451-
thirdPartyAuthRendered();
1440+
screen.getByRole('button', { name: 'Sign in' });
14521441
privacyAndTermsRendered();
14531442
differentAccountLinkRendered();
1454-
resetPasswordLinkRendered();
1443+
thirdPartyAuthNotRendered();
1444+
1445+
expect(
1446+
screen.queryByRole('link', { name: 'Forgot password?' })
1447+
).not.toBeInTheDocument();
14551448

14561449
passwordInputNotRendered();
14571450
});
14581451

1459-
it('renders as expected with linked account', () => {
1452+
it('renders as expected with linked account (passwordless)', () => {
14601453
renderWithLocalizationProvider(
14611454
<Subject
14621455
sessionToken={MOCK_SESSION_TOKEN}
@@ -1466,18 +1459,86 @@ describe('Signin component', () => {
14661459
);
14671460
signInHeaderRendered();
14681461
avatarAndEmailRendered();
1469-
thirdPartyAuthRendered();
14701462
privacyAndTermsRendered();
1463+
thirdPartyAuthNotRendered();
1464+
1465+
screen.getByRole('button', { name: 'Sign in' });
1466+
1467+
// OAuth buttons and forgot password should NOT be rendered for cached users
1468+
expect(
1469+
screen.queryByRole('link', { name: 'Forgot password?' })
1470+
).not.toBeInTheDocument();
14711471

14721472
passwordInputNotRendered();
1473+
});
1474+
});
1475+
1476+
describe('UI simplification for cached users', () => {
1477+
it('hides OAuth buttons for cached users', () => {
1478+
renderWithLocalizationProvider(
1479+
<Subject sessionToken={MOCK_SESSION_TOKEN} />
1480+
);
1481+
1482+
thirdPartyAuthNotRendered();
1483+
});
1484+
1485+
it('hides forgot password link for cached users', () => {
1486+
renderWithLocalizationProvider(
1487+
<Subject sessionToken={MOCK_SESSION_TOKEN} />
1488+
);
1489+
14731490
expect(
14741491
screen.queryByRole('link', { name: 'Forgot password?' })
14751492
).not.toBeInTheDocument();
1476-
expect(screen.queryByText('Or')).not.toBeInTheDocument();
1493+
});
1494+
1495+
it('hides "use a different account" link for Firefox Desktop users', () => {
1496+
renderWithLocalizationProvider(
1497+
<Subject
1498+
sessionToken={MOCK_SESSION_TOKEN}
1499+
isSignedIntoFirefoxDesktop={true}
1500+
/>
1501+
);
1502+
14771503
expect(
1478-
screen.queryByRole('button', { name: 'Sign in' })
1504+
screen.queryByRole('link', { name: 'Use a different account' })
14791505
).not.toBeInTheDocument();
14801506
});
1507+
1508+
it('shows "use a different account" link for non-Firefox Desktop users', () => {
1509+
renderWithLocalizationProvider(
1510+
<Subject sessionToken={MOCK_SESSION_TOKEN} />
1511+
);
1512+
1513+
expect(
1514+
screen.getByRole('link', { name: 'Use a different account' })
1515+
).toBeInTheDocument();
1516+
});
1517+
1518+
it('shows cached signin button for passwordless accounts', () => {
1519+
renderWithLocalizationProvider(
1520+
<Subject
1521+
sessionToken={MOCK_SESSION_TOKEN}
1522+
hasPassword={false}
1523+
hasLinkedAccount={true}
1524+
/>
1525+
);
1526+
1527+
// Sign in button should be visible for passwordless cached users
1528+
expect(
1529+
screen.getByRole('button', { name: 'Sign in' })
1530+
).toBeInTheDocument();
1531+
});
1532+
1533+
it('emits correct Glean metrics for cached signin with hidden OAuth', () => {
1534+
renderWithLocalizationProvider(
1535+
<Subject sessionToken={MOCK_SESSION_TOKEN} />
1536+
);
1537+
1538+
expect(GleanMetrics.cachedLogin.view).toHaveBeenCalledWith({
1539+
event: { thirdPartyLinks: false },
1540+
});
1541+
});
14811542
});
14821543
});
14831544

0 commit comments

Comments
 (0)