Supabase+FastAPI刷新令牌报错:Invalid Refresh Token: Already Used
问题分析与解决方案
错误原因
- Supabase Refresh Token的一次性机制:Supabase的refresh token采用一次性使用规则,每次成功完成令牌刷新后,旧的refresh token会被立即作废,同时返回全新的refresh token。若重复使用同一个旧token发起刷新请求,就会触发
Invalid Refresh Token: Already Used错误。 - 客户端token管理疏漏:如果Flutter客户端在获取新令牌对后,没有及时替换本地存储的旧refresh token,当access token过期后仍使用旧token刷新,必然会触发该错误。
- 重复请求冲突:若移动端在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
相关产品推荐
相关产品推荐

