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

如何通过google.accounts.oauth2 TokenClient获取Refresh Token

解决方法

要通过tokenClient获取Refresh Token,你需要满足Google OAuth2的核心配置要求,同时注意前端存储Refresh Token的安全风险。以下是具体调整:

1. 调整tokenClient配置获取Refresh Token

在初始化tokenClient时,必须添加access_type: 'offline'参数,这是触发Google返回Refresh Token的必要条件。同时保留prompt: 'consent'确保用户授权流程中包含离线访问权限。

调整后的JavaScript代码:

const start = async () => {
  await gapi.client.init({
    apiKey: API_KEY,
    discoveryDocs: ['https://www.googleapis.com/discovery/v1/apis/calendar/v3/rest'],
  });

  let tokenClient = google.accounts.oauth2.initTokenClient({
    client_id: CLIENT_ID,
    scope: SCOPES,
    access_type: 'offline', // 关键:启用离线访问,触发Refresh Token返回
    prompt: 'consent', // 强制用户重新授权,确保离线权限被授予
    callback: '', // 后续定义回调
  });

  tokenClient.callback = async (resp) => {
    if (resp.error !== undefined) {
      throw resp;
    }
    // 此时resp中会包含refresh_token(仅首次授权或重新授权时返回)
    console.log('resp', resp);
    // 注意:不要在前端长期存储refresh_token,应立即发送到服务器端安全存储
    await fetch('/your-server-token-save-endpoint', {
      method: 'POST',
      headers: {'Content-Type': 'application/json'},
      body: JSON.stringify({ refreshToken: resp.refresh_token })
    });
  };

  tokenClient.requestAccessToken();
};

gapi.load('client', start);

2. 关键注意事项

  • Refresh Token仅返回一次:Google仅在用户首次授权或**强制重新授权(prompt=consent)**时返回Refresh Token。如果用户之前已授权过相同scope且未包含离线权限,需要触发重新授权流程才能获取。
  • 前端存储风险:Refresh Token具有长期访问权限,前端存储容易被窃取,因此获取后应立即发送到服务器端安全存储,用于后续后台任务调用。

3. 推荐方案:授权码流程(更安全)

由于你的需求是服务器端执行后台任务,更安全的做法是使用授权码流程(带PKCE):前端获取授权码,服务器端用授权码交换Access Token和Refresh Token,全程不在前端暴露敏感凭证。

前端代码(替换原tokenClient实现)

const start = async () => {
  await gapi.client.init({
    apiKey: API_KEY,
    discoveryDocs: ['https://www.googleapis.com/discovery/v1/apis/calendar/v3/rest'],
  });

  const codeClient = google.accounts.oauth2.initCodeClient({
    client_id: CLIENT_ID,
    scope: SCOPES,
    access_type: 'offline',
    prompt: 'consent',
    callback: async (response) => {
      if (response.error) throw response.error;
      // 将授权码发送到服务器端交换Token
      await fetch('/server/token-exchange', {
        method: 'POST',
        headers: {'Content-Type': 'application/json'},
        body: JSON.stringify({ code: response.code })
      });
    }
  });

  codeClient.requestCode();
};

gapi.load('client', start);

服务器端交换逻辑(示例Node.js)

const axios = require('axios');

async function exchangeCodeForTokens(code) {
  const response = await axios.post('https://oauth2.googleapis.com/token', {
    client_id: CLIENT_ID,
    client_secret: CLIENT_SECRET, // 仅服务器端使用,绝对不能暴露在前端
    code: code,
    grant_type: 'authorization_code',
    redirect_uri: 'postmessage' // SPA场景固定值,需在Google控制台配置
  });

  // 获取并将refresh_token安全存储到数据库
  const { access_token, refresh_token, expires_in } = response.data;
}

额外配置要求

  • 在Google Cloud控制台中,确保你的客户端ID是Web应用类型,且正确配置了JavaScript来源和重定向URI(SPA场景重定向URI可设为postmessage)。
  • 如果用户之前已授权过,需要清除Google账户的对应授权记录(或使用prompt: 'select_account consent'强制重新授权),才能再次获取Refresh Token。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 08:50:57