How to handle authentication and authorization in React JS

In modern web applications, ensuring secure access to different features and resources is important. Authentication and authorization play a vital role in safeguarding user data and controlling access to various parts of an application.

Authentication and authorization

Authentication verifies the identity of users, typically through credentials such as username and password. Authorization, on the other hand, controls what resources and actions users can access based on their authenticated identity and assigned roles. In React JS, we can implement these functionalities using various libraries and techniques.

Setting up the project

To get started, create a new React JS project using your preferred setup (e.g., create-react-app or Next.js). Install the necessary dependencies, including a routing library (such as React Router) and an HTTP client (e.g., Axios) for API calls.

Create a project using the following command:

npx create-react-app your_project_name

Implementing user registration and login

To implement JWT (JSON Web Token) authentication in your React client, we need to make two API calls: one for user registration and another for user login. On form submission, make an API call to the server to validate the user’s credentials. If successful, store the authentication token received from the server securely (e.g., using the browser’s local storage or a state management library like Redux).

Authentication and authorization with React
Authentication and authorization with React

By following this flow and properly handling the JWT token in your React client, you can implement JWT authentication and protect your resources effectively.

Implementation

Let's see the implementation of the login and registration form.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Your App</title>
</head>
<body>
  <div id="root"></div>
  <div id="dialog-root"></div>
</body>
</html>

Securing routes with private and public access

In your routing configuration, specify which routes require authentication (private routes) and which are accessible to everyone (public routes). For private routes, redirect unauthenticated users to the login page.

// App.js
import React from 'react';
import { BrowserRouter as Router, Switch, Route, Redirect } from 'react-router-dom';
import LoginForm from './LoginForm';
import Dashboard from './Dashboard';
const PrivateRoute = ({ component: Component, isAuthenticated, ...rest }) => (
<Route
{...rest}
render={(props) =>
isAuthenticated ? (
<Component {...props} />
) : (
<Redirect to="/login" />
)
}
/>
);
const PublicRoute = ({ component: Component, isAuthenticated, restricted, ...rest }) => (
<Route
{...rest}
render={(props) =>
isAuthenticated && restricted ? (
<Redirect to="/dashboard" />
) : (
<Component {...props} />
)
}
/>
);
const App = () => {
const isAuthenticated = false; // Replace with actual authentication logic
return (
<Router>
<Switch>
<PublicRoute
restricted={false}
isAuthenticated={isAuthenticated}
component={LoginForm}
path="/login"
exact
/>
<PrivateRoute
isAuthenticated={isAuthenticated}
component={Dashboard}
path="/dashboard"
exact
/>
</Switch>
</Router>
);
};
export default App;

Explanation

  • Lines 7–18: This component is used to protect private routes that should only be accessible by authenticated users. The component checks if the user is authenticated. If they are, it renders the provided Component, passing any received props. If not, it redirects the user to the login page.

  • Lines 20–31: This component defines public routes that should be accessible to all users, including authenticated users. It checks if the user is authenticated and if the route is restricted. If both conditions are met, it redirects the user to the dashboard. Otherwise, it renders the provided Component, passing any received props.

  • Lines 33–55: It sets the initial value of isAuthenticated to false . Within the Router component, it renders a Switch component that contains two routes: the PublicRoute for the login page and the PrivateRoute for the dashboard page. The exact prop ensures that the routes match exactly.

Storing authentication tokens

To maintain user sessions, store the authentication token received from the server. Retrieve and validate the token during application startup or page reloads. If the token is valid, consider the user authenticated and allow access to private routes. If the token is expired or invalid, redirect the user to the login page.

