Firebase App Check Web SDK API 全解析:@firebase/app-check 的函数、类与接口详解

原创2026-10-15 12:43:191,627 阅读
文章标签:开发工具后端

Firebase App Check Web SDK API 全解析:@firebase/app-check 的函数、类与接口详解

导读

本文以 Firebase JS SDK 中 @firebase/app-check 模块的官方 API 参考文档为主体,系统梳理 Firebase App Check Web SDK 的完整公开 API 面:包括 initializeAppCheck()、getToken()、getLimitedUseToken()、onTokenChanged()、setTokenAutoRefreshEnabled() 五个核心函数,ReCaptchaV3Provider、ReCaptchaEnterpriseProvider、CustomProvider 三个 Provider 类,以及 AppCheck、AppCheckOptions、AppCheckToken 等接口与类型。在继承全部 API 签名与参数说明的基础上,结合仓库源码(packages/app-check/src)深入讲解 token 获取与缓存、主动刷新、指数退避、调试模式等底层机制,帮助你在 Web 应用中正确接入 App Check 并理解其运行原理。

App Check Web SDK 概览与使用前提

@firebase/app-check 是 Firebase JS SDK 的 App Check 组件,官方定位是"Firebase App Check Web SDK"。App Check 用于保护你的 Firebase 后端资源(如 Cloud Storage、Cloud Functions、Realtime Database)不被未授权客户端滥用:它通过设备与环境的证明(attestation)机制生成 App Check token,帮助 Firebase 服务识别请求是否来自真实可信的应用实例。

Node.js 环境的重要限制

根据 API 文档(docs-devsite/app-check.md)与源码入口注释(packages/app-check/src/index.ts)的明确说明:

Firebase App Check 在 Node.js 环境中不适用于 ReCaptchaV3Provider 或 ReCaptchaEnterpriseProvider,但如果使用 CustomProvider 并自行编写证明(attestation)方法,则可以在 Node.js 中使用。

原因是 reCAPTCHA 依赖浏览器环境加载 https://www.google.com/recaptcha/api.js 脚本并执行 DOM 渲染(见 packages/app-check/src/recaptcha.ts),Node.js 中没有对应环境;而 CustomProvider 完全由你提供 getToken 函数,不依赖浏览器 API,因此可以在服务端场景使用。

函数详解:App Check 的五个公开 API

@firebase/app-check 对外暴露的全部函数分为两组:function(app, ...) 即初始化函数 initializeAppCheck();function(appCheckInstance, ...) 即基于 App Check 实例操作的四个函数。所有函数的 TypeScript 声明实现在 packages/app-check/src/api.ts。

initializeAppCheck(app, options):激活 App Check

为给定的 FirebaseApp 激活 App Check,每个 app 只能调用一次。

签名:

export declare function initializeAppCheck(
  app: FirebaseApp | undefined,
  options: AppCheckOptions
): AppCheck;

参数:

参数 类型 说明
app FirebaseApp | undefined 要为其激活 App Check 的 FirebaseApp 实例
options AppCheckOptions App Check 初始化选项

返回值: AppCheck

源码行为(packages/app-check/src/api.ts):

  • app 参数默认为 getApp(),即未传时可自动使用默认 Firebase 实例;
  • 通过 _getProvider(app, 'app-check') 获取组件 Provider;
  • 首次调用时触发 initializeDebugMode(app)(仅执行一次);
  • 若处于调试模式,会异步打印调试 token 到控制台;
  • 重复初始化校验:若 provider.isInitialized() 为真,且传入的 isTokenAutoRefreshEnabled 与 provider.isEqual() 判断的 Provider 均与首次初始化相同,则直接返回已有实例;否则抛出 appCheck/already-initialized 错误。从源码看,重复调用必须"等价"才能复用实例。

内部 _activate()(packages/app-check/src/api.ts)完成真正的激活工作:创建状态对象、从 IndexedDB 读取缓存 token、设置 isTokenAutoRefreshEnabled(与全局 automaticDataCollectionEnabled 做与运算)并调用 state.provider.initialize(app)。

注意:若 isTokenAutoRefreshEnabled 为 true 但初始化 FirebaseApp 时将 automaticDataCollectionEnabled 设为 false,SDK 会发出 logger.warn 警告提示自动刷新被阻止,但不会抛错。

