Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 15 additions & 11 deletions package-lock.json

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

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
7 changes: 6 additions & 1 deletion src/components/UserNavbarItem/item.desktop.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -60,14 +61,18 @@ const DashboardActions: React.FC<IActionProps> = ({ handleClick, isDesktop }) =>
};

const SignedInActions: React.FC<IActionProps> = ({ handleClick, isDesktop }) => {
const { handleLogin } = useHandleLogin({
onClickLogin: handleClick,
});

const signedInButtonClasses = clsx('navbar__item', styles.UserNavbarItem, styles.SignedInButton);

return (
<nav className='right-navigation'>
<Button
variant='secondary'
color='black'
onClick={handleClick}
onClick={handleLogin}
className={signedInButtonClasses}
data-testid='sa_login'
>
Expand Down
7 changes: 6 additions & 1 deletion src/features/Apiexplorer/LoginDialog/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import useLoginUrl from '@site/src/hooks/useLoginUrl';
import styles from './LoginDialog.module.scss';
import Translate, { translate } from '@docusaurus/Translate';
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
import { useHandleLogin } from '@site/src/hooks/useHandleLogin';

type TLoginDialog = {
setToggleModal: React.Dispatch<React.SetStateAction<boolean>>;
Expand All @@ -25,6 +26,10 @@ export const LoginDialog = ({ setToggleModal }: TLoginDialog) => {
location.assign(getUrl(currentLocale));
};

const { handleLogin } = useHandleLogin({
onClickLogin: handleClick,
});

const handleSignUp = () => {
location.assign('https://deriv.com/signup/');
};
Expand Down Expand Up @@ -57,7 +62,7 @@ export const LoginDialog = ({ setToggleModal }: TLoginDialog) => {
<Button color='tertiary' onClick={handleSignUp} className={styles.btn}>
<Translate>Sign up</Translate>
</Button>
<Button color='primary' onClick={handleClick} className={styles.btn}>
<Button color='primary' onClick={handleLogin} className={styles.btn}>
<Translate>Log in</Translate>
</Button>
</div>
Expand Down
22 changes: 22 additions & 0 deletions src/features/Callback/CallbackPage.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<Callback
onSignInSuccess={(tokens) => {
const accounts = transformAccountsFromResponseBody(tokens);

updateLoginAccounts(accounts);

window.location.href = '/';
}}
/>
);
};

export default CallbackPage;
3 changes: 3 additions & 0 deletions src/features/Callback/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import CallbackPage from './CallbackPage';

export default CallbackPage;
11 changes: 7 additions & 4 deletions src/features/Login/Login.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,18 @@ import Footer from '@site/src/components/Footer';
import Translate from '@docusaurus/Translate';
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';

import { useHandleLogin } from '@site/src/hooks/useHandleLogin';

export const Login = () => {
const { getUrl } = useLoginUrl();
const {
i18n: { currentLocale },
} = useDocusaurusContext();

const handleClick = () => {
window.location.assign(getUrl(currentLocale));
};
const { handleLogin } = useHandleLogin({
onClickLogin: () => window.location.assign(getUrl(currentLocale)),
});

return (
<div>
<div className={styles.login} data-testid='login'>
Expand All @@ -26,7 +29,7 @@ export const Login = () => {
</Translate>
</Text>
<div className={styles.action}>
<Button color='primary' onClick={handleClick}>
<Button color='primary' onClick={handleLogin}>
<Translate>Log In</Translate>
</Button>
</div>
Expand Down
37 changes: 37 additions & 0 deletions src/hooks/useHandleLogin/index.tsx
Original file line number Diff line number Diff line change
@@ -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<TOAuth2EnabledAppList>({
featureFlag: 'hydra_be',
});

const isOAuth2Enabled = useIsOAuth2Enabled(OAuth2EnabledApps, OAuth2EnabledAppsInitialised);

const handleLogin = async () => {
if (isOAuth2Enabled) {
await requestOidcAuthentication({
redirectCallbackUri: `${window.location.origin}/callback`,
});
}
onClickLogin();
};

return { handleLogin };
};
21 changes: 21 additions & 0 deletions src/pages/callback.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<Layout title='Callback' description=''>
<main>
<CallbackPage />
</main>
</Layout>
);
};

export default Callback;
32 changes: 32 additions & 0 deletions src/utils/__tests__/utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ const {
isHost,
getServerConfig,
getCurrencyObject,
transformAccountsFromResponseBody,
} = utils;

describe('Get an object with currency data', () => {
Expand Down Expand Up @@ -106,6 +107,37 @@ describe('Get Is Browser', () => {
});
});

describe('Transform Accounts from Response Body', () => {
it('Should return an array of account objects', () => {
const inputData = {
acct1: 'CR111111',
cur1: 'USD',
token1: 'a1-123123123',
acct2: 'CR222222',
cur2: 'ETH',
token2: 'a1-34343434',
};

const expectedOutput = [
{
currency: 'USD',
name: 'CR111111',
token: 'a1-123123123',
},
{
currency: 'ETH',
name: 'CR222222',
token: 'a1-34343434',
},
];

const output = transformAccountsFromResponseBody(inputData);

expect(output.length).toBe(expectedOutput.length);
expect(output).toStrictEqual(expectedOutput);
});
});

describe('Get Accounts from Search Params', () => {
it('Should return user accounts', () => {
const test_search_params =
Expand Down
29 changes: 25 additions & 4 deletions src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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}`),
Expand Down
Loading