Session fetched in the middleware doesn't update on every request.

As shown in the video, After signing in. I can go back to /login page even though I protected it using middleware.ts. I even tried no-storing the session but in vain. Here is middleware.ts:
import createMiddleware from "next-intl/middleware";
import { locales } from "./config";
import { NextRequest, NextResponse } from "next/server";
import {
ONLY_UNAUTHENTICATED_ROUTES,
DEFAULT_UNAUTHENTICATED_REDIRECT,
PROTECTED_ROUTES,
DEFAULT_LOGIN_REDIRECT,
} from "./consts/routes";
import { absoluteURL } from "./lib/utils";

const intlMiddleware = createMiddleware({
// A list of all locales that are supported
locales,

// Used when no locale matches
defaultLocale: "ar",
});

export default async function middleware(req: NextRequest) {
const data = await fetch(absoluteURL("/api/auth/get-session"), {
headers: {
cookie: req.headers.get("cookie") || "",
},
cache: "no-cache",
});
const session = await data.json();

// Remove locale and clean pathname
const localeMatch = req.nextUrl.pathname.match(/^\/(ar|en)(\/|$)/);
const locale = localeMatch ? localeMatch[1] : null;
const pathnameWithoutLocale = locale
? req.nextUrl.pathname.replace(`/${locale}`, "") || "/"
: req.nextUrl.pathname;

const isProtectedRoute = PROTECTED_ROUTES.some((route) =>
pathnameWithoutLocale.startsWith(route),
);

if (isProtectedRoute && !session) {
return NextResponse.redirect(
new URL(DEFAULT_UNAUTHENTICATED_REDIRECT, req.url),
);
}

if (
session &&
ONLY_UNAUTHENTICATED_ROUTES.some((route) =>
pathnameWithoutLocale.startsWith(route),
)
) {
return NextResponse.redirect(new URL(DEFAULT_LOGIN_REDIRECT, req.url));
}

return intlMiddleware(req);
}
import createMiddleware from "next-intl/middleware";
import { locales } from "./config";
import { NextRequest, NextResponse } from "next/server";
import {
ONLY_UNAUTHENTICATED_ROUTES,
DEFAULT_UNAUTHENTICATED_REDIRECT,
PROTECTED_ROUTES,
DEFAULT_LOGIN_REDIRECT,
} from "./consts/routes";
import { absoluteURL } from "./lib/utils";

const intlMiddleware = createMiddleware({
// A list of all locales that are supported
locales,

// Used when no locale matches
defaultLocale: "ar",
});

