Subscribe
Protected Routes with React Router: How to Add Authentication
5 mins read

By: vishwesh

Protected Routes with React Router: How to Add Authentication

As web applications become more complex, it's becoming increasingly important to secure certain parts of the application and provide access only to authorized users. In this tutorial, we'll explore how to add authentication to a React application using React Router.

What is Authentication?

Authentication is the process of verifying the identity of a user or system. It is a security measure that ensures that only authorized users have access to specific resources or features of an application.

Authentication can be done in various ways, such as username/password, social login, single sign-on, and multi-factor authentication.

What is React Router?

React Router is a popular library for handling routing in React applications. It allows developers to define routes and navigate between them using a declarative API.

React Router provides several components, such as BrowserRouter, Route, and Link, which make it easy to create complex routing structures.

Why Use React Router for Authentication?

React Router can be used to implement authentication in a React application by protecting certain routes that require authentication.

By using React Router, you can easily define protected routes that require authentication and redirect unauthenticated users to a login page.

React Router also makes it easy to manage the state of the application and handle authentication-related errors.

Adding Authentication to a React Application

In this tutorial, we'll be using the react-router-dom package, which provides the components necessary for handling routing in a React application.

Step 1: Installing Dependencies

To get started, let's install the necessary dependencies using npm:

npm install react-router-dom

Step 2: Creating a Login Component

Next, we'll create a login component that will be used to authenticate users. This component will contain a form that accepts a username and password.

import React, { useState } from 'react';

function Login() {
  const [username, setUsername] = useState('');
  const [password, setPassword] = useState('');

  const handleSubmit = (event) => {
    event.preventDefault();
    // TODO: Handle login logic
  };

  return (
    <form onSubmit={handleSubmit}>
      <label>
        Username:
        <input type="text" value={username} onChange={(e) => setUsername(e.target.value)} />
      </label>
      <label>
        Password:
        <input type="password" value={password} onChange={(e) => setPassword(e.target.value)} />
      </label>
      <button type="submit">Submit</button>
    </form>
  );
}

export default Login;

Step 3: Creating a Private Route Component

Next, we'll create a private route component that will be used to protect routes that require authentication. This component will check if the user is authenticated and redirect them to the login page if they are not.

import React from 'react';
import { Route, Redirect } from 'react-router-dom';

function PrivateRoute({ component: Component, isAuthenticated, ...rest }) {
  return (
    <Route
      {...rest}
      render={(props) =>
        isAuthenticated ? (
          <Component {...props} />
        ) : (
          <Redirect
            to={{ pathname: '/login', state: { from: props.location } }}
          />
        )
      }
    />
  );
}

export default PrivateRoute;

Step 4: Setting Up Routes

Now that we have the login and private route components, we can set up our application's routes.

We'll define two routes: a public route that can be accessed by anyone, and a private route that requires authentication.

import React, { useState } from 'react';
import { BrowserRouter, Switch } from 'react-router-dom';
import Login from './Login';
import PrivateRoute from './PrivateRoute';

function App() {
  const [isAuthenticated, setIsAuthenticated] = useState(false);

  const handleLogin = () => {
    setIsAuthenticated(true);
  };

  const handleLogout = () => {
    setIsAuthenticated(false);
  };

  return (
    <BrowserRouter>
      <Switch>
        <Route exact path="/">
          <h1>Public Route</h1>
          <p>This route can be accessed by anyone.</p>
        </Route>
        <Route path="/login">
          <Login onLogin={handleLogin} />
        </Route>
        <PrivateRoute
          path="/private"
          component={() => (
            <>
              <h1>Private Route</h1>
              <p>This route requires authentication.</p>
              <button onClick={handleLogout}>Logout</button>
            </>
          )}
          isAuthenticated={isAuthenticated}
        />
      </Switch>
    </BrowserRouter>
  );
}

export default App;

In the above code, we've defined a public route that can be accessed by anyone, a login route, and a private route that requires authentication.

The PrivateRoute component checks if the user is authenticated and renders the component prop if they are. If the user is not authenticated, they are redirected to the login page.

We've also defined two methods, handleLogin and handleLogout, that update the isAuthenticated state.

Step 5: Protecting Routes

To protect routes that require authentication, we simply need to use the PrivateRoute component instead of the regular Route component.

import React, { useState } from 'react';
import { BrowserRouter, Switch } from 'react-router-dom';
import Login from './Login';
import PrivateRoute from './PrivateRoute';

function App() {
  const [isAuthenticated, setIsAuthenticated] = useState(false);

  const handleLogin = () => {
    setIsAuthenticated(true);
  };

  const handleLogout = () => {
    setIsAuthenticated(false);
  };

  return (
    <BrowserRouter>
      <Switch>
        <Route exact path="/">
          <h1>Public Route</h1>
          <p>This route can be accessed by anyone.</p>
        </Route>
        <Route path="/login">
          <Login onLogin={handleLogin} />
        </Route>
        <PrivateRoute
          path="/private"
          component={() => (
            <>
              <h1>Private Route</h1>
              <p>This route requires authentication.</p>
              <button onClick={handleLogout}>Logout</button>
            </>
          )}
          isAuthenticated={isAuthenticated}
        />
      </Switch>
    </BrowserRouter>
  );
}

export default App;

In the above code, we're using the PrivateRoute component to protect the /private route. This route can only be accessed if the user is authenticated.

Step 6: Handling Authentication Errors

In addition to protecting routes, we should also handle authentication-related errors, such as invalid credentials or expired tokens.

We can handle these errors by displaying error messages and clearing the authentication state.

import React, { useState } from 'react';
import { BrowserRouter, Switch } from 'react-router-dom';
import Login from './Login';
import PrivateRoute from './PrivateRoute';

function App() {
  const [isAuthenticated, setIsAuthenticated] = useState(false);
  const [error, setError] = useState(null);

  const handleLogin = async (credentials) => {
    try {
      const response = await fetch('/login', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json'
        },
        body: JSON.stringify(credentials)
      });

      if (response.ok) {
        const { token } = await response.json();
        localStorage.setItem('token', token);
        setIsAuthenticated(true);
      } else {
        setError('Invalid credentials');
      }
    } catch (err) {
      setError(err.message);
    }
  };

  const handleLogout = () => {
    localStorage.removeItem('token');
    setIsAuthenticated(false);
  };

  return (
    <BrowserRouter>
      <Switch>
        <Route exact path="/">
          <h1>Public Route</h1>
          <p>This route can be accessed by anyone.</p>
        </Route>
        <Route path="/login">
          <Login onLogin={handleLogin} error={error} />
        </Route>
        <PrivateRoute
          path="/private"
          component={() => (
            <>
              <h1>Private Route</h1>
              <p>This route requires authentication.</p>
              <button onClick={handleLogout}>Logout</button>
            </>
          )}
          isAuthenticated={isAuthenticated}
        />
      </Switch>
    </BrowserRouter>
  );
}

export default App;

In the above code, we've added an error state to handle authentication-related errors. We display the error message in the Login component and clear the error state when the user successfully logs in or logs out.

Conclusion

In this tutorial, we've learned how to protect routes in a React application using React Router and authentication. We've seen how to create public and private routes, handle authentication state, and display error messages. By using these techniques, we can ensure that our application's data and functionality are only accessible to authorized users.

Recent posts

Don't miss the latest trends

    Popular Posts

    Popular Categories