Skip to content

Commit 42aa28a

Browse files
chore: added front channel and silent callback page
1 parent d16383e commit 42aa28a

File tree

16 files changed

+200
-42
lines changed

16 files changed

+200
-42
lines changed

package-lock.json

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/core/build/config.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,16 @@ const copyConfig = base => {
115115
to: 'localstorage-sync.html',
116116
toType: 'file',
117117
},
118+
{
119+
from: path.resolve(__dirname, '../src/root_files/front-channel.html'),
120+
to: 'front-channel.html',
121+
toType: 'file',
122+
},
123+
{
124+
from: path.resolve(__dirname, '../src/root_files/silent-callback.html'),
125+
to: 'silent-callback.html',
126+
toType: 'file',
127+
},
118128
{ from: path.resolve(__dirname, '../src/root_files/robots.txt'), to: 'robots.txt', toType: 'file' },
119129
{ from: path.resolve(__dirname, '../src/root_files/sitemap.xml'), to: 'sitemap.xml', toType: 'file' },
120130
{

packages/core/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@
9898
"@datadog/browser-rum": "^5.11.0",
9999
"@deriv-com/analytics": "1.30.2",
100100
"@deriv-com/auth-client": "1.4.0",
101+
"oidc-client-ts": "^3.1.0",
101102
"@deriv-com/quill-tokens": "2.0.4",
102103
"@deriv-com/quill-ui": "1.24.4",
103104
"@deriv-com/translations": "1.3.9",

packages/core/src/App/AppContent.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -167,11 +167,13 @@ const AppContent: React.FC<{ passthrough: unknown }> = observer(({ passthrough }
167167
}, [has_wallet, current_language, changeSelectedLanguage, is_dark_mode_on, setDarkMode]);
168168

169169
const isCallBackPage = window.location.pathname.includes('callback');
170+
const isOauthFlowPage =
171+
window.location.pathname.includes('front-channel') || window.location.pathname.includes('silent-callback');
170172

171173
return (
172174
<ThemeProvider theme={is_dark_mode_on ? 'dark' : 'light'}>
173-
<LandscapeBlocker />
174-
{!isCallBackPage && <Header />}
175+
{!isOauthFlowPage && <LandscapeBlocker />}
176+
{!isCallBackPage && !isOauthFlowPage && <Header />}
175177
<ErrorBoundary root_store={store}>
176178
<AppContents>
177179
{/* TODO: [trader-remove-client-base] */}

packages/core/src/App/Constants/routes-config.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import Endpoint from 'Modules/Endpoint';
1111

1212
import OSRedirect from '../Containers/OSRedirect';
1313
import CallbackPage from '../../Modules/Callback/CallbackPage.tsx';
14+
import FrontChannelPage from '../../Modules/FrontChannel';
15+
import SilentCallbackPage from '../../Modules/SilentCallback';
1416

1517
const CFDCompareAccounts = React.lazy(
1618
() => import(/* webpackChunkName: "cfd-compare-accounts" */ '@deriv/cfd/src/Containers/cfd-compare-accounts')
@@ -367,6 +369,18 @@ const getModules = () => {
367369
is_authenticated: false,
368370
getTitle: () => 'Callback',
369371
},
372+
{
373+
path: routes.front_channel_page,
374+
component: FrontChannelPage,
375+
is_authenticated: false,
376+
getTitle: () => 'FrontChannel',
377+
},
378+
{
379+
path: routes.silent_callback_page,
380+
component: SilentCallbackPage,
381+
is_authenticated: false,
382+
getTitle: () => 'Silent Callback',
383+
},
370384
];
371385

372386
return modules;
@@ -385,6 +399,12 @@ const initRoutesConfig = () => [
385399
{ path: routes.os_redirect, component: OSRedirect, getTitle: () => localize('Redirect') },
386400
{ path: routes.redirect, component: Redirect, getTitle: () => localize('Redirect') },
387401
{ path: routes.callback_page, component: CallbackPage, getTitle: () => 'Callback' },
402+
{ path: routes.front_channel_page, component: FrontChannelPage, getTitle: () => 'FrontChannel' },
403+
{
404+
path: routes.silent_callback_page,
405+
component: SilentCallbackPage,
406+
getTitle: () => 'Silent Callback',
407+
},
388408
{
389409
path: routes.complaints_policy,
390410
component: lazyLoadComplaintsPolicy(),
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import React from 'react';
2+
import { withRouter } from 'react-router-dom';
3+
import { useStore } from '@deriv/stores';
4+
5+
const FrontChannelPage = () => {
6+
const store = useStore();
7+
const { logout, is_client_store_initialized } = store.client;
8+
9+
React.useEffect(() => {
10+
if (is_client_store_initialized) logout();
11+
}, [is_client_store_initialized]);
12+
return <></>;
13+
};
14+
15+
export default withRouter(FrontChannelPage);
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import FrontChannelPage from './FrontChannelPage';
2+
3+
export default FrontChannelPage;
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import { requestOidcToken, requestLegacyToken } from '@deriv-com/auth-client';
2+
import React, { useCallback, useEffect } from 'react';
3+
import { withRouter, useLocation } from 'react-router-dom';
4+
5+
const SilentCallbackPage = () => {
6+
const fetchTokens = useCallback(() => {
7+
try {
8+
window.parent.postMessage({
9+
event: 'login_successful',
10+
value: {
11+
acct1: 'abcd1234',
12+
},
13+
});
14+
} catch (err) {
15+
console.error('unable to exchange tokens during silent login', err);
16+
window.parent.postMessage({
17+
event: 'login_error',
18+
value: err,
19+
});
20+
}
21+
}, []);
22+
23+
useEffect(() => {
24+
const params = new URLSearchParams(window.location.search);
25+
const oneTimeCode = params.get('code');
26+
const errorType = params.get('error');
27+
28+
if (errorType === 'login_required') {
29+
window.parent.postMessage({
30+
event: 'login_required',
31+
});
32+
} else if (errorType === 'consent_required') {
33+
window.parent.postMessage({
34+
event: 'login_successful',
35+
value: {
36+
acct1: 'abcd1234',
37+
},
38+
});
39+
} else {
40+
if (oneTimeCode) {
41+
fetchTokens();
42+
}
43+
}
44+
}, []);
45+
46+
return (
47+
<div>
48+
<h1>Silent callback</h1>
49+
</div>
50+
);
51+
};
52+
53+
export default withRouter(SilentCallbackPage);
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import SilentCallbackPage from './SilentCallback';
2+
3+
export default SilentCallbackPage;

packages/core/src/index.html

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<!DOCTYPE html>
1+
<!doctype html>
22
<html lang="en" translate="no" dir="auto">
33
<head>
44
<!-- Set skeleton theme color properties -->
@@ -85,7 +85,10 @@
8585
var antiClickjack = document.getElementById('antiClickjack');
8686
antiClickjack.parentNode.removeChild(antiClickjack);
8787
} else {
88-
top.location = self.location;
88+
const isAuthFlow = /\/(silent-callback|front-channel)($|\/)/.test(self.location.pathname);
89+
if (!isAuthFlow) {
90+
top.location = self.location;
91+
}
8992
}
9093
</script>
9194
<!-- End Anti-Clickjack -->
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<!doctype html>
2+
<html>
3+
<head>
4+
<title>Deriv</title>
5+
<meta charset="utf-8" />
6+
<meta name="referrer" content="origin" />
7+
<script>
8+
localStorage.removeItem('client.accounts');
9+
</script>
10+
</head>
11+
12+
<body></body>
13+
</html>
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<!doctype html>
2+
<html>
3+
<head>
4+
<title>Deriv</title>
5+
<meta charset="utf-8" />
6+
<meta name="referrer" content="origin" />
7+
<script>
8+
const params = new URLSearchParams(window.location.search);
9+
const oneTimeCode = params.get('code');
10+
const errorType = params.get('error');
11+
12+
if (errorType === 'login_required') {
13+
window.parent.postMessage({
14+
event: 'login_required',
15+
});
16+
} else if (errorType === 'consent_required' || oneTimeCode) {
17+
window.parent.postMessage({
18+
event: 'sso_required',
19+
});
20+
}
21+
</script>
22+
</head>
23+
24+
<body></body>
25+
</html>

packages/hooks/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
"@binary-com/binary-document-uploader": "^2.4.8",
99
"@deriv-com/analytics": "1.30.2",
1010
"@deriv-com/auth-client": "1.4.0",
11+
"oidc-client-ts": "^3.1.0",
1112
"@deriv-com/ui": "1.36.4",
1213
"@deriv-com/utils": "^0.0.42",
1314
"@deriv/api": "^1.0.0",

packages/hooks/src/useOauth2.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ const useOauth2 = ({ handleLogout }: { handleLogout: () => Promise<void> }) => {
4646
});
4747
};
4848

49-
return { isOAuth2Enabled, oAuthLogout: logoutHandler, loginHandler };
49+
return { isOAuth2Enabled: true, oAuthLogout: logoutHandler, loginHandler };
5050
};
5151