getToken(appCheckInstance, forceRefresh):获取当前 App Check token

获取当前 App Check token。若 forceRefresh 为 false,函数先检查内存中的有效 token,再检查本地持久化存储(IndexedDB);若未找到,或 forceRefresh 为 true,则向 App Check 端点请求新 token;该请求会挂接到当前已在进行中的请求(in-flight request)之上,避免并发重复请求。

签名:

export declare function getToken(
  appCheckInstance: AppCheck,
  forceRefresh?: boolean
): Promise<AppCheckTokenResult>;

参数:

参数 类型 说明
appCheckInstance AppCheck App Check 服务实例
forceRefresh boolean 为 true 时总是尝试获取新 token;为 false 时若存储中存在缓存 token 则直接使用

返回值: Promise<AppCheckTokenResult>

源码行为(packages/app-check/src/api.ts): 公开的 getToken 是对内部 getToken 的薄封装,内部实现位于 packages/app-check/src/internal-api.ts,完整流程见下文"token 获取与缓存机制"一节。若结果携带 error 或 internalError 字段,公开 API 会直接抛出该错误。

getLimitedUseToken(appCheckInstance):获取限用 token

请求一个 Firebase App Check token。此方法仅在你需要向非 Firebase 后端授权请求时使用。它返回限用(limited-use)token,适用于启用了 Replay Protection(重放保护)的非 Firebase 后端端点。此方法不会影响 getToken() 的 token 生成行为。

签名:

export declare function getLimitedUseToken(
  appCheckInstance: AppCheck
): Promise<AppCheckTokenResult>;

参数:

参数 类型 说明
appCheckInstance AppCheck App Check 服务实例

返回值: Promise<AppCheckTokenResult> —— 限用 token。

源码行为(packages/app-check/src/api.ts): 直接委托内部实现 getLimitedUseTokenInternal(packages/app-check/src/internal-api.ts)。与普通 getToken 的关键区别是:跳过所有缓存与状态逻辑,直接调用底层 Provider 的 getToken(true)(isLimitedUse = true),在请求体中携带 limited_use: true(见 packages/app-check/src/providers.ts 与 packages/app-check/src/client.ts 的请求构造)。

onTokenChanged(appCheckInstance, observer / onNext, onError, onCompletion):监听 token 变化

注册 token 状态变化监听器。同一时间可以为一个或多个 App Check 实例注册多个监听器;每当该实例关联的当前 token 发生变化时,监听器会在 UI 线程被回调。

该函数有两个重载:

重载一(Observer 对象形式):

export declare function onTokenChanged(
  appCheckInstance: AppCheck,
  observer: PartialObserver<AppCheckTokenResult>
): Unsubscribe;

重载二(回调函数形式):

export declare function onTokenChanged(
  appCheckInstance: AppCheck,
  onNext: (tokenResult: AppCheckTokenResult) => void,
  onError?: (error: Error) => void,
  onCompletion?: () => void
): Unsubscribe;

参数:

参数 类型 说明
appCheckInstance AppCheck App Check 服务实例
observer PartialObserver <AppCheckTokenResult> 含 next、error、complete 属性的对象。next 在 token 变化时携带 AppCheckTokenResult 被调用;error 可选,在监听器(next 函数)抛出错误时被调用;complete 未被使用,因为 token 流是永不结束的
onNext (tokenResult: AppCheckTokenResult) => void token 变化时调用,参数为 AppCheckTokenResult
onError (error: Error) => void 可选。监听器(onNext 函数)抛出错误时被调用
onCompletion () => void 当前未使用,因为 token 流永不结束

返回值: Unsubscribe —— 一个用于取消该监听器的函数。

源码行为(packages/app-check/src/api.ts): 实现会将两种调用形式统一归一化为 next / error 回调(Observer 的 next / error 方法会被 bind 到 observer 对象上),然后调用内部 addTokenListener(appCheckInstance, ListenerType.EXTERNAL, nextFn, errorFn);返回的取消函数则调用 removeTokenListener(appCheckInstance.app, nextFn)。注意 onCompletion 在注释中明确说明"永远不会被调用,仅为了与 JS API 中 Observer 模式保持一致而保留"。

