nextauthjs / next-auth

Authentication for the Web.
https://authjs.dev
ISC License
22.88k stars 3.08k forks source link

Exporting auth as middleware doesn't work #10912

Open ShahriarKh opened 1 month ago

ShahriarKh commented 1 month ago

Environment

OS: Windows 10 10.0.19045
Node: 20.12.2
bun: 1.1.2

Firefox Developer Edition 126.0b9

next: 14.2.3 
next-auth: 5.0.0-beta.18
react: 18.3.1

Reproduction URL

https://github.com/ShahriarKh/middleware-test

Describe the issue

Using the method mentioned in the examples and also in the docs doesn't work at all.

export { auth as middleware } from 'auth'

export const config = {
  matcher: ['/((?!api|_next/static|_next/image|favicon.ico).*)'],
};

I tried switching bun/Node, and also switching Next.js version (downgraded to 14.1.0 and 14.2.0 and both had the same issue)

I have to use a custom function like this, which is not desirable:

import { auth } from 'auth'

export default auth((req) => {
  if (!req.auth) {
    const url = req.url.replace(req.nextUrl.pathname, '/login');
    return Response.redirect(url);
  }
});

export const config = {
  matcher: ['/((?!api|_next/static|_next/image|favicon.ico).*)'],
};

How to reproduce

Clone the app and visit / or /test.

Expected behavior

The middleware should automatically redirect to the login page if the request is unauthenticated.

MateusTum commented 4 weeks ago

Same error here! The workaround that I found was using request cookies to check for session-token and validating it by using an api route. Here is my code:

// middleware.ts
import type { NextRequest } from "next/server";
import { NextResponse } from "next/server";

export async function middleware(req: NextRequest) {
  const cookies = req.cookies;

  if (!cookies.get("authjs.session-token")) {
    return NextResponse.redirect(new URL("/login", req.url));
  } else {
    try {
      const sessionResponse = await fetch(
        `${req.nextUrl.origin}/api/auth/session`,
        {
          headers: {
            cookie: req.headers.get("cookie") || "", // Pass cookies to the API route
          },
        }
      );
      if (sessionResponse.status !== 200) {
        throw new Error("Session not found");
      }
    } catch (error) {

      console.log(error);
      const response = NextResponse.redirect(new URL("/login", req.url));
      response.cookies.set('authjs.session-token', '', { expires: new Date(0) });
      return response;
    }
  }
  return NextResponse.next();
}

export const config = {
  matcher: "/admin/:path*",
};
// api/auth/session/route.ts
import { auth } from "@/auth";
import { NextResponse } from "next/server";
import { Session } from "next-auth";

export async function GET() {
  const session: Session | null = await auth();
  if (!session) {
    return NextResponse.json({ message: "Auth required" }, { status: 401 });
  }
  return NextResponse.json({ message: "succc" }, { status: 200 });
}
neil-py commented 3 weeks ago

Same error here! The workaround that I found was using request cookies to check for session-token and validating it by using an api route. Here is my code:

// middleware.ts
import type { NextRequest } from "next/server";
import { NextResponse } from "next/server";

export async function middleware(req: NextRequest) {
  const cookies = req.cookies;

  if (!cookies.get("authjs.session-token")) {
    return NextResponse.redirect(new URL("/login", req.url));
  } else {
    try {
      const sessionResponse = await fetch(
        `${req.nextUrl.origin}/api/auth/session`,
        {
          headers: {
            cookie: req.headers.get("cookie") || "", // Pass cookies to the API route
          },
        }
      );
      if (sessionResponse.status !== 200) {
        throw new Error("Session not found");
      }
    } catch (error) {

      console.log(error);
      const response = NextResponse.redirect(new URL("/login", req.url));
      response.cookies.set('authjs.session-token', '', { expires: new Date(0) });
      return response;
    }
  }
  return NextResponse.next();
}

export const config = {
  matcher: "/admin/:path*",
};
// api/auth/session/route.ts
import { auth } from "@/auth";
import { NextResponse } from "next/server";
import { Session } from "next-auth";

export async function GET() {
  const session: Session | null = await auth();
  if (!session) {
    return NextResponse.json({ message: "Auth required" }, { status: 401 });
  }
  return NextResponse.json({ message: "succc" }, { status: 200 });
}

This workaround works so far. I hope a fix will be released soon

bibblebabl commented 3 weeks ago

I am also experiencing a problem with incorrect req type definition in middleware. Is it the same for you?

No overload matches this call.
  Overload 1 of 4, '(args_0: GetServerSidePropsContext): Promise<Session | null>', gave the following error.
    Argument of type '(req: NextAuthRequest) => Response | null' is not assignable to parameter of type 'GetServerSidePropsContext'.
  Overload 2 of 4, '(args_0: (req: NextAuthRequest, ctx: AppRouteHandlerFnContext) => void | Response | Promise<void | Response>): AppRouteHandlerFn', gave the following error.
    Argument of type '(req: NextAuthRequest) => Response | null' is not assignable to parameter of type '(req: NextAuthRequest, ctx: AppRouteHandlerFnContext) => void | Response | Promise<void | Response>'.
      Type 'Response | null' is not assignable to type 'void | Response | Promise<void | Response>'.
        Type 'null' is not assignable to type 'void | Response | Promise<void | Response>'.ts(2769)
image
drcodecamp commented 5 days ago

@bibblebabl same

dml0031 commented 5 days ago

Same error here! The workaround that I found was using request cookies to check for session-token and validating it by using an api route. Here is my code:

// middleware.ts
import type { NextRequest } from "next/server";
import { NextResponse } from "next/server";

export async function middleware(req: NextRequest) {
  const cookies = req.cookies;

  if (!cookies.get("authjs.session-token")) {
    return NextResponse.redirect(new URL("/login", req.url));
  } else {
    try {
      const sessionResponse = await fetch(
        `${req.nextUrl.origin}/api/auth/session`,
        {
          headers: {
            cookie: req.headers.get("cookie") || "", // Pass cookies to the API route
          },
        }
      );
      if (sessionResponse.status !== 200) {
        throw new Error("Session not found");
      }
    } catch (error) {

      console.log(error);
      const response = NextResponse.redirect(new URL("/login", req.url));
      response.cookies.set('authjs.session-token', '', { expires: new Date(0) });
      return response;
    }
  }
  return NextResponse.next();
}

export const config = {
  matcher: "/admin/:path*",
};
// api/auth/session/route.ts
import { auth } from "@/auth";
import { NextResponse } from "next/server";
import { Session } from "next-auth";

export async function GET() {
  const session: Session | null = await auth();
  if (!session) {
    return NextResponse.json({ message: "Auth required" }, { status: 401 });
  }
  return NextResponse.json({ message: "succc" }, { status: 200 });
}

This workaround works so far. I hope a fix will be released soon

FYI At least from what I found, the cookie name for me appears to be set as __Secure-authjs.session-token instead of just authjs.session-token on a site deployed to vercel that I was testing. Locally this was working for me fine, but once deployed still wasnt working and believe that is why. Still using a modified version of this to check for both so appreciate the workaround!

BertVanHeckeCertifisc commented 17 hours ago

Same error here.

surya-teja-222 commented 9 hours ago
import NextAuth from 'next-auth';
import { authConfig } from './auth.config';

const { auth } = NextAuth(authConfig);

export default async function middleware(...args: Parameters<typeof auth>) {
  return auth(...args);
}

export const config = {
  matcher: [ 'matchers/'],
};

This is what I use and it works fine for me. I'm on 5.0.0-beta.19