Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JWT maxAge: what is the default value? #796

Closed
1 of 5 tasks
fromi opened this issue Oct 24, 2020 · 6 comments
Closed
1 of 5 tasks

JWT maxAge: what is the default value? #796

fromi opened this issue Oct 24, 2020 · 6 comments
Labels
help-needed The maintainer needs help due to time constraint/missing knowledge question Ask how to do something or how something works

Comments

@fromi
Copy link

fromi commented Oct 24, 2020

Your question
In the session option documentation, the default value for maxAge seems to be 30 days. Is that also true if session.jwt is set to true?
I am wondering because in the FAQ there is this statement:
"You cannot as easily expire a JSON Web Token [...] Shorter session expiry times are used when using JSON Web Tokens as session tokens to allow sessions to be invalidated sooner and simplify this problem."

  • The session expiry time and session.maxAge are the same thing, right?
  • Is "using shorter session expiry time" a recommandation, or it is automatic? What is the default value if it is automatic?

Feedback
Documentation refers to searching through online documentation, code comments and issue history. The example project refers to next-auth-example.

  • Found the documentation helpful
  • Found documentation but was incomplete
  • Could not find relevant documentation
  • Found the example project helpful
  • Did not find the example project helpful
@fromi fromi added the question Ask how to do something or how something works label Oct 24, 2020
@iaincollins iaincollins added the help-needed The maintainer needs help due to time constraint/missing knowledge label Oct 29, 2020
@iaincollins
Copy link
Member

Good questions!

"JWT maxAge: what is the default value?"
"In the session option documentation, the default value for maxAge seems to be 30 days. Is that also true if session.jwt is set to true?"

The default value for jwt.maxAge currently set to whatever the default session.maxAge is set to, technically you never need to set it or worry about it and can just set session.maxAge.

The default session session.maxAge value is indeed 30 days (defined 30 * 24 * 60 * 60 seconds), it is set here:
https://github.com/nextauthjs/next-auth/blob/main/src/server/index.js#L112

It looks like we have covered this well in the session documentation, but mention of it is missing from the jwt option documentation, which is something we should probably address for clarity:
https://next-auth.js.org/configuration/options#session

I honestly forget why jwt.maxAge is even an option. It's very much intended as an advanced mode and currently undocumented feature (this currently applies to a few of the other undocumented JWT options).

"The session expiry time and session.maxAge are the same thing, right?"

session.maxAge defines the actual session expiry time and is main thing to worry about.

Where it gets complicated is the clientMaxAge value on the client side, which comes in to play if you want to use shorter session expiry times (e.g. minutes instead of days) or want to optimise for extreme scale.
https://next-auth.js.org/getting-started/client#provider

By default, a Next.js Single Page App using NextAuth.js will load session once and on subsequent page transitions, the useSession hook will reuse the response it already has.

However, the app wide session state WILL be updated in the SPA if the window loses/regains focus or if the user logs out in another window in the same browser (it uses event hooks to do this) OR anytime you call getSession() in your app. This makes Single Page Apps efficient by default - they don't check your session on every page navigation, which keeps network traffic down and page responses snappy - but keeps the session state from getting stale by instead re-validating when a page gains focus again.

The clientMaxAge option tells the client, hey if the last time you check was more than X seconds ago, go ahead and fetch it again, to make sure you don't have a stale copy and the session hasn't really expired. The default value is 0 whcih tells it to prefer cache under normal circumstances (and this is explained in more detail in the above URL).

The keepAlive option is useful for very short sessions; say you have a 5 minute session expiry time, then you set a keepAlive to 4 minutes so a users session should never expire as long as window remains open (but if the computer goes to sleep, the session should expire).

As per the docs above, in most cases, if your session.maxAge is measured in days (1 day, 30 days, 90 days, 365 days, etc) rather than hours or minutes, then you don't really need to worry about these options.

"Is "using shorter session expiry time" a recommandation, or it is automatic? What is the default value if it is automatic?"

Using something like 30 days (or even longer, you might want 90 days, or a year) is fine in almost all cases.

The only reason folks typically worry about shorter session times with JWT is that, unlike a database session, you can't just delete a user's session entry from the database to expire their session and force them to be signed out as the token is stored on their side.

However, you can still prevent them from preforming an action by checking the token and rejecting it in your own API routes, even if they have a JWT but you have since deleted / blocked their account on your servers.

Working out if this is a thing you need to worry about probably depends on the specifics of the particular app and your use cases.

Thinking about how you want to handle abuse / bad actors / deleted users / accounts not in good standing (e.g. expires) on your platform will help you figure out if you need to worry about this and what the easiest way for you to handle those situations is.

A typical example might be, if it was a forum you might check the user in the JWT against a database of blocked / suspended / temporarily banned accounts and prevent users who were in that list from posting. This downside is the effectively the crux of the tradeoff for using JSON Web Tokens - it can be faster for users and cheaper at scale, but means handling some flows is harder.

@iaincollins
Copy link
Member

Oops, sorry didn't mean to close this already!

If you have any follow up questions or I've written nonsense (it's a big wall of text so I might have in places....) please feel free to re-open so I don't miss them.

