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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 09:15:44