使用 Firebase Remote Config 逐步部署 Firebase App Check

1. 简介

您可以将 Firebase App Check 与 App Attest 搭配使用,以保护后端服务并验证发送到 Firebase 服务的请求是否来自真实的应用。

通常建议逐步引导用户开始使用 App Attest 服务,以免达到配额限制。如需了解详情,请参阅 Apple 的“准备使用 App Attest 服务”文档。

分阶段发布版本更新中所述,使用 Apple 的 App Store Connect 功能增量发布应用更新有助于更顺利地推出 App Check。这是一个简单明了的解决方案。但是,如果分阶段发布应用版本更新,那么您无法在不发布新应用版本的情况下控制现有更新应用的发布或更改行为。

如需更好地控制 App Check with App Attest 的发布,一种方法是使用 Firebase Remote Config 一次为应用的一定比例的用户启用 App Check with App Attest。这有助于避免来自认证服务器的节流。您可以使用 Google Analytics 来观察分阶段发布对用户的影响。

学习内容

在此分步 Codelab 中,您将学习如何使用 Firebase Remote Config 为您的应用发布 App Check。

此 Codelab 使用的是基于 DatabaseExample 快速入门应用且与 Firebase App Check 集成的 Firebase 项目,如 “适用于 Apple 平台的 Firebase App Check”Codelab 中所述。DatabaseExample 快速入门应用允许用户使用 Firebase Realtime Database 的功能登录并添加帖子。

您还可以根据此 Codelab 中的步骤测试自己的应用。

前提条件

所需条件

  • Xcode 12.5 或更高版本
  • 对于 App Attest 测试:
    • 一个允许您创建新的应用标识符的 Apple 开发者账号
    • 具有明确应用 ID 且已启用 App Attest 功能的应用。如果您需要与流程相关的帮助,请参阅注册应用 ID启用应用功能这两篇文章。
    • 支持 App Attest 的 iOS/iPadOS 设备
  • 具有以下各项的 Firebase 项目:
  • 访问您应用的关联 Firebase 项目,有权创建和管理 Remote Config 以及查看 Google Analytics

2. 创建自定义证明提供方

在此步骤中,我们将创建一个自定义提供程序类,以仅在启用 App Attest 时提供令牌。Remote Config 依赖于已配置的 Firebase 应用实例,而您在此步骤中实现的自定义提供程序将充当占位符来完成配置。

如需完成以下步骤,您需要在 Xcode 中应用的 “框架、库和嵌入内容”部分中添加 FirebaseFirebaseRemoteConfigFirebaseAnalytics。如需查看具体操作方法的示例,请参阅“适用于 Apple 平台的 Firebase App Check”Codelab

  1. 创建一个文件“MyAppCheckProvider”,它是符合 AppCheckProvider 协议的 NSObject 的子类。
  2. 添加一个稍后将填写的空 getToken() 方法。

如需了解 getToken() 方法为空的自定义提供程序类,请参阅以下示例代码。

// MyAppCheckProvider.swift

import Firebase
import FirebaseAnalytics
import FirebaseAppCheck
import FirebaseRemoteConfig

class MyAppCheckProvider: NSObject, AppCheckProvider {
  func getToken(completion handler: @escaping (AppCheckToken?, Error?) -> Void) {}
}

如需实例化 AppAttestProvider,您需要传递相应 FirebaseApp 的实例。为其创建一个存储属性,并将其作为初始化程序参数接受:

// MyAppCheckProvider.swift

import Firebase
import FirebaseAnalytics
import FirebaseAppCheck
import FirebaseRemoteConfig

class MyAppCheckProvider: NSObject, AppCheckProvider {
  // Firebase app instance served by the provider.
  let firebaseApp: FirebaseApp

  // The App Check provider factory should pass the FirebaseApp instance.
  init(app: FirebaseApp) {
    self.firebaseApp = app
    super.init()
  }

  func getToken(completion handler: @escaping (AppCheckToken?, Error?) -> Void) {}
}

将令牌请求转发给 App Attest 提供方

现在,您已完成所有操作,可以将令牌请求转发到 getToken() 方法中的应用认证提供程序。

注意:如需详细了解 getToken() 方法,请参阅 FirebaseAppCheck 框架参考

将以下代码添加到 getToken() 方法中:

// MyAppCheckProvider.swift

import Firebase
import FirebaseAnalytics
import FirebaseAppCheck
import FirebaseRemoteConfig

