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

如何在Cloud Function中验证Firebase自定义令牌createCustomToken?

验证Firebase自定义令牌并生成新令牌的实现方案

工具选择:优先结合Firebase Admin SDK与JWT库

Firebase Admin SDK是官方身份管理工具,负责生成新自定义令牌和用户身份管理;验证自定义令牌的签名与解码负载需用到jsonwebtoken第三方库——因为Admin SDK暂无直接验证自定义令牌的API,而JWT库可结合谷歌公钥完成签名验证,两者搭配是最安全且适配的方案。

验证并解码自定义令牌的具体实现

以下是Cloud Function中的完整逻辑示例:

const jwt = require('jsonwebtoken');
const admin = require('firebase-admin');
const fetch = require('node-fetch');

// 缓存谷歌公钥,避免重复请求
let cachedKeys = null;
let cacheExpiry = 0;

// 获取谷歌公钥(用于验证自定义令牌签名)
async function getGooglePublicKeys() {
  const now = Date.now();
  // 缓存有效期1小时
  if (cachedKeys && now < cacheExpiry) {
    return cachedKeys;
  }
  const response = await fetch('https://www.googleapis.com/robot/v1/metadata/x509/securetoken@system.gserviceaccount.com');
  cachedKeys = await response.json();
  cacheExpiry = now + 3600000;
  return cachedKeys;
}

exports.refreshCustomToken = async (req, res) => {
  const oldToken = req.body.token;
  if (!oldToken) {
    return res.status(400).send({ message: '缺少令牌参数' });
  }

  try {
    // 解码令牌头部获取公钥ID(kid),不验证签名
    const decodedToken = jwt.decode(oldToken, { complete: true });
    if (!decodedToken || !decodedToken.header || !decodedToken.header.kid) {
      throw new Error('无效的令牌格式');
    }

    // 获取对应公钥
    const publicKeys = await getGooglePublicKeys();
    const publicKey = publicKeys[decodedToken.header.kid];
    if (!publicKey) {
      throw new Error('无法获取验证公钥');
    }

    // 验证令牌的签名、有效期及关键字段
    const payload = jwt.verify(oldToken, publicKey, {
      audience: 'https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit',
      issuer: `${admin.app().options.credential.projectId}@appspot.gserviceaccount.com`
    });

    // 提取用户UID与自定义声明(移除JWT标准字段)
    const uid = payload.uid;
    const customClaims = { ...payload };
    ['iss', 'sub', 'aud', 'iat', 'exp', 'uid'].forEach(key => delete customClaims[key]);

    // 生成新的自定义令牌
    const newToken = await admin.auth().createCustomToken(uid, customClaims);

    // 发送新令牌至用户邮箱(替换为你的邮件发送逻辑)
    await sendUserLoginEmail(payload.email, newToken);

    res.status(200).send({ message: '您的链接已过期,新链接已发送至邮箱' });
  } catch (error) {
    if (error.name === 'TokenExpiredError') {
      res.status(400).send({ message: '您的链接已过期,新链接已发送至邮箱' });
    } else {
      res.status(401).send({ message: '无效的令牌,请重新请求登录链接' });
    }
  }
};

// 示例邮件发送函数(需替换为实际实现)
async function sendUserLoginEmail(email, token) {
  // 此处集成你的邮件服务(如SendGrid、Firebase Extensions邮件功能)
  // 生成包含token的登录链接并发送
}

安全验证与最佳实践

  • 严格验证令牌字段:除签名和过期时间外,必须验证aud(固定为上述谷歌API地址)和iss(你的项目服务账号邮箱),防止伪造令牌。
  • 缓存公钥:谷歌公钥更新频率低,缓存1小时可减少请求次数,提升性能。
  • 限制令牌有效期:自定义令牌设置较短过期时间(如15分钟),降低泄露风险。
  • 防止重复刷新:可在Firebase Firestore中记录用户最近一次发送令牌的时间,限制10分钟内无法重复请求,避免邮件轰炸。
  • 日志记录:记录每次令牌刷新操作的用户UID、时间、旧令牌状态,便于后续排查安全问题。
  • 权限控制:Cloud Function需设置合适的访问权限,仅允许合法客户端调用(如通过App Check验证请求来源)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 11:09:57