内部 addTokenListener(packages/app-check/src/internal-api.ts)会:将观察者追加到状态数组;若内存中已有有效 token,则异步立即回调一次;待 cachedTokenPromise 解析后启动 token 刷新器(Refresher)。而 removeTokenListener(packages/app-check/src/internal-api.ts)在监听器全部移除后会停止刷新器。

setTokenAutoRefreshEnabled(appCheckInstance, isTokenAutoRefreshEnabled):开关自动刷新

设置 App Check 是否按需自动刷新 token。

签名:

export declare function setTokenAutoRefreshEnabled(
  appCheckInstance: AppCheck,
  isTokenAutoRefreshEnabled: boolean
): void;

参数:

参数 类型 说明
appCheckInstance AppCheck App Check 服务实例
isTokenAutoRefreshEnabled boolean 为 true 时 SDK 按需自动刷新 App Check token。此值会覆盖 initializeAppCheck() 时设置的任何值

返回值: void

源码行为(packages/app-check/src/api.ts): 若状态中已存在 token 刷新器(state.tokenRefresher),为 true 时调用 refresher.start(),为 false 时调用 refresher.stop();同时更新状态字段 isTokenAutoRefreshEnabled。由于内部刷新器的启动条件同时要求 state.isTokenAutoRefreshEnabled 为 true,该函数是运行时动态切换自动刷新的唯一入口。

类详解:三种 App Check Provider

三个 Provider 类的共同点是都实现了内部 AppCheckProvider 接口(packages/app-check/src/types.ts),该接口要求实现 getToken(isLimitedUse?) 与 initialize(app) 两个方法。完整实现位于 packages/app-check/src/providers.ts。

ReCaptchaV3Provider:reCAPTCHA V3 Provider

能够获取 reCAPTCHA V3 token 并将其兑换为 App Check token 的 Provider。

签名:

export declare class ReCaptchaV3Provider implements AppCheckProvider

构造函数:

constructor(_siteKey: string);
  • _siteKey:ReCAPTCHA V3 的 site key(字符串)。

源码要点(packages/app-check/src/providers.ts):

  • initialize(app) 中保存 app 引用、获取 heartbeat 服务 Provider,并调用 initializeRecaptchaV3(app, this._siteKey) 加载 reCAPTCHA V3 脚本并渲染 widget(初始化失败被静默吞掉);
  • getToken() 先检查节流状态,然后通过 getReCAPTCHAToken 获取 attestation token(失败抛 appCheck/recaptcha-error),再通过 getExchangeRecaptchaV3TokenRequest 构造兑换请求(packages/app-check/src/client.ts)调用 exchangeToken;
  • 兑换端点请求体为 { recaptcha_v3_token: <token> },URL 形如 ${BASE_ENDPOINT}/projects/${projectId}/apps/${appId}:exchangeRecaptchaV3Token?key=${apiKey};
  • isEqual() 仅当另一 Provider 同为 ReCaptchaV3Provider 且 siteKey 相同时返回 true,这是 initializeAppCheck() 判断"等价重复初始化"的依据。

ReCaptchaEnterpriseProvider:reCAPTCHA Enterprise Provider

能够获取 reCAPTCHA Enterprise token 并将其兑换为 App Check token 的 Provider。

签名:

export declare class ReCaptchaEnterpriseProvider implements AppCheckProvider

构造函数:

constructor(_siteKey: string);
  • _siteKey:reCAPTCHA Enterprise 的 score-based site key。

源码要点(packages/app-check/src/providers.ts): 与 ReCaptchaV3Provider 结构完全对称:初始化时加载 https://www.google.com/recaptcha/enterprise.js 脚本(packages/app-check/src/recaptcha.ts),兑换请求体为 { recaptcha_enterprise_token: <token> }(packages/app-check/src/client.ts),同样具备节流与 isEqual 逻辑。

CustomProvider:自定义 Provider

自定义 Provider 类,允许你完全自行实现证明(attestation)流程,也是 Node.js 环境下使用 App Check 的唯一途径。

签名:

export declare class CustomProvider implements AppCheckProvider

构造函数:

constructor(_customProviderOptions: CustomProviderOptions);

