How to Set Router Authentication in React
We will introduce protected routes and authentication with react-router in React application.
React Router Authentication in React
When building a commercial web app or a website in React, we often need to hide certain routes in our application from users who are not logged in or do not have a certain user role required to access those routes.
React router provides us with protected routes; they let us choose which routes users can visit based on their user roles or whether they are logged in or not.
For example, we have a home
, about
, contact
, and login
page that all users can access. But protected routes such as dashboard
and profile settings
can only be accessed by logged-in users.
React router does not provide any functionality for this, but it is very easy and straightforward to add this functionality. First, we need to add functionality to authenticate the user to start working on protected routes.
We will use a fake AuthUser
Hook to check the authentication status.
Let’s create a new application by using the following command.
# react
npx create-react-app my-app
After creating our new application in React, we will go to our application directory using this command.
# react
cd my-app
Now, let’s run our app to check if all dependencies are installed correctly.
# react
npm start
We will create a new file, AuthUser.js
, in the’ src’ folder. Once we have created a new file, now we will create a context and assign it to a constant authUserContext
.
# react
const authUserContext = React.createContext();
Once we created a context, now we will create a function AuthUser
that will set the states of 2 variables to authenticate
and setAuthenticate
to false that we will use to check if the user is authenticated or not.
And we will create 2 methods in return login()
and logout()
. login()
will set the value of setAuthenticate
to true and logout()
will set it back to false.
Now, we will export the function AuthenticationProvider
that will save AuthUser()
in a constant auth
. So our code in AuthUser.js
will look like below.
import * as React from "react";
const authUserContext = React.createContext();
function AuthUser() {
const [authenticate, setAuthenticate] = React.useState(false);
return {
authenticate,
login() {
return new Promise((res) => {
setAuthenticate(true);
res();
});
},
logout() {
return new Promise((res) => {
setAuthenticate(false);
res();
});
}
};
}
export function AuthenticationProvider({ children }) {
const auth = AuthUser();
return (
<authUserContext.Provider value={auth}>{children}</authUserContext.Provider>
);
}
export default function AuthenticateConsumer() {
return React.useContext(authUserContext);
}
Whenever we want to use authenticate
, login()
, and logout()
, we can use the AuthUser
Hook. We will start building the navigation that will contain 5 components Home
, About
, Profile
, Login
, Dashboard
.
Home
, About
, and Login
will be publicly accessible, while Dashboard
and Profile
can only be accessed if the user is authenticated.
First of all, we will import Link
, Routes
, Route
, UseNavigate
and userLocation
from react-router-dom
. We will also import AuthUser
from the new file we created.
# react
import * as React from "react";
import {
Link,
Routes,
Route,
useNavigate,
Navigate,
useLocation
} from "react-router-dom";
import AuthUser from "./AuthUser";
Now, we will define constants for our navigations displayed when we visit a certain navigation link.
If it is a publicly accessible link, it will display the public route, and if it is a private link, it will display a private route only if the user is authenticated. Otherwise, it will be redirected to a login page.
# react
const Home = () => <h1>Home (Public Route)</h1>;
const About = () => <h1>About (Public Route)</h1>;
const Dashboard = () => <h1>Dashboard (Private)</h1>;
const Profile = () => <h1>Profile (Private)</h1>;
Now we will create a constant for the Login
method in which we will log in user, and after authenticating, we will redirect it to the dashboard
.
If the user is not logged in, it will return a login page content that will be displayed whenever a user without authentication tries to access the private pages.
const Login = () => {
const navigate = useNavigate();
const { login } = AuthUser();
const { state } = useLocation();
const handleLogin = () => {
login().then(() => {
navigate(state?.path || "/dashboard");
});
};
return (
<div>
<h1>Login</h1>
<button onClick={handleLogin}>Log in</button>
</div>
);
};
Now we will create a navigation function in which we will return a navigation bar with all the routes, including both public and private routes, with a login button if the user is not authenticated and a logout button that will display if the user is authenticated.
We will also create 2 methods, handleLogout
and handleLogin
.
function Nav() {
const { authenticate, logout, login } = AuthUser();
const navigate = useNavigate();
const handleLogout = () => {
logout();
navigate("/");
};
const handleLogin = () => {
login();
navigate("/dashboard");
};
return (
<nav>
<ul>
<li>
<Link to="/">Home</Link>
</li>
<li>
<Link to="/about">About</Link>
</li>
<li>
<Link to="/dashboard">Dashboard</Link>
</li>
<li>
<Link to="/profile">Profile</Link>
</li>
<li>{!authenticate && <button onClick={handleLogin}>Login</button>}</li>
</ul>
{authenticate && <button onClick={handleLogout}>Logout</button>}
</nav>
);
}
We will create a function RequireAuthentication
that will authenticate the user whenever a user tries to access private routes.
function RequireAuthentication({ children }) {
const { authenticate } = AuthUser();
const location = useLocation();
return authenticate === true ? (
children
) : (
<Navigate to="/login" replace state={{ path: location.pathname }} />
);
}
In our App()
function, we will define our routes.
export default function App() {
return (
<div>
<Nav />
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route
path="/dashboard"
element={
<RequireAuthentication>
<Dashboard />
</RequireAuthentication>
}
/>
<Route
path="/profile"
element={
<RequireAuthentication>
<Profile />
</RequireAuthentication>
}
/>
<Route path="/login" element={<Login />} />
</Routes>
</div>
);
}
Let’s test our app and check how it works. See the live demo here.
Output:
As you can see in the above example that it is quite easy to set authenticated routes, but this AuthUser
Hook we used was just a frontend check.
But if we are working on a proper web app, we need to get the user authenticated in the backend to secure our system.
Rana is a computer science graduate passionate about helping people to build and diagnose scalable web application problems and problems developers face across the full-stack.
LinkedIn