next.js 15 middleware auth cookie check flash screen issue before redirect #558

Closed
opened 2026-03-13 07:52:54 -05:00 by GiteaMirror · 3 comments
Owner

Originally created by @tauhid97k on GitHub (Jan 12, 2025).

Is this suited for github?

  • Yes, this is suited for github

To Reproduce

  1. Create next.js 15 project
  2. Add prisma
  3. Copy schema from better-auth documentation
  4. Add auth checking middleware in the given way

Current vs. Expected behavior

I have added middleware with route checking, following the better-auth next.js documentation.

`import { betterFetch } from "@better-fetch/fetch";
import { NextResponse, type NextRequest } from "next/server";
import type { auth } from "@/lib/auth";

type Session = typeof auth.$Infer.Session;

export default async function authMiddleware(request: NextRequest) {
const pathname = request.nextUrl.pathname;
const isAuthRoute = pathname.startsWith("/auth");
const isProtectedRoute = pathname.startsWith("/dashboard");

const { data: session } = await betterFetch(
"/api/auth/get-session",
{
baseURL: process.env.BETTER_AUTH_URL,
headers: {
//get the cookie from the request
cookie: request.headers.get("cookie") || "",
},
},
);

// Redirect to login if user is not authenticated
if (!session && isProtectedRoute) {
return NextResponse.redirect(new URL("/auth/sign-in", request.nextUrl));
}

// Redirect to dashboard if user is authenticated and trying to access auth routes
if (session && isAuthRoute) {
return NextResponse.redirect(new URL("/dashboard", request.nextUrl));
}

return NextResponse.next();
}

export const config = {
matcher: [
"/((?!api|_next/static|_next/image|favicon.ico|sitemap.xml|robots.txt).*)",
],
};`

It works if I try to access /dashboard without auth it redirects me to /auth/sign-in but here's the problem;
If I login successfully and then within a few seconds if I try to go back (Which would be sign-in route) using the browser back button I see a screen like this below for a split second and then it redirects me correctly. The same happens If I logout for the first time and then try to go back using browser back button to /dashboard.
Note: It only happens once for login and logout. It might be a rookie mistake in redirection logic but I have been trying to fix it for a while with no luck.

Screenshot 2025-01-12 164123

Here's how I am doing sign-in with router.replace()

const onSubmit = async (values: z.infer<typeof signInSchema>) => { await signIn.email( { email: values.email, password: values.password, }, { onRequest: () => { setFormError(""); }, onSuccess: () => { router.replace("/dashboard"); }, onError: (ctx) => { setFormError(ctx.error.message); }, }, ); };

What version of Better Auth are you using?

1.1.11

Provide environment information

- OS: Windows 11 Pro (23H2)
- Browser: Chrome 131.0.6778.265
- Node: 22.9.0
- Next.js: 15.1.4

Which area(s) are affected? (Select all that apply)

Backend

Auth config (if applicable)

import { betterAuth } from "better-auth";
import { prismaAdapter } from "better-auth/adapters/prisma";
import { db } from "@/lib/db";

export const auth = betterAuth({
  database: prismaAdapter(db, {
    provider: "postgresql",
  }),
  user: {
    modelName: "users",
  },
  session: {
    modelName: "sessions",
  },
  account: {
    modelName: "accounts",
  },
  verification: {
    modelName: "verifications",
  },
  emailAndPassword: {
    enabled: true,
  },
});

Additional context

I tested both dev and build.

