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

基于Google ID Token的React+FastAPI无状态认证续期方案咨询

React + FastAPI 无状态Google OAuth认证方案(含令牌自动续期)

核心结论

完全可行。基于Google OAuth的无状态认证不需要后端数据库,后端只需验证ID Token的合法性即可完成身份校验,非常适合企业内部应用场景。

令牌自动续期解决方案

你当前的问题在于未获取Refresh Token,导致ID Token过期后无法自动续期。通过调整前端登录配置和新增令牌刷新逻辑,可实现无缝续期,无需用户重复登录。

步骤1:调整前端登录配置,获取Refresh Token

使用@react-oauth/google库时,需在登录请求中添加access_type: 'offline'和prompt参数,触发Google返回Refresh Token:

import { GoogleLogin } from '@react-oauth/google';

<GoogleLogin
  clientId="你的Google Cloud客户端ID"
  onSuccess={(credentialResponse) => {
    const { id_token, refresh_token, expires_in } = credentialResponse;
    
    // 存储令牌与过期时间
    localStorage.setItem('id_token', id_token);
    if (refresh_token) localStorage.setItem('refresh_token', refresh_token);
    
    // 设置提前5分钟刷新的定时器
    const refreshTimer = setTimeout(refreshIdToken, (expires_in - 300) * 1000);
    localStorage.setItem('refresh_timer', refreshTimer.toString());
  }}
  onError={() => console.error('登录失败')}
  access_type="offline" // 强制返回Refresh Token
  prompt="consent" // 首次登录需用户授权,后续可改为"select_account"或"none"
/>

步骤2:实现前端令牌自动刷新逻辑

编写函数,使用Refresh Token向Google令牌端点请求新的ID Token,并重置刷新定时器:

async function refreshIdToken() {
  const refreshToken = localStorage.getItem('refresh_token');
  if (!refreshToken) return;

  try {
    const response = await fetch('https://oauth2.googleapis.com/token', {
      method: 'POST',
      headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
      body: new URLSearchParams({
        client_id: '你的Google Cloud客户端ID',
        refresh_token: refreshToken,
        grant_type: 'refresh_token',
      }),
    });

    const data = await response.json();
    if (data.id_token) {
      localStorage.setItem('id_token', data.id_token);
      
      // 清除旧定时器,设置新的刷新任务
      const oldTimer = localStorage.getItem('refresh_timer');
      if (oldTimer) clearTimeout(parseInt(oldTimer));
      const newTimer = setTimeout(refreshIdToken, (data.expires_in - 300) * 1000);
      localStorage.setItem('refresh_timer', newTimer.toString());
    } else {
      // 刷新失败,清除令牌并触发重新登录
      localStorage.removeItem('id_token');
      localStorage.removeItem('refresh_token');
      localStorage.removeItem('refresh_timer');
      // 这里可以添加登录提示逻辑
    }
  } catch (err) {
    console.error('令牌刷新失败:', err);
    // 处理刷新异常,比如提示用户重新登录
  }
}

步骤3:后端保持无状态验证逻辑

后端无需修改核心验证逻辑,依然通过Google官方库验证ID Token的合法性,同时可添加企业邮箱域名校验:

from fastapi import FastAPI, HTTPException, Depends
from fastapi.security import OAuth2PasswordBearer
from google.oauth2 import id_token
from google.auth.transport import requests

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
app = FastAPI()
CORPORATE_DOMAIN = "@你的企业域名.com"

async def validate_google_token(token: str = Depends(oauth2_scheme)):
    try:
        # 验证ID Token有效性
        user_info = id_token.verify_oauth2_token(
            token, requests.Request(), "你的Google Cloud客户端ID"
        )
        # 校验企业邮箱域名
        if not user_info["email"].endswith(CORPORATE_DOMAIN):
            raise HTTPException(status_code=403, detail="非内部账号禁止访问")
        return user_info
    except ValueError:
        raise HTTPException(status_code=401, detail="无效令牌")

@app.get("/api/protected")
async def protected_endpoint(user_info: dict = Depends(validate_google_token)):
    return {"message": "访问成功", "user_email": user_info["email"]}

关键注意事项

  • 安全防护:SPA将Refresh Token存在localStorage存在XSS风险,建议:
    • 在Google Cloud控制台限制OAuth客户端的授权域名,仅允许企业域名访问
    • 前端配置CSP策略,启用内容转义,降低XSS攻击风险
  • Refresh Token失效:Google Refresh Token可能因用户长期未使用、修改密码或撤销授权而失效,此时需引导用户重新登录
  • 无状态特性:后端不存储任何用户数据,所有身份信息均来自ID Token解码,完全符合无状态认证要求

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 05:25:00