export default async function middleware(req: NextRequest) {
const data = await fetch(absoluteURL("/api/auth/get-session"), {
headers: {
cookie: req.headers.get("cookie") || "",
},
cache: "no-cache",
});
const session = await data.json();

// Remove locale and clean pathname
const localeMatch = req.nextUrl.pathname.match(/^\/(ar|en)(\/|$)/);
const locale = localeMatch ? localeMatch[1] : null;
const pathnameWithoutLocale = locale
? req.nextUrl.pathname.replace(`/${locale}`, "") || "/"
: req.nextUrl.pathname;

const isProtectedRoute = PROTECTED_ROUTES.some((route) =>
pathnameWithoutLocale.startsWith(route),
);

if (isProtectedRoute && !session) {
return NextResponse.redirect(
new URL(DEFAULT_UNAUTHENTICATED_REDIRECT, req.url),
);
}

if (
session &&
ONLY_UNAUTHENTICATED_ROUTES.some((route) =>
pathnameWithoutLocale.startsWith(route),
)
) {
return NextResponse.redirect(new URL(DEFAULT_LOGIN_REDIRECT, req.url));
}

return intlMiddleware(req);
}
NOTE: same issue occurs when I sign out and I'm in a protected route. I don't get routed to /login for some reason.
22 Replies
Unknown User
Unknown User5w ago
Message Not Public
Sign In & Join Server To View
Maqed
MaqedOP5w ago
I'm migrating from next-auth. Everything was working perfectly in next-auth + middleware actually works when I refresh the page It just doesn't work right after signing up
Unknown User
Unknown User5w ago
Message Not Public
Sign In & Join Server To View
Maqed
MaqedOP5w ago
Nope, As shown in the docs, Nextjs middleware doesn't have access to node APIs https://www.better-auth.com/docs/integrations/next#middleware
Next.js integration | Better Auth
Integrate Better Auth with Next.js.
Unknown User
Unknown User5w ago
Message Not Public
Sign In & Join Server To View
Maqed
MaqedOP5w ago
The problem here is it's using betterFetch and I don't wanna install another package just for the middleware to work I hope there's a solution for this problem with normal fetch But I'll give it a try Thank you bro for trying to help ❤️
Unknown User
Unknown User5w ago
Message Not Public
Sign In & Join Server To View
Maqed
MaqedOP5w ago
It's still an issue even after using better-fetch @admin
bekacru
bekacru5w ago
first check if the headers contain the session cookie properly. and is it returning null?
Maqed
MaqedOP4w ago
Headers contain the session cookie. Also I've just noticed that the problem occurs ONLY in email/password login. It doesn't occur in OAuth signin @bekacru Is there any way to fix it?
bekacru
bekacru4w ago
there is no difference between email/pass generated cookie and oauth cookie. If it's not working for email/pass only there is an issue with your setup. Make sure on email/pass a new cookie is benig set in the browser and that cookie itself is reaching the middleware
Maqed
MaqedOP4w ago
Email/pass make a new cookie that's sent to the browser. But for some reason, it's not reaching th middleware. Here's a video of what's going on from the email/password side
Maqed
MaqedOP4w ago
Here's what's going on in oauth
Maqed
MaqedOP4w ago
middleware.ts:
import createMiddleware from "next-intl/middleware";
import { locales } from "./config";
import { NextRequest, NextResponse } from "next/server";
import {
ONLY_UNAUTHENTICATED_ROUTES,
DEFAULT_UNAUTHENTICATED_REDIRECT,
PROTECTED_ROUTES,
DEFAULT_LOGIN_REDIRECT,
} from "./consts/routes";
import { absoluteURL } from "./lib/utils";

const intlMiddleware = createMiddleware({
// A list of all locales that are supported
locales,

// Used when no locale matches
defaultLocale: "ar",
});

export default async function middleware(req: NextRequest) {
const data = await fetch(absoluteURL("/api/auth/get-session"), {
headers: {
cookie: req.headers.get("cookie") || "",
},
});
const session = await data.json();

// Remove locale and clean pathname
const localeMatch = req.nextUrl.pathname.match(/^\/(ar|en)(\/|$)/);
const locale = localeMatch ? localeMatch[1] : null;
const pathnameWithoutLocale = locale
? req.nextUrl.pathname.replace(`/${locale}`, "") || "/"
: req.nextUrl.pathname;

const isProtectedRoute = PROTECTED_ROUTES.some((route) =>
pathnameWithoutLocale.startsWith(route),
);

if (isProtectedRoute && !session) {
return NextResponse.redirect(
new URL(DEFAULT_UNAUTHENTICATED_REDIRECT, req.url),
);
}

if (
session &&
ONLY_UNAUTHENTICATED_ROUTES.some((route) =>
pathnameWithoutLocale.startsWith(route),
)
) {
return NextResponse.redirect(new URL(DEFAULT_LOGIN_REDIRECT, req.url));
}

return intlMiddleware(req);
}