Originally created by @tauhid97k on GitHub (Jan 12, 2025). ### Is this suited for github? - [X] Yes, this is suited for github ### To Reproduce 1. Create next.js 15 project 2. Add prisma 3. Copy schema from better-auth documentation 4. Add auth checking middleware in the given way ### Current vs. Expected behavior I have added middleware with route checking, following the better-auth next.js documentation. `import { betterFetch } from "@better-fetch/fetch"; import { NextResponse, type NextRequest } from "next/server"; import type { auth } from "@/lib/auth"; type Session = typeof auth.$Infer.Session; export default async function authMiddleware(request: NextRequest) { const pathname = request.nextUrl.pathname; const isAuthRoute = pathname.startsWith("/auth"); const isProtectedRoute = pathname.startsWith("/dashboard"); const { data: session } = await betterFetch<Session>( "/api/auth/get-session", { baseURL: process.env.BETTER_AUTH_URL, headers: { //get the cookie from the request cookie: request.headers.get("cookie") || "", }, }, ); // Redirect to login if user is not authenticated if (!session && isProtectedRoute) { return NextResponse.redirect(new URL("/auth/sign-in", request.nextUrl)); } // Redirect to dashboard if user is authenticated and trying to access auth routes if (session && isAuthRoute) { return NextResponse.redirect(new URL("/dashboard", request.nextUrl)); } return NextResponse.next(); } export const config = { matcher: [ "/((?!api|_next/static|_next/image|favicon.ico|sitemap.xml|robots.txt).*)", ], };` It works if I try to access /dashboard without auth it redirects me to /auth/sign-in but here's the problem; If I login successfully and then within a few seconds if I try to go back (Which would be sign-in route) using the browser back button I see a screen like this below for a split second and then it redirects me correctly. The same happens If I logout for the first time and then try to go back using browser back button to /dashboard. Note: It only happens once for login and logout. It might be a rookie mistake in redirection logic but I have been trying to fix it for a while with no luck. ![Screenshot 2025-01-12 164123](https://github.com/user-attachments/assets/b4fb1128-55f4-4d3e-bb21-6c720e159f58) Here's how I am doing sign-in with router.replace() ` const onSubmit = async (values: z.infer<typeof signInSchema>) => { await signIn.email( { email: values.email, password: values.password, }, { onRequest: () => { setFormError(""); }, onSuccess: () => { router.replace("/dashboard"); }, onError: (ctx) => { setFormError(ctx.error.message); }, }, ); };` ### What version of Better Auth are you using? 1.1.11 ### Provide environment information ```bash - OS: Windows 11 Pro (23H2) - Browser: Chrome 131.0.6778.265 - Node: 22.9.0 - Next.js: 15.1.4 ``` ### Which area(s) are affected? (Select all that apply) Backend ### Auth config (if applicable) ```typescript import { betterAuth } from "better-auth"; import { prismaAdapter } from "better-auth/adapters/prisma"; import { db } from "@/lib/db"; export const auth = betterAuth({ database: prismaAdapter(db, { provider: "postgresql", }), user: { modelName: "users", }, session: { modelName: "sessions", }, account: { modelName: "accounts", }, verification: { modelName: "verifications", }, emailAndPassword: { enabled: true, }, }); ``` ### Additional context I tested both dev and build.
GiteaMirror added the bug label 2026-03-13 07:52:54 -05:00
Author
Owner

@binhodev commented on GitHub (Jan 13, 2025):

Hay,

It seems like you are making some mistake in your logic:

  // Redirect to login if user is not authenticated
  if (!session && isProtectedRoute) {
    return NextResponse.redirect(new URL("/auth/sign-in", request.nextUrl));
  }

Set isAuthRoute to a list of authentication routes, example:

const authListRoutes = ["/auth/sign-in", "/auth/sign-up"];
const protectedRoutes = "/dashboard";

You just need to check that the session does not exist to be redirected to the sign-in page. Remove "isProtectdRoute".

  if (!session) {
        if (authListRoutes.includes(req.nextUrl.pathname)) {
            return NextResponse.next();
        }

        return NextResponse.redirect(new URL("/auth/sign-in", req.nextUrl.origin));
    }

When have a session, make changes too.

  // Redirect to dashboard if user is authenticated and trying to access auth routes
  if (session && isAuthRoute) {
    return NextResponse.redirect(new URL("/dashboard", request.nextUrl));
  }

In addition to having a session, check if the user is in the authentication route so that the redirection can be performed.

How to check if the user is accessing the authentication route:

const authRoutes = authListRoutes.some((route) =>
        req.nextUrl.pathname.startsWith(route)
    );

Now grant the redirection if there is a session and you are on the authentication route:

if (session && authRoutes) {
        return NextResponse.redirect(
            new URL(protectedRoutes, req.nextUrl.origin)
        );
    }
