diff --git a/package-lock.json b/package-lock.json index 1df6d60b..d5dc8bc8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -27,12 +27,14 @@ "@react-spring/web": "^9.7.3", "@textea/json-viewer": "^3.4.1", "@use-gesture/react": "^10.3.0", + "axios": "^1.7.7", "babel-plugin-jsx-remove-data-test-id": "^3.0.0", "clsx": "^1.2.1", "docusaurus-plugin-sass": "^0.2.2", "identity-obj-proxy": "^3.0.0", "install": "^0.13.0", "moment": "^2.29.4", + "oidc-client-ts": "^3.1.0", "prism-react-renderer": "^2.3.1", "react": "^18.2.0", "react-dom": "^18.2.0", @@ -9429,8 +9431,7 @@ "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "dev": true + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, "node_modules/at-least-node": { "version": "1.0.0", @@ -9491,6 +9492,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/axios": { + "version": "1.7.7", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.7.tgz", + "integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, "node_modules/babel-jest": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", @@ -10804,7 +10815,6 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, "dependencies": { "delayed-stream": "~1.0.0" }, @@ -12051,7 +12061,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "dev": true, "engines": { "node": ">=0.4.0" } @@ -14314,7 +14323,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "dev": true, "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", @@ -15357,6 +15365,7 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", + "dev": true, "dependencies": { "lru-cache": "^6.0.0" }, @@ -15368,6 +15377,7 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, "dependencies": { "yallist": "^4.0.0" }, @@ -15378,7 +15388,8 @@ "node_modules/hosted-git-info/node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true }, "node_modules/hpack.js": { "version": "2.1.6", @@ -22894,6 +22905,7 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", "integrity": "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==", + "dev": true, "dependencies": { "hosted-git-info": "^4.0.1", "is-core-module": "^2.5.0", @@ -22908,6 +22920,7 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, "dependencies": { "yallist": "^4.0.0" }, @@ -22919,6 +22932,7 @@ "version": "7.6.0", "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "dev": true, "dependencies": { "lru-cache": "^6.0.0" }, @@ -22932,7 +22946,8 @@ "node_modules/normalize-package-data/node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true }, "node_modules/normalize-path": { "version": "3.0.0", @@ -27584,6 +27599,11 @@ "node": ">= 0.10" } }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, "node_modules/psl": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", diff --git a/package.json b/package.json index 0bb7d5a6..15b81666 100644 --- a/package.json +++ b/package.json @@ -46,6 +46,7 @@ "@textea/json-viewer": "^3.4.1", "@use-gesture/react": "^10.3.0", "babel-plugin-jsx-remove-data-test-id": "^3.0.0", + "axios": "^1.7.7", "clsx": "^1.2.1", "docusaurus-plugin-sass": "^0.2.2", "identity-obj-proxy": "^3.0.0", @@ -56,6 +57,7 @@ "react-dom": "^18.2.0", "react-hook-form": "^7.41.5", "react-table": "^7.8.0", + "oidc-client-ts": "^3.1.0", "sass": "^1.57.1", "sass-loader": "^13.2.0", "swiper": "^8.3.2", diff --git a/src/components/UserNavbarItem/item.desktop.tsx b/src/components/UserNavbarItem/item.desktop.tsx index 7bbd9594..24d97c2a 100644 --- a/src/components/UserNavbarItem/item.desktop.tsx +++ b/src/components/UserNavbarItem/item.desktop.tsx @@ -3,6 +3,7 @@ import clsx from 'clsx'; import Translate from '@docusaurus/Translate'; import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; import { Button } from '@deriv-com/quill-ui'; +import { requestOidcAuthentication } from '@deriv-com/auth-client'; import { LabelPairedGridLgRegularIcon, StandaloneRightFromBracketBoldIcon, @@ -92,8 +93,13 @@ const UserNavbarDesktopItem = ({ authUrl, is_logged_in }: IUserNavbarItemProps) const { deviceType } = useDeviceType(); const isDesktop = deviceType === 'desktop'; - const handleClick = () => { - location.assign(authUrl); + const handleClick = async () => { + // location.assign(authUrl); + const app_id = localStorage.getItem('config.app_id'); + const redirect_uri = `${window.location.origin}/dashboard`; + const post_logout_redirect_uri = `${window.location.origin}/`; + + await requestOidcAuthentication(app_id, redirect_uri, post_logout_redirect_uri); }; return is_logged_in ? ( diff --git a/src/features/dashboard/dashboard.tsx b/src/features/dashboard/dashboard.tsx index 438f44da..3d5de147 100644 --- a/src/features/dashboard/dashboard.tsx +++ b/src/features/dashboard/dashboard.tsx @@ -3,17 +3,88 @@ import useAuthContext from '@site/src/hooks/useAuthContext'; import useAppManager from '@site/src/hooks/useAppManager'; import ManageDashboard from './manage-dashboard'; import { Login } from '../Login/Login'; +import axios from 'axios'; +import { getAccountsFromSearchParams } from '@site/src/utils'; const Dashboard = () => { - const { is_logged_in } = useAuthContext(); + const { is_logged_in, updateLoginAccounts } = useAuthContext(); const { setIsDashboard } = useAppManager(); useEffect(() => { setIsDashboard(true); + const exchangeToken = async () => { + try { + const urlParams = new URLSearchParams(window?.location?.search); + const oidc_endpoints = localStorage.getItem('config.oidc_endpoints') || '{}'; + + const token_endpoint = JSON.parse(oidc_endpoints).token_endpoint || ''; + + const code = urlParams.get('code'); + const state = urlParams.get('state'); + const oidc_key = `oidc.${state}`; + + const oidc_data = localStorage.getItem(oidc_key); + const code_verifier = oidc_data ? JSON.parse(oidc_data).code_verifier : null; + const appId = localStorage.getItem('config.app_id'); + + if (!code_verifier) return; + + const response = await fetch(token_endpoint, { + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + Accept: 'application/json', + }, + body: new URLSearchParams({ + grant_type: 'authorization_code', + redirect_uri: `${window.location.origin}/dashboard`, + code: code, + code_verifier: code_verifier, + client_id: appId, + }).toString(), + }); + + const data = await response.json(); + if (response.ok) { + localStorage.setItem('id_token', data.id_token); + + try { + const response = await axios.post( + 'https://qa101.deriv.dev/oauth2/legacy/tokens', + {}, + { + headers: { + Authorization: `Bearer ${data.access_token}`, + 'Content-Type': 'application/json', + }, + }, + ); + + const legacyData = response.data; + const accounts = getAccountsFromSearchParams(legacyData); + updateLoginAccounts(accounts); + window.history.replaceState({}, document.title, '/dashboard'); + } catch (error) { + if (error.response) { + const legacyData = error.response.data; + console.error('Error fetching legacy tokens:', legacyData); + } else { + console.error('Failed to fetch legacy tokens:', error); + } + } + } else { + console.error('Error exchanging token:', data); + } + } catch (error) { + console.error('Token exchange failed:', error); + } + }; + + exchangeToken(); return () => { setIsDashboard(false); }; - }, [setIsDashboard]); + }, [setIsDashboard, updateLoginAccounts]); if (is_logged_in) return ; return ;