diff --git a/package-lock.json b/package-lock.json index d79a17831f5..5965d9cb2fb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,7 +14,9 @@ "@binary-com/binary-style": "^0.2.26", "@binary-com/webtrader-charts": "^0.6.2", "@deriv-com/analytics": "^1.18.0", + "@deriv-com/auth-client": "^1.0.15", "@deriv-com/quill-ui": "^1.16.2", + "@deriv-com/utils": "^0.0.34", "@deriv/deriv-api": "^1.0.15", "@deriv/quill-icons": "^1.23.1", "@livechat/customer-sdk": "4.0.2", @@ -2523,6 +2525,52 @@ "uuid": "dist/bin/uuid" } }, + "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==", + "dependencies": { + "@deriv-com/utils": "^0.0.33", + "react": "^18.3.1", + "react-dom": "^18.3.1" + } + }, + "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==" + }, + "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" + }, + "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_modules/@deriv-com/quill-tokens": { "version": "2.0.10", "resolved": "https://registry.npmjs.org/@deriv-com/quill-tokens/-/quill-tokens-2.0.10.tgz", @@ -2570,6 +2618,11 @@ "uuid": "dist/bin/uuid" } }, + "node_modules/@deriv-com/utils": { + "version": "0.0.34", + "resolved": "https://registry.npmjs.org/@deriv-com/utils/-/utils-0.0.34.tgz", + "integrity": "sha512-Hq2DWVKiSR4dTnVTy1lMwAt1F58DPAP0VGB2Nfa9mtPWOfOBGO6ZIeU7+fLGrATukc55ZfHDmfqwgnAi6Nqczg==" + }, "node_modules/@deriv/deriv-api": { "version": "1.0.15", "resolved": "https://registry.npmjs.org/@deriv/deriv-api/-/deriv-api-1.0.15.tgz", diff --git a/package.json b/package.json index cb3dafd289d..d06ab1f3b3a 100644 --- a/package.json +++ b/package.json @@ -106,7 +106,9 @@ "@binary-com/binary-style": "^0.2.26", "@binary-com/webtrader-charts": "^0.6.2", "@deriv-com/analytics": "^1.18.0", + "@deriv-com/auth-client": "^1.0.15", "@deriv-com/quill-ui": "^1.16.2", + "@deriv-com/utils": "^0.0.34", "@deriv/deriv-api": "^1.0.15", "@deriv/quill-icons": "^1.23.1", "@livechat/customer-sdk": "4.0.2", diff --git a/src/javascript/_common/auth.js b/src/javascript/_common/auth.js new file mode 100644 index 00000000000..b9889b374e4 --- /dev/null +++ b/src/javascript/_common/auth.js @@ -0,0 +1,85 @@ +const Utils = require('@deriv-com/utils'); + +export const DEFAULT_OAUTH_LOGOUT_URL = 'https://oauth.deriv.com/oauth2/sessions/logout'; + +export const DEFAULT_OAUTH_ORIGIN_URL = 'https://oauth.deriv.com'; + +export const getOAuthLogoutUrl = () => { + const { appId, serverUrl } = Utils.WebSocketUtils.getServerInfo(); + + const oauthUrl = appId && serverUrl ? `https://${serverUrl}/oauth2/sessions/logout` : DEFAULT_OAUTH_LOGOUT_URL; + + return oauthUrl; +}; + +export const getOAuthOrigin = () => { + const { appId, serverUrl } = Utils.WebSocketUtils.getServerInfo(); + + const oauthUrl = appId && serverUrl ? `https://${serverUrl}` : DEFAULT_OAUTH_ORIGIN_URL; + + return oauthUrl; +}; + +export const AuthClient = (() => { + const isOAuth2Enabled = oAuth2GrowthbookConfig => { + const { OAuth2EnabledApps, OAuth2EnabledAppsInitialised } = oAuth2GrowthbookConfig; + const appId = Utils.WebSocketUtils.getAppId(); + + if (OAuth2EnabledAppsInitialised) { + const FEHydraAppIds = OAuth2EnabledApps?.length + ? OAuth2EnabledApps[OAuth2EnabledApps.length - 1]?.enabled_for ?? [] + : []; + return FEHydraAppIds.includes(+appId); + } + + return false; + }; + const getLogoutHandler = (oAuth2GrowthbookConfig, onWSLogoutAndRedirect) => { + const isAuthEnabled = isOAuth2Enabled(oAuth2GrowthbookConfig); + + if (!isAuthEnabled) { + console.log('lol auth2 is not enabled'); + return onWSLogoutAndRedirect; + } + + const onMessage = async event => { + const allowedOrigin = getOAuthOrigin(); + if (allowedOrigin === event.origin) { + if (event.data === 'logout_complete') { + console.log('logout_complete calledd!'); + onWSLogoutAndRedirect(); + } + } + }; + + window.addEventListener('message', onMessage); + + const oAuth2Logout = async () => { + if (!isAuthEnabled) { + console.log('lol auth2 is not enabled'); + onWSLogoutAndRedirect(); + return; + } + + let iframe = document.getElementById('logout-iframe'); + if (!iframe) { + iframe = document.createElement('iframe'); + iframe.id = 'logout-iframe'; + iframe.style.display = 'none'; + document.body.appendChild(iframe); + + setTimeout(() => { + onWSLogoutAndRedirect(); + }, 10000); + } + console.log('auth2 doneee'); + }; + + return oAuth2Logout; + }; + + return { + getLogoutHandler, + isOAuth2Enabled, + }; +})(); diff --git a/src/javascript/app/base/header.js b/src/javascript/app/base/header.js index 812fb94554c..567a7d79971 100644 --- a/src/javascript/app/base/header.js +++ b/src/javascript/app/base/header.js @@ -1,4 +1,6 @@ // const BinaryPjax = require('./binary_pjax'); +const AuthClient = require('../../_common/auth'); +const Analytics = require('../../_common/analytics'); const Client = require('./client'); const BinarySocket = require('./socket'); const showHidePulser = require('../common/account_opening').showHidePulser; @@ -303,7 +305,7 @@ const Header = (() => { el.removeEventListener('click', logoutOnClick); el.addEventListener('click', logoutOnClick); }); - + // Mobile menu const mobile_menu_overlay = getElementById('mobile__container'); const mobile_menu = getElementById('mobile__menu'); @@ -625,7 +627,11 @@ const Header = (() => { }; const logoutOnClick = () => { - Client.sendLogoutRequest(); + const value = Analytics.getGrowthbookFeatureValue({ + featureFlag: 'hydra_be', + }); + const onLogoutWithHydra = AuthClient.AuthClient.getLogoutHandler(value, () => Client.sendLogoutRequest()); + onLogoutWithHydra(); }; const populateWalletAccounts = () => { diff --git a/src/javascript/app/hooks/growthbook.js b/src/javascript/app/hooks/growthbook.js new file mode 100644 index 00000000000..da4b018f78c --- /dev/null +++ b/src/javascript/app/hooks/growthbook.js @@ -0,0 +1,57 @@ +// growthbook.js +import { useState, useEffect } from 'react'; + +import { Analytics } from '@deriv-com/analytics'; + +export const useIsGrowthbookIsLoaded = () => { + const [isGBLoaded, setIsGBLoaded] = useState(false); + + useEffect(() => { + let checksCounter = 0; + const analyticsInterval = setInterval(() => { + // Check if the analytics instance is available for 10 seconds before setting the feature flag value + if (checksCounter > 20) { + // If the analytics instance is not available after 10 seconds, clear the interval + clearInterval(analyticsInterval); + return; + } + checksCounter += 1; + if (Analytics?.getInstances()?.ab) { + setIsGBLoaded(true); + clearInterval(analyticsInterval); + } + }, 500); + + return () => { + clearInterval(analyticsInterval); + }; + }, []); + + return isGBLoaded; +}; + +export const useGrowthbookGetFeatureValue = ({ defaultValue, featureFlag }) => { + const resolvedDefaultValue = defaultValue !== undefined ? defaultValue : false; + const [featureFlagValue, setFeatureFlagValue] = useState( + Analytics?.getFeatureValue(featureFlag, resolvedDefaultValue) ?? resolvedDefaultValue + ); + const isGBLoaded = useIsGrowthbookIsLoaded(); + + useEffect(() => { + if (isGBLoaded) { + if (Analytics?.getInstances()?.ab) { + const setFeatureValue = () => { + const value = Analytics?.getFeatureValue(featureFlag, resolvedDefaultValue); + setFeatureFlagValue(value); + }; + setFeatureValue(); + Analytics?.getInstances()?.ab?.GrowthBook?.setRenderer(() => { + // this will be called whenever the feature flag value changes and acts as a event listener + setFeatureValue(); + }); + } + } + }, [isGBLoaded, resolvedDefaultValue, featureFlag]); + + return [featureFlagValue, isGBLoaded]; +};