What will be done?
We’ll learn how to protect a route by JWT authentication in react-router-dom v6. The routes will only be accessible when users have the token saved in cookies(or local storage).
Used technologies
- React ^18.2.0;
- JWT;
- Axios ^0.27.2;
- react-router-dom ^6.3.0;
- react-cookie ^4.1.1;
Starting
Let’s create a React App
create-react-app protect-route-with-jwt
This command will create a react project structure with some necessary files. Remove some files and create some folders that will be used in the project. The final structure will be like this:
Now, we are gonna install some project dependencies, open the terminal inside of project folder, and run the following commands:
yarn init
yarn add axios
yarn add react-router-dom
yarn add react-cookie
Defining API
Create a file in src > services > api.js
where we’ll define the baseURL to make requests in our application:
import axios from 'axios';
const api = axios.create({
baseURL: "http://localhost:8000" //your api URL
});
export default api;
src > services > api.js
We are defining our api variable from axios library, and exporting it.
Creating Hooks
Navigate to src > hooks
creating two folders, the auth
and theprotect Routes
. Inside them and the hooks
folder create a index.js file.
auth
import { createContext, useContext, useMemo } from 'react';
import { useCookies } from 'react-cookie';
import { useNavigate } from 'react-router-dom';
import api from '../../services/api';
const UserContext = createContext();
export const UserProvider = ({ children }) => {
const navigate = useNavigate();
const [cookies, setCookies, removeCookie] = useCookies(['session']);
const login = async ({ email, password }) => {
const res = await api.post('/auth', {
email: email,
password: password
});
setCookies('token', res.data.token); // your token
setCookies('name', res.data.name); // optional data
navigate('/home');
};
const logout = () => {
['token', 'name'].forEach(obj => removeCookie(obj)); // remove data save in cookies
navigate('/login');
};
const value = useMemo(
() => ({
cookies,
login,
logout
}),
[cookies]
);
return (
<UserContext.Provider value={value}>
{children}
</UserContext.Provider>
)
};
export const useAuth = () => {
return useContext(UserContext)
};
src > hooks > auth > index.js
Basically, here we are creating the user Context that have login and logout functions, and exporting it to can be used through the component tree without having to pass props down manually at every level. If you dont understand see the contextAPI documentation.
protectRoutes
import { Outlet, Navigate } from 'react-router-dom';
import { useAuth } from '../auth';
export const ProtectRoutes = () => {
const { cookies } = useAuth();
return cookies.token ? <Outlet/> : <Navigate to='/login' exact />
};
src > hooks > protectRoutes > index.js
Here we are creating a Route Protection using our useAuth that we define previously, if exists token in cookies follow the application, else redirect to login page.
hooks
Create the provider index of hooks adding all providers that we’ll use in the application. In this case, only the auth hook that will be used in a global context of the application, so just import and add it.
import { UserProvider } from './auth';
const AppProvider = ({ children }) => (
<>
<UserProvider>{ children }</UserProvider>
</>
);
export default AppProvider;
src > hooks > index.js
Now, everything we put inside the AppProvider will be able to access the useAuth hook.
Creating Pages
Create two pages for this example, home and login page. They will be located in src > pages
:
The example pages will be like this:
import React from 'react';
export default function Home() {
return <div>Home Page</div>
}
src > pages > Home > index.js
import React from 'react';
export default function Login() {
return <div>Login Page</div>
}
src > pages > Login > index.js
Creating Routes
Navigate to index.js of src folder, and add the following code:
import React from 'react';
import ReactDOM from 'react-dom/client';
import { BrowserRouter } from 'react-router-dom';
import AppProvider from './hooks';
import App from './App';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<BrowserRouter>
<AppProvider>
<App />
</AppProvider>
</BrowserRouter>
</React.StrictMode>
);
src > index.js
Let’s define some applications routes and, finally, protect them. Navigate to App.js
and add the following code:
import React from 'react';
import { Route, Routes, Navigate } from 'react-router-dom';
import { ProtectRoutes } from './hooks/protectRoutes';
import Home from './pages/Home';
import Login from './pages/Login';
export default function App() {
return (
<Routes>
<Route path='/' element={ <Navigate to='home' exact /> } />
<Route path='/login' element={ <Login /> } />
<Route element={ <ProtectRoutes /> }>
<Route path='/home' element={ <Home /> } />
</Route>
</Routes>
)
}
src > App.js
Here, we define routes inside Routes
component. To add our Protectd Route around the routes that we want protect, enough open a
tag with our ProtectRoutes
as the element. Inside de tag we’ll add the routes, in this case we are protecting the home route, that is, the user will only be able to access it if they have a token in the cookies, which is what we defined in the protection of the route previously.
That is all, now all routes that you need protect with a user authentication, must be placed in Route that have
element.
You can access this project in my github: https://github.com/ViniBGoulart/protect-route-with-jwt