Unlocking NextAuth Secrets: Manage JWTs and Session Expiry Like a Pro

A complete guide to mastering JWT-based tokens and session expiry in NextAuth for secure and efficient user authentication.

November 25, 2024

By Karan Patel

Understanding Expiry of JWTs

Session management is at the heart of user authentication, ensuring that users remain securely logged in while maintaining a smooth experience. When working with NextAuth, its robust support for JWT-based tokens makes it a go-to solution for modern, scalable applications. However, fine-tuning session expiry with JWTs can sometimes feel overwhelming without a clear understanding of its configuration and behavior.

This blog will explore on how to set up token durations, manage expiry effectively, and align your settings to match your application’s security needs—all while keeping the focus on practical, actionable insights.

How NextAuth Manages JWT Sessions

By default NextAuth uses JWTs (JSON Web Tokens) to manage server-side authentication and session cookies to communicate authentication state to the client. To learn more about JWTs you can go visit jwt.io. Let’s break down how these components work together to handle user sessions securely and efficiently.

  • Token: The server-side representation of the session. It contains the user’s authentication information and is generated based on the configuration of your NextAuth setup.
  • Session Cookie: Sent to the user’s browser, this is an encrypted version of the token. It uses the NEXTAUTH_SECRET to ensure secure storage on the client side while preventing tampering.

These two elements work together: the token is used for validating user sessions on the server, while the session cookie keeps the user logged in on the client.

Default Expiry Settings

By default, NextAuth sets a maxAge of 30 days for both:

  1. JWT Token (jwt.maxAge): The lifespan of the server-side token before it expires and the user must reauthenticate.
  2. Session Cookie (session.maxAge): The time the session cookie remains valid on the client side.

These durations can be configured independently, allowing flexibility for applications that need different lifespans for server and client-side authentication states.

Customize Expiry

To align session durations with your application’s needs, NextAuth allows you to configure separate expiry durations for both the JWT token and the session cookie. This setup provides flexibility to balance security and user convenience.

For instance, you might want the JWT token (server-side) to remain valid for 14 days, while the session cookie (client-side) expires after 1 day. Here’s how you can configure this:

// [...nextauth].ts
import NextAuth from "next-auth";
import Providers from "next-auth/providers";

export default NextAuth({
  providers: [
    // Add your provider(s) here
    // ...
  ],
  callbacks: {
    // Add your JWT or session callbacks here if needed
    // ...
  },
  jwt: {
    maxAge: 14 * 86400, // 14 days in seconds
  },
  session: {
    maxAge: 1 * 86400, // 1 day in seconds
  },
  secret: process.env.NEXTAUTH_SECRET, // Ensure this is securely set
  pages: {
    signIn: "/login", // Custom login page
  },
});

A general rule of thumb is to configure session cookie expiry equal to or less than the JWT token expiry. So in this example, the maximum validity of session cookie should be set to 14 days or less. Another thing to keep in mind is when you set the session cookie’s expiry to be less than the JWT token’s expiry, then it is good to implement rolling sessions i.e. modifying the expiry based on activity. Otherwise if you’ve implemented custom middleware validation for client’s session, then it would redirect user to the login page and ask them to authenticate which would generate a new token while the old token is still valid and isn’t expired.

Static vs. Rolling Sessions

When a session is created, its expiry time is set based on the maxAge property. By default, static sessions will expire at the time set in the configuration. This means the session will remain active for the specified maxAge (i.e., 1 day for our example) and will expire once that time is reached on client side, regardless of user activity.

In contrast, rolling sessions extend the session's expiry time each time the user makes a request or takes some action, thus "renewing" the session. This can be configured by updating the session's expires value based on the latest activity. For example, in NextAuth, you can configure session renewal with the updateAge property. This ensures that the session's expiry is refreshed based on user activity, allowing users to stay logged in as long as they are actively engaging with the app. The updateAge setting determines how often the session is updated, in seconds. To add this:

