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

如何记录Google Apps Script中apps-script-oauth2的token交换错误日志

Google Apps Script OAuth2.0 token交换payload日志查看方案

问题概述

使用apps-script-oauth2库实现OAuth2.0流程时,在授权码交换token阶段遇到错误:

Error: Error retrieving token: invalid_grant (line 605, file "Service")
at Service_.getTokenFromResponse_(Service:605:11)
at Service_.fetchToken_(Service:580:15)
at Service_.handleCallback(Service:463:20)
at authCallback(Code:108:31)

怀疑问题与code_verifier/code_challenge相关,已确认初始授权URL的code_challenge生成正常,但需要查看token交换请求的payload,确认code_verifier是否被正确包含。

查看token交换payload的可行方法

方法1:修改库代码添加日志(推荐)

由于官方库无法直接修改,需将apps-script-oauth2库的代码复制到自己的脚本项目中,找到Service.js里的fetchToken_方法,在发送token请求前添加日志:

// 在fetchToken_方法构建完payload后、发送请求前插入日志代码
Logger.log('Token Exchange Payload: ' + JSON.stringify(payload));

// 原库中的请求发送代码
var response = UrlFetchApp.fetch(this.tokenUrl_, {
  method: 'post',
  headers: this.tokenHeaders_,
  payload: payload,
  muteHttpExceptions: true
});

修改后替换原库引用,运行流程即可在日志中查看payload内容。

方法2:包装UrlFetchApp拦截请求

无需修改库代码,通过包装UrlFetchApp.fetch来捕获token请求的payload:

// 定义包装函数,仅拦截token URL的请求
function wrapUrlFetch() {
  const originalFetch = UrlFetchApp.fetch;
  UrlFetchApp.fetch = function(url, params) {
    if (url === TOKEN_URL) {
      Logger.log('Token Request URL: ' + url);
      Logger.log('Token Request Payload: ' + JSON.stringify(params.payload));
    }
    return originalFetch.apply(this, arguments);
  };
}

// 修改authCallback函数,先执行包装
function authCallback(request) {
   wrapUrlFetch();
   var service = getService_();
   Logger.log('AuthCallback Request: ' + JSON.stringify(request));
   var isAuthorized = service.handleCallback(request);
   if (isAuthorized) {
     Logger.log('Authorization successful');
     return HtmlService.createHtmlOutput('Success! You can close this tab.');
   } else {
     Logger.log('Authorization denied');
     return HtmlService.createHtmlOutput('Denied. You can close this tab');
   }
}

脚本单次运行时会临时替换fetch方法,运行后环境自动重置,不会影响其他功能。

方法3:验证存储的code_verifier

generateCodeVerifier()会将code_verifier存入用户属性,可直接查看是否存在:

function checkStoredCodeVerifier() {
  const props = PropertiesService.getUserProperties();
  // 这里的"mp"是创建服务时的名称(OAuth2.createService('mp'))
  Logger.log('Stored code_verifier: ' + props.getProperty('oauth2.mp.codeVerifier'));
}

执行该函数即可确认code_verifier是否被正确存储,为后续排查提供依据。

额外排查建议

除了查看payload,还可以从以下方向排查invalid_grant错误:

  • 确认授权码未过期(通常授权码有效期仅5-10分钟)
  • 检查回调URL与OAuth应用后台配置的完全一致
  • 验证client_id、client_secret的正确性
  • 确保code_verifier与授权阶段生成的code_challenge匹配(库默认用S256算法)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 15:16:08