class MyAppCheckProvider: NSObject, AppCheckProvider {
  // Firebase app instance served by the provider.
  let firebaseApp: FirebaseApp

  // The App Check provider factory should pass the FirebaseApp instance.
  init(app: FirebaseApp) {
    self.firebaseApp = app
    super.init()
  }

  private lazy var appAttestProvider = AppAttestProvider(app: firebaseApp)

  func getToken(completion handler: @escaping (AppCheckToken?, Error?) -> Void) {
    // Fetch App Attest flag from Remote Config
    let remoteConfig = RemoteConfig.remoteConfig(app: firebaseApp)
    remoteConfig.fetchAndActivate { remoteConfigStatus, error in
      // Get App Attest flag value
      let appAttestEnabled = remoteConfig.configValue(forKey: "AppAttestEnabled").boolValue

      guard appAttestEnabled else {
        // Skip attestation if App Attest is disabled. Another attestation
        // method like DeviceCheck may be used instead of just skipping.
        handler(nil, MyProviderError.appAttestIsDisabled)
        return
      }

      // Try to obtain an App Attest provider instance and fail if cannot
      guard let appAttestProvider = self.appAttestProvider else {
        handler(nil, MyProviderError.appAttestIsUnavailable)
        return
      }

      // If App Attest is enabled for the app instance, then forward the
      // Firebase App Check token request to the App Attest provider
      appAttestProvider.getToken(completion: handler)
    }
  }
}

enum MyProviderError: Error {
  case appAttestIsDisabled
  case appAttestIsUnavailable
  case unexpected(code: Int)
}

上述代码会检查 Remote Config AppAttestEnabled 布尔值参数(此 Remote Config 参数将在本 Codelab 的后面部分创建)。如果值为 false,则代码会失败,这表示 App Check 未在当前设备上发布。如果该值为 true,则代码会尝试获取 App Attest 提供程序,如果无法获取,则会失败。如果通过这些错误检查,代码会将令牌请求转发给应用认证提供程序。

添加 Google Analytics 事件

通过添加 Analytics 事件,您可以更好地了解 App Check 发布的成功情况。Analytics 将帮助确定是否应为更广泛的受众群体启用 App Attest。

记录两个 Google Analytics 事件:成功时记录 AppAttestSuccess,失败时记录 AppAttestFailure。这两个 Google Analytics 事件有助于跟踪 App Check 的发布效果,并帮助您确定是否应进行更大规模的发布。

func getToken(completion handler: @escaping (AppCheckToken?, Error?) -> Void) {
  // Fetch Remote Config.
  let remoteConfig = RemoteConfig.remoteConfig(app: firebaseApp)
  remoteConfig.fetchAndActivate { remoteConfigStatus, error in
    // Get App Attest flag value from Remote Config.
    let appAttestEnabled = remoteConfig.configValue(forKey: "AppAttestEnabled").boolValue

    guard appAttestEnabled else {
      // Skip attestation if App Attest is disabled. Another attestation
      // method like DeviceCheck may be used instead of just skipping.
      handler(nil, MyProviderError.appAttestIsDisabled)
      return
    }

    // Try to obtain an App Attest provider instance and fail otherwise.
    guard let appAttestProvider = self.appAttestProvider else {
      handler(nil, MyProviderError.appAttestIsUnavailable)
      return
    }

    // If App Attest is enabled for the app instance, then forward the
    // Firebase App Check token request to the App Attest provider.
    appAttestProvider.getToken { token, error in
      // Log an Analytics event to track attestation success rate.
      let appAttestEvent: String
      if (token != nil && error == nil) {
        appAttestEvent = "AppAttestSuccess"
      } else {
        appAttestEvent = "AppAttestFailure"
      }
      Analytics.logEvent(appAttestEvent, parameters: nil)

      // Pass the result to the handler
      handler(token, error)
    }
  }
}

3. 更新提供程序工厂类

实现了将令牌请求转发到 App Attest 提供程序的逻辑并添加一些 Analytics 事件后,您需要更新在“适用于 Apple 平台的 App Check”Codelab 中创建的 MyAppCheckProviderFactory.class。对于模拟器,此类将定位到 App Check 调试提供程序;对于其他设备,则会定位到您的自定义提供程序。

在您于“适用于 Apple 平台的 Firebase App Check”Codelab 中创建的 MyAppCheckProviderFactory 类中,修改以下代码:

// MyAppCheckProviderFactory.swift

import Firebase

