Skip to main content

Authentication & Protected Routes

In this chapter, we will learn how to consume the JWT authentication API we built in the Node.js course.

Storing the Token

When the user logs in, the server sends a JWT. We need to store this token, usually in localStorage.
const login = async (email, password) => {
  const res = await fetch('/api/auth/login', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ email, password })
  });
  
  const data = await res.json();
  
  if (data.token) {
    localStorage.setItem('token', data.token);
  }
};

Sending the Token

For every subsequent request to a protected route, we must include the token in the headers.
const token = localStorage.getItem('token');

const res = await fetch('/api/protected', {
  headers: {
    'x-auth-token': token
  }
});

Auth Context

We can manage the authentication state globally using Context API. AuthContext.js
import { createContext, useState, useEffect, useContext } from 'react';

const AuthContext = createContext(null);

export const AuthProvider = ({ children }) => {
  const [user, setUser] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    // Check if token exists and verify it
    const token = localStorage.getItem('token');
    if (token) {
      // Ideally, verify token with backend here
      setUser({ token }); 
    }
    setLoading(false);
  }, []);

  const login = (token) => {
    localStorage.setItem('token', token);
    setUser({ token });
  };

  const logout = () => {
    localStorage.removeItem('token');
    setUser(null);
  };

  return (
    <AuthContext.Provider value={{ user, login, logout, loading }}>
      {!loading && children}
    </AuthContext.Provider>
  );
};

export const useAuth = () => useContext(AuthContext);

Protected Routes

We can create a wrapper component to protect routes that require authentication. PrivateRoute.jsx
import { Navigate, Outlet } from 'react-router-dom';
import { useAuth } from './AuthContext';

const PrivateRoute = () => {
  const { user } = useAuth();

  // If not authenticated, redirect to login
  return user ? <Outlet /> : <Navigate to="/login" />;
};

export default PrivateRoute;

Using Private Routes

Wrap your protected routes with the PrivateRoute component. App.jsx
import { Routes, Route } from 'react-router-dom';
import PrivateRoute from './PrivateRoute';
import Dashboard from './Dashboard';
import Login from './Login';

function App() {
  return (
    <Routes>
      <Route path="/login" element={<Login />} />
      
      {/* Protected Routes */}
      <Route element={<PrivateRoute />}>
        <Route path="/dashboard" element={<Dashboard />} />
        <Route path="/profile" element={<Profile />} />
      </Route>
    </Routes>
  );
}

Summary

  • Store JWTs securely (e.g., localStorage or HttpOnly cookies).
  • Send the token in the Authorization or custom header for requests.
  • Use Context API to manage global auth state.
  • Create a PrivateRoute component to redirect unauthenticated users.