I’m working on a Next.js project with Supabase authentication, and I’m encountering an issue where the user session is not being detected in the middleware after a successful authentication callback. This causes the middleware to continuously redirect to the signin page, creating a redirect loop.
Here’s the relevant code:
/api/auth/callback/route.ts :
import { NextResponse, NextRequest } from "next/server";
import { createClient } from "@/utils/supabase/server";
import config from "@/config";
export const dynamic = "force-dynamic";
// This route is called after a successful login. It exchanges the code for a session and redirects to the callback URL (see config.js).
export async function GET(req: NextRequest) {
const requestUrl = new URL(req.url);
const code = requestUrl.searchParams.get("code");
const next = requestUrl.searchParams.get("next");
console.log("next in auth callback:", next);
if (code) {
const supabase = createClient();
const result = await supabase.auth.exchangeCodeForSession(code);
console.log("exchangeCodeForSession result:", JSON.stringify(result, null, 2));
if (result.error) {
console.error("Error exchanging code for session:", result.error);
return NextResponse.redirect(requestUrl.origin + '/auth-error');
}
// You can access other properties like result.data here if needed
}
// URL to redirect to after sign in process completes
return NextResponse.redirect(requestUrl.origin);
}
middelware.ts:
/* middleware.ts */
import { type NextRequest } from 'next/server';
import { updateSession } from '@/utils/supabase/middleware';
export async function middleware(request: NextRequest) {
return await updateSession(request);
}
export const config = {
matcher: [
/*
* Match all request paths except:
* - _next/static (static files)
* - _next/image (image optimization files)
* - favicon.ico (favicon file)
* - images - .svg, .png, .jpg, .jpeg, .gif, .webp
* Feel free to modify this pattern to include more paths.
*/
'/((?!_next/static|_next/image|favicon.ico|.*\.(?:svg|png|jpg|jpeg|gif|webp)$).*)'
]
};
/* utils/supabase/middleware.ts */
import { createServerClient } from "@supabase/ssr";
import { NextResponse, type NextRequest } from "next/server";
export async function updateSession(request: NextRequest) {
let supabaseResponse = NextResponse.next({
request,
});
const supabase = createServerClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
{
cookies: {
getAll() {
return request.cookies.getAll();
},
setAll(cookiesToSet) {
cookiesToSet.forEach(({ name, value, options }) =>
request.cookies.set(name, value)
);
supabaseResponse = NextResponse.next({
request,
});
cookiesToSet.forEach(({ name, value, options }) =>
supabaseResponse.cookies.set(name, value, options)
);
},
},
}
);
const {
data: { user },
} = await supabase.auth.getUser();
//console data
console.log("user in middleware:",user)
if (
!user &&
!request.nextUrl.pathname.startsWith("/signin") &&
!request.nextUrl.pathname.startsWith("/api")
) {
// no user, potentially respond by redirecting the user to the login page
const url = request.nextUrl.clone();
url.pathname = "/signin";
return NextResponse.redirect(url);
}
return supabaseResponse;
}
The issue:
- The /api/auth/callback route successfully calls exchangeCodeForSession.
- However, in the subsequent middleware, getUser() returns null.
- Because the user is null, the middleware redirects to the signin page.
Logs:
GET /api/auth/callback?code=97738d2a-3b9f-4c2e-a1aa-d9c667478e29 307 in 29ms
user in middleware: null
What I’ve tried:
Logging the result of exchangeCodeForSession in the callback route, which shows a successful session creation.
Checking the middleware multiple times, but it consistently shows the user as null.
Questions:
Why isn’t the user session being detected in the middleware after a successful callback?
Is there a timing issue where the middleware is executing before the session is fully established?
How can I ensure that the session is properly set and detectable in the middleware after the callback?
What’s the best way to prevent the redirect loop while still protecting routes that require authentication?
Environment:
"@supabase/ssr": "^0.4.0",
"@supabase/supabase-js": "^2.38.3",
"next": "^14.0.0",
Any insights or suggestions would be greatly appreciated. Thank you!
2
Answers
I set the Site URL to http://localhost:3000 instead of http://127.0.0.1:3000, the problem solved
Timing Issue
been fully established. You can try introducing a slight delay in
your middleware to see if that resolves the issue. While this isn’t
a permanent solution, it can help identify if timing is the problem.
Cookie Handling
is established. In your callback, you should ensure that the cookies
for the session are set correctly. You might need to add code to set
the cookies explicitly in the response from the /api/auth/callback
route.
Here’s an example of how to set the session cookies:
Middleware Logic
In your middleware, you may want to verify if the cookies are being
sent back correctly. Adjust the getAll method in the middleware to
ensure the cookies are being read correctly:
Checking Middleware Execution Order
Ensure that your middleware is executing in the expected order. You
might want to log out the order of operations to confirm that the
authentication flow is correct.
– Preventing Redirect Loops
To prevent the redirect loop, you might want to include a condition in your middleware that checks for a specific query parameter or session state that indicates the user has just logged in.
For example:
Make sure
Set cookies correctly in your authentication callback.
Verify cookie handling in the middleware.
Introduce logging to trace the flow.
Consider adding logic to skip checks in specific requests (like the callback).