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

Node.js中无法获取Promise返回的access_token及全局使用方案

问题原因
  • 你当前导出的getAccessToken是返回Promise的函数,不是最终的token值。在app.js中直接打印ac.getAccessToken输出的是函数本身,既没有执行函数,也没有处理Promise的异步返回,自然拿不到token值。
  • firebase.js中自行调用getAccessToken().then(...)的逻辑里,返回的access_token只在当前Promise链内部生效,既没有做持久缓存,也没有对外暴露,外部模块无法读取。
  • Firebase的access_token默认有效期为1小时,仅在服务启动时获取一次就永久使用,运行一段时间后会因为token过期出现接口鉴权失败的问题,必须配套过期自动刷新逻辑。
实现方案(全局可用+自动刷新)

第一步:改造firebase.js,增加token缓存与自动刷新逻辑

替换原有firebase.js代码如下:

var {google} = require('googleapis');
var MESSAGING_SCOPE = 'https://www.googleapis.com/auth/firebase.messaging';
var SCOPES = [MESSAGING_SCOPE];

// 缓存token与过期时间
let cachedToken = null;
let tokenExpireTime = 0;

function getAccessToken() {
    return new Promise(function(resolve, reject) {
        const now = Date.now();
        // 缓存的token距离过期还有5分钟以上时,直接返回缓存值
        if (cachedToken && now < tokenExpireTime - 5 * 60 * 1000) {
            return resolve(cachedToken);
        }

        var key = require('./serviceAccountKey.json');
        var jwtClient = new google.auth.JWT(
            key.client_email,
            null,
            key.private_key,
            SCOPES,
            null
        );
        jwtClient.authorize(function(err, tokens) {
            if (err) {
                reject(err);
                return;
            }
            // 更新缓存
            cachedToken = tokens.access_token;
            tokenExpireTime = tokens.expiry_date;
            resolve(cachedToken);
        });
   });
}

module.exports = getAccessToken;

第二步:改造app.js,启动时初始化token并全局挂载

替换原有app.js代码如下,确保服务启动前先完成第一次token拉取,启动后全项目可直接调用:

const express = require("express");
const getAccessToken = require('./firebase.js');
const app = express();

const port = process.env.PORT || 1111 ;

async function startServer() {
    try {
        // 启动前先拉取初始token,校验账号配置正确性
        const initToken = await getAccessToken();
        console.log('初始access_token获取成功:', initToken);

        // 挂载到全局,全项目无需重复引入firebase.js即可调用
        global.getFirebaseAccessToken = getAccessToken;

        app.listen(port, () => {
            console.log(`server up and running on ${port}`);
        });
    } catch (err) {
        console.error('服务启动失败,access_token获取异常:', err);
        process.exit(1);
    }
}

startServer();

全局调用方式

项目内任意路由、业务文件中,不需要重复引入firebase模块,直接调用全局方法即可拿到当前有效的access_token,无需关心缓存、过期刷新逻辑:

// 任意业务文件示例
router.post('/send-firebase-push', async (req, res) => {
    try {
        // 直接获取最新有效token
        const accessToken = await global.getFirebaseAccessToken();
        // 后续将accessToken放入请求Authorization头,调用FCM接口即可
        res.send({code: 0, msg: '推送请求发送成功'})
    } catch (err) {
        res.status(500).send({code: -1, msg: err.message})
    }
})
补充说明
  • 不建议将固定token值直接存为全局变量,token过期后会自动失效,必须通过带缓存判断的方法获取,才能保证运行期间一直可用。
  • 如果不想使用global挂载,也可以在需要使用token的文件中单独引入firebase.js的getAccessToken方法,Node.js模块是单例模式,缓存的token在全项目中共享,效果和全局挂载一致。
  • Promise类型的返回值必须通过await或者.then()回调才能拿到最终结果,直接打印Promise函数本身无法获取内部resolve的值。

内容的提问来源于stack exchange,提问作者aasir khan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 04:45:38