Skip to content

Commit bd25379

Browse files
Adrienne / Fix SSO triggering multiple times due to useeffect dependencies (#18114)
* feat: store redirect metadata for traders hub from os-redirect * Merge branch 'master' of github.com:deriv-com/deriv-app * chore: refactored sso and slo conditions * chore: remove debouncing in sso and slo handler * chore: resolve test cases
1 parent 39c1841 commit bd25379

File tree

4 files changed

+73
-55
lines changed

4 files changed

+73
-55
lines changed

packages/core/src/App/AppContent.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,6 @@ const AppContent: React.FC<{ passthrough: unknown }> = observer(({ passthrough }
7373
is_client_store_initialized,
7474
isOAuth2Enabled,
7575
oAuthLogout,
76-
prevent_single_login,
7776
});
7877

7978
const [isWebPasskeysFFEnabled, isGBLoaded] = useGrowthbookIsOn({

packages/core/src/App/Containers/RootComponent/root-component.jsx

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,14 @@ const RootComponent = observer(props => {
2525
setIsWalletsOnboardingTourGuideVisible,
2626
notification_messages_ui,
2727
} = ui;
28-
const { has_wallet, logout, prevent_redirect_to_hub, is_client_store_initialized, setPreventSingleLogin } = client;
28+
const {
29+
has_wallet,
30+
logout,
31+
prevent_redirect_to_hub,
32+
is_client_store_initialized,
33+
prevent_single_login,
34+
setPreventSingleLogin,
35+
} = client;
2936

3037
const { oAuthLogout } = useOauth2({ handleLogout: logout });
3138

@@ -56,14 +63,15 @@ const RootComponent = observer(props => {
5663
}
5764

5865
const shouldStayInDerivApp = !isHubRedirectionEnabled || !has_wallet || prevent_redirect_to_hub;
59-
if (isHubRedirectionLoaded && is_client_store_initialized && shouldStayInDerivApp) {
66+
if (prevent_single_login && isHubRedirectionLoaded && is_client_store_initialized && shouldStayInDerivApp) {
6067
setPreventSingleLogin(false);
6168
}
6269
}, [
6370
isHubRedirectionLoaded,
6471
isHubRedirectionEnabled,
6572
has_wallet,
6673
prevent_redirect_to_hub,
74+
prevent_single_login,
6775
is_client_store_initialized,
6876
]);
6977

packages/hooks/src/__tests__/useSilentLoginAndLogout.spec.ts renamed to packages/hooks/src/__tests__/useSilentLoginAndLogout.spec.tsx

Lines changed: 48 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1+
import React from 'react';
12
import Cookies from 'js-cookie';
23

34
import { requestOidcAuthentication } from '@deriv-com/auth-client';
45
import { renderHook } from '@testing-library/react-hooks';
56

67
import useSilentLoginAndLogout from '../useSilentLoginAndLogout';
8+
import { mockStore, StoreProvider } from '@deriv/stores';
79

810
jest.mock('js-cookie', () => ({
911
get: jest.fn(),
@@ -15,6 +17,12 @@ jest.mock('@deriv-com/auth-client', () => ({
1517

1618
describe('useSilentLoginAndLogout', () => {
1719
const mockOAuthLogout = jest.fn();
20+
const mockStoreData = mockStore({
21+
client: { prevent_single_login: false },
22+
});
23+
const wrapper = ({ children }: { children: JSX.Element }) => (
24+
<StoreProvider store={mockStoreData}>{children}</StoreProvider>
25+
);
1826

1927
beforeEach(() => {
2028
jest.clearAllMocks();
@@ -42,12 +50,14 @@ describe('useSilentLoginAndLogout', () => {
4250

4351
jest.spyOn(Storage.prototype, 'getItem').mockReturnValue(JSON.stringify({}));
4452

45-
renderHook(() =>
46-
useSilentLoginAndLogout({
47-
is_client_store_initialized: true,
48-
isOAuth2Enabled: true,
49-
oAuthLogout: mockOAuthLogout,
50-
})
53+
renderHook(
54+
() =>
55+
useSilentLoginAndLogout({
56+
is_client_store_initialized: true,
57+
isOAuth2Enabled: true,
58+
oAuthLogout: mockOAuthLogout,
59+
}),
60+
{ wrapper }
5161
);
5262

5363
expect(requestOidcAuthentication).toHaveBeenCalledWith({
@@ -61,12 +71,14 @@ describe('useSilentLoginAndLogout', () => {
6171

6272
jest.spyOn(Storage.prototype, 'getItem').mockReturnValue(JSON.stringify({}));
6373

64-
renderHook(() =>
65-
useSilentLoginAndLogout({
66-
is_client_store_initialized: true,
67-
isOAuth2Enabled: true,
68-
oAuthLogout: mockOAuthLogout,
69-
})
74+
renderHook(
75+
() =>
76+
useSilentLoginAndLogout({
77+
is_client_store_initialized: true,
78+
isOAuth2Enabled: true,
79+
oAuthLogout: mockOAuthLogout,
80+
}),
81+
{ wrapper }
7082
);
7183

7284
expect(requestOidcAuthentication).not.toHaveBeenCalled();
@@ -83,12 +95,14 @@ describe('useSilentLoginAndLogout', () => {
8395
value: { pathname: '/callback' },
8496
});
8597

86-
renderHook(() =>
87-
useSilentLoginAndLogout({
88-
is_client_store_initialized: true,
89-
isOAuth2Enabled: true,
90-
oAuthLogout: mockOAuthLogout,
91-
})
98+
renderHook(
99+
() =>
100+
useSilentLoginAndLogout({
101+
is_client_store_initialized: true,
102+
isOAuth2Enabled: true,
103+
oAuthLogout: mockOAuthLogout,
104+
}),
105+
{ wrapper }
92106
);
93107

94108
expect(requestOidcAuthentication).not.toHaveBeenCalled();
@@ -100,12 +114,14 @@ describe('useSilentLoginAndLogout', () => {
100114

101115
jest.spyOn(Storage.prototype, 'getItem').mockReturnValue(JSON.stringify({ account1: {}, account2: {} }));
102116

103-
renderHook(() =>
104-
useSilentLoginAndLogout({
105-
is_client_store_initialized: true,
106-
isOAuth2Enabled: true,
107-
oAuthLogout: mockOAuthLogout,
108-
})
117+
renderHook(
118+
() =>
119+
useSilentLoginAndLogout({
120+
is_client_store_initialized: true,
121+
isOAuth2Enabled: true,
122+
oAuthLogout: mockOAuthLogout,
123+
}),
124+
{ wrapper }
109125
);
110126

111127
expect(requestOidcAuthentication).not.toHaveBeenCalled();
@@ -117,12 +133,14 @@ describe('useSilentLoginAndLogout', () => {
117133

118134
jest.spyOn(Storage.prototype, 'getItem').mockReturnValue(JSON.stringify({ account1: {}, account2: {} }));
119135

120-
renderHook(() =>
121-
useSilentLoginAndLogout({
122-
is_client_store_initialized: true,
123-
isOAuth2Enabled: true,
124-
oAuthLogout: mockOAuthLogout,
125-
})
136+
renderHook(
137+
() =>
138+
useSilentLoginAndLogout({
139+
is_client_store_initialized: true,
140+
isOAuth2Enabled: true,
141+
oAuthLogout: mockOAuthLogout,
142+
}),
143+
{ wrapper }
126144
);
127145

128146
expect(requestOidcAuthentication).not.toHaveBeenCalled();

packages/hooks/src/useSilentLoginAndLogout.ts

Lines changed: 15 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
import { useEffect } from 'react';
1+
import { useEffect, useRef } from 'react';
22
import Cookies from 'js-cookie';
33

44
import { requestOidcAuthentication } from '@deriv-com/auth-client';
5-
5+
import { useStore } from '@deriv/stores';
66
/**
77
* Handles silent login and single logout logic for OAuth2.
88
*
@@ -16,60 +16,53 @@ const useSilentLoginAndLogout = ({
1616
is_client_store_initialized,
1717
isOAuth2Enabled,
1818
oAuthLogout,
19-
prevent_single_login,
2019
}: {
2120
is_client_store_initialized: boolean;
2221
isOAuth2Enabled: boolean;
2322
oAuthLogout: () => Promise<void>;
24-
prevent_single_login?: boolean;
2523
}) => {
2624
const loggedState = Cookies.get('logged_state');
2725

26+
const { client } = useStore();
2827
const clientAccounts = JSON.parse(localStorage.getItem('client.accounts') || '{}');
2928
const isClientAccountsPopulated = Object.keys(clientAccounts).length > 0;
3029
const isSilentLoginExcluded =
3130
window.location.pathname.includes('callback') || window.location.pathname.includes('endpoint');
3231

32+
// state to manage and ensure OIDC callback functions are invoked once only
33+
const isAuthenticating = useRef(false);
34+
const isLoggingOut = useRef(false);
35+
const { prevent_single_login } = client;
36+
3337
useEffect(() => {
34-
if (prevent_single_login) return;
38+
if (prevent_single_login || !isOAuth2Enabled || !is_client_store_initialized || isSilentLoginExcluded) return;
3539
// NOTE: Remove this logic once social signup is intergated with OIDC
3640
const params = new URLSearchParams(window.location.search);
3741
const isUsingLegacyFlow = params.has('token1') && params.has('acct1');
3842
if (isUsingLegacyFlow && loggedState === 'false' && isOAuth2Enabled) {
3943
return;
4044
}
4145

42-
if (
43-
!isUsingLegacyFlow &&
44-
loggedState === 'true' &&
45-
!isClientAccountsPopulated &&
46-
isOAuth2Enabled &&
47-
is_client_store_initialized &&
48-
!isSilentLoginExcluded
49-
) {
46+
if (!isUsingLegacyFlow && loggedState === 'true' && !isClientAccountsPopulated) {
5047
// Perform silent login
48+
if (isAuthenticating.current) return;
49+
isAuthenticating.current = true;
5150
requestOidcAuthentication({
5251
redirectCallbackUri: `${window.location.origin}/callback`,
5352
});
5453
}
5554

56-
if (
57-
!isUsingLegacyFlow &&
58-
loggedState === 'false' &&
59-
is_client_store_initialized &&
60-
isOAuth2Enabled &&
61-
isClientAccountsPopulated &&
62-
!window.location.pathname.includes('callback')
63-
) {
55+
if (!isUsingLegacyFlow && loggedState === 'false' && isClientAccountsPopulated) {
6456
// Perform single logout
57+
if (isLoggingOut.current) return;
58+
isLoggingOut.current = true;
6559
oAuthLogout();
6660
}
6761
}, [
6862
loggedState,
6963
isClientAccountsPopulated,
7064
is_client_store_initialized,
7165
isOAuth2Enabled,
72-
oAuthLogout,
7366
isSilentLoginExcluded,
7467
prevent_single_login,
7568
]);

0 commit comments

Comments
 (0)