You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Supabase OAuth登录问题:受保护路由登录后无法返回原地址

解决Supabase OAuth登录的Code Challenge不匹配与重定向问题

问题1:Code Challenge不匹配错误修复

原因

中间件调用signInWithOAuth时,Supabase会生成并存储code verifier到cookie,但原代码的cookie处理逻辑没有正确将这些cookie写入响应,导致重定向到Google登录时丢失verifier,回调阶段exchangeCodeForSession无法匹配验证。

修改后的Supabase中间件代码

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) {
          // 同步更新request和响应的cookie,确保verifier不丢失
          cookiesToSet.forEach(({ name, value, options }) => {
            request.cookies.set(name, value, options);
            supabaseResponse.cookies.set(name, value, options);
          });
        },
      },
    }
  );

  const { data: { user } } = await supabase.auth.getUser();

  if (!user) {
    const redirectTo = request.nextUrl.pathname;
    const { data, error: signInError } = await supabase.auth.signInWithOAuth({
      provider: "google",
      options: {
        // 对路径编码,避免特殊字符解析错误
        redirectTo: `${request.nextUrl.origin}/auth/callback?redirectTo=${encodeURIComponent(redirectTo)}`,
      },
    });

    if (signInError) {
      console.error("登录错误:", signInError.message);
      throw new Error("OAuth登录失败");
    }

    if (data?.url) {
      return NextResponse.redirect(data.url);
    }
  }

  return supabaseResponse;
}

问题2:登录后重定向到原页面修复

原因

原回调路由错误拼接next和redirectTo参数,导致路径格式错误;同时未正确区分两种登录场景的目标路径。

修改后的回调路由代码

import { NextResponse } from "next/server";
import { createClient } from "@/utils/supabase/server";

export async function GET(request: Request) {
  const { searchParams, origin } = new URL(request.url);
  const code = searchParams.get("code");
  // 解码获取目标路径,默认对应添加产品按钮的跳转地址
  const redirectTo = decodeURIComponent(searchParams.get("redirectTo") ?? "/dashboard/create");

  if (code) {
    const supabase = createClient();
    const { error } = await supabase.auth.exchangeCodeForSession(code);
    
    if (!error) {
      const forwardedHost = request.headers.get("x-forwarded-host");
      const targetUrl = new URL(redirectTo, origin);

      // 根据环境构建正确的重定向地址
      if (process.env.NODE_ENV !== "development" && forwardedHost) {
        targetUrl.host = forwardedHost;
        targetUrl.protocol = "https:";
      }
      return NextResponse.redirect(targetUrl.toString());
    }
  }

  // 登录失败跳转错误页
  return NextResponse.redirect(`${origin}/auth/auth-code-error`);
}

修改后的Server Action代码

"use server";

import { createClient } from "@/utils/supabase/server";
import { redirect } from "next/navigation";

export default async function signIn() {
  const supabase = createClient();

  const { data, error } = await supabase.auth.signInWithOAuth({
    provider: "google",
    options: {
      // 指定添加产品按钮登录后的默认跳转路径
      redirectTo: `${process.env.NEXT_PUBLIC_SUPABASE_URL}/auth/callback?redirectTo=/dashboard/create`,
    },
  });

  if (error) {
    console.error("登录错误:", error.message);
    throw new Error("OAuth登录失败");
  }

  if (data.url) {
    redirect(data.url);
  }
}

验证步骤

  1. 访问受保护路由(如/dashboard),自动跳转Google登录,完成后返回原路由
  2. 点击“添加产品”按钮,登录后跳转至/dashboard/create
  3. 控制台无code challenge does not match错误输出

内容的提问来源于stack exchange,提问作者illuminate

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 15:13:13