Session management
Unanswered
American black bear posted this in #help-forum
American black bearOP
Hello everyone, I have a question. I am currently developing a dashboard with Next.js and plan to implement session management in such a way that a session ID is stored in the cookies on the client, while the associated session data is stored in Redis.
My idea was to use middleware to check whether a user is logged in.
I wanted to read the session ID from the cookies and check whether this session is still valid in Redis. In addition, I wanted to check whether the user agent and IP address of the client match the session data stored in Redis. Unfortunately, I found that the official Redis package cannot be used in Next.js middleware.
In this case, what options are there for checking the login status or session, and does my basic approach to session management make sense, or should it be implemented differently?
My idea was to use middleware to check whether a user is logged in.
I wanted to read the session ID from the cookies and check whether this session is still valid in Redis. In addition, I wanted to check whether the user agent and IP address of the client match the session data stored in Redis. Unfortunately, I found that the official Redis package cannot be used in Next.js middleware.
In this case, what options are there for checking the login status or session, and does my basic approach to session management make sense, or should it be implemented differently?
13 Replies
Cinnamon Teal
I have not used Redis and not familiar with it other than knowing it's a key-value store.
I'll just share how I usually handle auth so maybe you can get an idea.
Next.js suggests not to do database lookups or API calls in middleware/proxy. (Now as I said I don't know whether this applies to Redis as well). So for example, if you have a stateful auth setup, where you need to do a database check to verify the auth status of the cookie, you shouldn't do it in middleware. However, you can do an optimistic auth check in middleware. You may have a signed token in cookies, which can be verified cryptographically. That you can do in middleware, as it doesn't include a database lookup, and redirect the requests based on that.
Then later at the top of your
https://nextjs.org/docs/app/guides/authentication#optimistic-checks-with-proxy-optional
https://nextjs.org/docs/app/guides/authentication#creating-a-data-access-layer-dal
I'll just share how I usually handle auth so maybe you can get an idea.
Next.js suggests not to do database lookups or API calls in middleware/proxy. (Now as I said I don't know whether this applies to Redis as well). So for example, if you have a stateful auth setup, where you need to do a database check to verify the auth status of the cookie, you shouldn't do it in middleware. However, you can do an optimistic auth check in middleware. You may have a signed token in cookies, which can be verified cryptographically. That you can do in middleware, as it doesn't include a database lookup, and redirect the requests based on that.
Then later at the top of your
page.tsx file and in your data access layer (right before you call your database), you can do the full database lookup. If you have a optimistic check in middleware you can simply skip the full database check in the page, and only do it before querying your database.https://nextjs.org/docs/app/guides/authentication#optimistic-checks-with-proxy-optional
https://nextjs.org/docs/app/guides/authentication#creating-a-data-access-layer-dal
@Cinnamon Teal I have not used Redis and not familiar with it other than knowing it's a key-value store.
I'll just share how I usually handle auth so maybe you can get an idea.
Next.js suggests not to do database lookups or API calls in middleware/proxy. (Now as I said I don't know whether this applies to Redis as well). So for example, if you have a stateful auth setup, where you need to do a database check to verify the auth status of the cookie, you shouldn't do it in middleware. However, you can do an optimistic auth check in middleware. You may have a signed token in cookies, which can be verified cryptographically. That you can do in middleware, as it doesn't include a database lookup, and redirect the requests based on that.
Then later at the top of your `page.tsx` file and in your data access layer (right before you call your database), you can do the full database lookup. If you have a optimistic check in middleware you can simply skip the full database check in the `page`, and only do it before querying your database.
https://nextjs.org/docs/app/guides/authentication#optimistic-checks-with-proxy-optional
https://nextjs.org/docs/app/guides/authentication#creating-a-data-access-layer-dal
but what difference does it really make if the proxy only runs on pages where i would do the auth check anyways? imo it just prevents duplication of the code
@aa55h but what difference does it really make if the proxy only runs on pages where i would do the auth check anyways? imo it just prevents duplication of the code
Cinnamon Teal
You mean why do an optimistic check inside middleware, if you are doing a full check in pages?
Proxy runs on the edge, so you can redirect the request before it hits the server, if you used a simple check. Plus if I can do an optimistic check in proxy, I would skip the check at the page level and do the full check in the data access layer, before querying the data.
But yeah, if the proxy is not needed, you could skip it and do the check on the page. But ideally a check should happen closest to your data source as well.
Proxy runs on the edge, so you can redirect the request before it hits the server, if you used a simple check. Plus if I can do an optimistic check in proxy, I would skip the check at the page level and do the full check in the data access layer, before querying the data.
But yeah, if the proxy is not needed, you could skip it and do the check on the page. But ideally a check should happen closest to your data source as well.
@Cinnamon Teal I have not used Redis and not familiar with it other than knowing it's a key-value store.
I'll just share how I usually handle auth so maybe you can get an idea.
Next.js suggests not to do database lookups or API calls in middleware/proxy. (Now as I said I don't know whether this applies to Redis as well). So for example, if you have a stateful auth setup, where you need to do a database check to verify the auth status of the cookie, you shouldn't do it in middleware. However, you can do an optimistic auth check in middleware. You may have a signed token in cookies, which can be verified cryptographically. That you can do in middleware, as it doesn't include a database lookup, and redirect the requests based on that.
Then later at the top of your `page.tsx` file and in your data access layer (right before you call your database), you can do the full database lookup. If you have a optimistic check in middleware you can simply skip the full database check in the `page`, and only do it before querying your database.
https://nextjs.org/docs/app/guides/authentication#optimistic-checks-with-proxy-optional
https://nextjs.org/docs/app/guides/authentication#creating-a-data-access-layer-dal
American black bearOP
Thank you for your reply. I took a closer look at the documentation yesterday and realized that my project involves a database session. As I currently understand it, the middleware is executed before the page is rendered, but it should only perform optimistic checks—for example, whether a session cookie exists at all—and not trigger a direct database query. The same seems to apply to the proxy, at least according to your text.
I came across the same solution that you linked to in your reply, namely the use of a data access layer (DAL). At this point, however, I am still not entirely clear on the exact difference between a DAL and a DTO. Could you please explain this to me?
I would also be interested in your recommendation if I still want to synchronize the session with the database before rendering to prevent the dashboard from loading briefly and the user from being redirected only after the page has been rendered if there is no valid session in Redis.
I came across the same solution that you linked to in your reply, namely the use of a data access layer (DAL). At this point, however, I am still not entirely clear on the exact difference between a DAL and a DTO. Could you please explain this to me?
I would also be interested in your recommendation if I still want to synchronize the session with the database before rendering to prevent the dashboard from loading briefly and the user from being redirected only after the page has been rendered if there is no valid session in Redis.
@American black bear Thank you for your reply. I took a closer look at the documentation yesterday and realized that my project involves a database session. As I currently understand it, the middleware is executed before the page is rendered, but it should only perform optimistic checks—for example, whether a session cookie exists at all—and not trigger a direct database query. The same seems to apply to the proxy, at least according to your text.
I came across the same solution that you linked to in your reply, namely the use of a data access layer (DAL). At this point, however, I am still not entirely clear on the exact difference between a DAL and a DTO. Could you please explain this to me?
I would also be interested in your recommendation if I still want to synchronize the session with the database before rendering to prevent the dashboard from loading briefly and the user from being redirected only after the page has been rendered if there is no valid session in Redis.
Cinnamon Teal
middleware and proxy are the exact same thing in Next.js. middleware was the name until Next.js 15. In the new Next.js 16 they renamed it to proxy. It's only a rename, so basically the exact same thing, but different name. And yes they run before the page is rendered because they run on what Vercel calls as the "edge". It's basically a CDN with some extra capabilities. And your page gets rendered on the compute server, which comes after the edge.And yeah, Next.js suggests not doing database lookups in proxy. As for an optimistic check, you can just check whether the cookie exists, but that can be easily bypassed by just creating a cookie with that name. So ideally the cookie should store a signed token and the token should be verified using some cryptographic function. That way it's actually secure. This is if you are doing a check in middleware at all. Since you already have planned using a stateful auth where the session needs to verified by doing a database lookup, you can skip the middleware part if you like.
DAL is basically where all your data query requests go through. So for example you may have functions to read, create or delete data in there. And at the top of each function you will check whether your session is valid by doing the session lookup first. Maybe you do it using Redis or some other database. Won't really matter. The point is that data queries go through one layer which has the auth check, so no matter where you call those functions from, it's guaranteed not to let an unauthorized request to pass through. This also assumes your Next.js app is what queries the database. If you have a separate backend then I guess it will be doing the auth checks.
As for DTO, it seems like a pattern where only the necessary properties are returned from a request. I have never used it, so I also know what that docs section mentions about it.
want to synchronize the session with the database before renderingDo you mean doing the session lookup? Checking whether the session is valid against what's in the database?
If so yeah, you can do that at the top of the
page files. They will be server components, so you can read the cookies, grab the session token or whatever you have in cookies, and call the database and verify it's valid. If not valid, you can use redirect("/login") to redirect before anything below that code gets rendered. https://nextjs.org/docs/app/api-reference/functions/redirect@Cinnamon Teal `middleware` and `proxy` are the exact same thing in Next.js. `middleware` was the name until Next.js 15. In the new Next.js 16 they renamed it to `proxy`. It's only a rename, so basically the exact same thing, but different name. And yes they run before the page is rendered because they run on what Vercel calls as the "edge". It's basically a CDN with some extra capabilities. And your page gets rendered on the compute server, which comes after the edge.
And yeah, Next.js suggests not doing database lookups in proxy. As for an optimistic check, you can just check whether the cookie exists, but that can be easily bypassed by just creating a cookie with that name. So ideally the cookie should store a signed token and the token should be verified using some cryptographic function. That way it's actually secure. This is if you are doing a check in middleware at all. Since you already have planned using a stateful auth where the session needs to verified by doing a database lookup, you can skip the middleware part if you like.
DAL is basically where all your data query requests go through. So for example you may have functions to read, create or delete data in there. And at the top of each function you will check whether your session is valid by doing the session lookup first. Maybe you do it using Redis or some other database. Won't really matter. The point is that data queries go through one layer which has the auth check, so no matter where you call those functions from, it's guaranteed not to let an unauthorized request to pass through. This also assumes your Next.js app is what queries the database. If you have a separate backend then I guess it will be doing the auth checks.
As for DTO, it seems like a pattern where only the necessary properties are returned from a request. I have never used it, so I also know what that docs section mentions about it.
American black bearOP
Thank you very much for your detailed answer. Where is the verify function of the session usually called when using DAL? My current file for authentication methods looks like this: https://pastebin.com/iX4YgR5j
@American black bear Thank you very much for your detailed answer. Where is the verify function of the session usually called when using DAL? My current file for authentication methods looks like this: https://pastebin.com/iX4YgR5j
Cinnamon Teal
I usually have a folder called
And here I will put the functions like
And on the top of each function, I would call my
Almost the same setup as here: https://nextjs.org/docs/app/guides/authentication#creating-a-data-access-layer-dal
data-access. And then inside it I would have files for each feature. So for example one feature could be called something. So I'll have a file called something.ts.And here I will put the functions like
getSomething(), createSomethig(), updateSomething() etc.And on the top of each function, I would call my
verifySession() function.// something.ts
export async function getSomething(data) {
const session = await verifySession()
if (!session) redirect("/login")
// your db queries
export async function createSomething(data) {
const session = await verifySession()
if (!session) redirect("/login")
// your db queriesverifySession() it self can be defined in a file called /lib/auth or something like that, and it will read from cookies and grab the data stored in cookies and do the session lookups and will return either undefine/false, or the session data. (Looks like you already have those created).Almost the same setup as here: https://nextjs.org/docs/app/guides/authentication#creating-a-data-access-layer-dal
American black bearOP
Thanks, I'll give it a try.
You're on the right track. In Next.js, proxy (middleware) is best for quick checks (e.g. checking if a session cookie exists), but no Redis/db calls there — those go in your DAL.
Typical setup:
1. Proxy/middleware: Check if session cookie exists. Optionally verify a signed token.
2. DAL: Use
3. Server page/layout: Call
Example:
If you want the session check before render: do it at the top of
Docs: [https://nextjs.org/docs/app/api-reference/functions/redirect](https://nextjs.org/docs/app/api-reference/functions/redirect)
Typical setup:
1. Proxy/middleware: Check if session cookie exists. Optionally verify a signed token.
2. DAL: Use
verifySession() to query Redis and validate session, IP, UA, etc.3. Server page/layout: Call
verifySession() early, and redirect with redirect("/login") if invalid.Example:
// lib/auth.ts
export async function verifySession() {
const sid = cookies().get("sid")?.value
if (!sid) return null
const session = await redis.get(`session:${sid}`)
return session ?? null
}// data-access/some.ts
export async function getData() {
const session = await verifySession()
if (!session) redirect("/login")
// protected data logic
}DAL = where you fetch data + check session
DTO = what you return to the client (shaped/sanitized object)
If you want the session check before render: do it at the top of
page.tsx or layout.tsx (server comp).Docs: [https://nextjs.org/docs/app/api-reference/functions/redirect](https://nextjs.org/docs/app/api-reference/functions/redirect)
@Cinnamon Teal You mean why do an optimistic check inside middleware, if you are doing a full check in pages?
Proxy runs on the edge, so you can redirect the request before it hits the server, if you used a simple check. Plus if I can do an optimistic check in proxy, I would skip the check at the page level and do the full check in the data access layer, before querying the data.
But yeah, if the proxy is not needed, you could skip it and do the check on the page. But ideally a check should happen closest to your data source as well.
ill ask the question differently...
the whole app requires user to be authenticated (aside from the login page). does it really matter if i do the check in proxy.ts or in each page separately? seems too unnecessary to do it in each page ngl
the whole app requires user to be authenticated (aside from the login page). does it really matter if i do the check in proxy.ts or in each page separately? seems too unnecessary to do it in each page ngl
@aa55h ill ask the question differently...
the whole app requires user to be authenticated (aside from the login page). does it really matter if i do the check in proxy.ts or in each page separately? seems too unnecessary to do it in each page ngl
Cinnamon Teal
Yeah, I get your point. And I am also kind of saying the same thing. If you can do your auth in proxy, you can skip it in pages.
Now, if your auth includes database lookups and you still want to keep it in middleware, that's up to you. I would personally avoid it. But again, it's something I see some people still do even if Next.js recommends against it.
Here's my understanding about it: Your compute server (where your pages get executed) and your database server should ideally be close. In the case of middleware doing database lookups what happens is your middleware code may run in the closest edge region to the user, but that database query still needs to travel to the database which is probably in a different region. Unless your database is also distributed and can run close to where the middleware is executing.
But either way (whether you did the auth in middleware or not), I would still have a data access layer which does auth as well, before querying the database. IIRC there was a CVE once in Next.js middleware that allowed bypassing the auth checks. It was fixed obviously but a good reason not to rely only on the middleware checks.
Now, if your auth includes database lookups and you still want to keep it in middleware, that's up to you. I would personally avoid it. But again, it's something I see some people still do even if Next.js recommends against it.
Here's my understanding about it: Your compute server (where your pages get executed) and your database server should ideally be close. In the case of middleware doing database lookups what happens is your middleware code may run in the closest edge region to the user, but that database query still needs to travel to the database which is probably in a different region. Unless your database is also distributed and can run close to where the middleware is executing.
But either way (whether you did the auth in middleware or not), I would still have a data access layer which does auth as well, before querying the database. IIRC there was a CVE once in Next.js middleware that allowed bypassing the auth checks. It was fixed obviously but a good reason not to rely only on the middleware checks.
Well obviously all the routes are secured, so it would redirect the user either way, but i guess in that case optimistic check would be enough