export const config = {
// Matcher entries are linked with a logical "or", therefore
// if one of them matches, the middleware will be invoked.
// Copied from https://next-intl-docs.vercel.app/docs/routing/middleware#matcher-no-prefix
matcher: [
// Match all pathnames except for
"/((?!api|_next|_vercel|.*\\..*).*)",
// However, match all pathnames
"/([\\w-]+)",
],
};
import createMiddleware from "next-intl/middleware";
import { locales } from "./config";
import { NextRequest, NextResponse } from "next/server";
import {
ONLY_UNAUTHENTICATED_ROUTES,
DEFAULT_UNAUTHENTICATED_REDIRECT,
PROTECTED_ROUTES,
DEFAULT_LOGIN_REDIRECT,
} from "./consts/routes";
import { absoluteURL } from "./lib/utils";

const intlMiddleware = createMiddleware({
// A list of all locales that are supported
locales,

// Used when no locale matches
defaultLocale: "ar",
});

export default async function middleware(req: NextRequest) {
const data = await fetch(absoluteURL("/api/auth/get-session"), {
headers: {
cookie: req.headers.get("cookie") || "",
},
});
const session = await data.json();

// Remove locale and clean pathname
const localeMatch = req.nextUrl.pathname.match(/^\/(ar|en)(\/|$)/);
const locale = localeMatch ? localeMatch[1] : null;
const pathnameWithoutLocale = locale
? req.nextUrl.pathname.replace(`/${locale}`, "") || "/"
: req.nextUrl.pathname;

const isProtectedRoute = PROTECTED_ROUTES.some((route) =>
pathnameWithoutLocale.startsWith(route),
);

if (isProtectedRoute && !session) {
return NextResponse.redirect(
new URL(DEFAULT_UNAUTHENTICATED_REDIRECT, req.url),
);
}

if (
session &&
ONLY_UNAUTHENTICATED_ROUTES.some((route) =>
pathnameWithoutLocale.startsWith(route),
)
) {
return NextResponse.redirect(new URL(DEFAULT_LOGIN_REDIRECT, req.url));
}

return intlMiddleware(req);
}

export const config = {
// Matcher entries are linked with a logical "or", therefore
// if one of them matches, the middleware will be invoked.
// Copied from https://next-intl-docs.vercel.app/docs/routing/middleware#matcher-no-prefix
matcher: [
// Match all pathnames except for
"/((?!api|_next|_vercel|.*\\..*).*)",
// However, match all pathnames
"/([\\w-]+)",
],
};
Just ignore the localization part of the middleware
daveycodez
daveycodez4w ago
Hey, Next.js caches middleware rewrites jsyk If you are using the App Router you need to call router.refresh() if the session changes or manually refresh the page router.refresh does a soft refresh and clears the router cache. It would be great if they would provide a router.clear function ;/ Also just check for presence of the session_token cookie for middleware, you don't want to do fetches there It works for OAuth because you hard navigate back to the website
Maqed
MaqedOP4w ago
I'll try that
daveycodez
daveycodez4w ago
request.cookies.has("better-auth.session_token")
request.cookies.has("better-auth.session_token")
This is what you want to do instead of better fetch Just check if that cookie exists, instead of fetching a session In middleware There is a merged PR releasing soon for better auth that will provide getSessionCookie() If you are using signIn() manually somewhere in your client pages, just call router.refresh() in the onSuccess
Maqed
MaqedOP4w ago
I'm testing that. Gimme a second @daveycodez That works! Thank you so much! ❤️
daveycodez
daveycodez4w ago
Yea it's the router cache.. I was gonna open a GitHub issue to get Next to provide a "clear" function but. they require making a repo for each issue and yea Make sure you also do router.refresh() for your sign out any session changes that don't involve a hard redirect
Maqed
MaqedOP4w ago
Thank you for reminding me! But what happens if the session is expired? The session token is still in the cookie but it won't work so it won't redirect the user to /login. Right?
Unknown User
Unknown User4w ago
Message Not Public
Sign In & Join Server To View
daveycodez
daveycodez4w ago
If that session_token cookie is gone then the user is signed out Middleware is also meant to be a non-blocking optimistic check, your routes themselves should check using getSession if they are dynamic routes

Did you find this page helpful?