源码要点(packages/app-check/src/providers.ts):

  • getToken() 调用你提供的 _customProviderOptions.getToken() 拿到 AppCheckToken,然后尝试从 token 中提取 JWT iat(issued-at)字段(JWT 时间戳以秒为单位):若 iat 存在、小于当前时间且大于 0,则换算为毫秒作为 issuedAtTimeMillis,否则使用当前时间。该时间戳是内部主动刷新调度(见下文)的重要依据;
  • isEqual() 通过比较 getToken 函数的 toString() 字符串判断两个 CustomProvider 是否等价;
  • initialize(app) 仅保存 app 引用,不做任何网络或脚本加载操作。

接口与类型别名:理解返回结构与配置项

AppCheck 接口

Firebase App Check 服务接口。

export interface AppCheck

属性:

属性 类型 说明
app FirebaseApp 此 AppCheck 实例所关联的 FirebaseApp

AppCheckOptions 接口

App Check 初始化选项,是 initializeAppCheck() 的 options 参数类型。

export interface AppCheckOptions

属性:

属性 类型 说明
provider CustomProvider | ReCaptchaV3Provider | ReCaptchaEnterpriseProvider reCAPTCHA V3 Provider、reCAPTCHA Enterprise Provider 或自定义 Provider
isTokenAutoRefreshEnabled boolean 设为 true 时启用 App Check token 的后台自动刷新(可选)

AppCheckToken 接口

Provider 返回的 token。

export interface AppCheckToken

属性:

属性 类型 说明
token string token 字符串
expireTimeMillis number token 过期后的本地时间戳(只读)

CustomProvider 的 getToken 函数需要返回此结构。

AppCheckTokenResult 接口

getToken() 返回的结果。

export interface AppCheckTokenResult

属性:

属性 类型 说明
token string JWT 格式的 token 字符串(只读)

需要注意的是:公开的 AppCheckTokenResult(packages/app-check/src/public-types.ts)只暴露 token 字段,而内部类型 AppCheckTokenResult(packages/app-check/src/types.ts)还包含可选的 error 与 internalError 字段,供内部库(storage、functions 等)识别错误场景——当发生错误时公开 API 会直接抛出,而内部消费者则收到携带 dummy token 与错误字段的结果。

CustomProviderOptions 接口

创建 CustomProvider 时的选项。

export interface CustomProviderOptions

属性:

属性 类型 说明
getToken () => Promise<AppCheckToken> 通过自定义 Provider 服务获取 App Check token 的函数

AppCheckTokenListener 类型别名

每当 App Check token 变化时被调用的监听器。

export type AppCheckTokenListener = (token: AppCheckTokenResult) => void;

源码级原理:token 生命周期与刷新机制

token 获取与缓存流程

内部 getToken(packages/app-check/src/internal-api.ts)实现了文档所述的完整查找链:

  1. 内存检查:读取 state.token,若存在但已过期(expireTimeMillis - Date.now() <= 0,见 isValid internal-api.ts)则清空;
  2. 持久化检查:无有效内存 token 时等待 state.cachedTokenPromise(该 Promise 在激活时通过 readTokenFromStorage 从 IndexedDB 读取,见 packages/app-check/src/storage.ts);缓存 token 有效则直接返回,无效则清除持久化内容;
  3. 返回缓存:forceRefresh 为 false 且 token 有效时直接返回,不发网络请求;
  4. 调试模式分支:若处于调试模式且无缓存,用调试 token 调用 exchangeDebugToken 端点获取 token;
  5. 正常兑换:否则调用 state.provider.getToken() 请求新 token。关键设计是 in-flight 请求合并:若 state.exchangeTokenPromise 已存在(已有请求在途),后续调用直接 await 同一 Promise,从而"挂接到最近的进行中请求";请求 settle 后置空该字段;
  6. 写入存储:成功拿到新 token 后写入内存状态与 IndexedDB(internal-api.ts),并通知所有监听器。

当兑换失败时,内部实现返回 makeDummyTokenResult(internal-api.ts):token 字段填充一个 base64 编码的 { error: 'UNKNOWN_ERROR' } dummy token(formatDummyToken,internal-api.ts),错误放入 error 字段。这保证了"此函数总是 resolve"的契约,内部消费者可据此降级处理。

主动刷新(proactive refresh)与节流退避

