v4: Session is expired, but after Server Action is called i dont get redirected
Checklist
- [x] The issue can be reproduced in the nextjs-auth0 sample app (or N/A).
- [x] I have looked into the Readme, Examples, and FAQ and have not found a suitable solution or answer.
- [x] I have looked into the API documentation and have not found a suitable solution or answer.
- [x] I have searched the issues and have not found a suitable solution or answer.
- [x] I have searched the Auth0 Community forums and have not found a suitable solution or answer.
- [x] I agree to the terms within the Auth0 Code of Conduct.
Description
I wanted to see how my app behaves when the session is expired and i call a server action.
In prev version (v3.5) server action would be called and then await getAccessToken would throw an error after which I would just redirect to api/auth/login . But in the current v4 the error is thrown before an action has started. Which is ok, Im creating a POST request when i want to call a server action and Auth0 can see in the middleware that I dont have session.
The issue is that the middleware sees the session is null and calls:
return NextResponse.redirect(new URL("/auth/login", req.url), {
status: 303,
});
I can see in the terminal that the new path is /auth/login but browser is still showing page from which I initiated Server Action. In the browser Network I can see 3 requests:
My middleware looks like this:
import { NextRequest, NextResponse } from "next/server";
import createIntlMiddleware from "next-intl/middleware";
import { routing } from "./i18n/routing";
import { auth0 } from "./lib/auth0";
const intlMiddleware = createIntlMiddleware(routing);
const loginPages = ["/", "/en-US"] as const;
export default async function middleware(req: NextRequest) {
const pathname = req.nextUrl.pathname;
const isLoginPage = loginPages.some((page) => page === pathname);
const isProtectedPage = !isLoginPage
const authResponse = await auth0.middleware(req);
console.log("👀 Path:", pathname);
if (pathname.startsWith("/auth")) {
return authResponse;
}
const session = await auth0.getSession(req);
console.log("🔥 Session:", session);
// User is not logged in and tries to access a page that requires authentication
if (!session && isProtectedPage) {
return NextResponse.redirect(new URL("/auth/login", req.url), {
status: 303,
});
}
// Apply intl middleware
const intlResponse = intlMiddleware(req);
// Add Auth0 headers to the response
for (const [key, value] of authResponse.headers) {
intlResponse.headers.set(key, value);
}
return intlResponse;
}
export const config = {
matcher: [
"/",
"/(en-US|is|de)/:path*",
"/auth/:path*", // Make sure Auth0 routes are included
"/((?!_next/image|_next/static|images|api|favicon.ico|_vercel|.*\\..*).*)",
],
};
My logs in the terminal:
👀 Path: /en-US/user-management/1/departments
🔥 Session: null
👀 Path: /auth/login
Browser logs:
Reproduction
- Set auth0 server client:
export const auth0 = new Auth0Client({
authorizationParameters: {
audience: process.env.AUTH0_AUDIENCE,
},
session: {
absoluteDuration: 15, // short session so I can test how app behaves after session is expired
},
});
- Set middleware I provided in the description
- Call a server Action after the session is expired
Additional context
next-intl
nextjs-auth0 version
v4.02
Next.js version
v15.1.7
Node.js version
v20.10.0
Thank you for reporting this issue.
This looks like an interesting edge case with how Next.js Server Actions handle redirects from middleware. Based on the logs you've provided, the middleware is correctly identifying the expired session and attempting to redirect, but the redirect isn't being properly handled in the context of a server action.
We're investigating this issue to better understand how redirects should be handled in this scenario.
In the meantime, you could work around this by:
- Checking the session validity at the beginning of your server action and throwing a redirect error:
import { redirect } from 'next/navigation';
async function myServerAction() {
const session = await auth0.getSession();
if (!session) {
redirect('/auth/login');
}
// Rest of your server action
}
- Or handling the expired session case in your client component after the server action fails
Could you share a simplified version of your server action code? This would help us reproduce and fix the issue more effectively.
Hi @tusharpandey13 , ty for your comment. I also tried with that approach of checking the session and then call a redirect, but i still dont get redirected. When i click on submit button and call a server action i get tag that my page becomes static (which is not possible because im using next-intl and params) and I get this error:
The simplified version of my server action:
export const serverAction = async (
data: ActionInputType
): Promise<ActionReturnType> => {
let responseData;
try {
const result = ValidationSchema.readonly().safeParse(data);
if (!result.success) {
throw new Error("Invalid data");
}
const { customerId } = result.data;
const session = await auth0.getSession();
if (!session) {
throw new AccessTokenError("No session found", "Failed");
}
const response = await fetch(MY_URL,
{
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${session.tokenSet.accessToken}`,
},
body: JSON.stringify(result.data),
}
);
responseData = await response.json();
} catch (error) {
if (error instanceof AccessTokenError) {
redirect("/auth/login");
} else
return {
error: DEFAULT_SERVER_ERROR,
};
}
return { departmentId: responseData };
};
Keep in mind that I still used your code (just checking if I have session and then redirecting, but it doesnt work).
Im not sure if all of this happens because of:
export const auth0 = new Auth0Client({
authorizationParameters: {
audience: process.env.AUTH0_AUDIENCE,
max_age: 1, // also confused why we cant write 0 like in v3.5
},
session: {
rolling: false,
absoluteDuration: 15, // for short sessions, so I can check the behaviour
},
});