NextJS Middleware Auth Examples recommends using function that doesn't exist #34842
Replies: 16 comments 1 reply
|
Just to link things together I thought I'd share that there's also a Discussion I just came across that covers a couple other documentation issues on the same page (https://supabase.com/docs/guides/auth/server-side/nextjs?queryGroups=router&router=app). Discussion: https://github.com/orgs/supabase/discussions/27619 To summarize quickly:
EDIT: I've opened a PR for the issues from the Discussion, but it doesn't address this Issue. #27622. |
|
@ErikPetersenDev running in this issue, could you propose a solution for the setAll issue ? My previous middleware but using the new documentation, I loose the cookies import { NextRequest } from 'next/server';
import { createI18nMiddleware } from 'next-international/middleware';
import { type CookieOptions, createServerClient } from '@supabase/ssr';
const handleI18nRouting = createI18nMiddleware({
locales: ['en', 'fr'],
defaultLocale: 'en',
urlMappingStrategy: 'rewriteDefault',
});
export async function middleware(request: NextRequest) {
const response = handleI18nRouting(request);
const supabase = createServerClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
{
cookies: {
get(name: string) {
return request.cookies.get(name)?.value;
},
set(name: string, value: string, options: CookieOptions) {
request.cookies.set({name, value, ...options});
response.cookies.set({name, value, ...options});
},
remove(name: string, options: CookieOptions) {
request.cookies.set({name, value: '', ...options});
response.cookies.set({name, value: '', ...options});
}
}
}
);
await supabase.auth.getUser();
return response;
}Thank you. |
|
Hi @wadjeroudi! I'm not part of the Supabase team and not very familiar with this particular issue, but I did just spend some time looking into it and might have a solution: For the "setAll" issue, I think the right way to do it may be something like: |
|
The points outlined in this issue are fixed by the linked PR so I think this is safe to close. Massive thanks for doing this and keeping the docs up to date. If you think the cookie setting in the example is not correct currently, comment here and we can re-open. |
|
Hi @encima ! In my original comment above I was noting a different issue in the docs close to this one. I ended up fixing that issue and mentioned the PR in an edit (which then linked that PR to this Issue). I don't believe that addressed the original issue opened by @ethanniser though. Sorry if linking that caused a problem! For example, the title of the issue is related to a comment in the example code that suggests using |
|
Thanks for clarifying, I will reopen and keep it open to address this! |
Glad I found this issue, since I'm very confused now. The function updateSession now returns I've multiple middlewares in a NextJS project with responses so I had to create a chain in order to run through them. Which looks like: middleware.ts chain function: withMiddleware1.ts <-- localization withMiddleware2.ts <--- supabase auth If I return the updatedResonse instead of response here: |
|
@RowinVanAmsterdam I think you should have it work by passing the response to updateSession as a argument, and within updateSession check if response is undefined (so not created from a previous middleware) and in that case take the supabaseResponse as defined in their documentation. |
|
any news on this? I actually have the exact same issue as mentioned by @RowinVanAmsterdam. It's been two days I am working on this, could not find a way to make it work properly. I managed to get either the user session or the internationalisation but never both. [EDIT] The middleware supabase is setup correctly. Whenever I remove the chained middlewares and I work only with the supabase one, everything is ok. But when I add back the middleware for handling internationalisation, getUser() fails. So it's really about how to pass the request/response correctly so that cookies are not lost in the way. |
|
Finally solved this, but not sure it is the recommended way. Instead of having supabase middleware run first, I put it has the last middleware to ensure the response is not modified after it runs. And within the previous middlewares I modify the "middleware-scope" request, which I pass to the next middleware. So far that makes the job, did not run into any issue. |
Sounds more like a workaround than a definitive fix. Especially if you have a similar middleware that also needs to be at the end. I have fixed it by creating a response object in the first and pass it down the chain, so maybe you can use it or adapt it to your project: middleware.ts: stackMiddlewares.ts: This is my first middleware, so it has to create a response object and returns it: i18nMiddleware.ts Here it receives the response from the previous middlewares, modifies it and once again returns it so the next middleware in the chain has access to it. authenticationMiddleware.ts: I hope this helps a bit. |
|
Hello, I am also having the same problem. Will there be a fix for this bug soon? Also, why do you market this product as an alternative to Firebase? If you still plan to do that, at the very least, please update your documentation for Next.js. It would save others (including me) from having to deal with this huge headache while using Supabase. |
|
UPDATE: I think I have a solution for my headache: This is what should have been put in the documentation import { createServerClient } from '@supabase/ssr'
import { cookies } from 'next/headers'
export const createClient = (cookieStore: ReturnType<typeof cookies>) => createServerClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!, {
cookies: {
async getAll() { return (await cookieStore)?.getAll() },
setAll(cookiesToSet) {
try {
cookiesToSet.forEach(async ({ name, value, options }) =>
(await cookieStore)?.set(name, value, options))
} catch {
// The `setAll` method was called from a Server Component.
// This can be ignored if you have middleware refreshing
// user sessions.
}
}
}
})Other solution (async arrow function) import { createServerClient } from '@supabase/ssr'
import { cookies } from 'next/headers'
export const createClient = async (cookieStore: ReturnType<typeof cookies>) => createServerClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!, {
cookies: {
async getAll() { return (await cookieStore)?.getAll() },
setAll(cookiesToSet) {
try {
cookiesToSet.forEach(async ({ name, value, options }) =>
(await cookieStore)?.set(name, value, options))
} catch {
// The `setAll` method was called from a Server Component.
// This can be ignored if you have middleware refreshing
// user sessions.
}
}
}
}) |
|
@RowinVanAmsterdam I love the pattern you suggested 🎉 I had to slightly modify this pattern in order to get import { createServerClient } from "@supabase/ssr";
import { NextResponse, type NextFetchEvent, type NextRequest } from "next/server";
import { NextMiddlewareExtended } from "./stack-middlewares";
export function authenticationMiddleware(middleware: NextMiddlewareExtended) {
return async (request: NextRequest, event: NextFetchEvent, response: NextResponse) => {
let supabaseResponse = response;
const supabase = createServerClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
{
cookies: {
getAll() {
return request.cookies.getAll();
},
setAll(cookiesToSet) {
// Preserve existing response and its headers
const existingHeaders = new Headers(supabaseResponse.headers);
cookiesToSet.forEach(({ name, value, options }) => request.cookies.set(name, value));
supabaseResponse = NextResponse.next({
request,
});
// Copy over all headers from the previous response
existingHeaders.forEach((value, key) => {
supabaseResponse.headers.set(key, value);
});
// Now set the new cookies
cookiesToSet.forEach(({ name, value, options }) => supabaseResponse.cookies.set(name, value, options));
},
},
}
);
await supabase.auth.getUser();
// IMPORTANT: You *must* return the supabaseResponse object as it is. If you're
// creating a new response object with NextResponse.next() make sure to:
// 1. Pass the request in it, like so:
// const myNewResponse = NextResponse.next({ request })
// 2. Copy over the cookies, like so:
// myNewResponse.cookies.setAll(supabaseResponse.cookies.getAll())
// 3. Change the myNewResponse object to fit your needs, but avoid changing
// the cookies!
// 4. Finally:
// return myNewResponse
// If this is not done, you may be causing the browser and server to go out
// of sync and terminate the user's session prematurely!
return middleware(request, event, supabaseResponse);
};
}
|
|
@RowinVanAmsterdam just moved to your approach today to have something cleaner. it works perfectly, thanks! |
|
Hi everyone, due to inactivity on this issue I've moved the issue over to discussions/enhancements. Thank you for your help on this one! |

Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Improve documentation
Link
https://supabase.com/docs/guides/auth/server-side/nextjs?queryGroups=router&router=app
Describe the problem
The main issue is that the current example is structured in a way were it assumes the only thing you will ever do in your middleware is authenticate with supabase, but often I would think that is not the case.
My current project has some complex redirection logic, so I can't just simply return the
supabaseResponse. The docs suggest making a newNextResponseand manually setting the cookiesThe only problem is the
cookiesproperty onNextResponseis of typeResponseCookieswhich does not actually have asetAllmethod.Describe the improvement
It would be fantastic if there was:
return await updateSessionI'm upgrading my auth from 0.6
@supabase/auth-helpers-nextjsand I was actually in the process of writing a separate issue earlier asking for the docs to no longer recommendget,setanddelete, but #27242 literally merged as I was writing it (thanks @hf), but I've run into this separate issue now. If any other additional information is needed just let me know- happy to help.Thanks so much
All reactions