Node.js服务端携带AppCheck令牌请求Cloud Firestore实现方案
Node.js中间层透传App Check令牌访问Firestore的可行方案
核心前提:放弃使用Admin SDK直接执行用户态Firestore CRUD操作——Admin SDK默认持有项目全量读写权限,会完全跳过App Check校验与安全规则,无法实现基于客户端身份的权限控制。
完整实现流程如下:
1. 前置校验客户端上传的凭证
中间层拿到Android客户端请求携带的App Check令牌、Firebase Auth ID令牌(如果你的安全规则依赖用户身份)后,首先用Admin SDK做合法性校验,拦截伪造、过期的无效请求,这一步仅用Admin SDK做令牌校验,不涉及Firestore读写。
const admin = require('firebase-admin'); // 仅将Admin SDK用于凭证校验,不用于Firestore操作 admin.initializeApp({ credential: admin.credential.applicationDefault() }); // 校验App Check令牌有效性 async function validateAppCheckToken(clientToken) { try { const claims = await admin.appCheck().verifyToken(clientToken); return { valid: true, claims }; } catch (err) { return { valid: false, err: err.message }; } } // 校验Auth ID令牌有效性(按需使用) async function validateAuthToken(clientIdToken) { try { const decoded = await admin.auth().verifyIdToken(clientIdToken); return { valid: true, uid: decoded.uid }; } catch (err) { return { valid: false, err: err.message }; } }
2. 初始化Firebase客户端SDK(非Admin SDK)
只有Firebase客户端SDK会默认走App Check校验、安全规则校验流程,Node.js服务端可以直接集成官方JS客户端SDK,注意初始化时使用项目公开的Web端配置,不要使用服务账号凭证。
const { initializeApp } = require('firebase/app'); const { getFirestore } = require('firebase/firestore'); const { initializeAppCheck, CustomProvider } = require('firebase/app-check'); const { getAuth, signInWithCustomToken } = require('firebase/auth'); // 替换为你项目的Web端Firebase公开配置 const firebaseClientConfig = { apiKey: "项目Web API Key", authDomain: "项目Auth域名", projectId: "项目ID", storageBucket: "项目存储桶地址", messagingSenderId: "项目消息发送者ID", appId: "项目Web端AppID" }; // 每个请求单独初始化客户端实例,避免身份、令牌串用 async function initRequestScopedClient(validAppCheckToken, uid) { const app = initializeApp(firebaseClientConfig); const db = getFirestore(app); const auth = getAuth(app); // 注入校验通过的客户端App Check令牌 const appCheckProvider = new CustomProvider({ getToken: () => ({ token: validAppCheckToken, expireTimeMillis: Date.now() + 3600 * 1000 // 与令牌实际过期时间对齐即可 }) }); initializeAppCheck(app, { provider: appCheckProvider, isTokenAutoRefreshEnabled: false }); // 如果需要用户身份,生成短期自定义令牌登录 if (uid) { const customToken = await admin.auth().createCustomToken(uid); await signInWithCustomToken(auth, customToken); } return { db, auth, app }; }
3. 单请求处理流程
每一个来自Android客户端的请求,按以下流程处理即可保证Firestore请求正常通过校验:
- 从请求头提取客户端上传的App Check令牌、Auth ID令牌
- 校验App Check令牌有效性,校验失败直接返回403拒绝
- 按需校验Auth ID令牌有效性,校验失败直接返回403拒绝
- 基于校验通过的凭证初始化当前请求专属的Firebase客户端实例,拿到db对象
- 执行中间层需要的额外业务逻辑,通过拿到的db对象执行Firestore CRUD操作
- 操作完成后销毁当前请求的客户端实例,避免内存泄漏
关键注意事项
- 禁止全局复用Firebase客户端实例处理不同用户的请求,必须按请求维度初始化,避免令牌、身份串用导致越权
- 所有客户端上传的令牌必须先经过Admin SDK校验,禁止直接透传未校验的令牌,防止伪造请求绕过校验
- 生成的自定义令牌、缓存的App Check令牌必须设置最短有效期,降低凭证泄露风险
- 该方案下Firestore请求的校验逻辑和Android客户端直连完全一致,不需要修改现有安全规则,规则中
request.app、request.auth字段均可正常读取 - 不要在客户端SDK中配置服务账号凭证,Web端Firebase配置本身属于公开信息,不存在安全风险
内容的提问来源于stack exchange,提问作者Madhavan Swami
相关产品推荐
相关产品推荐