自动刷新由 Refresher 类(packages/app-check/src/proactive-refresh.ts)驱动,其调度参数定义在 packages/app-check/src/constants.ts:

  • 成功后的下次刷新时间:issuedAtTimeMillis + (expireTimeMillis - issuedAtTimeMillis) * 0.5 + 5 分钟,即"签发时间 + 50% TTL + 5 分钟",且上限不超过 expireTimeMillis - 5 分钟(internal-api.ts);
  • 失败重试退避:首次重试等待 30 秒(RETRIAL_MIN_WAIT),每次失败翻倍,最大 16 分钟(RETRIAL_MAX_WAIT);
  • HTTP 状态码特殊处理:兑换请求返回 403(attestation 失败 / API key 错误 / 项目已删除)或 404(URL 畸形)时,setBackoff 会直接封禁重试 1 天(ONE_DAY);其他错误走指数退避(providers.ts)。处于节流期时 getToken 会抛出 appCheck/throttled 或 appCheck/initial-throttle 错误(providers.ts)。

刷新器与监听器联动:addTokenListener 之后(含内部库注册的 ListenerType.INTERNAL 监听器,见 factory.ts)会启动刷新器;当 isTokenAutoRefreshEnabled 为 true 时,initializeAppCheck 会主动注册一个内部监听器,实现"预热"——提前取好 token,避免内部库首次请求时产生延迟(api.ts)。

调试模式(Debug Mode)

调试模式由全局变量 FIREBASE_APPCHECK_DEBUG_TOKEN 触发(packages/app-check/src/debug.ts):可设为字符串(直接指定调试 token)或 true(从 IndexedDB 读取或通过 crypto.randomUUID() 生成新 token)。调试模式下 initializeAppCheck() 会在控制台打印 Firebase App Check debug token: ...,并提示通过 Firebase Console 或 CLI(firebase appcheck:debugtokens:create / delete)注册该 token(storage.ts)。

组件注册与内部服务

@firebase/app-check 通过 _registerComponent 注册两个组件(packages/app-check/src/index.ts):

  • app-check(PUBLIC):公开服务,实例化模式为 EXPLICIT,仅在 initializeAppCheck() 显式调用时创建;实例创建完成后自动初始化内部组件 app-check-internal;
  • app-check-internal:提供给其他 Firebase SDK(如 Storage、Functions)的内部接口 FirebaseAppCheckInternal(packages/app-check/src/types.ts),封装了 getToken、getLimitedUseToken、addTokenListener、removeTokenListener。

服务实现类 AppCheckService(packages/app-check/src/factory.ts)在 _delete() 时会移除全部 token 监听器。

错误码参考

SDK 的错误统一由 ErrorFactory('appCheck', 'AppCheck', ERRORS) 生成(packages/app-check/src/errors.ts),常见错误码如下:

错误码 触发场景
appCheck/already-initialized 以不同选项重复调用 initializeAppCheck()
appCheck/use-before-activation 在调用 initializeAppCheck() 之前使用 App Check
appCheck/fetch-network-error 网络连接失败
appCheck/fetch-status-error 兑换端点返回非 200 HTTP 状态
appCheck/fetch-parse-error 响应解析失败或 ttl 字段不符合 Protobuf Duration 格式
appCheck/storage-open / storage-get / storage-set IndexedDB 存储操作失败
appCheck/recaptcha-error reCAPTCHA token 获取失败
appCheck/initial-throttle / appCheck/throttled 请求被节流(含 403/404 封禁 1 天与指数退避两种情况)

实践建议与限制说明

  • 安装与引入:@firebase/app-check 的 README(packages/app-check/README.md)明确提示该包不面向直接使用,应通过官方 firebase 聚合包接入;包当前版本为 0.13.1(packages/app-check/package.json),peerDependency 为 @firebase/app。
  • Web 端标准接入:浏览器中优先使用 ReCaptchaV3Provider 或 ReCaptchaEnterpriseProvider,配合 isTokenAutoRefreshEnabled: true 让 SDK 自动维护 token;
  • Node.js 场景:必须使用 CustomProvider,自行实现后端签发 AppCheckToken(含 token 与 expireTimeMillis)的证明逻辑;
  • 非 Firebase 后端:需要重放保护时使用 getLimitedUseToken(),它绕开缓存与刷新状态、直接向端点索取一次性 token;
  • token 变更感知:需要及时感知 token 更新(例如刷新上游 SDK 的 Authorization 头)时注册 onTokenChanged 监听器,并妥善处理返回的取消函数。
登录后查看全文
firebase-js-sdk