Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Next.js 13 - Middleware - Expected an instance of Response to be returned #5649

Closed
creazy231 opened this issue Oct 26, 2022 · 33 comments · Fixed by #5710
Closed

Next.js 13 - Middleware - Expected an instance of Response to be returned #5649

creazy231 opened this issue Oct 26, 2022 · 33 comments · Fixed by #5710
Assignees
Labels
middleware Related to middleware

Comments

@creazy231
Copy link

Environment

System:
OS: macOS 12.6
CPU: (8) arm64 Apple M1
Memory: 1.03 GB / 16.00 GB
Shell: 3.2.57 - /bin/bash
Binaries:
Node: 16.18.0 - ~/.nvm/versions/node/v16.18.0/bin/node
Yarn: 1.22.19 - ~/.nvm/versions/node/v16.18.0/bin/yarn
npm: 8.19.2 - ~/.nvm/versions/node/v16.18.0/bin/npm
Browsers:
Brave Browser: 106.1.44.112
Chrome: 106.0.5249.119
Firefox: 104.0.2
Safari: 16.0

Reproduction URL

.

Describe the issue

Implementing a middleware.js in Next.js 13 like described here (https://next-auth.js.org/tutorials/securing-pages-and-api-routes#nextjs-middleware) results in the following error:

Server Error
TypeError: Expected an instance of Response to be returned

Call Stack
adapter
../node_modules/next/dist/esm/server/web/adapter.js (90:0)

How to reproduce

Implement Next-Auth with Next.js 13 and create a middleware.js with the following content:

export { default } from "next-auth/middleware";

Expected behavior

No fatal error

@creazy231 creazy231 added the triage Unseen or unconfirmed by a maintainer yet. Provide extra information in the meantime. label Oct 26, 2022
@creazy231 creazy231 changed the title Next.js 13 - Middleware Next.js 13 - Middleware - Expected an instance of Response to be returned Oct 26, 2022
@joaopaulomoraes
Copy link

The same here!

In my case I protect all /admin/* routes and I have the same error, only on these pages, the others compile successfully. Code example:

export { default } from 'next-auth/middleware'

export const config = { matcher: ['/admin/(.*)'] }

@RudiPersson
Copy link

RudiPersson commented Oct 27, 2022

Also experiencing this.

No issues if i remove my middleware.ts file.

Code:

export { default } from "next-auth/middleware";

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

@lantaozi
Copy link

lantaozi commented Oct 27, 2022

I meet the same issue, and I found something interesting.

I used withAuth middleware by calling in the way withAuth(req, nextAuthOption);, and it return a function instead of Response. It causes the error.

After review the source code, I found it doesn't identify the first param as instance of NextRequest:

export function withAuth(...args: WithAuthArgs) {
  if (!args.length || args[0] instanceof NextRequest) {
    // @ts-expect-error
    return handleMiddleware(...args)
  }

  if (typeof args[0] === "function") {
    const middleware = args[0]
    const options = args[1] as NextAuthMiddlewareOptions | undefined
    return async (...args: Parameters<NextMiddlewareWithAuth>) =>
      await handleMiddleware(args[0], options, async (token) => {
        args[0].nextauth = { token }
        return await middleware(...args)
      })
  }

  const options = args[0]
  return async (...args: Parameters<NextMiddleware>) =>
    await handleMiddleware(args[0], options)
}

Interesting, isn't it ???

I print the stack of the call potint, the result is:

    at Object.middleware [as handler] (webpack-internal:///(middleware)/./middleware.ts:27:24)
    at adapter (webpack-internal:///(middleware)/./node_modules/next/dist/esm/server/web/adapter.js:102:33)
    at __WEBPACK_DEFAULT_EXPORT__ (webpack-internal:///(middleware)/./node_modules/next/dist/build/webpack/loaders/next-middleware-loader.js?absolutePagePath=%2FUsers%2Flantaozi%2FWorkspace%2Ficecream%2Fmiddleware.ts&page=%2Fmiddleware&rootDir=%2FUsers%2Flantaozi%2FWorkspace%2Ficecream&matchers=!:19:180)
    at /Users/lantaozi/Workspace/icecream/node_modules/next/dist/server/web/sandbox/sandbox.js:73:30
    at runMicrotasks (<anonymous>)
    at processTicksAndRejections (node:internal/process/task_queues:96:5)
    at async DevServer.runMiddleware (/Users/lantaozi/Workspace/icecream/node_modules/next/dist/server/next-server.js:1122:24)
    at async DevServer.runMiddleware (/Users/lantaozi/Workspace/icecream/node_modules/next/dist/server/dev/next-dev-server.js:634:28)
    at async Object.fn (/Users/lantaozi/Workspace/icecream/node_modules/next/dist/server/next-server.js:1201:38)
    at async Router.execute (/Users/lantaozi/Workspace/icecream/node_modules/next/dist/server/router.js:252:36)

The adapter.js is from node_modules/next/dist/esm/server/web/adapter.js which used to be node_modules/next/dist/server/web/adapter.js. So the request is a NextRequest instance from esm/server/web/spec-extension package instead of server/web/spec-extension

It seems that there is no package with the path of esm in previous version of Next.js (before 13).

I'm not familar with it, anyone know what it is can help me explain it.

For now, i just use the returned function call the middle handler:

  const request = req as NextRequestWithAuth;
  const withAuthResult = withAuth(nextAuthOption);
  if (typeof withAuthResult === 'function') {
    return withAuthResult(request, event);
  }
  return withAuthResult;

@PauloFerrazACCT
Copy link

I'm getting the same error here

@midzdotdev
Copy link

Yep same here, we're looking forward to a fix on this. Putting a pause on our migration to Next 13 until this is stable.

@ThangHuuVu
Copy link
Member

Very good catch @lantaozi, the issue is this check

if (!args.length || args[0] instanceof NextRequest) {

With the default middleware configuration, this condition should be met, but for next@13, it fails.
cc @balazsorban44 could this be an upstream issue? There's nothing related to middleware changes in The migration doc 🤔

@ThangHuuVu ThangHuuVu added middleware Related to middleware and removed triage Unseen or unconfirmed by a maintainer yet. Provide extra information in the meantime. labels Oct 27, 2022
@3atharvkurdukar
Copy link

Is there any workaround for now?

@copini
Copy link

copini commented Oct 28, 2022

For me explicitly exporting withAuth with an empty options argument seems to work:

import { withAuth } from 'next-auth/middleware'

export default withAuth({})

@joaopaulomoraes
Copy link

For me explicitly exporting withAuth with an empty options argument seems to work:

import { withAuth } from 'next-auth/middleware'

export default withAuth({})

But then all routes are private, including login/logout and in that case if you log out for some reason, you are in several infinite loops

@klee3
Copy link

klee3 commented Oct 30, 2022

For me explicitly exporting withAuth with an empty options argument seems to work:

import { withAuth } from 'next-auth/middleware'

export default withAuth({})

But then all routes are private, including login/logout and in that case if you log out for some reason, you are in several infinite loops

it redirecting to the Sign In page for all the routes

With a little hack around exporting config and withAuth and boom It's working for me

import { withAuth } from "next-auth/middleware"

export const config = { matcher: ["/dashboard/:path*"] }
export default withAuth({})

@balazsorban44 balazsorban44 self-assigned this Oct 31, 2022
HaNdTriX added a commit to HaNdTriX/next-auth that referenced this issue Nov 2, 2022
balazsorban44 pushed a commit that referenced this issue Nov 4, 2022
* Migrate dev app to Next.js Version 13

* Update core types

* Fix middleware

#5649

* Use new ResponseCookie API

vercel/next.js#41526
@Lucasvo1
Copy link

Lucasvo1 commented Nov 8, 2022

I am using middleware with the following content and the oAuth is done correctly, but the page always stays on the login page, no way of going to another page. I upgraded to the last release btw. Debug is enabled but no error messages. I'm using next 13.0.2

export { default } from 'next-auth/middleware';

@jeslenbucci
Copy link

jeslenbucci commented Nov 16, 2022

I was just having this same issue after upgrading to Next 13. I was using "next-auth": "^4.12.0", and ran yarn upgrade next-auth and it started working with this as my middleware. My middleware is also a typescript file, if that makes a difference.

export { default } from 'next-auth/middleware'

export const config = { matcher: ['/dashboard/:path*'] }

After upgrading, nothing the version number for next-auth remained the same. I have no idea what happened, but it works

@dawidseipold
Copy link

None of the workarounds seem to work for me. It just redirects me to either login page (doesn't matter if the user is logged in or not), or doesn't do anything at all.

@jeslenbucci
Copy link

@dawidseipold

None of the workarounds seem to work for me. It just redirects me to either login page (doesn't matter if the user is logged in or not), or doesn't do anything at all.

Same thing ended up happening to me. This is what I eventually set my middleware file to:

Headers.prototype.getAll = () => []

export { default as middleware } from 'next-auth/middleware'

export const config = { matcher: ['/dashboard/:path*'] }

The first part is a hack because there was a breaking change when publishing to production. Not also that I'm exporting the next-auth/middleware as middleware because this update expects a function, not default, called middleware to be exported as of Next 13

@dawidseipold
Copy link

@jeslenbucci Unfortunately your solution doesn't work for me ☹️

@maitrungduc1410
Copy link

maitrungduc1410 commented Nov 18, 2022

With a little hack around exporting config and withAuth and boom It's working for me

import { withAuth } from "next-auth/middleware"

export const config = { matcher: ["/dashboard/:path*"] }
export default withAuth({})

doesn't work for me, it all returns 307 Temporary Redirect with route http://localhost:3000/api/reports:

// middleware.ts
import { withAuth } from "next-auth/middleware"

export const config = { matcher: ["/api/:path*"] }
export default withAuth({})

@tronxdev
Copy link

tronxdev commented Nov 29, 2022

@Lucasvo1 @jeslenbucci Where is the middleware.ts file located in your project? I put it in the project's root directory and it didn't work. I'm not sure how middleware works in the latest Next.js with Turbopack. The document of Next.js upgrade to v13+ doesn't give any instruction of middleware setup yet.

@d9j
Copy link

d9j commented Dec 9, 2022

nextjs 13 seems quite alpha.. middleware doesnt work at all..

@Alwurts
Copy link

Alwurts commented Dec 26, 2022

I was having trouble with withAuth() where the token was being returned null

import { withAuth } from "next-auth/middleware";

export default withAuth(
  {
    callbacks: {
      async authorized({ token }) {
        return !!token;
      },
    },
  }
);

export const config = {
  matcher: ["/app/:path*", "/api/((?!auth).*)"],
};

What fixed it for me was to downgrade next js to 13.0.0

@BeezBeez
Copy link

Even today on nextjs 13.2.4 and nextauth 4.20.1. My middleware is not working properly. I am constantly redirected to the login page when I try to access the protected routes, even after logging in.

@SpBills
Copy link

SpBills commented Apr 13, 2023

Any update here? I am having the same problem as above.

@viniyr
Copy link

viniyr commented May 21, 2023

Same error here with:
"next-auth": "^4.22.1", "next": "13.4.3"

middleware.ts

import { withAuth } from 'next-auth/middleware'


export const config = {
  matcher: ["/((?!auth|signin|auth/signin).*)"],
};
export default withAuth({})

If I try to access any page it redirects me to signin page, even if I'm successfully logged in

@JuanQuiro
Copy link

I had the same error and updated nextjs to 13.4.5-canary.4, it allows me to compile, but still can't identify if the user is logged in or not.

Commands applied

pnpm install --save next@canary

Reproduction of the problem

bandicam.2023-06-02.19-12-57-453.mp4

middleware.ts

export { default } from 'next-auth/middleware';
export const config = { matcher: ['/workout'] };

Versions

"next": "13.4.5-canary.4",
"next-auth": "4.22.1",

@abheist
Copy link

abheist commented Jun 14, 2023

I'm still getting this. @balazsorban44 any updates ? 🙃

@abheist
Copy link

abheist commented Jun 14, 2023

Following change worked for me:

middleware.ts

export { default } from "next-auth/middleware";

export const config = { matcher: ["/n"] };

auth.ts authOptions

  • Added session strategy
  • change in session callback
  • change in jwt callback
import type { NextAuthOptions } from "next-auth";
import GoogleProvider from "next-auth/providers/google";
import { PrismaAdapter } from "@auth/prisma-adapter";
import { prisma } from "@/lib/prisma";

export const authOptions: NextAuthOptions = {
  // @ts-ignore
  adapter: PrismaAdapter(prisma),
  session: {
    strategy: "jwt",
  },
  providers: [
    GoogleProvider({
      clientId: "****",
      clientSecret: "****",
      authorization: {
        params: {
          prompt: "consent",
          access_type: "offline",
          response_type: "code",
        },
      },
    }),
  ],
  secret: process.env.NEXTAUTH_SECRET,
  callbacks: {
    async signIn({ user, account, profile, email, credentials }) {
      console.log("fire signin Callback");
      return true;
    },
    async redirect({ url, baseUrl }) {
      console.log("fire redirect Callback");
      return baseUrl;
    },
    async session({ session, user, token }) {
      console.log("fire SESSION Callback");
      return {
        ...session,
        user: {
          ...session.user,
          id: token.id,
          randomKey: token.randomKey,
        },
      };
    },
    async jwt({ token, user, account, profile, isNewUser }) {
      console.log("fire jwt Callback");
      if (user) {
        const u = user as unknown as any;
        return {
          ...token,
          id: u.id,
          randomKey: u.randomKey,
        };
      }
      return token;
    },
  },
};

https://abheist.com/nextauth-nextjs-app-router-with-prisma/

@rharkor
Copy link

rharkor commented Jul 25, 2023

Still doesnt work for me..

@ronnycoding
Copy link

ronnycoding commented Aug 26, 2023

the following solution works for me

export default withAuth(
  function middleware(req: NextRequestWithAuth) {
	//...
  },
  {
    pages: {
      signIn: LOGIN_ROUTE,
    },
    jwt: { decode: jwtConfig?.decode },
    callbacks: {
      authorized: async ({ req, token }) => {
        const rawToken = await getToken({
          req,
          secret: process.env.NEXTAUTH_SECRET,
          decode: jwtConfig?.decode,
          raw: true,
        });
        
        if (toke) return true

		if (!!rawToken) {
			... validate signature 
			
			return true
		}
		
		return false
      },
    },
    secret: process.env.NEXTAUTH_SECRET,
  }
);

@joaopaulomoraes
Copy link

The following works to me in src/middleware.ts

export { default } from 'next-auth/middleware'

export const config = { matcher: ['/admin/(.*)'] }

Version "next-auth": "^4.22.3"

@joaopaulomoraes
Copy link

pages/api/auth/[...nextauth].ts

import NextAuth from 'next-auth'

export default NextAuth({
  secret: env.NEXTAUTH_SECRET
})

@carlosriveroib
Copy link

Still not working with app directory.... and the issue is close. This package is really difficult to use. I'm really have troubles with this

@danesto
Copy link

danesto commented Oct 18, 2023

Had the same problem with: "next": "13.4.9", "next-auth": "^4.23.1",. What's weird is that it is working when deployed to Vercel, but not locally.

It did work locally for some time, but for some reason it just stopped working, and no matter what - it would always stay on login page.

After hours of investigation, what finally worked for me (for now) was adding:
NEXTAUTH_URL=http://localhost:3000 inside my .env file.

And my middleware.ts file is the same as in the comments above:

import { withAuth } from 'next-auth/middleware';

export const config = { matcher: ['/dashboard/:path*'] };
export default withAuth({});

@gavinwun
Copy link

gavinwun commented Nov 3, 2023

I got this working in the end - sorry a bit rough but sleeping for the night after pulling my hair out for a while lol

import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
import { withAuth } from "next-auth/middleware";

export default withAuth(
  {
    callbacks: {
      async authorized({ token }) {
        return !!token;
      },
    },
  }
);

// This function can be marked `async` if using `await` inside
export function middleware(request: NextRequest) {
    //   return NextResponse.redirect(new URL('/', request.url))
}

// See "Matching Paths" below to learn more
export const config = {
    matcher: '/api/books/:path*',
}

Basically I had to add an empty middleware function to get the rest of my app routes working without throwing errors, then the config worked as it did, without breaking all the pages.

@lucasccampos
Copy link

If you're using AuthJS v5 and next-intl

try this middleware.ts

import { auth } from "@/auth";
import createMiddleware from 'next-intl/middleware';
import { routing } from './i18n/routing';
import { NextResponse} from "next/server";

const intlMiddleware = createMiddleware(routing);

export const config = {
    matcher: [
        '/((?!api|_next|.*\\..*).*)'
    ]
};

export default auth((req) => {
    if (req.nextUrl.pathname.startsWith("/client") && !req.auth) {
        const loginUrl = new URL("/login", req.nextUrl.origin);
        loginUrl.searchParams.set("callbackUrl", req.nextUrl.pathname); // Store the original page

        return NextResponse.redirect(loginUrl)
    }
    return intlMiddleware(req);
})

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
middleware Related to middleware
Projects
None yet
Development

Successfully merging a pull request may close this issue.