Part 68: Creating a Secure User Authentication API in Next.js

[Pages] Authentication

[Pages] Authentication

In this post, we will explore how to fetch user data securely in a Next.js application. We previously set up a NavBar component to display user-specific information based on authentication status. Now, we'll create an API route to fetch user details from a CMS securely using JSON Web Tokens (JWT).

Why Do We Need a Server-Side API?

When a user signs in, a JWT is stored in an HTTP-only cookie. This type of cookie is inaccessible to JavaScript running in the browser, enhancing security by preventing cross-site scripting attacks. However, this also means we need to handle the cookie on the server side to access the user data.

We'll create a server-side API endpoint to act as a proxy to the CMS's /users/me endpoint, which returns user details.

Setting Up the API Route

We'll create a new API route called /api/user to handle user data requests. This route will extract the JWT from the cookie and use it to fetch user data from the CMS.

Step 1: Create the API Handler

The first step is to create an API handler in your Next.js application. This handler will read the JWT from the cookies and call the CMS to fetch user details.

// pages/api/user.js

import { fetchJson } from '../../lib/api';

const { CMS_URL } = process.env;

async function handleUser(req, res) {
  const { jwt } = req.cookies; // Extract JWT from cookies
  if (!jwt) {
    res.status(401).end(); // Return unauthorized if JWT is missing
    return;
  }
  try {
    // Fetch user details from the CMS
    const user = await fetchJson(`${CMS_URL}/users/me`, {
      headers: { 'Authorization': `Bearer ${jwt}` },
    });
    // Return user ID and name
    res.status(200).json({
      id: user.id,
      name: user.username,
    });
  } catch (err) {
    res.status(401).end(); // Return unauthorized on error
  }
}

export default handleUser;

Step 2: Handling Authentication Errors

If the JWT is missing or invalid, we immediately return a 401 Unauthorized response. This prevents unnecessary requests to the CMS and provides a clear indication that the user is not authenticated.

Step 3: Testing the API Route

To test this API route, we can simulate requests from the browser console using the fetch function.

// Run this in the browser console
fetch('/api/user')
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(err => console.error('Request failed:', err));

If the JWT is present and valid, this should log an object containing the user ID and name. If not, it will log a 401 Unauthorized error.

Step 4: Handling Missing Tokens

To simulate a missing token, you can delete the cookie from the browser's storage and rerun the fetch command. The server should correctly respond with a 401 Unauthorized status, confirming that our API route handles authentication as expected.

Conclusion

We've successfully created an API route in Next.js to securely fetch user data using JWTs. This route checks for the presence of a JWT cookie and fetches user details from a CMS if the token is valid. This setup not only enhances security by keeping sensitive tokens server-side but also provides a clear mechanism for handling user authentication.

In the next steps, we'll integrate this API call into our NavBar component to dynamically display user information or prompt for sign-in based on authentication status. Stay tuned as we continue to build out our Next.js application with secure and efficient user authentication!

Last updated