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

Supabase Auth回调异常:成功认证后仍要求code_verifier与auth_code

NextJS 13(App Router)+ FastAPI + Supabase 认证异常:登录流程完成后仍提示缺少auth code和code verifier

环境配置

使用Supabase处理认证,前端通过AuthCallback组件处理OAuth回调:

// AuthCallback.tsx
'use client';

import { useEffect } from 'react';
import { useRouter, useSearchParams } from 'next/navigation';
import { useToast } from "@/hooks/use-toast";
import axios, { AxiosError } from 'axios';
import { BACKEND_URL } from '../src/lib/utils';

export default function AuthCallback() {
  const router = useRouter();
  const searchParams = useSearchParams();
  const { toast } = useToast();

  useEffect(() => {
    const handleCallback = async () => {
      const code = searchParams.get('code');
      const provider = searchParams.get('provider');

      if (code) {
        try {
          const response = await axios.post(`${BACKEND_URL}/api/auth/callback/${provider}`, { code });
          if (response.data.user) {
            localStorage.setItem('access_token', response.data.session.access_token);
            router.push('/dashboard');
          } else {
            throw new Error('Login failed');
          }
        } catch (error) {
          // Hack 
          if (error instanceof AxiosError && 
              error.response?.data.detail === "invalid request: both auth code and code verifier should be non-empty" && 
              localStorage.getItem('access_token') &&
              provider === 'google'
            ) {
            router.push('/dashboard');
          } else {
            router.push('/');
          }
        }
      } else {
        router.push('/');
      }
    };

    handleCallback();
  }, [searchParams, router, toast]);

  return <div>Processing authentication...</div>;
}

后端通过FastAPI路由处理回调请求:

# auth.py
from fastapi import APIRouter, HTTPException, Depends
from supabase import create_client, Client
import os
from api.utils.auth_middleware import get_current_user
from pydantic import BaseModel

router = APIRouter()

url = os.environ.get("SUPABASE_URL")
key = os.environ.get("SUPABASE_SERVICE_KEY")
supabase: Client = create_client(url, key)

class CallbackInput(BaseModel):
    code: str

@router.post("/api/auth/callback/{provider}")
async def auth_callback(provider: str, input: CallbackInput):
    try:
        response = supabase.auth.exchange_code_for_session({"auth_code": input.code})
        
        if response.user:
            return {"user": response.user, "session": response.session}
        else:
            raise HTTPException(status_code=400, detail="Failed to exchange code for session")
    except Exception as e:
        print(f"Error in auth_callback: {str(e)}")
        raise HTTPException(status_code=400, detail=str(e))

问题现象

经调试确认以下事实:

  1. 已成功从URL参数中获取到code;
  2. 后端成功调用exchange_code_for_session并返回有效响应;
  3. 在Supabase的gotrue_client.py中,exchange_code_for_session执行时code_verifier存在于存储中:
# gotrue_client.py (excerpt)
def exchange_code_for_session(self, params: CodeExchangeParams):
    code_verifier = params.get("code_verifier") or self._storage.get_item(
        f"{self._storage_key}-code-verifier"
    )
    response = self._request(
        "POST",
        "token?grant_type=pkce",
        body={
            "auth_code": params.get("auth_code"),
            "code_verifier": code_verifier,
        },
        redirect_to=params.get("redirect_to"),
        xform=parse_auth_response,
    )
    self._storage.remove_item(f"{self._storage_key}-code-verifier")
    if response.session:
        self._save_session(response.session)
        self._notify_all_subscribers("SIGNED_IN", response.session)
    return response
  1. access_token已成功存入localStorage。

但调用router.push('/dashboard')后,立即抛出错误:

AxiosError: invalid request: both auth code and code verifier should be non-empty

临时解决方案

实现了一个临时绕过方案,但存在安全隐患,且控制台会输出POST http://localhost:8000/api/auth/callback/google 400 (Bad Request)错误:

catch (error) {
  if (error instanceof AxiosError && 
      error.response?.data.detail === "invalid request: both auth code and code verifier should be non-empty" && 
      localStorage.getItem('access_token') &&
      provider === 'google'
    ) {
    router.push('/dashboard');
  } else {
    router.push('/');
  }
}

已尝试的排查步骤

  • 确认exchange_code_for_session调用时code_verifier存在,且回调完成后已被移除;
  • 确认交换流程中auth_code和code_verifier均非空;
  • 逐行调试代码(包括exchange_code_for_session的实现),未找到错误来源。

疑问

明明auth_code和code_verifier存在且已被成功使用,为何仍会出现该错误?看起来错误发生在流程再次循环时,此时code_verifier已被销毁。希望了解问题的根本原因,而非仅依赖临时方案。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 06:05:58