From 495b2fda39218f155c28241e9a2a1f9078c76a67 Mon Sep 17 00:00:00 2001 From: Thisyahlen Date: Tue, 3 Dec 2024 10:18:09 +0800 Subject: [PATCH 1/9] chore: oidc implementation --- package-lock.json | 26 +++++++++++++++----------- package.json | 2 +- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5d70b939..267ea675 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "1.0.0", "dependencies": { "@deriv-com/analytics": "^1.22.1", - "@deriv-com/auth-client": "^1.0.29", + "@deriv-com/auth-client": "^1.2.15", "@deriv-com/quill-ui": "^1.16.21", "@deriv/deriv-api": "^1.0.11", "@deriv/ui": "^0.8.0", @@ -2599,12 +2599,16 @@ } }, "node_modules/@deriv-com/auth-client": { - "version": "1.0.29", - "resolved": "https://registry.npmjs.org/@deriv-com/auth-client/-/auth-client-1.0.29.tgz", - "integrity": "sha512-4iBxaSKM9hu6qVHGUr4MHtik59j1ZIuvveRnSqAbqQgcQGzKfSLRwGJ53P/5lVByh9fIIyICpuxbTgaaYA4eXg==", + "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.37", + "@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/quill-tokens": { @@ -2688,9 +2692,9 @@ } }, "node_modules/@deriv-com/utils": { - "version": "0.0.37", - "resolved": "https://registry.npmjs.org/@deriv-com/utils/-/utils-0.0.37.tgz", - "integrity": "sha512-+ngUvT+OqwblBoqkHcsbLtljjwOGIjjMpo5xLS5fwyhtNvBe8Rcq+140QV1j0xq9vlm2kmcowEKIVBq33imFmg==" + "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/api-types": { "version": "1.0.292", @@ -6472,9 +6476,9 @@ } }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.24.0.tgz", - "integrity": "sha512-ZXFk7M72R0YYFN5q13niV0B7G8/5dcQ9JDp8keJSfr3GoZeXEoMHP/HlvqROA3OMbMdfr19IjCeNAnPUG93b6A==", + "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" ], diff --git a/package.json b/package.json index 0bb7d5a6..f3d57b41 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,7 @@ "dependencies": { "@deriv-com/quill-ui": "^1.16.21", "@deriv-com/analytics": "^1.22.1", - "@deriv-com/auth-client": "^1.0.29", + "@deriv-com/auth-client": "^1.2.15", "@deriv/deriv-api": "^1.0.11", "@radix-ui/react-tooltip": "^1.0.7", "@react-spring/web": "^9.7.3", From 4a37d2c89546e5d7faa189bcbaca4ff880b1eb60 Mon Sep 17 00:00:00 2001 From: Thisyahlen Date: Tue, 3 Dec 2024 10:20:33 +0800 Subject: [PATCH 2/9] chore: add callback page --- src/features/Callback/CallbackPage.tsx | 22 +++++++++++++++++++ src/features/Callback/index.ts | 3 +++ src/pages/callback.tsx | 21 +++++++++++++++++++ src/utils/index.ts | 29 ++++++++++++++++++++++---- 4 files changed, 71 insertions(+), 4 deletions(-) create mode 100644 src/features/Callback/CallbackPage.tsx create mode 100644 src/features/Callback/index.ts create mode 100644 src/pages/callback.tsx diff --git a/src/features/Callback/CallbackPage.tsx b/src/features/Callback/CallbackPage.tsx new file mode 100644 index 00000000..2c88aadb --- /dev/null +++ b/src/features/Callback/CallbackPage.tsx @@ -0,0 +1,22 @@ +import React from 'react'; +import { Callback } from '@deriv-com/auth-client'; +import { transformAccountsFromResponseBody } from '@site/src/utils'; +import useAuthContext from '@site/src/hooks/useAuthContext'; + +const CallbackPage = () => { + const { updateLoginAccounts } = useAuthContext(); + + return ( + { + const accounts = transformAccountsFromResponseBody(tokens); + + updateLoginAccounts(accounts); + + window.location.href = '/'; + }} + /> + ); +}; + +export default CallbackPage; diff --git a/src/features/Callback/index.ts b/src/features/Callback/index.ts new file mode 100644 index 00000000..1c7f3911 --- /dev/null +++ b/src/features/Callback/index.ts @@ -0,0 +1,3 @@ +import CallbackPage from './CallbackPage'; + +export default CallbackPage; diff --git a/src/pages/callback.tsx b/src/pages/callback.tsx new file mode 100644 index 00000000..93448376 --- /dev/null +++ b/src/pages/callback.tsx @@ -0,0 +1,21 @@ +import React, { useEffect } from 'react'; +import Layout from '@theme/Layout'; +import CallbackPage from '../features/Callback'; + +const Callback = () => { + useEffect(() => { + const navbar = document.getElementsByClassName('navbar navbar--fixed-top')[0] as HTMLElement; + if (navbar) { + navbar.style.display = 'none'; + } + }, []); + return ( + +
+ +
+
+ ); +}; + +export default Callback; diff --git a/src/utils/index.ts b/src/utils/index.ts index 827bb4dc..7ec6e46a 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -106,6 +106,30 @@ export const getIsBrowser = () => { return typeof window !== 'undefined'; }; +/** + * @description This function takes the response data from the accounts endpoint and transforms it into an array of objects + * @param {object} data - The response data from the accounts endpoint + * @returns {array} An array of objects with the shape of { currency: string, name: string, token: string } + */ +export const transformAccountsFromResponseBody = (data) => { + const result = []; + const keys = Object.keys(data); + + for (let i = 1; i <= keys.length / 3; i++) { + const groupedObject = { + currency: data[`cur${i}`], + name: data[`acct${i}`], + token: data[`token${i}`], + }; + + if (groupedObject.currency && groupedObject.name && groupedObject.token) { + result.push(groupedObject); + } + } + + return result; +}; + /** * @description based on the received query params after successful login, generates the array of user's accounts * @param searchParams the query params in the auth path when user does the login successfully @@ -126,10 +150,7 @@ export const getAccountsFromSearchParams = (searchParams: string) => { const queryIndex = index + 1; // we should check each account in the search params, this is some kind of validation for the URL search params - if ( - params.has(`acct${queryIndex}`) && - params.has(`token${queryIndex}`) - ) { + if (params.has(`acct${queryIndex}`) && params.has(`token${queryIndex}`)) { accounts.push({ name: params.get(`acct${queryIndex}`), token: params.get(`token${queryIndex}`), From e72d2a60db651bfbde54ac93e55367acc488ff73 Mon Sep 17 00:00:00 2001 From: Thisyahlen Date: Tue, 3 Dec 2024 10:22:43 +0800 Subject: [PATCH 3/9] chore: add useHandleLogin hook --- src/hooks/useHandleLogin/index.tsx | 37 ++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 src/hooks/useHandleLogin/index.tsx diff --git a/src/hooks/useHandleLogin/index.tsx b/src/hooks/useHandleLogin/index.tsx new file mode 100644 index 00000000..96ffee13 --- /dev/null +++ b/src/hooks/useHandleLogin/index.tsx @@ -0,0 +1,37 @@ +import useGrowthbookGetFeatureValue from '../useGrowthbookGetFeatureValue/'; +import { + requestOidcAuthentication, + TOAuth2EnabledAppList, + useIsOAuth2Enabled, +} from '@deriv-com/auth-client'; + +/** + * Handles the new login flow for the user using OIDC. + * + * If the user is not logged in and OAuth2 is enabled, it will redirect the user to the + * OAuth2 authorization page from the OIDC config endpoint. If OAuth2 is not enabled it will + * redirect the user to the legacy oauth url coming from the onClickLogin callback. + * + * @param {Object} props - The props object. + * @param {Function} props.onClickLogin - The callback to be called when the user is logged in. + * @returns {Object} - An object with the `handleLogin` function. + */ +export const useHandleLogin = ({ onClickLogin }: { onClickLogin: () => void }) => { + const [OAuth2EnabledApps, OAuth2EnabledAppsInitialised] = + useGrowthbookGetFeatureValue({ + featureFlag: 'hydra_be', + }); + + const isOAuth2Enabled = useIsOAuth2Enabled(OAuth2EnabledApps, OAuth2EnabledAppsInitialised); + + const handleLogin = async () => { + if (isOAuth2Enabled) { + await requestOidcAuthentication({ + redirectCallbackUri: `${window.location.origin}/callback`, + }); + } + onClickLogin(); + }; + + return { handleLogin }; +}; From f7ff25985a46caed7fc065fd1992cbc8229f27a1 Mon Sep 17 00:00:00 2001 From: Thisyahlen Date: Tue, 3 Dec 2024 10:36:28 +0800 Subject: [PATCH 4/9] chore: use the hook in the login actions --- .../UserNavbarItem/item.desktop.tsx | 7 +++- .../Apiexplorer/LoginDialog/index.tsx | 7 +++- src/features/Login/Login.tsx | 8 ++++- src/utils/__tests__/utils.test.ts | 32 +++++++++++++++++++ 4 files changed, 51 insertions(+), 3 deletions(-) diff --git a/src/components/UserNavbarItem/item.desktop.tsx b/src/components/UserNavbarItem/item.desktop.tsx index 7bbd9594..19d6add5 100644 --- a/src/components/UserNavbarItem/item.desktop.tsx +++ b/src/components/UserNavbarItem/item.desktop.tsx @@ -12,6 +12,7 @@ import useDeviceType from '@site/src/hooks/useDeviceType'; import { IUserNavbarItemProps } from './item.types'; import styles from './UserNavbarItem.module.scss'; +import { useHandleLogin } from '@site/src/hooks/useHandleLogin'; interface IActionProps { handleClick: () => void; @@ -62,12 +63,16 @@ const DashboardActions: React.FC = ({ handleClick, isDesktop }) => const SignedInActions: React.FC = ({ handleClick, isDesktop }) => { const signedInButtonClasses = clsx('navbar__item', styles.UserNavbarItem, styles.SignedInButton); + const { handleLogin } = useHandleLogin({ + onClickLogin: handleClick, + }); + return (