Node.js中如何检查Google API是否已通过OAuth2授权?
如何在页面GET请求中检查Google API授权状态以控制按钮显示?
我刚接触Node.js和Google API,开发的应用里有个OAuth 2.0登录/授权按钮,用来给Google日历添加事件,这部分功能正常,能成功登录并添加事件。相关代码如下:
const oAuth2Client = new google.auth.OAuth2( CLIENT_ID, CLIENT_SECRET, REDIRECT_URL, ); exports.googleAuthCode = (req, res) => { const authUrl = oAuth2Client.generateAuthUrl({ access_type: 'offline', scope: SCOPES, }); console.log('authURL: ', authUrl) res.redirect(authUrl); } exports.googleRedirect = async (req, res) => { const code = req.query.code; const { tokens } = await oAuth2Client.getToken(code) oAuth2Client.setCredentials(tokens); req.session.tokens = tokens; res.redirect('/jobs?message=Success') }
现在遇到的问题是:我想在包含授权按钮的页面的GET请求中,检查用户是否已经完成Google API的连接/授权,未授权时显示按钮并提示登录,已授权时隐藏按钮。试了各种方法都没解决,卡了好几天,查文档也没看明白,猜测可能和验证ID token有关,但不确定具体操作方法,希望能得到建议。
补充:如果用户未授权就调用添加事件接口,系统不会触发授权流程,而是直接抛出错误 An error occurred: Error: No access, refresh token, API key or refresh handler callback is set. 并刷新页面。添加事件的代码如下:
exports.scheduleEvent = async (req, res) => { const date = req.query['date']; let events = await Events.findOne({}).sort({ _id: -1 }); events = events.events; events.forEach(async (event) => { try { const result = await calendar.events.insert({ auth: oAuth2Client, calendarId: 'primary', resource: event, }); console.log(`Event created: ${result.data.htmlLink}`); } catch (err) { console.error(`An error occurred: ${err}`); } }); res.send({ msg: "Done" }) }
解决方案
1. 在页面GET路由中检查授权状态
在渲染包含授权按钮的页面的GET接口里,从req.session读取保存的tokens,同时验证token有效性(避免access token过期但refresh token仍可用的情况):
exports.renderJobsPage = async (req, res) => { let isAuthorized = false; if (req.session.tokens) { // 用session中的tokens初始化独立客户端 const client = new google.auth.OAuth2( CLIENT_ID, CLIENT_SECRET, REDIRECT_URL, ); client.setCredentials(req.session.tokens); try { // 调用简单API验证token有效性 await client.request({ url: 'https://www.googleapis.com/oauth2/v3/userinfo' }); isAuthorized = true; // 若access token自动刷新,更新session中的tokens const refreshedTokens = client.credentials; if (refreshedTokens.access_token !== req.session.tokens.access_token) { req.session.tokens = refreshedTokens; } } catch (err) { // token无效或无法刷新,清空session delete req.session.tokens; isAuthorized = false; } } // 将授权状态传递给前端模板(以EJS为例) res.render('jobs', { isAuthorized, message: req.query.message }); }
2. 前端控制按钮显示
在页面模板中,根据isAuthorized变量控制按钮的显示逻辑:
<% if (!isAuthorized) { %> <button onclick="location.href='/google-auth'">登录Google授权</button> <p>请先授权以使用日历添加功能</p> <% } %>
3. 修复添加事件接口的问题
当前使用全局oAuth2Client会导致多用户凭证冲突,需从session获取tokens初始化独立客户端:
exports.scheduleEvent = async (req, res) => { if (!req.session.tokens) { return res.status(401).send({ msg: "未授权,请先登录Google" }); } // 初始化独立客户端 const client = new google.auth.OAuth2( CLIENT_ID, CLIENT_SECRET, REDIRECT_URL, ); client.setCredentials(req.session.tokens); const date = req.query['date']; let events = await Events.findOne({}).sort({ _id: -1 }); events = events.events; // 用for...of替代forEach,避免异步执行导致的错误处理问题 const results = []; for (const event of events) { try { const result = await calendar.events.insert({ auth: client, calendarId: 'primary', resource: event, }); results.push(result.data.htmlLink); console.log(`Event created: ${result.data.htmlLink}`); } catch (err) { console.error(`An error occurred: ${err}`); } } res.send({ msg: "Done", createdEvents: results }) }
内容的提问来源于stack exchange,提问作者acodeaday
相关产品推荐
相关产品推荐

