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

Firebase FCM 401错误:send方法正常但sendToTopic调用失败

解决Firebase Admin SDK发送主题消息时的401 PROJECT_NOT_PERMITTED错误

问题描述

用Node.js搭建后端服务,调用admin.messaging().send()发送单设备消息完全正常,但调用sendToTopic发送主题消息时,出现401错误:

An error occurred when trying to authenticate to the FCM servers.
Make sure the credential used to authenticate this SDK has the proper permissions.
See https://firebase.google.com/docs/admin/setup for setup instructions.

PROJECT_NOT_PERMITTED
Error 401

示例代码如下:
示例1(正常工作的单设备消息)

// Load the AWS SDK for Node.js
var AWS = require("aws-sdk");
// Set the region
AWS.config.update({ region: "ap-northeast-2" });

var admin = require("firebase-admin");

var serviceAccount = require("../firebadeCredentialInformation.json");

admin.initializeApp({
  credential: admin.credential.cert(serviceAccount),
});

module.exports.handler = async (event) => {
  console.log(event);
  var body = JSON.parse(event.body);
  console.log(body);
  var topic;
  var topicPayload;

  try {
    //body, title, data, token
    const message = {
      data: {
        type: "VOTE",
        senderName: body.senderName,
        question: body.question,
        questionRangeKey: body.questionRangeKey,
        senderToken: body.senderToken,
        gender: body.gender,
        schoolGrade: body.schoolGrade,
        schoolName: body.schoolName,
      },
      token: body.token,
    };
    //? Make sure the message is sent
    //TODO REMOVE BELOW AWAIT TO IMPROVE SPEED
    var result = await admin.messaging().send(message);
    console.log(result);

    return {
      statusCode: 200,
      body: JSON.stringify(
        {
          message: "Sucessfully sent response",
          input: event,
        },
        null,
        2
      ),
    };
  } catch (e) {
    console.log("Error", e);
    return {
      statusCode: 500,
      body: JSON.stringify(
        {
          message: e,
          input: event,
        },
        null,
        2
      ),
    };
  }
};

示例2(报错的主题消息)

// Load the AWS SDK for Node.js
var AWS = require("aws-sdk");
// Set the region
AWS.config.update({ region: "ap-northeast-2" });

var admin = require("firebase-admin");

var serviceAccount = require("../firebadeCredentialInformation.json");

admin.initializeApp({
  credential: admin.credential.cert(serviceAccount),
});

module.exports.handler = async (event) => {
  console.log(event);
  var body = JSON.parse(event.body);
  console.log(body);
  var topic;
  var topicPayload;

  try {
    //body, title, data, token
    const message = {
      data: {
        type: "VOTE",
        senderName: body.senderName,
        question: body.question,
        questionRangeKey: body.questionRangeKey,
        senderToken: body.senderToken,
        gender: body.gender,
        schoolGrade: body.schoolGrade,
        schoolName: body.schoolName,
      },
      token: body.token,
    };
    //? Make sure the message is sent
    //TODO REMOVE BELOW AWAIT TO IMPROVE SPEED
    var result = await admin.messaging().send(message);
    console.log(result);

    if (body.schoolId != "") {
      //* Ff not missing school id
      const topicPayload = {
        data: {
          type: "TOPIC",
          senderName: body.senderName,
          question: body.question,
          questionRangeKey: body.questionRangeKey,
          senderToken: body.senderToken,
          gender: body.gender,
          schoolGrade: body.schoolGrade,
          schoolName: body.schoolName,
        },
      };
      const schoolId = body.schoolId;
      const topic = "/topics/" + schoolId;
      console.log(topic);

      //? Make sure the message is sent
      //TODO REMOVE BELOW AWAIT TO IMPROVE SPEED
      // Send a message to devices subscribed to the provided topic.
      var topicResult = await admin
        .messaging()
        .sendToTopic(topic, topicPayload);
      console.log(topicResult);
    }

    return {
      statusCode: 200,
      body: JSON.stringify(
        {
          message: "Sucessfully sent response",
          input: event,
        },
        null,
        2
      ),
    };
  } catch (e) {
    console.log("Error", e);
    return {
      statusCode: 500,
      body: JSON.stringify(
        {
          message: e,
          input: event,
        },
        null,
        2
      ),
    };
  }
};

解决方案

1. 补充服务账号的主题消息权限

发送主题消息需要比单设备消息更宽泛的权限:

  • 登录Firebase控制台,进入你的项目→项目设置→服务账号
  • 找到你正在使用的服务账号(对应本地firebadeCredentialInformation.json的账号)
  • 点击该账号的编辑角色按钮,添加Firebase Cloud Messaging Admin角色(这个角色包含发送主题消息的所有必要权限)
  • 保存后等待5-10分钟让权限生效

2. 修正主题名格式

sendToTopic方法不需要手动添加/topics/前缀,SDK会自动处理。你的代码中手动拼接了前缀,会导致主题名格式错误,这也是触发权限验证失败的可能原因:
将:

const topic = "/topics/" + schoolId;

修改为:

const topic = schoolId;

如果客户端订阅的是/topics/xxx格式的主题,SDK会自动将传入的schoolId转换为正确的主题路径,无需手动添加前缀。

3. 重新下载服务账号密钥

如果权限配置正确但仍报错,可能是本地密钥文件过期或损坏:

  • 回到Firebase控制台的服务账号页面,点击生成新的私钥,下载最新的JSON密钥文件
  • 替换项目中的firebadeCredentialInformation.json文件,重启服务后重试

内容的提问来源于stack exchange,提问作者Daniel Kang

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 12:51:18