skip to Main Content

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:

  1. The /api/auth/callback route successfully calls exchangeCodeForSession.
  2. However, in the subsequent middleware, getUser() returns null.
  3. 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


  1. Chosen as BEST ANSWER

    I set the Site URL to http://localhost:3000 instead of http://127.0.0.1:3000, the problem solved


  2. Timing Issue

    1. It’s possible that the middleware is running before the session has
      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

    1. Make sure that the cookies are being set correctly after the session
      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:

    if (result.data) {
      // Set session cookies
      const { access_token, refresh_token } = result.data.session;
      const options = { path: '/', httpOnly: true, secure: process.env.NODE_ENV === 'production' };
    
      NextResponse.setCookie('supabase.auth.token', access_token, options);
      NextResponse.setCookie('supabase.auth.refresh_token', refresh_token, options);
    }
    

    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:

    cookies: {
      getAll() {
        return Object.fromEntries(request.cookies.getAll().map(cookie => [cookie.name, cookie.value]));
      },
      setAll(cookiesToSet) {
        // Same logic as before
      },
    },
    

    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:

    if (request.nextUrl.pathname === '/api/auth/callback') {
      // Skip user check for this request
      return NextResponse.next();
    }
    

    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).

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search