jwt: {
  maxAge: 14 * 86400, // 14 days in seconds
},
session: {
  maxAge: 1 * 86400, // 1 day in seconds
  updateAge: 6 * 3600 // 6 hours in seconds
},

This approach automatically keeps the session alive as long as the user is active, without manual intervention, pushing the expiry of the session cookie (in our case by 6 hours). If a user doesn’t interact within the last expiry that was set, the session expires. It is recommended to use rolling sessions instead of static sessions as this is the industry standard. Think about it, how long has it been since you logged into Gmail Or YouTube?

Debugging Tips

When managing sessions in NextAuth, you might encounter issues and to help you avoid them below are some debugging tips you can use.

  1. Inspect Cookies and Tokens: Use browser developer tools to inspect session cookie. Check if the maxAge and expiry properties are set as expected.
  2. Use the NextAuth Debug Mode: Set debug: true in your NextAuth configuration to get detailed logs that can help identify issues with session management.
    jwt: {},
    session: {},
    debug: true,
    
  3. Log Session Data: Add a logger and see when it’s being updated or reset. Following is an example logger and if you have a custom logger added, then debug: true is ignored.
     import log from "logging-service"
    
     export default NextAuth({
       ...
       jwt: {},
         session: {},
       logger: {
         error(code, metadata) {
           log.error(code, metadata)
         },
         warn(code) {
           log.warn(code)
         },
         debug(code, metadata) {
           log.debug(code, metadata)
         }
       }
       ...
     })
    

Best Practices

When configuring session expiry and management in NextAuth, following best practices ensures that your application remains secure, reliable, and user-friendly. Here are some key recommendations:

Set Appropriate Expiry Values

Balancing security and user convenience is crucial. Setting excessively long session expiry values could pose security risks, while too-short values might frustrate users for prompting to re-login. For example, sessions for general users could last 1 day, while sensitive sessions (e.g., admin) could have a much shorter duration.

Best Practice: Use maxAge values tailored to the sensitivity of the data. For general users, 30 days might be acceptable, but for users with more critical roles or data access, use shorter time periods like 1 or 2 hours.

Use Secure Cookies

When using cookies, ensure that the secure flag is set, particularly if your site uses HTTPS. This ensures that cookies are only sent over secure connections, mitigating risks of man-in-the-middle attacks.

Best Practice: Always set secure: true for cookies in production environments. Additionally, use the sameSite cookie attribute to help prevent cross-site request forgery (CSRF) attacks. You can read more information on how to set these properties here.

Implement Rolling Sessions Where Appropriate

Rolling sessions are ideal for keeping users logged in as long as they remain active. This approach, used by popular applications like Gmail, refreshes the session's expiry whenever the user interacts with the app. This enhances user experience by preventing unexpected logouts.

Best Practice: Use the updateAge option in NextAuth to refresh the session expiration on user activity. Set updateAge to a reasonable value, such as 1 day for general users and 1 hour for admin users, to maintain session validity during periods of activity.

Key Takeaways on Effective Session Management

That’s it folks! By understanding the role of session cookies and JWTs, configuring appropriate session cookie expiry, JWT expiry, and implementing strategies like rolling sessions, you can maintain both security and usability. Furthermore, leveraging best practices, such as using secure cookies, implementing logging, and monitoring session data, will help you identify and solve issues quickly, ensuring your application remains reliable. NextAuth provides flexibility to suit a variety of needs when managing user sessions. Keep experimenting with different setups to find the best configuration for your application, and remember to always keep security in mind.

Karan Developer

Karan Patel

Sitecore Developer

Karan is a Sitecore Certified Developer with over 2 years of experience building and implementing Sitecore solutions using ASP .NET MVC, React, Azure and AWS. He's an avid fan of Counter-Strike and can usually be found playing it if he is not developing. In his spare time, he likes to play guitar and cook Indian food.

These are some related articles