Passport.js认证后调用GDrive API提示无有效凭证问题求助
报错核心原因
报错由两个代码逻辑问题直接导致:
fs.readFile是异步非阻塞IO操作,当前代码发起文件读取请求后,立刻同步执行listFiles(oauth2Client)和响应返回逻辑,此时文件读取尚未完成,oauth2Client.setCredentials方法根本没有执行,OAuth2客户端对象未携带任何有效凭证,触发无有效令牌的报错。- 凭证配置的字段取值错误:Passport.js完成Google OAuth授权后返回的令牌对象中,
access_token、refresh_token为独立平级字段,代码中取data.token无法拿到正确的访问令牌;同时缺失refresh_token配置会导致访问令牌(默认有效期1小时)过期后直接鉴权失败。
修复方案
1. 修正异步执行顺序
将接口调用、响应返回逻辑移到文件读取完成的回调内,确保凭证设置完成后再调用Drive API,推荐使用fs的Promise版本配合async/await避免回调嵌套问题:
// 引入fs的Promise API const fs = require('fs/promises'); app.get("/getinfo", async (req, resp) => { const oauth2Client = new google.auth.OAuth2(myClientID, myClientSecret, redirectURL); try { // 等待文件读取、解析、凭证配置全流程完成 const fileContent = await fs.readFile("token.json", "utf-8"); const tokenData = JSON.parse(fileContent); oauth2Client.setCredentials(tokenData); // 凭证配置完成后再调用文件列表接口 const files = await listFiles(oauth2Client); resp.send({ message: "获取Drive文件列表成功", data: files }); } catch (err) { resp.status(500).send(`接口调用失败: ${err.message}`); } });
2. 调整listFiles方法适配异步流程
将原有回调写法改为Promise形式,配合async/await保证时序正确:
async function listFiles(auth) { const drive = google.drive({version: 'v3', auth}); const res = await drive.files.list({ pageSize: 10, fields: 'nextPageToken, files(id, name)', }); const files = res.data.files; if (files.length) { console.log('Drive文件列表:'); files.forEach(file => console.log(`${file.name} (${file.id})`)); return files; } console.log('账号下未找到文件'); return []; }
3. 修正token存储逻辑
Passport.js的Google授权策略返回的完整令牌对象可以直接传入setCredentials方法,不需要额外包装,存储token.json时直接保存完整令牌结构即可,不要自定义token字段包装access_token,正确的token结构参考:
{ "access_token": "ya29.xxx实际访问令牌xxx", "refresh_token": "1//0gxxx实际刷新令牌xxx", "scope": "https://www.googleapis.com/auth/drive.readonly", "token_type": "Bearer", "expiry_date": 172xxxxxxxxx }
注意:如果授权后拿不到refresh_token,需要在Passport的Google策略配置中添加两个参数:
passport.use(new GoogleStrategy({ clientID: myClientID, clientSecret: myClientSecret, callbackURL: redirectURL, accessType: 'offline', // 申请离线访问权限,才会返回refresh_token prompt: 'consent' // 强制弹出授权页,解决历史授权未返回refresh_token的问题 }, async (accessToken, refreshToken, params, profile, done) => { // 直接将完整令牌对象存入token.json即可 const fullToken = { access_token: accessToken, refresh_token: refreshToken, ...params }; await fs.writeFile("token.json", JSON.stringify(fullToken)); // 后续用户信息存储、登录逻辑 } ));
内容的提问来源于stack exchange,提问作者Tushar Dugar
相关产品推荐
相关产品推荐