// AuthContext.js
import React, { createContext, useEffect, useState } from 'react';
const AuthContext = createContext();
const AuthProvider = ({ children }) => {
const [isAuthenticated, setIsAuthenticated] = useState(false);
useEffect(() => {
const token = localStorage.getItem('token');
// Validate token with the server and set isAuthenticated accordingly
setIsAuthenticated(!!token);
}, []);
return <AuthContext.Provider value={isAuthenticated}>{children}</AuthContext.Provider>;
};
export { AuthContext, AuthProvider };
  • Line 4: This line creates a new context object called AuthContext using the createContext function. Context provides a way to share data across components without explicitly passing it through props at every level.

  • Line 6: The component AuthProvider which will be responsible for providing authentication-related data to its children components. It takes a children prop, which represents the child components that this provider will wrap.

  • Line 7: This line uses the useState hook to create a new state variable isAuthenticated and a corresponding function setIsAuthenticated to update its value.

  • Lines 9–13: This is an effect hook that runs after the initial rendering of the component. It retrieves a token from the browser's localStorage using the key 'token'. The token is then used to validate the user's authentication status with the server. If a valid token is found, isAuthenticated is set to true using the setIsAuthenticated function. Otherwise, it is set to false.

  • Line 15: This line returns a JSX element that wraps the children components with the AuthContext.Provider. It provides the isAuthenticated value as the context value, making it accessible to any child components that access this context.

Protecting sensitive API endpoints

To protect sensitive API endpoints, include the authentication token in the request headers for authenticated requests. Create an Axios interceptorAn Axios interceptor is a function that allows you to intercept and modify requests or responses before they are handled by the application. to automatically add the token to each outgoing request. On the server side, validate the token and allow or deny access to the requested resources.

// axiosConfig.js
import axios from 'axios';
const instance = axios.create({
baseURL: 'https://api.example.com',
});
instance.interceptors.request.use((config) => {
const token = localStorage.getItem('token');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
});
export default instance;

Explanation

  • Lines 4–6: This line creates a new Axios instance using the axios.create method. The instance is assigned to the instance variable.

  • Lines 8: This code sets up a request interceptor for the Axios instance. The interceptors.request.use method allows you to intercept outgoing requests before they are sent. In this case, the interceptor function receives the config object as a parameter, which represents the request configuration.

  • Lines 9–13: Interceptor function first retrieves a token from the browser's localStorage using the key 'token'. If a token is found, it adds an 'Authorization' header to the request's config object. Finally, the interceptor function returns the modified config object, allowing the request to proceed with the updated configuration.

// Dashboard.js
import React, { useContext, useEffect, useState } from 'react';
import axios from './axiosConfig';
import { AuthContext } from './AuthContext';
const Dashboard = () => {
const [data, setData] = useState([]);
const isAuthenticated = useContext(AuthContext);
useEffect(() => {
const fetchData = async
() => {
try {
const response = await axios.get('/api/data');
setData(response.data);
} catch (error) {
console.error(error);
}
};
if (isAuthenticated) {
fetchData();
}
}, [isAuthenticated]);
return (
<div>
{data.map((item) => (
<div key={item.id}>{item.name}</div>
))}
</div>
);
};
export default Dashboard;

Logging out and clearing session

Implement a logout mechanism that removes the authentication token and clears any user-specific data stored locally. Upon logout, redirect the user to the login page or a public landing page.

// LogoutButton.js
import React from 'react';
import { useHistory } from 'react-router-dom';
const LogoutButton = () => {
const history = useHistory();
const handleLogout = () => {
localStorage.removeItem('token');
// Clear any other user-specific data
history.push('/login');
};
return (
<button onClick={handleLogout}>
Logout
</button>
);
};
export default LogoutButton;

Handling authorization errors

In cases where a user tries to access a resource, they are not authorized to handle the authorization errors. Display meaningful error messages or redirect the user to an appropriate page indicating the lack of access permissions.

Conclusion

Implementing authentication and authorization in React JS applications is important for maintaining data security and controlling user access. By following the steps outlined in this Answer, you can build an authentication and authorization system.

Free Resources

Copyright ©2025 Educative, Inc. All rights reserved