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

使用Gmail API获取Token遇invalid_grant错误:页面刷新后失败

问题分析与解决

核心原因

OAuth 2.0的authorization_code是一次性凭证,首次用它请求Token后就会立即失效。刷新页面后你仍复用同一个code发起请求,必然会返回invalid_grant错误。

解决方案

1. 存储并复用refresh_token

首次获取到包含refresh_token的响应后,将其存入本地存储(如localStorage)。后续刷新页面时,不再使用已失效的authorization code,而是通过refresh_token请求新的access_token。

修改后的代码示例:

// 页面加载时优先检查是否存在refresh_token
const refreshToken = localStorage.getItem('gmail_refresh_token');
const client_id = 'clientid';
const client_secret = 'Clientsecret';
const tokenEndpoint = 'https://www.googleapis.com/oauth2/v4/token';
const redirectUri = 'http://localhost/12345/main';
const authScope = 'https://www.googleapis.com/auth/gmail.readonly'; // 替换为你需要的权限范围

// 用refresh_token获取新access_token
function refreshAccessToken() {
  const requestBody = `grant_type=refresh_token&refresh_token=${refreshToken}&client_id=${client_id}&client_secret=${client_secret}`;
  
  fetch(tokenEndpoint, {
    method: 'POST',
    headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
    body: requestBody
  })
  .then(res => res.json())
  .then(data => {
    console.log('新access_token:', data.access_token);
  })
  .catch(err => {
    console.error('刷新Token失败:', err);
    // 若refresh_token失效,重新引导用户授权
    window.location.href = `https://accounts.google.com/o/oauth2/v2/auth?client_id=${client_id}&redirect_uri=${redirectUri}&response_type=code&scope=${authScope}&access_type=offline`;
  });
}

// 用authorization_code首次获取Token
function getTokenWithAuthCode(code) {
  const requestBody = `grant_type=authorization_code&code=${code}&redirect_uri=${redirectUri}&client_id=${client_id}&client_secret=${client_secret}`;
  
  fetch(tokenEndpoint, {
    method: 'POST',
    headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
    body: requestBody
  })
  .then(res => res.json())
  .then(data => {
    console.log('首次获取Token:', data);
    // 存储refresh_token到本地
    if (data.refresh_token) localStorage.setItem('gmail_refresh_token', data.refresh_token);
  })
  .catch(err => console.error('首次获取Token失败:', err));
}

// 页面初始化逻辑
const code = new URLSearchParams(window.location.search).get('code');
if (refreshToken) {
  refreshAccessToken();
} else if (code) {
  getTokenWithAuthCode(code);
} else {
  // 无任何凭证,引导用户授权(需加access_type=offline才能拿到refresh_token)
  window.location.href = `https://accounts.google.com/o/oauth2/v2/auth?client_id=${client_id}&redirect_uri=${redirectUri}&response_type=code&scope=${authScope}&access_type=offline`;
}

2. 关键注意事项

  • 授权URL必须携带access_type=offline参数,否则Google不会返回refresh_token。
  • 绝对不要在前端代码中暴露client_secret:前端代码可被任意查看,会导致密钥泄露。建议将Token请求逻辑移至后端,前端仅与自有后端交互,避免直接接触client_secret。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 15:43:20