class MyAppCheckProviderFactory: NSObject, AppCheckProviderFactory {
  func createProvider(with app: FirebaseApp) -> AppCheckProvider? {
      #if targetEnvironment(simulator)
      // App Attest is not available on simulators.
      // Use a debug provider.
      let provider = AppCheckDebugProvider(app: app)

      // Print only locally generated token to avoid a valid token leak on CI.
      print("Firebase App Check debug token: \(provider?.localDebugToken() ?? "" )")

      return provider
      #else
      if #available(iOS 14.0, *) {
        // Use your custom App Attest provider on real devices.
        return MyAppCheckProvider(app: app)
      } else {
        return DeviceCheckProvider(app: app)
      }
      #endif
  }
}

在配置 FirebaseApp 之前,请确认您已设置 AppCheckProviderFactory

// DatabaseExampleApp.swift

import SwiftUI
import Firebase
import FirebaseAppCheck

@main
struct DatabaseExampleApp: App {
  init() {
    AppCheck.setAppCheckProviderFactory(MyAppCheckProviderFactory())
    FirebaseApp.configure()
  }

  // ...
}

4. 在 Firebase 控制台中添加 Remote Config 参数

现在,您将向 Firebase 控制台添加 Remote Config 参数 AppAttestEnabled。您的 getToken 方法需要此参数。

如需在 Firebase 控制台中创建 Remote Config 参数,请执行以下操作:

  1. 打开项目的 Remote Config,然后点击添加参数。如果您是首次使用 Remote Config,请点击创建配置
  2. 参数名称(键)字段中,输入 AppAttestEnabled
  3. 数据类型下拉菜单中,选择布尔值
  4. 默认值下拉菜单中,选择 false

在 Firebase 控制台中创建 Remote Config 参数

在点击“保存”之前,为 10% 的用户创建一个条件值:

  1. 依次点击添加新内容 > 基于条件的值 > 创建新条件
  2. 名称字段中,输入条件名称。
  3. 在“适用条件”下,依次选择用户位于随机百分位范围内<=,然后在“%”字段中输入 10
  4. 点击创建条件

在 Firebase 控制台中定义 Remote Config 条件

将条件值设置为 true,以便 App Attest 向 10% 的用户发布。

  1. 将您刚刚创建的条件的值设置为 true
  2. 点击保存

在 Firebase 控制台中查看 Remote Config 参数

完成后,发布 Remote Config 更改。

在您的设备上测试发布

如需在设备上测试不同的 Remote Config 标志值,而无需修改应用代码,请按照“使用 A/B Testing 创建 Firebase Remote Config 实验”教程中的说明,针对 AppAttestEnabled 参数配置实验。教程部分“在测试设备上验证您的实验”介绍了如何为测试设备分配不同的值。

最后一步是使用 Google Analytics 监控 App Attest 发布的成功情况。

5. 查看 AppCheck 的发布是否成功

您可以在 Analytics 事件信息中心衡量发布的成功与否。留意 AppAttestSuccessAppAttestFailure 事件。事件最长可能需要 24 小时才会显示在信息中心内。或者,您也可以启用调试功能,并使用 DebugView 更快速地查看调试事件。

(可选)您可以在 Crashlytics 信息中心内监控崩溃率的增长情况。如需详细了解如何将 Crashlytics 添加到您的应用,请参阅 Firebase Crashlytics 使用入门

如果您看到的大部分是 AppAttestSuccess 事件,而少量 AppAttestFailure 事件,那么建议您修改 Remote Config 参数 AppAttestEnabled 中的条件,以提高启用 App Attest 的用户的百分比。

在 Firebase 控制台中查看 Analytics 事件

可选:利用 Google Analytics 受众群体

如果您想进一步利用 AppAttestEnabled Google Analytics 事件,可以创建 Google Analytics 受众群体,以跟踪将 AppAttestEnabled 设为 true 的用户。

App Attest 随 iOS 14.0 一起发布。您的部分用户可能未使用此版本,因此不符合使用应用验证的条件。您可以记录另一个 Analytics 事件来跟踪这些用户,然后针对该受众群体使用其他证明方法(例如 DeviceCheck)。

可选:使用 Crashlytics 监控崩溃

如需更好地了解应用在发布期间的稳定性,请使用 Firebase Crashlytics 监控崩溃和非严重错误。

6. 恭喜!

您已成功使用 Remote Config 发布了 App Check 🎉?

其他资源: