如何记录Google Apps Script中apps-script-oauth2的token交换错误日志
问题概述
使用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

