From 730fe48dd981f4b7fd790c4437f383e869caaa93 Mon Sep 17 00:00:00 2001
From: Adrienne Rio
Date: Mon, 2 Dec 2024 15:39:32 +0800
Subject: [PATCH 01/15] chore: regenerate package lock
---
package-lock.json | 85 ++++++++++++++++++++++++-----------------------
package.json | 2 +-
2 files changed, 45 insertions(+), 42 deletions(-)
diff --git a/package-lock.json b/package-lock.json
index cfd75a34be1..8ddf213f30a 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -14,7 +14,7 @@
"@binary-com/binary-style": "^0.2.26",
"@binary-com/webtrader-charts": "^0.6.2",
"@deriv-com/analytics": "^1.26.1",
- "@deriv-com/auth-client": "^1.0.15",
+ "@deriv-com/auth-client": "^1.2.15",
"@deriv-com/quill-ui": "^1.16.2",
"@deriv-com/utils": "^0.0.38",
"@deriv/deriv-api": "^1.0.15",
@@ -2527,49 +2527,30 @@
}
},
"node_modules/@deriv-com/auth-client": {
- "version": "1.0.15",
- "resolved": "https://registry.npmjs.org/@deriv-com/auth-client/-/auth-client-1.0.15.tgz",
- "integrity": "sha512-2G6IUtPIX2TmlpW2khDUeltqfsxJtxZgiFdoHidW19rjZYDaj4aHDEh/RXArbiUUKF0i0FGgupzAuW4Id/o7SA==",
+ "version": "1.2.15",
+ "resolved": "https://registry.npmjs.org/@deriv-com/auth-client/-/auth-client-1.2.15.tgz",
+ "integrity": "sha512-tplrAeklKg/o1EZZXd6FSowvfwKzebzyUPLk0S/HN/tGeCDljCu8jo2aMXeJGzX9G338686nPYq2EnfeCnVgUg==",
"dependencies": {
- "@deriv-com/utils": "^0.0.33",
- "react": "^18.3.1",
- "react-dom": "^18.3.1"
+ "@deriv-com/utils": "^0.0.42",
+ "js-cookie": "3.0.5",
+ "oidc-client-ts": "^3.1.0"
+ },
+ "optionalDependencies": {
+ "@rollup/rollup-linux-x64-gnu": "^4.27.3"
}
},
"node_modules/@deriv-com/auth-client/node_modules/@deriv-com/utils": {
- "version": "0.0.33",
- "resolved": "https://registry.npmjs.org/@deriv-com/utils/-/utils-0.0.33.tgz",
- "integrity": "sha512-LzIpzMvfWhK9y06Qpe/HOB4pFCizk2wAyhv9I0s48Romq+d5MM1mmsuh5CvS4SnzzdLyuBy4rgXrOO3394HB7w=="
+ "version": "0.0.42",
+ "resolved": "https://registry.npmjs.org/@deriv-com/utils/-/utils-0.0.42.tgz",
+ "integrity": "sha512-4JhTpg0sQWCq94RSMGpuT/09bYSV8yO3WdunM2R84qxWNitRH/i4k/xfdleRVzX+xLSmMmJWlkbD6NAlP8U5eg=="
},
- "node_modules/@deriv-com/auth-client/node_modules/react": {
- "version": "18.3.1",
- "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
- "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==",
- "dependencies": {
- "loose-envify": "^1.1.0"
- },
+ "node_modules/@deriv-com/auth-client/node_modules/js-cookie": {
+ "version": "3.0.5",
+ "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz",
+ "integrity": "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==",
+ "license": "MIT",
"engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/@deriv-com/auth-client/node_modules/react-dom": {
- "version": "18.3.1",
- "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz",
- "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==",
- "dependencies": {
- "loose-envify": "^1.1.0",
- "scheduler": "^0.23.2"
- },
- "peerDependencies": {
- "react": "^18.3.1"
- }
- },
- "node_modules/@deriv-com/auth-client/node_modules/scheduler": {
- "version": "0.23.2",
- "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz",
- "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==",
- "dependencies": {
- "loose-envify": "^1.1.0"
+ "node": ">=14"
}
},
"node_modules/@deriv-com/quill-tokens": {
@@ -3244,12 +3225,13 @@
"integrity": "sha512-yNN7S2HHh2is9WrLlfEnPdX5rO9Nlv3wmBXSWXXQTt9mAC5PU/NFQRfGGI/fBNRSQA4PGqgz5LYHwTcHhEwXeQ=="
},
"node_modules/@rollup/rollup-linux-x64-gnu": {
- "version": "4.20.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.20.0.tgz",
- "integrity": "sha512-y+eoL2I3iphUg9tN9GB6ku1FA8kOfmF4oUEWhztDJ4KXJy1agk/9+pejOuZkNFhRwHAOxMsBPLbXPd6mJiCwew==",
+ "version": "4.28.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.28.0.tgz",
+ "integrity": "sha512-Nl4KIzteVEKE9BdAvYoTkW19pa7LR/RBrT6F1dJCV/3pbjwDcaOq+edkP0LXuJ9kflW/xOK414X78r+K84+msw==",
"cpu": [
"x64"
],
+ "license": "MIT",
"optional": true,
"os": [
"linux"
@@ -12181,6 +12163,15 @@
"resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.2.1.tgz",
"integrity": "sha512-g3UB796vUFIY90VIv/WX3L2c8CS2MdWUww3CNrYmqza1Fg0DURc2K/O4YrnklBdQarSJ/y8JnJYDGc+1iumQjg=="
},
+ "node_modules/jwt-decode": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-4.0.0.tgz",
+ "integrity": "sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
+ },
"node_modules/keyv": {
"version": "4.5.4",
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
@@ -16422,6 +16413,18 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/oidc-client-ts": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/oidc-client-ts/-/oidc-client-ts-3.1.0.tgz",
+ "integrity": "sha512-IDopEXjiwjkmJLYZo6BTlvwOtnlSniWZkKZoXforC/oLZHC9wkIxd25Kwtmo5yKFMMVcsp3JY6bhcNJqdYk8+g==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "jwt-decode": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
"node_modules/on-finished": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
diff --git a/package.json b/package.json
index a0c3ec3bbcc..c3925b24fe4 100644
--- a/package.json
+++ b/package.json
@@ -107,7 +107,7 @@
"@binary-com/binary-style": "^0.2.26",
"@binary-com/webtrader-charts": "^0.6.2",
"@deriv-com/analytics": "^1.26.1",
- "@deriv-com/auth-client": "^1.0.15",
+ "@deriv-com/auth-client": "^1.2.15",
"@deriv-com/quill-ui": "^1.16.2",
"@deriv-com/utils": "^0.0.38",
"@deriv/deriv-api": "^1.0.15",
From 1110bd3c3114791eb0e775bfe47211b3cf9d57e5 Mon Sep 17 00:00:00 2001
From: Adrienne Rio
Date: Mon, 2 Dec 2024 15:48:04 +0800
Subject: [PATCH 02/15] feat: migrated oidc implementation from old pr
---
scripts/config/pages.js | 3 +-
src/javascript/_common/auth.js | 83 +++++++++--
src/javascript/app/base/binary_pages.js | 2 +
src/javascript/app/base/callback.js | 16 ++
src/javascript/app/base/header.js | 92 +++++++-----
src/javascript/app/base/logged_in.js | 2 -
src/javascript/app/base/page.js | 2 +
.../app/pages/callback/callback.jsx | 137 ++++++++++++++++++
src/javascript/app/pages/deriv_iframe.jsx | 19 ---
src/templates/_common/_layout/layout.jsx | 1 -
.../_common/includes/deriv-iframe.jsx | 5 -
src/templates/app/callback.jsx | 30 ++++
src/templates/app/logged_in.jsx | 1 -
13 files changed, 315 insertions(+), 78 deletions(-)
create mode 100644 src/javascript/app/base/callback.js
create mode 100644 src/javascript/app/pages/callback/callback.jsx
delete mode 100644 src/javascript/app/pages/deriv_iframe.jsx
delete mode 100644 src/templates/_common/includes/deriv-iframe.jsx
create mode 100644 src/templates/app/callback.jsx
diff --git a/scripts/config/pages.js b/scripts/config/pages.js
index 370d3179c2b..e9a98f8add6 100644
--- a/scripts/config/pages.js
+++ b/scripts/config/pages.js
@@ -64,6 +64,7 @@ module.exports = [
// ['dialog', 'app/dialog', null],
['explanation', 'app/trade/explanation', null],
['logged_inws', 'app/logged_in', null],
+ ['callback', 'app/callback', null],
// ['redirect', 'app/logged_in', null, 'Redirecting...'],
// ==================== Section: "static" ====================
@@ -120,4 +121,4 @@ module.exports = [
// ['graduates', 'landing_pages/graduate_program', null, 'Binary.com Graduate Program', 'NOT-en'],
// ['hackathon', 'landing_pages/hackathon', null, 'Hackathon Competition', 'NOT-en'],
// ['introducing-usb', 'landing_pages/usb', null, 'Introducing USB', 'NOT-en'],
-];
+];
\ No newline at end of file
diff --git a/src/javascript/_common/auth.js b/src/javascript/_common/auth.js
index 6a54713b52c..73bdbad0d12 100644
--- a/src/javascript/_common/auth.js
+++ b/src/javascript/_common/auth.js
@@ -5,6 +5,8 @@ const {
URLConstants,
WebSocketUtils,
} = require('@deriv-com/utils');
+const Cookies = require('js-cookie');
+const requestOidcAuthentication = require('@deriv-com/auth-client').requestOidcAuthentication;
const Analytics = require('./analytics');
export const DEFAULT_OAUTH_LOGOUT_URL = 'https://oauth.deriv.com/oauth2/sessions/logout';
@@ -79,22 +81,33 @@ export const isOAuth2Enabled = () => {
export const getLogoutHandler = onWSLogoutAndRedirect => {
const isAuthEnabled = isOAuth2Enabled();
+ let timeout;
if (!isAuthEnabled) {
return onWSLogoutAndRedirect;
}
- const onMessage = async event => {
- const allowedOrigin = getOAuthOrigin();
- if (allowedOrigin === event.origin) {
- if (event.data === 'logout_complete') {
- try {
- await onWSLogoutAndRedirect();
- } catch (err) {
- // eslint-disable-next-line no-console
- console.error(`logout was completed successfully on oauth hydra server, but logout handler returned error: ${err}`);
- }
+ const cleanup = () => {
+ clearTimeout(timeout);
+
+ const iframe = document.getElementById('logout-iframe');
+ if (iframe) iframe.remove();
+ };
+
+ const onMessage = event => {
+ if (event.data === 'logout_complete') {
+ const domains = ['deriv.com', 'binary.sx', 'pages.dev', 'localhost'];
+ const currentDomain = window.location.hostname.split('.').slice(-2).join('.');
+ if (domains.includes(currentDomain)) {
+ Cookies.set('logged_state', 'false', {
+ expires: 30,
+ path : '/',
+ secure : true,
+ });
}
+ onWSLogoutAndRedirect();
+ window.removeEventListener('message', onMessage);
+ cleanup();
}
};
@@ -113,8 +126,10 @@ export const getLogoutHandler = onWSLogoutAndRedirect => {
iframe.style.display = 'none';
document.body.appendChild(iframe);
- setTimeout(() => {
+ timeout = setTimeout(() => {
onWSLogoutAndRedirect();
+ window.removeEventListener('message', onMessage);
+ cleanup();
}, LOGOUT_HANDLER_TIMEOUT);
}
@@ -123,3 +138,49 @@ export const getLogoutHandler = onWSLogoutAndRedirect => {
return oAuth2Logout;
};
+
+export const requestSingleSignOn = async () => {
+ const _requestSingleSignOn = async () => {
+ // if we have previously logged in,
+ // this cookie will be set by the Callback page (which is exported from @deriv-com/auth-client library) to true when we have successfully logged in from other apps
+ const isLoggedInCookie = Cookies.get('logged_state') === 'true';
+ const clientAccounts = JSON.parse(localStorage.getItem('client.accounts') || '{}');
+ const isClientAccountsPopulated = Object.keys(clientAccounts).length > 0;
+ const isAuthEnabled = isOAuth2Enabled();
+ const isCallbackPage = window.location.pathname.includes('callback');
+ const isEndpointPage = window.location.pathname.includes('endpoint');
+
+ // we only do SSO if:
+ // we have previously logged-in before from SmartTrader or any other apps (Deriv.app, etc) - isLoggedInCookie
+ // if we are not in the callback route to prevent re-calling this function - !isCallbackPage
+ // if client.accounts in localStorage is empty - !isClientAccountsPopulated
+ // and if feature flag for OIDC Phase 2 is enabled - isAuthEnabled
+ if (isLoggedInCookie && !isCallbackPage && !isEndpointPage && !isClientAccountsPopulated && isAuthEnabled) {
+ await requestOidcAuthentication({
+ redirectCallbackUri: `${window.location.origin}/en/callback`,
+ });
+ }
+ };
+
+ const isGrowthbookLoaded = Analytics.isGrowthbookLoaded();
+ if (!isGrowthbookLoaded) {
+ let retryInterval = 0;
+ // this interval is to check if Growthbook is already initialised.
+ // If not, keep checking it (max 10 times) and SSO if conditions are met
+ const interval = setInterval(() => {
+ if (retryInterval > 10) {
+ clearInterval(interval);
+ } else {
+ const isLoaded = Analytics.isGrowthbookLoaded();
+ if (isLoaded) {
+ _requestSingleSignOn();
+ clearInterval(interval);
+ } else {
+ retryInterval += 1;
+ }
+ }
+ }, 500);
+ } else {
+ _requestSingleSignOn();
+ }
+};
diff --git a/src/javascript/app/base/binary_pages.js b/src/javascript/app/base/binary_pages.js
index b35779f60d7..4ec8f9d00c0 100644
--- a/src/javascript/app/base/binary_pages.js
+++ b/src/javascript/app/base/binary_pages.js
@@ -3,6 +3,7 @@
// ==================== app ====================
const LoggedInHandler = require('./logged_in');
+const CallbackHandler = require('./callback');
// const Redirect = require('./redirect');
// const AccountTransfer = require('../pages/cashier/account_transfer');
// const Cashier = require('../pages/cashier/cashier');
@@ -98,6 +99,7 @@ const pages_config = {
// landing_page : { module: StaticPages.LandingPage, is_authenticated: true, only_virtual: true },
// limitsws : { module: Limits, is_authenticated: true, no_mf: true, only_real: true, needs_currency: true },
logged_inws: { module: LoggedInHandler },
+ callback : { module: CallbackHandler },
// lost_passwordws : { module: LostPassword, not_authenticated: true },
// malta : { module: StaticPages.Locations },
// maltainvestws : { module: FinancialAccOpening, is_authenticated: true },
diff --git a/src/javascript/app/base/callback.js b/src/javascript/app/base/callback.js
new file mode 100644
index 00000000000..6a1db2c0563
--- /dev/null
+++ b/src/javascript/app/base/callback.js
@@ -0,0 +1,16 @@
+const SocketCache = require('../../_common/base/socket_cache');
+const CallbackElement = require('../pages/callback/callback.jsx');
+
+const CallbackHandler = (() => {
+ const onLoad = async () => {
+ parent.window.is_logging_in = 1; // this flag is used in base.js to prevent auto-reloading this page
+ CallbackElement.init();
+ SocketCache.clear();
+ };
+
+ return {
+ onLoad,
+ };
+})();
+
+module.exports = CallbackHandler;
diff --git a/src/javascript/app/base/header.js b/src/javascript/app/base/header.js
index 5f29ce9a82b..dabcf07a787 100644
--- a/src/javascript/app/base/header.js
+++ b/src/javascript/app/base/header.js
@@ -1,33 +1,33 @@
// const BinaryPjax = require('./binary_pjax');
-const Client = require('./client');
-const BinarySocket = require('./socket');
-const AuthClient = require('../../_common/auth');
-const showHidePulser = require('../common/account_opening').showHidePulser;
-const updateTotal = require('../pages/user/update_total');
-const isAuthenticationAllowed = require('../../_common/base/client_base').isAuthenticationAllowed;
-const GTM = require('../../_common/base/gtm');
-const Login = require('../../_common/base/login');
-const SocketCache = require('../../_common/base/socket_cache');
+const requestOidcAuthentication = require('@deriv-com/auth-client').requestOidcAuthentication;
+const Client = require('./client');
+const BinarySocket = require('./socket');
+const AuthClient = require('../../_common/auth');
+const showHidePulser = require('../common/account_opening').showHidePulser;
+const updateTotal = require('../pages/user/update_total');
+const isAuthenticationAllowed = require('../../_common/base/client_base').isAuthenticationAllowed;
+const GTM = require('../../_common/base/gtm');
+const Login = require('../../_common/base/login');
+const SocketCache = require('../../_common/base/socket_cache');
// const elementInnerHtml = require('../../_common/common_functions').elementInnerHtml;
-const getElementById = require('../../_common/common_functions').getElementById;
-const localize = require('../../_common/localize').localize;
-const localizeKeepPlaceholders = require('../../_common/localize').localizeKeepPlaceholders;
-const State = require('../../_common/storage').State;
-const Url = require('../../_common/url');
-const applyToAllElements = require('../../_common/utility').applyToAllElements;
-const createElement = require('../../_common/utility').createElement;
-const findParent = require('../../_common/utility').findParent;
-const getTopLevelDomain = require('../../_common/utility').getTopLevelDomain;
-const getPlatformSettings = require('../../../templates/_common/brand.config').getPlatformSettings;
-const getHostname = require('../../_common/utility').getHostname;
-const template = require('../../_common/utility').template;
-const Language = require('../../_common/language');
-const mapCurrencyName = require('../../_common/base/currency_base').mapCurrencyName;
-const isEuCountry = require('../common/country_base').isEuCountry;
-const DerivIFrame = require('../pages/deriv_iframe.jsx');
-const DerivLiveChat = require('../pages/livechat.jsx');
-const openChat = require('../../_common/utility.js').openChat;
-const getRemoteConfig = require('../hooks/useRemoteConfig').getRemoteConfig;
+const getElementById = require('../../_common/common_functions').getElementById;
+const localize = require('../../_common/localize').localize;
+const localizeKeepPlaceholders = require('../../_common/localize').localizeKeepPlaceholders;
+const State = require('../../_common/storage').State;
+const Url = require('../../_common/url');
+const applyToAllElements = require('../../_common/utility').applyToAllElements;
+const createElement = require('../../_common/utility').createElement;
+const findParent = require('../../_common/utility').findParent;
+const getTopLevelDomain = require('../../_common/utility').getTopLevelDomain;
+const getPlatformSettings = require('../../../templates/_common/brand.config').getPlatformSettings;
+const getHostname = require('../../_common/utility').getHostname;
+const template = require('../../_common/utility').template;
+const Language = require('../../_common/language');
+const mapCurrencyName = require('../../_common/base/currency_base').mapCurrencyName;
+const isEuCountry = require('../common/country_base').isEuCountry;
+const DerivLiveChat = require('../pages/livechat.jsx');
+const openChat = require('../../_common/utility.js').openChat;
+const getRemoteConfig = require('../hooks/useRemoteConfig').getRemoteConfig;
const header_icon_base_path = '/images/pages/header/';
const wallet_header_icon_base_path = '/images/pages/header/wallets/';
@@ -45,7 +45,6 @@ const Header = (() => {
};
const onLoad = () => {
- DerivIFrame.init();
populateAccountsList();
populateWalletAccounts();
bindSvg();
@@ -650,11 +649,25 @@ const Header = (() => {
// }
// };
- const loginOnClick = (e) => {
+ const loginOnClick = async (e) => {
e.preventDefault();
- Login.redirectToLogin();
+ const isOAuth2Enabled = AuthClient.isOAuth2Enabled();
+
+ if (isOAuth2Enabled) {
+ const redirectCallbackUri = `${window.location.origin}/en/callback`;
+ const postLoginRedirectUri = window.location.href;
+ const postLogoutRedirectUri = `${window.location.origin}/en/trading`;
+ // Test commit
+ await requestOidcAuthentication({
+ redirectCallbackUri,
+ postLoginRedirectUri,
+ postLogoutRedirectUri,
+ });
+ } else {
+ Login.redirectToLogin();
+ }
};
-
+
const logoutOnClick = async () => {
window.fcWidget?.user.clear().then(
() => window.fcWidget.destroy(),
@@ -663,8 +676,10 @@ const Header = (() => {
// This will wrap the logout call Client.sendLogoutRequest with our own logout iframe, which is to inform Hydra that the user is logging out
// and the session should be cleared on Hydra's side. Once this is done, it will call the passed-in logout handler Client.sendLogoutRequest.
// If Hydra authentication is not enabled, the logout handler Client.sendLogoutRequest will just be called instead.
- const onLogoutWithOauth = await AuthClient.getLogoutHandler(Client.sendLogoutRequest);
-
+ const onLogoutWithOauth = await AuthClient.getLogoutHandler(
+ Client.sendLogoutRequest
+ );
+
onLogoutWithOauth();
};
@@ -891,13 +906,14 @@ const Header = (() => {
const account_switcher_seperator = document.getElementById('cfd-link-seperator');
const multiplier_text = localize('Multipliers');
const account_header = document.querySelectorAll('.header__accounts-multiple');
+ const is_callback_page = window.location.pathname.includes('callback');
let is_virtual;
if (current_active_login) {
is_virtual = current_active_login.startsWith('VRTC');
}
const showTradersHubLink = (show) => {
- traders_hub_link.style.display = show ? 'flex' : 'none';
- account_switcher_seperator.style.display = show ? 'block' : 'none';
+ if (traders_hub_link.style) traders_hub_link.style.display = show ? 'flex' : 'none';
+ if (account_switcher_seperator.style) account_switcher_seperator.style.display = show ? 'block' : 'none';
};
account_header.forEach(header => {
@@ -908,8 +924,8 @@ const Header = (() => {
$(``).insertAfter('#header__acc-balance');
}
- if (has_real_account) showTradersHubLink(true);
- if (is_virtual) showTradersHubLink(true);
+ if (has_real_account && !is_callback_page) showTradersHubLink(true);
+ if (is_virtual && !is_callback_page) showTradersHubLink(true);
if (is_virtual || !has_real_account) {
manage_acc_btn.style.visibility = 'hidden';
}
diff --git a/src/javascript/app/base/logged_in.js b/src/javascript/app/base/logged_in.js
index 2a250d20dcd..0c081b0e3b2 100644
--- a/src/javascript/app/base/logged_in.js
+++ b/src/javascript/app/base/logged_in.js
@@ -12,7 +12,6 @@ const removeCookies = require('../../_common/storage').removeCookies;
const paramsHash = require('../../_common/url').paramsHash;
const urlFor = require('../../_common/url').urlFor;
const getPropertyValue = require('../../_common/utility').getPropertyValue;
-const DerivIFrame = require('../pages/deriv_iframe.jsx');
const LoggedInHandler = (() => {
const onLoad = () => {
@@ -20,7 +19,6 @@ const LoggedInHandler = (() => {
parent.window.is_logging_in = 1; // this flag is used in base.js to prevent auto-reloading this page
let redirect_url;
const params = paramsHash(window.location.href);
- DerivIFrame.init();
BinarySocket.send({ authorize: params.token1 }).then((response) => {
const account_list = getPropertyValue(response, ['authorize', 'account_list']);
if (isStorageSupported(localStorage) && isStorageSupported(sessionStorage) && account_list) {
diff --git a/src/javascript/app/base/page.js b/src/javascript/app/base/page.js
index 9b9d7282427..b5883366ce2 100644
--- a/src/javascript/app/base/page.js
+++ b/src/javascript/app/base/page.js
@@ -23,6 +23,7 @@ const toISOFormat = require('../../_common/string_util').toISOFormat;
const Url = require('../../_common/url');
const Analytics = require('../../_common/analytics');
const { openChatWithParam } = require('../../_common/utility');
+const { requestSingleSignOn } = require('../../_common/auth');
const createElement = require('../../_common/utility').createElement;
const isLoginPages = require('../../_common/utility').isLoginPages;
const isProduction = require('../../config').isProduction;
@@ -100,6 +101,7 @@ const Page = (() => {
updateLinksURL('#content');
} else {
init();
+ requestSingleSignOn();
if (!isLoginPages()) {
Language.setCookie(Language.urlLang());
const url_query_strings = Url.paramsHash();
diff --git a/src/javascript/app/pages/callback/callback.jsx b/src/javascript/app/pages/callback/callback.jsx
new file mode 100644
index 00000000000..7c47b26641e
--- /dev/null
+++ b/src/javascript/app/pages/callback/callback.jsx
@@ -0,0 +1,137 @@
+import React from 'react';
+import ReactDOM from 'react-dom';
+
+import { Callback } from '@deriv-com/auth-client';
+
+import Cookies from 'js-cookie';
+import moment from 'moment';
+import Client from '../../base/client';
+import BinarySocket from '../../base/socket';
+import GTM from '../../../_common/base/gtm';
+import { get as getLanguage, urlLang } from '../../../_common/language';
+import { isStorageSupported, removeCookies } from '../../../_common/storage';
+import { urlFor } from '../../../_common/url';
+import { getPropertyValue } from '../../../_common/utility';
+import { getElementById } from '../../../_common/common_functions';
+
+const map_names = {
+ country : 'residence',
+ landing_company_name: 'landing_company_shortcode',
+};
+
+const storeClientAccounts = (tokens, account_list) => {
+ // Parse url for loginids, tokens, and currencies returned by OAuth
+
+ // Clear all accounts before entering the loop
+ Client.clearAllAccounts();
+
+ account_list.forEach(account => {
+ Object.keys(account).forEach(param => {
+ if (param === 'loginid') {
+ if (!Client.get('loginid') && !account.is_disabled && !account.is_virtual) {
+ Client.set(param, account[param]);
+ }
+ } else {
+ const param_to_set = map_names[param] || param;
+ const value_to_set = typeof account[param] === 'undefined' ? '' : account[param];
+ Client.set(param_to_set, value_to_set, account.loginid);
+ }
+ });
+ });
+
+ let i = 1;
+ while (tokens[`acct${i}`]) {
+ const loginid = tokens[`acct${i}`];
+ const token = tokens[`token${i}`];
+ if (loginid && token) {
+ Client.set('token', token, loginid);
+ }
+ i++;
+ }
+
+ // if didn't find any login ID that matched the above condition
+ // or the selected one doesn't have a token, set the first one
+ if (!Client.get('loginid') || !Client.get('token')) {
+ Client.set('loginid', tokens.acct1 || account_list[0].loginid);
+ }
+
+ if (Client.isLoggedIn()) {
+ GTM.setLoginFlag('log_in');
+ Client.set('session_start', parseInt(moment().valueOf() / 1000));
+ // Remove cookies that were set by the old code
+ removeCookies('email', 'login', 'loginid', 'loginid_list', 'residence');
+ }
+};
+
+const CallbackContainer = () => {
+ const onLoginSuccess = async tokens => {
+ let redirect_url;
+ BinarySocket.send({ authorize: tokens.token1 }).then(response => {
+ const account_list = getPropertyValue(response, ['authorize', 'account_list']);
+ if (
+ isStorageSupported(localStorage) &&
+ isStorageSupported(sessionStorage) &&
+ account_list
+ ) {
+ // redirect url
+ redirect_url = sessionStorage.getItem('redirect_url');
+ sessionStorage.removeItem('redirect_url');
+
+ storeClientAccounts(tokens, account_list);
+ } else {
+ Client.doLogout({ logout: 1 });
+ }
+
+ // redirect back
+ let set_default = true;
+ if (redirect_url) {
+ const do_not_redirect = [
+ 'reset_passwordws',
+ 'lost_passwordws',
+ 'change_passwordws',
+ 'home',
+ '404',
+ ];
+ const reg = new RegExp(do_not_redirect.join('|'), 'i');
+ if (!reg.test(redirect_url) && urlFor('') !== redirect_url) {
+ set_default = false;
+ }
+ }
+ if (set_default) {
+ const lang_cookie = urlLang(redirect_url) || Cookies.get('language');
+ const language = getLanguage();
+ redirect_url =
+ Client.isAccountOfType('financial') || Client.isOptionsBlocked()
+ ? urlFor('user/metatrader')
+ : Client.defaultRedirectUrl();
+ if (lang_cookie && lang_cookie !== language) {
+ redirect_url = redirect_url.replace(
+ new RegExp(`/${language}/`, 'i'),
+ `/${lang_cookie.toLowerCase()}/`
+ );
+ }
+ }
+ getElementById('loading_link').setAttribute('href', redirect_url);
+ const domains = ['deriv.com', 'binary.sx', 'pages.dev', 'localhost'];
+ const currentDomain = window.location.hostname.split('.').slice(-2).join('.');
+ if (domains.includes(currentDomain)) {
+ Cookies.set('logged_state', 'false', {
+ expires: 30,
+ path : '/',
+ domain : currentDomain,
+ secure : true,
+ });
+ }
+ window.location.href = redirect_url; // need to redirect not using pjax
+ });
+ };
+
+ // eslint-disable-next-line no-console
+ return ;
+};
+
+export const init = () => {
+ ReactDOM.render(, document.getElementById('callback_container'));
+};
+
+export default init;
diff --git a/src/javascript/app/pages/deriv_iframe.jsx b/src/javascript/app/pages/deriv_iframe.jsx
deleted file mode 100644
index 82781c14f79..00000000000
--- a/src/javascript/app/pages/deriv_iframe.jsx
+++ /dev/null
@@ -1,19 +0,0 @@
-import React from 'react';
-import ReactDOM from 'react-dom';
-import { isOAuth2Enabled } from '../../_common/auth';
-
-const DerivIFrame = () => (
-
-);
-
-export const init = () => {
- const isAuthEnabled = isOAuth2Enabled();
-
- if (!isAuthEnabled) ReactDOM.render(, document.getElementById('deriv_iframe'));
-};
-
-export default init;
diff --git a/src/templates/_common/_layout/layout.jsx b/src/templates/_common/_layout/layout.jsx
index 3b03c6a9b25..d5cf9929a9b 100644
--- a/src/templates/_common/_layout/layout.jsx
+++ b/src/templates/_common/_layout/layout.jsx
@@ -75,7 +75,6 @@ const Layout = () => {
-
{/* */}
+
+
diff --git a/src/templates/_common/includes/deriv-iframe.jsx b/src/templates/_common/includes/deriv-iframe.jsx
deleted file mode 100644
index e336b78ca6d..00000000000
--- a/src/templates/_common/includes/deriv-iframe.jsx
+++ /dev/null
@@ -1,5 +0,0 @@
-import React from 'react';
-
-const DerivIFrame = () => ;
-
-export default DerivIFrame;
diff --git a/src/templates/app/callback.jsx b/src/templates/app/callback.jsx
new file mode 100644
index 00000000000..f203c00f8ea
--- /dev/null
+++ b/src/templates/app/callback.jsx
@@ -0,0 +1,30 @@
+import React from 'react';
+import AntiClickjack from '../_common/includes/anti_clickjack.jsx';
+
+const Callback = () => (
+
+