Authentication System with React's Context API

Mohsin AliMohsin Ali | Sept 2022
Cover image for Authentication System with React's Context API

Introduction

Managing user authentication in a React application can become complex as your application grows. React's Context API offers a streamlined approach to handle authentication, including login, registration, and token management. In this post, we'll walk through creating an authentication system using the Context API with practical code examples.

Setting Up the Auth Context

First, we need to create an authentication context to manage the authentication state and provide necessary actions like login and register.

import React, { createContext, useState } from 'react';

// Create a Context
const AuthContext = createContext();

const AuthProvider = ({ children }) => {
  const [authState, setAuthState] = useState({
    token: null,
    user: null,
  });

  const login = (token, user) => {
    setAuthState({ token, user });
  };

  const logout = () => {
    setAuthState({ token: null, user: null });
  };

  const register = (token, user) => {
    setAuthState({ token, user });
  };

  return (
    <AuthContext.Provider value={{ authState, login, logout, register }}>
      {children}
    </AuthContext.Provider>
  );
};

export { AuthContext, AuthProvider };

In this example, AuthContext is created with default values. The AuthProvider component manages the authentication state using the useState hook and provides login, logout, and register functions.

Implementing the Login Component

Next, let's create a login component that uses the AuthContext to log in a user and store the authentication token.

import React, { useState, useContext } from 'react';
import { AuthContext } from './AuthContext';

const Login = () => {
  const [credentials, setCredentials] = useState({ username: '', password: '' });
  const { login } = useContext(AuthContext);

  const handleSubmit = async (e) => {
    e.preventDefault();
    // Simulate API call
    const token = 'sample-token'; // Replace with real token
    const user = { username: credentials.username };
    login(token, user);
  };

  return (
    <form onSubmit={handleSubmit}>
      <input
        type="text"
        placeholder="Username"
        value={credentials.username}
        onChange={(e) => setCredentials({ ...credentials, username: e.target.value })}
      />
      <input
        type="password"
        placeholder="Password"
        value={credentials.password}
        onChange={(e) => setCredentials({ ...credentials, password: e.target.value })}
      />
      <button type="submit">Login</button>
    </form>
  );
};

export default Login;

In this component, we use the useContext hook to access the login function from AuthContext. The handleSubmit function simulates an API call to authenticate the user and store the token.

Creating the Registration Component

Similarly, we can create a registration component that uses the register function from AuthContext.

import React, { useState, useContext } from 'react';
import { AuthContext } from './AuthContext';

const Register = () => {
  const [credentials, setCredentials] = useState({ username: '', password: '' });
  const { register } = useContext(AuthContext);

  const handleSubmit = async (e) => {
    e.preventDefault();
    // Simulate API call
    const token = 'sample-token'; // Replace with real token
    const user = { username: credentials.username };
    register(token, user);
  };

  return (
    <form onSubmit={handleSubmit}>
      <input
        type="text"
        placeholder="Username"
        value={credentials.username}
        onChange={(e) => setCredentials({ ...credentials, username: e.target.value })}
      />
      <input
        type="password"
        placeholder="Password"
        value={credentials.password}
        onChange={(e) => setCredentials({ ...credentials, password: e.target.value })}
      />
      <button type="submit">Register</button>
    </form>
  );
};

export default Register;

This component follows the same structure as the login component, using the register function to handle user registration.

Protecting Routes with Auth Context

To protect certain routes and ensure that only authenticated users can access them, we can create a PrivateRoute component.

import React, { useContext } from 'react';
import { Route, Redirect } from 'react-router-dom';
import { AuthContext } from './AuthContext';

const PrivateRoute = ({ component: Component, ...rest }) => {
  const { authState } = useContext(AuthContext);

  return (
    <Route
      {...rest}
      render={(props) =>
        authState.token ? (
          <Component {...props} />
        ) : (
          <Redirect to="/login" />
        )
      }
    />
  );
};

export default PrivateRoute;

This component checks if the user is authenticated by looking for a token in the authState. If the user is not authenticated, they are redirected to the login page.

Conclusion

Using React's Context API to manage authentication state simplifies the process of handling login, registration, and token storage in your application. By providing a central context for authentication, you can easily access and manage the authentication state across your application. This approach not only makes your code cleaner but also enhances the maintainability and scalability of your application. Try integrating the Context API for authentication in your next React project and see the benefits firsthand!