Skip to content

Commit

Permalink
feat: migrated oidc implementation from old pr
Browse files Browse the repository at this point in the history
  • Loading branch information
adrienne-deriv committed Dec 2, 2024
1 parent 730fe48 commit 1110bd3
Show file tree
Hide file tree
Showing 13 changed files with 315 additions and 78 deletions.
3 changes: 2 additions & 1 deletion scripts/config/pages.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

83 changes: 72 additions & 11 deletions src/javascript/_common/auth.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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();
}
};

Expand All @@ -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);
}

Expand All @@ -123,3 +138,49 @@ export const getLogoutHandler = onWSLogoutAndRedirect => {

return oAuth2Logout;
};

export const requestSingleSignOn = async () => {
const _requestSingleSignOn = async () => {

Check warning on line 143 in src/javascript/_common/auth.js

View workflow job for this annotation

GitHub Actions / build_and_deploy_preview_link

Unexpected dangling '_' in '_requestSingleSignOn'

Check warning on line 143 in src/javascript/_common/auth.js

View workflow job for this annotation

GitHub Actions / build_and_deploy_preview_link

Unexpected dangling '_' in '_requestSingleSignOn'

Check warning on line 143 in src/javascript/_common/auth.js

View workflow job for this annotation

GitHub Actions / build_and_deploy_preview_link

Unexpected dangling '_' in '_requestSingleSignOn'

Check warning on line 143 in src/javascript/_common/auth.js

View workflow job for this annotation

GitHub Actions / Build and Test

Unexpected dangling '_' in '_requestSingleSignOn'

Check warning on line 143 in src/javascript/_common/auth.js

View workflow job for this annotation

GitHub Actions / Build and Test

Unexpected dangling '_' in '_requestSingleSignOn'
// 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();
}
};
2 changes: 2 additions & 0 deletions src/javascript/app/base/binary_pages.js
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand Down Expand Up @@ -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 },
Expand Down
16 changes: 16 additions & 0 deletions src/javascript/app/base/callback.js
Original file line number Diff line number Diff line change
@@ -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;
92 changes: 54 additions & 38 deletions src/javascript/app/base/header.js
Original file line number Diff line number Diff line change
@@ -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/';
Expand All @@ -45,7 +45,6 @@ const Header = (() => {
};

const onLoad = () => {
DerivIFrame.init();
populateAccountsList();
populateWalletAccounts();
bindSvg();
Expand Down Expand Up @@ -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(),
Expand All @@ -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();
};

Expand Down Expand Up @@ -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 => {
Expand All @@ -908,8 +924,8 @@ const Header = (() => {
$(`<span class="header__acc-display-text">${multiplier_text}</span>`).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';
}
Expand Down
2 changes: 0 additions & 2 deletions src/javascript/app/base/logged_in.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,13 @@ 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 = () => {
SocketCache.clear();
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) {
Expand Down
2 changes: 2 additions & 0 deletions src/javascript/app/base/page.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -100,6 +101,7 @@ const Page = (() => {
updateLinksURL('#content');
} else {
init();
requestSingleSignOn();
if (!isLoginPages()) {
Language.setCookie(Language.urlLang());
const url_query_strings = Url.paramsHash();
Expand Down
Loading

0 comments on commit 1110bd3

Please sign in to comment.