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

Supabase+FastAPI刷新令牌报错:Invalid Refresh Token: Already Used

问题分析与解决方案

错误原因

  1. Supabase Refresh Token的一次性机制:Supabase的refresh token采用一次性使用规则,每次成功完成令牌刷新后,旧的refresh token会被立即作废,同时返回全新的refresh token。若重复使用同一个旧token发起刷新请求,就会触发Invalid Refresh Token: Already Used错误。
  2. 客户端token管理疏漏:如果Flutter客户端在获取新令牌对后,没有及时替换本地存储的旧refresh token,当access token过期后仍使用旧token刷新,必然会触发该错误。
  3. 重复请求冲突:若移动端在access token过期后多次触发/refresh请求,同一个refresh token被重复提交,也会导致此错误。

解决方法

1. 强制客户端更新refresh token

要求Flutter客户端在调用/refresh端点成功获取新令牌对后,立刻将本地存储的refresh token替换为接口返回的新token,绝对禁止继续使用旧token发起后续刷新请求。

2. 优化FastAPI端点逻辑

调整端点代码,增强错误提示的精准性,同时确保调用Supabase接口的逻辑正确:

@router.post("/refresh/", response_model=TokenResponse)
async def refresh_token(
    refresh_request: RefreshRequest,
    supabase: Client = Depends(get_supabase_client),
):
    if not refresh_request.refresh_token:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Refresh token is required",
        )

    try:
        # 确保传入的是未被使用过的最新refresh token
        response = supabase.auth.refresh_session(refresh_request.refresh_token)

        if not response.session:
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail="Invalid refresh token",
            )
        
        # 返回新令牌对,客户端必须存储新的refresh token
        return TokenResponse(
            access_token=response.session.access_token,
            refresh_token=response.session.refresh_token,
            token_type="bearer",
            expires_in=response.session.expires_in,
            expires_at=response.session.expires_at or 0,
        )
    except Exception as e:
        error_msg = str(e)
        # 根据错误类型返回精准提示
        if "Already Used" in error_msg:
            detail = "Refresh token已失效,请使用最新的refresh token"
        elif "Invalid Refresh Token" in error_msg:
            detail = "无效的refresh token"
        else:
            detail = f"令牌刷新失败: {error_msg}"
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail=detail,
        )

3. 避免重复刷新请求

在Flutter客户端的刷新逻辑中添加请求防抖或锁机制,确保同一时间只有一个/refresh请求被发送,防止同一个refresh token被多次提交。

4. 独立脚本的正确测试方式

在测试脚本中,若要显式传入refresh token刷新,必须使用最新的、未被使用过的token:

import time
from supabase import create_client

SUPABASE_URL = "你的Supabase地址"
SUPABASE_KEY = "你的Supabase密钥"

supabase = create_client(SUPABASE_URL, SUPABASE_KEY)

# 登录获取初始令牌
login_response = supabase.auth.sign_in_with_password({"email": "test@example.com", "password": "your-password"})
initial_refresh_token = login_response.session.refresh_token
print(f"初始refresh token: {initial_refresh_token}")

# 等待access token过期
time.sleep(31)

# 使用未被使用过的初始token刷新(此时应该成功)
refresh_response = supabase.auth.refresh_session(initial_refresh_token)
if refresh_response.session:
    print(f"新access token: {refresh_response.session.access_token}")
    print(f"新refresh token: {refresh_response.session.refresh_token}")
else:
    print("刷新失败")

注意:如果再次使用initial_refresh_token发起刷新,会触发“Already Used”错误,必须使用新返回的refresh token。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 16:40:13