@fromi
Copy link
Author

fromi commented Oct 29, 2020

Thanks @iaincollins, that's great answers 👍

@ilijaNL
Copy link

ilijaNL commented Jul 16, 2021

Great explanation @iaincollins. However for security perspectives and to avoid having a blocked/suspended central list, a short living access token could be used to protect your routes. Instead of using the session jwt, you could generate a short living access token jwt yourself and use it in your backend/api routes. The session jwt from next-auth will be then used as a "refresh" token.

One possible implementation is this:

callbacks: {
    async session(session, userOrToken: JWT) {
      session.access_token = genShortLivingAccessToken(userOrToken);

      session.user = {
        email: userOrToken.email!,
        id: userOrToken.sub!
      };
      return session;
    },
    // this enhances the jwt and stores additional data
    async jwt(token, user, account, profile, isNewUser) {
      if (user) {
        const result = await DB.getUserByEmail({ email: user.email }).then(
          (d) => d.users[0]
        );
        token.sub = result.id;
        token.email = result.email;
      }
      return token;
    }
  },

and in the _app.js provider set the keepAlive time lower than the ttl of the jwt.

A good article to read is: https://hasura.io/blog/best-practices-of-using-jwt-with-graphql/

@felinto-dev
Copy link

@iaincollins could you please help me?
Thanks!

#4578

@AnmolSaini16
Copy link

AnmolSaini16 commented Aug 30, 2022

Good questions!

"JWT maxAge: what is the default value?"
"In the session option documentation, the default value for maxAge seems to be 30 days. Is that also true if session.jwt is set to true?"

The default value for jwt.maxAge currently set to whatever the default session.maxAge is set to, technically you never need to set it or worry about it and can just set session.maxAge.

The default session session.maxAge value is indeed 30 days (defined 30 * 24 * 60 * 60 seconds), it is set here: https://github.com/nextauthjs/next-auth/blob/main/src/server/index.js#L112

It looks like we have covered this well in the session documentation, but mention of it is missing from the jwt option documentation, which is something we should probably address for clarity: https://next-auth.js.org/configuration/options#session

I honestly forget why jwt.maxAge is even an option. It's very much intended as an advanced mode and currently undocumented feature (this currently applies to a few of the other undocumented JWT options).

"The session expiry time and session.maxAge are the same thing, right?"

session.maxAge defines the actual session expiry time and is main thing to worry about.

Where it gets complicated is the clientMaxAge value on the client side, which comes in to play if you want to use shorter session expiry times (e.g. minutes instead of days) or want to optimise for extreme scale. https://next-auth.js.org/getting-started/client#provider

By default, a Next.js Single Page App using NextAuth.js will load session once and on subsequent page transitions, the useSession hook will reuse the response it already has.

However, the app wide session state WILL be updated in the SPA if the window loses/regains focus or if the user logs out in another window in the same browser (it uses event hooks to do this) OR anytime you call getSession() in your app. This makes Single Page Apps efficient by default - they don't check your session on every page navigation, which keeps network traffic down and page responses snappy - but keeps the session state from getting stale by instead re-validating when a page gains focus again.

The clientMaxAge option tells the client, hey if the last time you check was more than X seconds ago, go ahead and fetch it again, to make sure you don't have a stale copy and the session hasn't really expired. The default value is 0 whcih tells it to prefer cache under normal circumstances (and this is explained in more detail in the above URL).

The keepAlive option is useful for very short sessions; say you have a 5 minute session expiry time, then you set a keepAlive to 4 minutes so a users session should never expire as long as window remains open (but if the computer goes to sleep, the session should expire).

As per the docs above, in most cases, if your session.maxAge is measured in days (1 day, 30 days, 90 days, 365 days, etc) rather than hours or minutes, then you don't really need to worry about these options.

"Is "using shorter session expiry time" a recommandation, or it is automatic? What is the default value if it is automatic?"

Using something like 30 days (or even longer, you might want 90 days, or a year) is fine in almost all cases.

The only reason folks typically worry about shorter session times with JWT is that, unlike a database session, you can't just delete a user's session entry from the database to expire their session and force them to be signed out as the token is stored on their side.

However, you can still prevent them from preforming an action by checking the token and rejecting it in your own API routes, even if they have a JWT but you have since deleted / blocked their account on your servers.

Working out if this is a thing you need to worry about probably depends on the specifics of the particular app and your use cases.

Thinking about how you want to handle abuse / bad actors / deleted users / accounts not in good standing (e.g. expires) on your platform will help you figure out if you need to worry about this and what the easiest way for you to handle those situations is.

A typical example might be, if it was a forum you might check the user in the JWT against a database of blocked / suspended / temporarily banned accounts and prevent users who were in that list from posting. This downside is the effectively the crux of the tradeoff for using JSON Web Tokens - it can be faster for users and cheaper at scale, but means handling some flows is harder.

Thanks @iaincollins for this excellent explanation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
help-needed The maintainer needs help due to time constraint/missing knowledge question Ask how to do something or how something works
Projects
None yet
Development

No branches or pull requests

5 participants