通过 Node.js 通过 FCM HTTP v1 API 发送通知

Wil*_*Kru 5 node.js firebase

由于 FCM 的所有 npm 模块似乎仍然使用旧版 HTTP API,因此我尝试自己在节点上通过最新的 HTTP v1 API 实现 FCM 消息传递。

虽然文档引用了Google Client API,但要求它似乎有些过分,因为我只需要 JWT oAuth2Client,它是Google Auth 库的一部分。

到目前为止我想出的模块:

var request = require('request');
var googleAuth = require('google-auth-library');

module.exports.Sender = function(projectId, key) {

  var _projectId = projectId;
  var _key = key;
  var _tokens = {};

  var _jwtClient = new googleAuth.JWT(
    key.client_email,
    null,
    key.private_key,
    ['https://www.googleapis.com/auth/firebase.messaging'],
    null
  );

  var _getAccessToken = function() {

    return new Promise(function(resolve, reject) {
      _jwtClient.authorize(function(error, tokens) {
        if (error) {
          reject(error);
          return;
        }
        _tokens = tokens;
        resolve(tokens.access_token);
      });
    });
  };

  var _sendMessage = function(message) {

    return new Promise(function(resolve, reject) {
      request({
        method: 'POST',
        url: 'https://fcm.googleapis.com/v1/projects/' + _projectId + '/messages:send',
        headers: {
          'Content-type': 'application/json',
          'Authorization': 'Bearer ' + _tokens.access_token
        },
        body: message,
        json: true
      }, function(error, response, body) {
        if(error) reject(error);
        else {
          resolve(response);
        }
      });
    });
  };

  return {
    getAccessToken: _getAccessToken,
    sendMessage: _sendMessage
  }
}
Run Code Online (Sandbox Code Playgroud)

至于使用它:

var fcm = require('./index.js');

var projectId = 'someProjectId';
var key = require('./someServiceAccount.json');

var sender = new fcm.Sender(projectId, key);

var message = {
  validate_only: false,
  message: {
    token: 'someDeviceToken',
    android: {
      collapse_key: 'someCollapseKey',
      priority: 'HIGH',
      restricted_package_name: 'com.some.domain',
      data: {
        title: 'someTitle',
        body: 'someBody',
        sound: 'default',
        action: 'someAction'
      }
    },
    apns: {
      headers: {
        'apns-priority': '10'
      },
      payload: {
        aps: {
          alert: {
            title: 'someTitle',
            body: 'someBody'
          },
          sound: 'default',
          badge: 2
        },
        action: 'someAction'
      }
    }
  }
};


sender.getAccessToken() //TODO: should be done once in sender
.then(function(accessToken) {
  return sender.sendMessage(message);
})
.then(function(response) {
  console.log(response.body);
})
.catch(function(error) {
  console.log(error);
});
Run Code Online (Sandbox Code Playgroud)

一切都很好,花花公子;我使用cordova-plugin-firebase在我的设备上收到通知。

API 调用使用 oAuth2 进行身份验证,其中访问令牌需要定期(似乎 1 小时)刷新。文档指出:

请注意,刷新令牌的调用是幂等的。令牌过期后,会自动调用令牌刷新方法以检索更新的令牌。

我似乎无法弄清楚这是如何实现的。JWT 授权调用只给我留下一个将访问令牌作为字符串保存的通用对象。由于 JWT 客户端扩展了 oAuth2Client,我浏览了它的源代码,它似乎有一个自动刷新访问令牌的请求方法。

此外,JWT.authorize 返回的对象的刷新令牌似乎始终是“ jwt-placeholder ”。

给我留下疑问:

  • 如何获取有效的刷新令牌?

  • 我可以通过 JWT 客户端使用 oAuth2Client 方法吗,因为它不会使用任何前端交互(generateAuthUrl

  • 使用请求方法会阻塞我的代码吗?

  • 我需要某种消息队列吗?

当我继续研究和编码时,我想知道是否有人可以同时给我一些指导或有用的文档。

Wil*_*Kru 4

我实际上使用请求方法让它工作。这也消除了对请求模块的需要。如果我理解正确,oAuth2Client 应该自动刷新访问令牌,并在请求失败时进行一次重试。

我更新的发件人模块:

var googleAuth = require('google-auth-library');

module.exports.Sender = function(projectId, key) {

  var _projectId = projectId;
  var _jwtClient = new googleAuth.JWT(
    key.client_email,
    null,
    key.private_key,
    ['https://www.googleapis.com/auth/firebase.messaging'],
    null
  );

  var _init = function() {

    return new Promise(function(resolve, reject) {
      _jwtClient.authorize(function(error, tokens) {
        if (error) {
          reject(error);
          return;
        }
        resolve();
      });
    });
  };

  var _sendMessage = function(message) {

    return _jwtClient.request({
      method: 'post',
      url: 'https://fcm.googleapis.com/v1/projects/' + _projectId + '/messages:send',
      data: message
    });
  }

  return {
    init: _init,
    sendMessage: _sendMessage
  }
}
Run Code Online (Sandbox Code Playgroud)

和用法:

sender.init()
.then(function(accessToken) {
  return sender.sendMessage(message);
})
.then(function(response) {
  console.log(response.data);
})
.catch(function(error) {
  console.log(error);
});
Run Code Online (Sandbox Code Playgroud)

尽管如此,有关刷新令牌的问题仍然存在,因为 JWT 客户端会覆盖它。如何正确实施这一点?