Part 122: Securing User Authentication with JSON Web Tokens

[App] Authentication Overview

[App] Authentication Overview

In our web application, we've taken a significant step forward in securing user authentication by using JSON Web Tokens (JWTs). JWTs provide a robust method for encoding user information, ensuring that any tampering attempts are easily detectable. In this blog post, we'll explore how to decode JWTs and utilize them to maintain user sessions in our application.

The Importance of JWTs

JWTs are a secure way to encode information between the client and the server. They consist of three parts:

  1. Header: Specifies the algorithm used for signing the token.

  2. Payload: Contains the claims, such as user information.

  3. Signature: Validates the authenticity of the token, ensuring it hasn't been altered.

In our application, we encode user details as a JWT and set it as a cookie upon successful login. This token is signed with a server-side secret key, preventing unauthorized modifications.

Decoding JWTs in the NavBar

To display user information in the NavBar, we need to decode the JWT stored in the cookie. Here's how we can achieve that:

Step 1: Define a Function to Decode the JWT

First, we create a function called getUserFromSession to extract user details from the session token.

// File path: components/NavBar.jsx

mport { jwtVerify } from 'jose';
import { cookies } from 'next/headers';
import NavLink from './NavLink';

const JWT_SECRET = new TextEncoder().encode('some-random-string');

async function getUserFromSession() {
  const sessionTokenCookie = cookies().get('sessionToken');
  if (sessionTokenCookie) {
    try {
      const { payload } = await jwtVerify(sessionTokenCookie.value, JWT_SECRET);
      return payload;
    } catch (error) {
      console.warn('Invalid JWT', error);
    }
  }
}

Step 2: Update the NavBar Component

Next, we modify the NavBar component to use the getUserFromSession function. This allows us to determine whether the user is logged in based on the decoded JWT.

// File path: components/NavBar.jsx

export default async function NavBar() {
  const user = await getUserFromSession();
  return (
    <nav>
      <ul className="flex gap-2">
        <li className="font-bold font-orbitron">
          <NavLink href="/">
            Indie Gamer
          </NavLink>
        </li>
        <li className="ml-auto">
          <NavLink href="/reviews">
            Reviews
          </NavLink>
        </li>
        <li>
          <NavLink href="/about" prefetch={false}>
            About
          </NavLink>
        </li>
        {user ? (
          <li>
            {user.email}
          </li>
        ) : (
        <li>
          <NavLink href="/sign-in">
            Sign in
          </NavLink>
        </li>
        )}
      </ul>
    </nav>
  );
}

Step 3: Handling Errors

If the JWT is invalid or has been tampered with, the jwtVerify function will throw an error. We handle this by wrapping the call in a try-catch block, logging a warning if verification fails.

Testing the Implementation

  1. Logged-In User: When a user logs in, their email should appear in the NavBar.

  2. No Cookie: If there's no session token, the NavBar should display the "Sign in" link.

  3. Tampered Token: If the token is modified, an error is logged, and the user is treated as not authenticated.

Conclusion

By using JWTs, we enhance the security of our application, ensuring that user data stored in cookies cannot be manipulated by malicious users. This approach allows us to trust the data retrieved from cookies, as long as we verify the token using our server's secret key. This method secures user authentication, providing a reliable way to manage user sessions in a web application.

Last updated