I am creating a Nextjs project using Supabase. I am authenticating user using Supabase clients through Google OAuth providers. I followed the exact code of this github repo: https://github.com/SamuelSackey/nextjs-supabase-example
The entire authentication process works well. But when the user has been logged in for some time, after some time, the page does not load and I get the error:
Error: Cookies can only be modified in a Server Action or Route Handler. Read more:
https://nextjs.org/docs/app/api-reference/functions/cookies#cookiessetname-value-
options
at Object.set (./src/lib/supabase/server-clients.ts:27:73)
at Array.map (<anonymous>)
Here is my server-client.ts
import { type CookieOptions, createServerClient } from '@supabase/ssr';
import { getCookie, setCookie } from 'cookies-next';
import { cookies } from 'next/headers';
import { type NextRequest, type NextResponse } from 'next/server';
import { SUPABASE_ANON_KEY, SUPABASE_URL } from '@/constant/env';
// server component can only get cookies and not set them, hence the "component" check
export function createSupabaseServerClient(component = false) {
return createServerClient(SUPABASE_URL, SUPABASE_ANON_KEY, {
cookies: {
get(name: string) {
return cookies().get(name)?.value;
},
set(name: string, value: string, options: CookieOptions) {
if (component) return;
cookies().set(name, value, options);
},
remove(name: string, options: CookieOptions) {
if (component) return;
cookies().set(name, '', options);
},
},
});
}
export function createSupabaseServerComponentClient() {
return createSupabaseServerClient(true);
}
export function createSupabaseReqResClient(
req: NextRequest,
res: NextResponse
) {
cookies().getAll(); // Keep cookies in the JS execution context for Next.js build
return createServerClient(SUPABASE_URL, SUPABASE_ANON_KEY, {
cookies: {
get(name: string) {
return getCookie(name, { req, res });
},
set(name: string, value: string, options: CookieOptions) {
setCookie(name, value, { req, res, ...options });
},
remove(name: string, options: CookieOptions) {
setCookie(name, '', { req, res, ...options });
},
},
});
}
middleware.ts
import { type NextRequest, NextResponse } from 'next/server';
import { createSupabaseReqResClient } from '@/lib/supabase/server-clients';
export async function middleware(request: NextRequest) {
const response = NextResponse.next({
request: {
headers: request.headers,
},
});
const supabase = createSupabaseReqResClient(request, response);
const {
data: { session },
} = await supabase.auth.getSession();
const user = session?.user;
if (!user && request.nextUrl.pathname.startsWith('/my-profile')) {
return NextResponse.redirect(new URL('/', request.url));
}
return response;
}
export const config = {
matcher: ['/my-profile'],
};
api/auth/callback/route.ts
import { NextResponse } from 'next/server';
import { createSupabaseServerClient } from '@/lib/supabase/server-clients';
export async function GET(request: Request) {
const { searchParams, origin } = new URL(request.url);
const code = searchParams.get('code');
// if "next" is in param, use it in the redirect URL
// const next = searchParams.get('next') ?? '/';
if (code) {
const supabase = createSupabaseServerClient();
const { error } = await supabase.auth.exchangeCodeForSession(code);
if (!error) {
return NextResponse.redirect(origin);
}
}
// TODO:
// return the user to an error page with instructions
return NextResponse.redirect(`${origin}/auth/auth-error`);
}
Why do I get this error only after some time of user being logged in and not during the initial process of authentication? How do solve this error?
I'm wondering if the SamuelSackey code has an error, in
lib/supabase/server-client the code is not spreading the options.
See lines 18 and 22 from the repo (https://github.com/SamuelSackey/nextjs-supabase-example/blob/main/src/lib/supabase/server-client.ts)
The Supabase SSR docs has the following
For example (on the Supabase SSR docs) https://supabase.com/docs/guides/auth/server-side/creating-a-client?environment=route-handler
His middleware code also looks abbreviated, here are the supabase docs for middleware
https://supabase.com/docs/guides/auth/server-side/creating-a-client?environment=middleware, note the get, set, and remove code is missing from his middleware.ts. When calling the ssr CreateServerClient, the third argument is the cooking handling (cookies and cookieOptions),
Edit: also found this on the Supabase Social Login documents ( https://supabase.com/docs/guides/auth/social-login/auth-google)