5252
export default useOauth2;

packages/hooks/src/useSilentLoginAndLogout.ts

Lines changed: 42 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { useEffect } from 'react';
22
import Cookies from 'js-cookie';
33

44
import { requestOidcAuthentication } from '@deriv-com/auth-client';
5+
import { UserManager, WebStorageStateStore } from 'oidc-client-ts';
56

67
/**
78
* Handles silent login and single logout logic for OAuth2.
@@ -26,49 +27,58 @@ const useSilentLoginAndLogout = ({
2627
const clientAccounts = JSON.parse(localStorage.getItem('client.accounts') || '{}');
2728
const isClientAccountsPopulated = Object.keys(clientAccounts).length > 0;
2829
const isSilentLoginExcluded =
29-
window.location.pathname.includes('callback') || window.location.pathname.includes('endpoint');
30+
window.location.pathname.includes('callback') ||
31+
window.location.pathname.includes('silent-callback') ||
32+
window.location.pathname.includes('endpoint');
3033

3134
useEffect(() => {
35+
window.addEventListener(
36+
'message',
37+
message => {
38+
if (message.data?.event === 'login_required') {
39+
console.log('OIDC: prompt none says we are logged out');
40+
if (isClientAccountsPopulated) {
41+
oAuthLogout();
42+
}
43+
} else if (message.data?.event === 'sso_required') {
44+
console.log('OIDC: we need to SSO NOW');
45+
// requestOidcAuthentication({
46+
// redirectCallbackUri: `${window.location.origin}/callback`,
47+
// });
48+
}
49+
},
50+
false
51+
);
3252
// NOTE: Remove this logic once social signup is intergated with OIDC
3353
const params = new URLSearchParams(window.location.search);
3454
const isUsingLegacyFlow = params.has('token1') && params.has('acct1');
3555
if (isUsingLegacyFlow && loggedState === 'false' && isOAuth2Enabled) {
3656
return;
3757
}
3858

39-
if (
40-
!isUsingLegacyFlow &&
41-
loggedState === 'true' &&
42-
!isClientAccountsPopulated &&
43-
isOAuth2Enabled &&
44-
is_client_store_initialized &&
45-
!isSilentLoginExcluded
46-
) {
47-
// Perform silent login
48-
requestOidcAuthentication({
49-
redirectCallbackUri: `${window.location.origin}/callback`,
59+
if (isOAuth2Enabled && !isUsingLegacyFlow && !isClientAccountsPopulated && !isSilentLoginExcluded) {
60+
console.log('OIDC: checking if we need SSO...');
61+
const userManager = new UserManager({
62+
authority: 'https://qa20.deriv.dev',
63+
client_id: '1000005',
64+
redirect_uri: 'https://localhost:8443/callback',
65+
silent_redirect_uri: 'https://localhost:8443/silent-callback',
66+
response_type: 'code',
67+
scope: 'openid',
68+
stateStore: new WebStorageStateStore({ store: window.localStorage }),
69+
// this is enabled by default, it runs a silent renew service in the background which triggers the prompt=none auth calls
70+
// Source: https://github.com/authts/oidc-client-ts/blob/9ccae8f87b3e9e2df349aaf6f007964ced287b02/src/UserManagerSettings.ts#L140
71+
// Notable issue: https://github.com/authts/oidc-client-ts/issues/1174
72+
automaticSilentRenew: false,
73+
});
74+
userManager.signinSilent({
75+
extraQueryParams: {
76+
brand: 'deriv',
77+
},
78+
silentRequestTimeoutInSeconds: 60000,
5079
});
5180
}
52-
53-
if (
54-
!isUsingLegacyFlow &&
55-
loggedState === 'false' &&
56-
is_client_store_initialized &&
57-
isOAuth2Enabled &&
58-
isClientAccountsPopulated &&
59-
!window.location.pathname.includes('callback')
60-
) {
61-
// Perform single logout
62-
oAuthLogout();
63-
}
64-
}, [
65-
loggedState,
66-
isClientAccountsPopulated,
67-
is_client_store_initialized,
68-
isOAuth2Enabled,
69-
oAuthLogout,
70-
isSilentLoginExcluded,
71-
]);
81+
}, [isOAuth2Enabled]);
7282
};
7383

7484
export default useSilentLoginAndLogout;

packages/shared/src/utils/routes/routes.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import { getUrlSmartTrader } from '../url/helpers';
22

33
export const routes = {
44
callback_page: '/callback',
5+
front_channel_page: '/front-channel',
6+
silent_callback_page: '/silent-callback',
57
reset_password: '/',
68
error404: '/404',
79
index: '/index',
@@ -128,8 +130,3 @@ export const ACCOUNTS_OS_POI_STATUS_URL =
128130
process.env.NODE_ENV === 'production'
129131
? 'https://hub.deriv.com/Accounts/ProofOfIdentityStatus'
130132
: 'https://staging-hub.deriv.com/Accounts/ProofOfIdentityStatus';
131-
132-
export const ACCOUNTS_OS_POA_URL =
133-
process.env.NODE_ENV === 'production'
134-
? 'https://hub.deriv.com/Accounts/ProofOfAddress'
135-
: 'https://staging-hub.deriv.com/Accounts/ProofOfAddress';

0 commit comments

Comments
 (0)