Part 70: Implementing Sign Out Functionality in a Next.js NavBar

[Pages] Authentication

[Pages] Authentication

In this blog post, we'll walk through adding sign-out functionality to a NavBar component in a Next.js application. This involves handling user authentication states and ensuring cookies are managed correctly for security.

Adding Sign Out Functionality

The NavBar component already fetches and displays user data from the /api/user route, showing the user's name alongside a "Sign Out" button. However, clicking "Sign Out" currently does nothing. Let's fix that by adding the necessary logic to remove the authentication cookie and update the application state.

Step 1: Handle Sign Out in the NavBar

First, we'll add an onClick event handler to the "Sign Out" button. This handler will call a function named handleSignOut, which we will define shortly.

// components/NavBar.js

import Link from 'next/link';
import { useEffect, useState } from 'react';
import { fetchJson } from '../lib/api';

function NavBar() {
  const [user, setUser] = useState();

  useEffect(() => {
    (async () => {
      try {
        const user = await fetchJson('/api/user');
        setUser(user);
      } catch (err) {
        // Handle unauthenticated state
      }
    })();
  }, []);

  // Sign Out handler
  const handleSignOut = async () => {
    await fetchJson('/api/logout');
    setUser(undefined); // Reset user state
  };

  console.log('[NavBar] user:', user);
  return (
    <nav className="px-2 py-1 text-sm">
      <ul className="flex gap-2">
        {user ? (
          <>
            <li>{user.name}</li>
            <li>
              <button onClick={handleSignOut}>Sign Out</button>
            </li>
          </>
        ) : (
          // Other NavBar items
        )}
      </ul>
    </nav>
  );
}

export default NavBar;

Step 2: Create a Logout API Route

Since our JWT is stored as an httpOnly cookie, we cannot modify it directly from the client side. Instead, we'll create a new API route, /api/logout, to handle removing the cookie securely on the server side.

// pages/api/logout.js

import cookie from 'cookie';

function handleLogout(req, res) {
  res.status(200)
    .setHeader('Set-Cookie', cookie.serialize('jwt', '', {
      path: '/api',
      expires: new Date(0), // Set expiry in the past to remove cookie
    }))
    .json({}); // Send empty JSON response
}

export default handleLogout;

In our API route, we use the cookie module to manipulate the cookie headers. Setting the expires date to a past date effectively instructs the browser to discard the cookie, thus logging the user out.

Step 4: Update the Application State

Once the logout request succeeds, we reset the user state in the NavBar component to undefined, which indicates the user is no longer authenticated. This update triggers a re-render of the component, replacing the user-specific content with a "Sign In" link.

Testing the Implementation

To ensure everything is working correctly:

  1. Sign In: First, sign in to your application to set the JWT cookie.

  2. Sign Out: Click the "Sign Out" button in the NavBar.

  3. Check the Network Tab: Open Developer Tools and observe the network requests. You should see a request to /api/logout and a Set-Cookie header with an expired date.

  4. Verify Cookie Removal: Check the Application tab in Developer Tools to confirm that the JWT cookie is no longer present.

Conclusion

We've successfully implemented a sign-out functionality by creating a logout API route that manages the JWT cookie on the server side. This approach ensures that user authentication states are managed securely and efficiently. In upcoming posts, we'll explore optimizing API requests and caching responses using libraries like React Query to enhance performance further. Stay tuned for more insights on building robust Next.js applications!

Last updated