diff --git a/front/src/App.tsx b/front/src/App.tsx index 999a3be73..a9f6953d2 100644 --- a/front/src/App.tsx +++ b/front/src/App.tsx @@ -5,6 +5,7 @@ import AuthCallback from './pages/auth/Callback'; import Login from './pages/auth/Login'; import AppLayout from './layout/AppLayout'; import { Routes, Route, Navigate } from 'react-router-dom'; +import RequireAuth from './components/auth/RequireAuth'; function App() { const user = { @@ -25,9 +26,30 @@ function App() { { - } /> - } /> - } /> + + + + } + /> + + + + } + /> + + + + } + /> } /> } /> diff --git a/front/src/components/auth/RequireAuth.tsx b/front/src/components/auth/RequireAuth.tsx new file mode 100644 index 000000000..d63a09b9c --- /dev/null +++ b/front/src/components/auth/RequireAuth.tsx @@ -0,0 +1,19 @@ +import { useNavigate } from 'react-router-dom'; +import { useHasAccessToken } from '../../hooks/auth/useHasAccessToken'; +import { useEffect } from 'react'; + +function RequireAuth({ children }: { children: JSX.Element }): JSX.Element { + const hasAccessToken = useHasAccessToken(); + + const navigate = useNavigate(); + + useEffect(() => { + if (!hasAccessToken) { + navigate('/auth/login'); + } + }, [hasAccessToken, navigate]); + + return children; +} + +export default RequireAuth; diff --git a/front/src/components/auth/__stories__/RequireAuth.stories.tsx b/front/src/components/auth/__stories__/RequireAuth.stories.tsx new file mode 100644 index 000000000..6a889a74d --- /dev/null +++ b/front/src/components/auth/__stories__/RequireAuth.stories.tsx @@ -0,0 +1,17 @@ +import { MemoryRouter } from 'react-router-dom'; +import RequireAuth from '../RequireAuth'; + +const component = { + title: 'RequireAuth', + component: RequireAuth, +}; + +export default component; + +export const RequireAuthWithHelloChild = () => ( + + +
Hello
+
+
+); diff --git a/front/src/components/auth/__tests__/RequireAuth.test.tsx b/front/src/components/auth/__tests__/RequireAuth.test.tsx new file mode 100644 index 000000000..4123ce448 --- /dev/null +++ b/front/src/components/auth/__tests__/RequireAuth.test.tsx @@ -0,0 +1,9 @@ +import { render } from '@testing-library/react'; + +import { RequireAuthWithHelloChild } from '../__stories__/RequireAuth.stories'; + +it('Checks the Require Auth renders', () => { + const { getAllByText } = render(); + + expect(getAllByText('Hello')).toBeTruthy(); +}); diff --git a/front/src/hooks/auth/__tests__/useHasAccessToken.test.tsx b/front/src/hooks/auth/__tests__/useHasAccessToken.test.tsx new file mode 100644 index 000000000..b3ead063c --- /dev/null +++ b/front/src/hooks/auth/__tests__/useHasAccessToken.test.tsx @@ -0,0 +1,32 @@ +import { render, waitFor } from '@testing-library/react'; +import { useHasAccessToken } from '../useHasAccessToken'; + +function TestComponent() { + const hasAccessToken = useHasAccessToken(); + + return ( +
{hasAccessToken &&
}
+ ); +} + +test('useHasAccessToken works properly if access token is present', async () => { + localStorage.setItem('accessToken', 'test-access-token'); + const { getByTestId } = render(); + + await waitFor(() => { + expect(getByTestId('has-access-token')).toBeDefined(); + }); +}); + +test('useHasAccessToken works properly if access token is not present', async () => { + localStorage.removeItem('accessToken'); + const { container } = render(); + + await waitFor(() => { + expect(container.firstChild).toBeEmptyDOMElement(); + }); +}); + +afterEach(() => { + jest.clearAllMocks(); +}); diff --git a/front/src/hooks/auth/__tests__/useRefreshToken.test.tsx b/front/src/hooks/auth/__tests__/useRefreshToken.test.tsx index 87d26904b..80f53d8e4 100644 --- a/front/src/hooks/auth/__tests__/useRefreshToken.test.tsx +++ b/front/src/hooks/auth/__tests__/useRefreshToken.test.tsx @@ -1,25 +1,6 @@ import { render, waitFor } from '@testing-library/react'; import { useRefreshToken } from '../useRefreshToken'; -const localStorageMock = (function () { - let store: { [key: string]: string } = {}; - return { - getItem: function (key: string) { - return store[key]; - }, - setItem: function (key: string, value: string) { - store[key] = value.toString(); - }, - clear: function () { - store = {}; - }, - removeItem: function (key: string) { - delete store[key]; - }, - }; -})(); -Object.defineProperty(window, 'localStorage', { value: localStorageMock }); - function TestComponent() { const { loading } = useRefreshToken(); @@ -47,10 +28,11 @@ test('useRefreshToken works properly', async () => { render(); await waitFor(() => { - expect(localStorageMock.getItem('accessToken')).toBe('test-access-token'); + expect(localStorage.getItem('accessToken')).toBe('test-access-token'); }); }); afterEach(() => { jest.clearAllMocks(); + localStorage.removeItem('refreshToken'); }); diff --git a/front/src/hooks/auth/useHasAccessToken.tsx b/front/src/hooks/auth/useHasAccessToken.tsx new file mode 100644 index 000000000..839b8b76d --- /dev/null +++ b/front/src/hooks/auth/useHasAccessToken.tsx @@ -0,0 +1,5 @@ +export const useHasAccessToken = () => { + const accessToken = localStorage.getItem('accessToken'); + + return accessToken ? true : false; +}; diff --git a/front/src/pages/auth/Login.tsx b/front/src/pages/auth/Login.tsx index fb98fe083..29b2a67c0 100644 --- a/front/src/pages/auth/Login.tsx +++ b/front/src/pages/auth/Login.tsx @@ -1,16 +1,18 @@ import { useEffect } from 'react'; import { useNavigate } from 'react-router-dom'; +import { useHasAccessToken } from '../../hooks/auth/useHasAccessToken'; function Login() { - const refreshToken = localStorage.getItem('refreshToken'); + const hasAccessToken = useHasAccessToken(); const navigate = useNavigate(); useEffect(() => { - if (!refreshToken) { + if (!hasAccessToken) { window.location.href = process.env.REACT_APP_AUTH_URL + '/signin/provider/google' || ''; + } else { + navigate('/'); } - navigate('/'); - }, [refreshToken, navigate]); + }, [hasAccessToken, navigate]); return <>; }