使用OAuth2授权Gmail API失败:无访问/刷新令牌等配置
问题描述
我试过某Stack Overflow回答里的解决方案,但没用。也尝试撤销应用权限后重新授权,同样无效。以下是我的授权相关代码:
授权URL生成代码
export function handleAuth() { const oauth2Client = getOAuth2Client(); const url = oauth2Client.generateAuthUrl({ access_type: 'offline', scope: ['https://mail.google.com/'], }); }
授权回调代码
app.get('/oauth2callback', async (req, res) => { const query = req.query; const code = query.code as string; const { tokens } = await oauth2Client.getToken(code); oauth2Client.setCredentials({ refresh_token: tokens.refresh_token, }); });
令牌监听器代码
oauth2Client.on('tokens', async (tokens) => { if (tokens.refresh_token) { oauth2Client.setCredentials({ refresh_token: tokens.refresh_token, }); if (tokens.access_token && tokens.refresh_token) { const tokenRepo = getCustomRepository(GcpTokenRepository); await tokenRepo.create({ // 数据库存储逻辑... }); } } });
监听邮件的watch方法
async watch() { const gmail = await this.authGmail(); // 返回Promise<gmail_v1.Gmail>的方法 const res = await gmail.users.watch({ userId: 'me', requestBody: { labelIds: ['INBOX'], topicName: `topic name`, }, }); console.log('👀 Watch re-initialized!', res); }
运行watch方法时抛出错误:Error: No access, refresh token, API key or refresh handler callback is set,打印google.auth.OAuth2()返回的auth变量,发现credentials字段是空对象。请问我哪里遗漏了?
解决方案
核心问题:OAuth2客户端实例未共享/未加载存储的凭证
你的代码存在几个关键漏洞,逐个修复即可解决:
确保OAuth2客户端实例全局复用
如果getOAuth2Client()每次调用都创建新的google.auth.OAuth2实例,那么回调中设置的凭证只会存在于临时实例中,watch方法调用authGmail()时拿到的是全新无凭证的实例,必然报错。- 修复:把OAuth2客户端做成全局单例,或者在
authGmail()中从数据库读取存储的凭证并绑定到实例上。
- 修复:把OAuth2客户端做成全局单例,或者在
回调中传入完整的tokens对象
原代码只设置了refresh_token,但客户端需要完整的令牌信息才能正常工作,包括access_token和expiry_date:// 替换原回调中的setCredentials代码 oauth2Client.setCredentials(tokens);在authGmail()中加载存储的凭证
watch方法依赖的authGmail()必须确保客户端实例已加载之前存储的凭证,否则就是空的:async authGmail() { const oauth2Client = getOAuth2Client(); // 从数据库读取已保存的令牌 const storedToken = await getCustomRepository(GcpTokenRepository).findOne(/* 你的查询条件 */); if (storedToken) { oauth2Client.setCredentials({ refresh_token: storedToken.refresh_token, access_token: storedToken.access_token, expiry_date: storedToken.expiry_date }); } return google.gmail({ version: 'v1', auth: oauth2Client }); }移除令牌监听器中的冗余操作
监听器里重复调用setCredentials是多余的,getToken成功后已经完成了凭证设置,直接把令牌存储到数据库即可:oauth2Client.on('tokens', async (tokens) => { if (tokens.access_token && tokens.refresh_token) { const tokenRepo = getCustomRepository(GcpTokenRepository); await tokenRepo.create({ // 数据库存储逻辑... }); } });
内容的提问来源于stack exchange,提问作者Brace Sproul
相关产品推荐
相关产品推荐

