next-auth / session middleware?
Unanswered
Golden Eagle posted this in #help-forum
Golden EagleOP
Hi everyone,
I'm working on a feature to check users' subscription status and expiration date in a Next.js 14 project using NextAuth. My goal is to ensure that only users with an active subscription can access the system.
Storing the expiration date in the JWT doesn't seem secure since it can be manipulated, and making fetch calls in middleware.ts feels like it's not the right place either.
If anyone has experience with this setup or can suggest a more elegant solution, I'd really appreciate your help!
Thanks in advance.
I'm working on a feature to check users' subscription status and expiration date in a Next.js 14 project using NextAuth. My goal is to ensure that only users with an active subscription can access the system.
Storing the expiration date in the JWT doesn't seem secure since it can be manipulated, and making fetch calls in middleware.ts feels like it's not the right place either.
If anyone has experience with this setup or can suggest a more elegant solution, I'd really appreciate your help!
Thanks in advance.
18 Replies
@Golden Eagle Hi everyone,
I'm working on a feature to check users' subscription status and expiration date in a Next.js 14 project using NextAuth. My goal is to ensure that only users with an active subscription can access the system.
Storing the expiration date in the JWT doesn't seem secure since it can be manipulated, and making fetch calls in middleware.ts feels like it's not the right place either.
If anyone has experience with this setup or can suggest a more elegant solution, I'd really appreciate your help!
Thanks in advance.
Storing the expiration date in the JWT doesn't seem secure since it can be manipulatedYes, it can be manipulated and the jwt is signed by the server. So when the user manipulates the data, then server can't very the data anymore and then the data is invalid. So only when the server signs the data, the data is valid. And the user (client) does not have the secret token for the jwt, so the client is unable to manipulate it, without invalidating the data.
So: store it inside your jwt and sign and secure your jwt.
@B33fb0n3 > Storing the expiration date in the JWT doesn't seem secure since it can be manipulated
Yes, it can be manipulated and the jwt is signed by the server. So when the user manipulates the data, then server can't very the data anymore and then the data is invalid. So only when the server signs the data, the data is valid. And the user (client) does not have the secret token for the jwt, so the client is unable to manipulate it, without invalidating the data.
So: store it inside your jwt and sign and secure your jwt.
Golden EagleOP
I didn't quite understand your response. Do I understand correctly that you are suggesting to check it in middleware? But what if the subscription was updated on the server? As far as I understand, the JWT callback is triggered only once during authentication and until a new JWT token is created, the user will have the old subscription information?
@Golden Eagle I didn't quite understand your response. Do I understand correctly that you are suggesting to check it in middleware? But what if the subscription was updated on the server? As far as I understand, the JWT callback is triggered only once during authentication and until a new JWT token is created, the user will have the old subscription information?
I am saying to you, that you should store the expiration date inside the jwt, but don’t trust it. As you said, the server can change it. However: jwt is clientside and won’t be updated, when the server updates it. So when you want to have a trustworthy value for the expiration time, also store a reference to the backend object of the subscription, to be able to ask for the current expiration date.
Like that you can easily display and make checks with the expiration date from the client (no network requests) but can still trust the value (even if it’s change from the server) with storing a reference to the subscription
Like that you can easily display and make checks with the expiration date from the client (no network requests) but can still trust the value (even if it’s change from the server) with storing a reference to the subscription
@Golden Eagle solved?
@B33fb0n3 <@225685293506887680> solved?
Golden EagleOP
Yes and no. From what I understand, you’re suggesting storing this information in a token. However, I’d like to ask how you envision verifying the subscription status going forward. For instance, all authenticated pages should only be accessible if the user has an active subscription. There are many internal pages, and if we add a check on each page individually, it would increase the amount of code we need to maintain, violating the DRY principle. Is there a tool or method we can use to perform this check universally for all pages?
Here’s what I’m thinking: we could implement a middleware that checks the subscription’s expiration date from the token. If the subscription has expired, we could redirect the user to a page where a hook updates the subscription information. If the subscription is still active, we would redirect them to the main protected page. Otherwise, we could send them to a page displaying a banner about the expired subscription.
However, I have some concerns about this approach:
1. It doesn’t fully guarantee that the subscription information in the token is up to date.
2. At what point should the expiration date in the token be updated if the subscription status changes? For example, a payment via Stripe could be delayed (due to a webhook), and I don’t think a JWT can be updated in a delayed manner.
Here’s what I’m thinking: we could implement a middleware that checks the subscription’s expiration date from the token. If the subscription has expired, we could redirect the user to a page where a hook updates the subscription information. If the subscription is still active, we would redirect them to the main protected page. Otherwise, we could send them to a page displaying a banner about the expired subscription.
However, I have some concerns about this approach:
1. It doesn’t fully guarantee that the subscription information in the token is up to date.
2. At what point should the expiration date in the token be updated if the subscription status changes? For example, a payment via Stripe could be delayed (due to a webhook), and I don’t think a JWT can be updated in a delayed manner.
@Golden Eagle Yes and no. From what I understand, you’re suggesting storing this information in a token. However, I’d like to ask how you envision verifying the subscription status going forward. For instance, all authenticated pages should only be accessible if the user has an active subscription. There are many internal pages, and if we add a check on each page individually, it would increase the amount of code we need to maintain, violating the DRY principle. Is there a tool or method we can use to perform this check universally for all pages?
Here’s what I’m thinking: we could implement a middleware that checks the subscription’s expiration date from the token. If the subscription has expired, we could redirect the user to a page where a hook updates the subscription information. If the subscription is still active, we would redirect them to the main protected page. Otherwise, we could send them to a page displaying a banner about the expired subscription.
However, I have some concerns about this approach:
1. It doesn’t fully guarantee that the subscription information in the token is up to date.
2. At what point should the expiration date in the token be updated if the subscription status changes? For example, a payment via Stripe could be delayed (due to a webhook), and I don’t think a JWT can be updated in a delayed manner.
well, congratulations: you now know the pros and cons with JWT. Yes, JWT a saved on client and because of that you can only change them, when the client does something. But the good thing about that is, that you don't have to ask the backend everytime if the subscription is still active.
You can also use the session auth approach: with that the client only saves an id to an active session and all other data to this session will be fetched when needed. So a session can be updated serverside.
So you might want to rethink which auth method works best for you. Here is a little help to decide it: https://velog.velcdn.com/images/devfish/post/6e8c7828-09d3-46c6-93cd-d683f31b0ae2/image.png
You can also use the session auth approach: with that the client only saves an id to an active session and all other data to this session will be fetched when needed. So a session can be updated serverside.
So you might want to rethink which auth method works best for you. Here is a little help to decide it: https://velog.velcdn.com/images/devfish/post/6e8c7828-09d3-46c6-93cd-d683f31b0ae2/image.png
@Golden Eagle solved?
@B33fb0n3 <@225685293506887680> solved?
Golden EagleOP
Ok, I still have the same question: suppose I abandon JWT in favour of a DB session, is there some way to check subscriptions once for a specific path (rather than on each page individually) so that if the subscription is up to date not active (taken from the DB session), to deny access? As far as I understand, this can't be done in the root layout?
@Golden Eagle Ok, I still have the same question: suppose I abandon JWT in favour of a DB session, is there some way to check subscriptions once for a specific path (rather than on each page individually) so that if the subscription is up to date not active (taken from the DB session), to deny access? As far as I understand, this can't be done in the root layout?
yes you are right, you shouldn't do that inside the root layout. You can check that inside your middleware and if the subscription is expired redirect him to a subscription page or login page when he's not logged in yet
@B33fb0n3 yes you are right, you shouldn't do that inside the root layout. You can check that inside your middleware and if the subscription is expired redirect him to a subscription page or login page when he's not logged in yet
Golden EagleOP
At the same time as I understand DB session is not available in middleware, is it?
@Golden Eagle At the same time as I understand DB session is not available in middleware, is it?
that depends on the software you are using. When using next-auth, you can access the data inside your jwt and depending on your software for the database you can also make queries against your db
@B33fb0n3 that depends on the software you are using. When using next-auth, you can access the data inside your jwt and depending on your software for the database you can also make queries against your db
Golden EagleOP
Ok. Let’s close it. I will try to figure it out
@Golden Eagle Ok. Let’s close it. I will try to figure it out
Which message solved this thread?
@Golden Eagle?
@B33fb0n3 <@225685293506887680>?
Golden EagleOP
Well. It's hard to say. Initially I thought maybe there was some mechanism, some I don't know about. But what I see now is that middleware seems to be the place where it can be done without checking manually on every page. On the other hand, in middleware judging by this track, db session https://github.com/nextauthjs/next-auth/discussions/4265 works, but only jwt, which as I understand can easily be outdated. So for now I have to leave manual checking of the database for active subscription as a solution. That's why I haven't solved my problem yet
https://authjs.dev/guides/edge-compatibility
perhaps this will help when using NextAuth
perhaps this will help when using NextAuth
@Reki https://authjs.dev/guides/edge-compatibility
perhaps this will help when using NextAuth
Golden EagleOP
Thanks. I'll read it again. Have you had an experience with solving similar things and edge solved it for you?
@Golden Eagle Thanks. I'll read it again. Have you had an experience with solving similar things and edge solved it for you?
yeah i was using drizzle orm and mysql2. and following that doc, i implemented the auth successfully (split config and session strategy jwt) and can have redirect in middleware checking the req.auth. Right now i try to go deep using the database session perhaps i can't implement the check in middleware (still don't know how to do this)