@binhodev commented on GitHub (Jan 13, 2025): Hay, It seems like you are making some mistake in your logic: ```typescript // Redirect to login if user is not authenticated if (!session && isProtectedRoute) { return NextResponse.redirect(new URL("/auth/sign-in", request.nextUrl)); } ``` Set isAuthRoute to a list of authentication routes, example: ``` const authListRoutes = ["/auth/sign-in", "/auth/sign-up"]; const protectedRoutes = "/dashboard"; ``` You just need to check that the session does not exist to be redirected to the sign-in page. Remove "isProtectdRoute". ```typescript if (!session) { if (authListRoutes.includes(req.nextUrl.pathname)) { return NextResponse.next(); } return NextResponse.redirect(new URL("/auth/sign-in", req.nextUrl.origin)); } ``` When have a session, make changes too. ```typescript // Redirect to dashboard if user is authenticated and trying to access auth routes if (session && isAuthRoute) { return NextResponse.redirect(new URL("/dashboard", request.nextUrl)); } ``` In addition to having a session, check if the user is in the authentication route so that the redirection can be performed. How to check if the user is accessing the authentication route: ```typescript const authRoutes = authListRoutes.some((route) => req.nextUrl.pathname.startsWith(route) ); ``` Now grant the redirection if there is a session and you are on the authentication route: ```typescript if (session && authRoutes) { return NextResponse.redirect( new URL(protectedRoutes, req.nextUrl.origin) ); } ```
Author
Owner

@tauhid97k commented on GitHub (Jan 14, 2025):

Hay,

It seems like you are making some mistake in your logic:

  // Redirect to login if user is not authenticated
  if (!session && isProtectedRoute) {
    return NextResponse.redirect(new URL("/auth/sign-in", request.nextUrl));
  }

Set isAuthRoute to a list of authentication routes, example:

const authListRoutes = ["/auth/sign-in", "/auth/sign-up"];
const protectedRoutes = "/dashboard";

You just need to check that the session does not exist to be redirected to the sign-in page. Remove "isProtectdRoute".

  if (!session) {
        if (authListRoutes.includes(req.nextUrl.pathname)) {
            return NextResponse.next();
        }

        return NextResponse.redirect(new URL("/auth/sign-in", req.nextUrl.origin));
    }

When have a session, make changes too.

  // Redirect to dashboard if user is authenticated and trying to access auth routes
  if (session && isAuthRoute) {
    return NextResponse.redirect(new URL("/dashboard", request.nextUrl));
  }

In addition to having a session, check if the user is in the authentication route so that the redirection can be performed.

How to check if the user is accessing the authentication route:

const authRoutes = authListRoutes.some((route) =>
        req.nextUrl.pathname.startsWith(route)
    );

Now grant the redirection if there is a session and you are on the authentication route:

if (session && authRoutes) {
        return NextResponse.redirect(
            new URL(protectedRoutes, req.nextUrl.origin)
        );
    }

Thanks so much.

@tauhid97k commented on GitHub (Jan 14, 2025): > Hay, > > It seems like you are making some mistake in your logic: > > ```ts > // Redirect to login if user is not authenticated > if (!session && isProtectedRoute) { > return NextResponse.redirect(new URL("/auth/sign-in", request.nextUrl)); > } > ``` > > Set isAuthRoute to a list of authentication routes, example: > > ``` > const authListRoutes = ["/auth/sign-in", "/auth/sign-up"]; > const protectedRoutes = "/dashboard"; > ``` > > You just need to check that the session does not exist to be redirected to the sign-in page. Remove "isProtectdRoute". > > ```ts > if (!session) { > if (authListRoutes.includes(req.nextUrl.pathname)) { > return NextResponse.next(); > } > > return NextResponse.redirect(new URL("/auth/sign-in", req.nextUrl.origin)); > } > ``` > > When have a session, make changes too. > > ```ts > // Redirect to dashboard if user is authenticated and trying to access auth routes > if (session && isAuthRoute) { > return NextResponse.redirect(new URL("/dashboard", request.nextUrl)); > } > ``` > > In addition to having a session, check if the user is in the authentication route so that the redirection can be performed. > > How to check if the user is accessing the authentication route: > > ```ts > const authRoutes = authListRoutes.some((route) => > req.nextUrl.pathname.startsWith(route) > ); > ``` > > Now grant the redirection if there is a session and you are on the authentication route: > > ```ts > if (session && authRoutes) { > return NextResponse.redirect( > new URL(protectedRoutes, req.nextUrl.origin) > ); > } > ``` Thanks so much.
Author
Owner

@binhodev commented on GitHub (Jan 14, 2025):

😊

@binhodev commented on GitHub (Jan 14, 2025): 😊
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#558