Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement Token Refresh in talawa-admin for Seamless Session Renewal #1061

Merged
merged 8 commits into from
Nov 28, 2023
37 changes: 18 additions & 19 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from 'react';
import React, { useEffect } from 'react';
import { Route, Switch } from 'react-router-dom';
import { useQuery } from '@apollo/client';
import * as installedPlugins from 'components/plugins/index';
Expand Down Expand Up @@ -63,6 +63,23 @@ function app(): JSX.Element {

const { data, loading } = useQuery(CHECK_AUTH);

useEffect(() => {
if (data) {
localStorage.setItem(
'name',
`${data.checkAuth.firstName} ${data.checkAuth.lastName}`
);
localStorage.setItem('id', data.checkAuth._id);
localStorage.setItem('email', data.checkAuth.email);
localStorage.setItem('IsLoggedIn', 'TRUE');
localStorage.setItem('UserType', data.checkAuth.userType);
localStorage.setItem('FirstName', data.checkAuth.firstName);
localStorage.setItem('LastName', data.checkAuth.lastName);
localStorage.setItem('UserImage', data.checkAuth.image);
localStorage.setItem('Email', data.checkAuth.email);
}
}, [data, loading]);

const extraRoutes = Object.entries(installedPlugins).map(
(plugin: any, index) => {
const extraComponent = plugin[1];
Expand All @@ -79,24 +96,6 @@ function app(): JSX.Element {
if (loading) {
return <Loader />;
}

if (data) {
localStorage.setItem(
'name',
`${data.checkAuth.firstName} ${data.checkAuth.lastName}`
);
localStorage.setItem('id', data.checkAuth._id);
localStorage.setItem('email', data.checkAuth.email);
localStorage.setItem('IsLoggedIn', 'TRUE');
localStorage.setItem('UserType', data.checkAuth.userType);
localStorage.setItem('FirstName', data.checkAuth.firstName);
localStorage.setItem('LastName', data.checkAuth.lastName);
localStorage.setItem('UserImage', data.checkAuth.image);
localStorage.setItem('Email', data.checkAuth.email);
} else {
localStorage.clear();
}

return (
<>
<Switch>
Expand Down
19 changes: 19 additions & 0 deletions src/GraphQl/Mutations/mutations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,25 @@ export const LOGIN_MUTATION = gql`
}
`;

// to get the refresh token

export const REFRESH_TOKEN_MUTATION = gql`
mutation RefreshToken($refreshToken: String!) {
refreshToken(refreshToken: $refreshToken) {
refreshToken
accessToken
}
}
`;

// to revoke a refresh token

export const REVOKE_REFRESH_TOKEN = gql`
mutation RevokeRefreshTokenForUser {
revokeRefreshTokenForUser
}
`;

// To verify the google recaptcha

export const RECAPTCHA_MUTATION = gql`
Expand Down
122 changes: 70 additions & 52 deletions src/components/LeftDrawer/LeftDrawer.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ import { BrowserRouter } from 'react-router-dom';
import i18nForTest from 'utils/i18nForTest';
import type { InterfaceLeftDrawerProps } from './LeftDrawer';
import LeftDrawer from './LeftDrawer';
import { REVOKE_REFRESH_TOKEN } from 'GraphQl/Mutations/mutations';
import { StaticMockLink } from 'utils/StaticMockLink';
import { MockedProvider } from '@apollo/react-testing';

const props = {
hideDrawer: true,
Expand All @@ -29,6 +32,17 @@ const propsUsers: InterfaceLeftDrawerProps = {
screenName: 'Users',
};

const MOCKS = [
{
request: {
query: REVOKE_REFRESH_TOKEN,
},
result: {},
},
];

const link = new StaticMockLink(MOCKS, true);

jest.mock('react-toastify', () => ({
toast: {
success: jest.fn(),
Expand Down Expand Up @@ -56,11 +70,13 @@ describe('Testing Left Drawer component for SUPERADMIN', () => {
localStorage.setItem('UserImage', '');
localStorage.setItem('UserType', 'SUPERADMIN');
render(
<BrowserRouter>
<I18nextProvider i18n={i18nForTest}>
<LeftDrawer {...propsOrg} />
</I18nextProvider>
</BrowserRouter>
<MockedProvider addTypename={false} link={link}>
<BrowserRouter>
<I18nextProvider i18n={i18nForTest}>
<LeftDrawer {...propsOrg} />
</I18nextProvider>
</BrowserRouter>
</MockedProvider>
);

expect(screen.getByText('Organizations')).toBeInTheDocument();
Expand Down Expand Up @@ -97,26 +113,16 @@ describe('Testing Left Drawer component for SUPERADMIN', () => {
test('Testing in requests screen', () => {
localStorage.setItem('UserType', 'SUPERADMIN');
render(
<BrowserRouter>
<I18nextProvider i18n={i18nForTest}>
<LeftDrawer {...propsReq} />
</I18nextProvider>
</BrowserRouter>
<MockedProvider addTypename={false} link={link}>
<BrowserRouter>
<I18nextProvider i18n={i18nForTest}>
<LeftDrawer {...propsOrg} />
</I18nextProvider>
</BrowserRouter>
</MockedProvider>
);

const orgsBtn = screen.getByTestId(/orgsBtn/i);
const requestsBtn = screen.getByTestId(/requestsBtn/i);
const rolesBtn = screen.getByTestId(/rolesBtn/i);

expect(
requestsBtn.className.includes('text-white btn btn-success')
).toBeTruthy();
expect(
orgsBtn.className.includes('text-secondary btn btn-light')
).toBeTruthy();
expect(
rolesBtn.className.includes('text-secondary btn btn-light')
).toBeTruthy();

// Send to organizations screen
userEvent.click(orgsBtn);
Expand All @@ -126,11 +132,13 @@ describe('Testing Left Drawer component for SUPERADMIN', () => {
test('Testing in roles screen', () => {
localStorage.setItem('UserType', 'SUPERADMIN');
render(
<BrowserRouter>
<I18nextProvider i18n={i18nForTest}>
<LeftDrawer {...propsUsers} />
</I18nextProvider>
</BrowserRouter>
<MockedProvider addTypename={false} link={link}>
<BrowserRouter>
<I18nextProvider i18n={i18nForTest}>
<LeftDrawer {...propsUsers} />
</I18nextProvider>
</BrowserRouter>
</MockedProvider>
);

const orgsBtn = screen.getByTestId(/orgsBtn/i);
Expand All @@ -155,11 +163,13 @@ describe('Testing Left Drawer component for SUPERADMIN', () => {
test('Testing Drawer open close functionality', () => {
localStorage.setItem('UserType', 'SUPERADMIN');
render(
<BrowserRouter>
<I18nextProvider i18n={i18nForTest}>
<LeftDrawer {...propsOrg} />
</I18nextProvider>
</BrowserRouter>
<MockedProvider addTypename={false} link={link}>
<BrowserRouter>
<I18nextProvider i18n={i18nForTest}>
<LeftDrawer {...propsOrg} />
</I18nextProvider>
</BrowserRouter>
</MockedProvider>
);
const closeModalBtn = screen.getByTestId(/closeModalBtn/i);
userEvent.click(closeModalBtn);
Expand All @@ -168,33 +178,39 @@ describe('Testing Left Drawer component for SUPERADMIN', () => {
test('Testing Drawer when hideDrawer is null', () => {
localStorage.setItem('UserType', 'SUPERADMIN');
render(
<BrowserRouter>
<I18nextProvider i18n={i18nForTest}>
<LeftDrawer {...propsUsers} />
</I18nextProvider>
</BrowserRouter>
<MockedProvider addTypename={false} link={link}>
<BrowserRouter>
<I18nextProvider i18n={i18nForTest}>
<LeftDrawer {...propsUsers} />
</I18nextProvider>
</BrowserRouter>
</MockedProvider>
);
});

test('Testing Drawer when hideDrawer is true', () => {
localStorage.setItem('UserType', 'SUPERADMIN');
render(
<BrowserRouter>
<I18nextProvider i18n={i18nForTest}>
<LeftDrawer {...propsReq} />
</I18nextProvider>
</BrowserRouter>
<MockedProvider addTypename={false} link={link}>
<BrowserRouter>
<I18nextProvider i18n={i18nForTest}>
<LeftDrawer {...propsReq} />
</I18nextProvider>
</BrowserRouter>
</MockedProvider>
);
});

test('Testing logout functionality', async () => {
localStorage.setItem('UserType', 'SUPERADMIN');
render(
<BrowserRouter>
<I18nextProvider i18n={i18nForTest}>
<LeftDrawer {...propsOrg} />
</I18nextProvider>
</BrowserRouter>
<MockedProvider addTypename={false} link={link}>
<BrowserRouter>
<I18nextProvider i18n={i18nForTest}>
<LeftDrawer {...propsOrg} />
</I18nextProvider>
</BrowserRouter>
</MockedProvider>
);
userEvent.click(screen.getByTestId('logoutBtn'));
expect(localStorage.clear).toHaveBeenCalled();
Expand All @@ -206,11 +222,13 @@ describe('Testing Left Drawer component for ADMIN', () => {
test('Components should be rendered properly', () => {
localStorage.setItem('UserType', 'ADMIN');
render(
<BrowserRouter>
<I18nextProvider i18n={i18nForTest}>
<LeftDrawer {...propsOrg} />
</I18nextProvider>
</BrowserRouter>
<MockedProvider addTypename={false} link={link}>
<BrowserRouter>
<I18nextProvider i18n={i18nForTest}>
<LeftDrawer {...propsOrg} />
</I18nextProvider>
</BrowserRouter>
</MockedProvider>
);

expect(screen.getByText('Organizations')).toBeInTheDocument();
Expand Down
5 changes: 5 additions & 0 deletions src/components/LeftDrawer/LeftDrawer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import { ReactComponent as RequestsIcon } from 'assets/svgs/requests.svg';
import { ReactComponent as RolesIcon } from 'assets/svgs/roles.svg';
import { ReactComponent as TalawaLogo } from 'assets/svgs/talawa.svg';
import styles from './LeftDrawer.module.css';
import { useMutation } from '@apollo/client';
import { REVOKE_REFRESH_TOKEN } from 'GraphQl/Mutations/mutations';

export interface InterfaceLeftDrawerProps {
hideDrawer: boolean | null;
Expand All @@ -30,7 +32,10 @@ const leftDrawer = ({
const userId = localStorage.getItem('id');
const history = useHistory();

const [revokeRefreshToken] = useMutation(REVOKE_REFRESH_TOKEN);

const logout = (): void => {
revokeRefreshToken();
localStorage.clear();
history.push('/');
};
Expand Down
7 changes: 7 additions & 0 deletions src/components/LeftDrawerEvent/LeftDrawerEvent.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import LeftDrawerEvent, {
} from './LeftDrawerEvent';
import { MockedProvider } from '@apollo/react-testing';
import { EVENT_FEEDBACKS } from 'GraphQl/Queries/Queries';
import { REVOKE_REFRESH_TOKEN } from 'GraphQl/Mutations/mutations';

const props: InterfaceLeftDrawerProps = {
event: {
Expand Down Expand Up @@ -40,6 +41,12 @@ const props2: InterfaceLeftDrawerProps = {
};

const mocks = [
{
request: {
query: REVOKE_REFRESH_TOKEN,
},
result: {},
},
{
request: {
query: EVENT_FEEDBACKS,
Expand Down
4 changes: 4 additions & 0 deletions src/components/LeftDrawerEvent/LeftDrawerEvent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import IconComponent from 'components/IconComponent/IconComponent';
import { EventRegistrantsWrapper } from 'components/EventRegistrantsModal/EventRegistrantsWrapper';
import { CheckInWrapper } from 'components/CheckIn/CheckInWrapper';
import { EventStatsWrapper } from 'components/EventStats/EventStatsWrapper';
import { REVOKE_REFRESH_TOKEN } from 'GraphQl/Mutations/mutations';
import { useMutation } from '@apollo/client';

export interface InterfaceLeftDrawerProps {
event: {
Expand All @@ -30,6 +32,7 @@ const leftDrawerEvent = ({
setHideDrawer,
setShowAddEventProjectModal,
}: InterfaceLeftDrawerProps): JSX.Element => {
const [revokeRefreshToken] = useMutation(REVOKE_REFRESH_TOKEN);
const userType = localStorage.getItem('UserType');
const firstName = localStorage.getItem('FirstName');
const lastName = localStorage.getItem('LastName');
Expand All @@ -38,6 +41,7 @@ const leftDrawerEvent = ({

const history = useHistory();
const logout = (): void => {
revokeRefreshToken();
localStorage.clear();
history.push('/');
};
Expand Down
7 changes: 7 additions & 0 deletions src/components/LeftDrawerOrg/LeftDrawerOrg.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { store } from 'state/store';
import { ORGANIZATIONS_LIST } from 'GraphQl/Queries/Queries';
import { act } from 'react-dom/test-utils';
import { StaticMockLink } from 'utils/StaticMockLink';
import { REVOKE_REFRESH_TOKEN } from 'GraphQl/Mutations/mutations';

const props: InterfaceLeftDrawerProps = {
screenName: 'Dashboard',
Expand Down Expand Up @@ -63,6 +64,12 @@ const props: InterfaceLeftDrawerProps = {
};

const MOCKS = [
{
request: {
query: REVOKE_REFRESH_TOKEN,
},
result: {},
},
{
request: {
query: ORGANIZATIONS_LIST,
Expand Down
6 changes: 5 additions & 1 deletion src/components/LeftDrawerOrg/LeftDrawerOrg.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useQuery } from '@apollo/client';
import { useMutation, useQuery } from '@apollo/client';
import { WarningAmberOutlined } from '@mui/icons-material';
import { ORGANIZATIONS_LIST } from 'GraphQl/Queries/Queries';
import CollapsibleDropdown from 'components/CollapsibleDropdown/CollapsibleDropdown';
Expand All @@ -13,6 +13,7 @@ import { ReactComponent as AngleRightIcon } from 'assets/svgs/angleRight.svg';
import { ReactComponent as LogoutIcon } from 'assets/svgs/logout.svg';
import { ReactComponent as TalawaLogo } from 'assets/svgs/talawa.svg';
import styles from './LeftDrawerOrg.module.css';
import { REVOKE_REFRESH_TOKEN } from 'GraphQl/Mutations/mutations';

export interface InterfaceLeftDrawerProps {
orgId: string;
Expand Down Expand Up @@ -44,6 +45,8 @@ const leftDrawerOrg = ({
variables: { id: orgId },
});

const [revokeRefreshToken] = useMutation(REVOKE_REFRESH_TOKEN);

const userType = localStorage.getItem('UserType');
const firstName = localStorage.getItem('FirstName');
const lastName = localStorage.getItem('LastName');
Expand All @@ -63,6 +66,7 @@ const leftDrawerOrg = ({
}, [data]);

const logout = (): void => {
revokeRefreshToken();
localStorage.clear();
history.push('/');
};
Expand Down
Loading
Loading