運用 Firebase 遠端設定逐步推出 Firebase App Check

1. 簡介

您可以將 Firebase App Check 與 App Attest 搭配使用,藉此保護後端服務,並確認對 Firebase 服務的要求是否來自真實的應用程式。

我們一般建議逐步讓使用者開始使用 App Attest 服務,以免達到配額限制。詳情請參閱 Apple 的「準備使用 App Attest 服務」說明文件。

能夠使用 Apple 的 App Store Connect 功能逐步發布應用程式更新,請參閱「分階段發布版本更新」一節。可以讓 App Check 推出作業更順暢這項解決方案相當簡單明瞭。不過,如果是分階段發布應用程式版本更新,就無法在未發布新版應用程式的情況下,控制現有應用程式的發布或變更行為。

如要在推出 App Attest 時進一步控管 App Check,其中一種方法是使用 Firebase 遠端設定,透過 App Attest 為一部分的應用程式使用者啟用 App Check。這有助於避免認證伺服器進行節流。Google Analytics 可以用來觀察這項更新對使用者的影響。

課程內容

在本多步驟程式碼研究室中,您將瞭解如何使用 Firebase 遠端設定,為應用程式推出 App Check。

本程式碼研究室會使用以 DatabaseExample 快速入門導覽課程應用程式為基礎的 Firebase 專案,並與 Firebase App Check 整合,如 Apple App Check for Apple 平台程式碼研究室所述。DatabaseExample 快速入門導覽課程應用程式可讓使用者透過 Firebase 即時資料庫的功能登入並新增貼文。

您也可以調整本程式碼研究室中的步驟,測試自己的應用程式。

必要條件

軟硬體需求

  • Xcode 12.5 以上版本
  • 針對 App Attest 測試:
    • 可用於建立新應用程式 ID 的 Apple 開發人員帳戶
    • 具備明確應用程式 ID,且已啟用 App Attest 功能的應用程式。如需這項程序的相關協助,請參閱「註冊應用程式 ID」和「啟用應用程式功能」這兩篇文章。
    • 支援 App Attest 的 iOS/iPadOS 裝置
  • Firebase 專案:
  • 可存取與應用程式相關聯的 Firebase 專案,且具備建立和管理遠端設定的權限,以及查看 Google Analytics 的權限

2. 建立自訂認證提供者

在這個步驟中,我們會建立自訂提供者類別,以便只在啟用 App Attest 時提供權杖。遠端設定需要使用已設定的 Firebase 應用程式執行個體,而您在這個步驟中實作的自訂提供者即為完成設定的預留位置。

如要完成下列步驟,您必須在應用程式的 Xcode 中,於「Frameworks、Library and Embedded Content」部分加入 FirebaseFirebaseRemoteConfigFirebaseAnalytics如需相關操作範例,請參閱「Apple 平台適用的 Firebase App Check」程式碼研究室。

  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() 方法中,將權杖要求轉送至 App Attest 供應商。

注意:如要進一步瞭解 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)
}

先前的程式碼會檢查遠端設定 AppAttestEnabled 布林值參數 (這個遠端設定參數稍後會在程式碼研究室中建立)。如果值為 false,程式碼就會失敗,表示目前裝置尚未推出 App Check。如果值為 true,程式碼會嘗試取得 App Attest 供應商,如果無法提供,則會失敗。如果通過這些錯誤檢查,程式碼會將權杖要求轉送至 App Attest 供應商。

新增 Analytics 事件

新增 Analytics 事件,您就能深入瞭解推出 App Check 的成效。Analytics 有助於判斷是否應為更多目標對象啟用 App Attest。

記錄兩個 Analytics 事件:成功時記錄 AppAttestSuccess 和失敗時的 AppAttestFailure。這兩項 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. 更新 Provider Factory 類別

實作邏輯,將權杖要求轉送至 App Attest 供應商,並新增一些 Analytics 事件後,您需要更新在適用於 Apple 平台的 App Check 程式碼研究室中建立的 MyAppCheckProviderFactory.class。這個類別將會針對模擬工具指定 App Check 偵錯供應商,反之則會指定自訂供應商。

Apple App Check for Apple 平台程式碼研究室中建立的 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
  }
}

請先確認您已設定 AppCheckProviderFactory,再設定 FirebaseApp

// DatabaseExampleApp.swift

import SwiftUI
import Firebase
import FirebaseAppCheck

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

  // ...
}

4. 在 Firebase 控制台中新增遠端設定參數

現在請將遠端設定參數 AppAttestEnabled 新增至 Firebase 控制台。您的 getToken 方法需要這個參數。

如何在 Firebase 控制台建立遠端設定參數:

  1. 開啟專案的遠端設定,然後按一下「新增參數」。如果您是第一次使用遠端設定,請按一下「建立設定」
  2. 在「Parameter name (key)」欄位中輸入 AppAttestEnabled
  3. 從「資料類型」下拉式選單中選取「布林值」
  4. 在「Default value」(預設值) 下拉式選單中,選取「false」

在 Firebase 控制台中建立遠端設定參數

點選「儲存」前,請先為 10% 的使用者建立條件式值:

  1. 點選「新增」>條件值 >建立新條件
  2. 在「Name」欄位中輸入條件名稱。
  3. 在「Apply if...」下方,依序選取「User in 隨機百分位數」和「<=」,然後在「%」欄位中輸入 10
  4. 按一下「建立條件」

在 Firebase 控制台中定義遠端設定條件

將條件值設為 true,即可向所有使用者推出 App Attest。

  1. 針對剛建立的條件,將值設為 true
  2. 按一下 [儲存]

在 Firebase 控制台中查看遠端設定參數

完成後,請發布遠端設定變更內容。

在裝置上測試推出作業

如要在不修改應用程式程式碼的情況下測試裝置上的不同遠端設定旗標值,請按照「透過 A/B 測試建立 Firebase 遠端設定實驗」教學課程的說明,在 AppAttestEnabled 參數設定實驗。「在測試裝置上驗證實驗」教學課程一節說明如何為測試裝置指派不同的值。

最後一步是使用 Google Analytics 監控 App Attest 推出作業的成效。

5. 查看 AppCheck 推出作業的成效

您可以在 Analytics 的「事件」資訊主頁中評估推出作業的成效。留意 AppAttestSuccessAppAttestFailure 事件。事件最多可能需要 24 小時才會顯示在資訊主頁中。或者,您也可以啟用偵錯功能,並使用 DebugView 更快查看偵錯事件。

您可以選擇監控 Crashlytics 資訊主頁,掌握當機率的上升情況。如要進一步瞭解如何在應用程式中新增 Crashlytics,請參閱「開始使用 Firebase Crashlytics」。

如果您看到大部分的 AppAttestSuccess 事件和少數 AppAttestFailure 事件,建議您前往遠端設定參數 AppAttestEnabled 修改條件,藉此提高啟用 App Attest 的使用者百分比。

在 Firebase 控制台中查看 Analytics 事件

選用:運用 Google Analytics 目標對象

如要進一步運用 AppAttestEnabled Analytics 事件,您可以建立 Analytics 目標對象,藉此追蹤 AppAttestEnabled 設為 true 的使用者。

App Attest 推出 iOS 14.0 版。部分使用者可能並未使用這個版本,因此不符合使用 App Attest 的資格。您可以記錄其他 Analytics 事件來追蹤這些使用者,然後根據該目標對象設定其他認證方式,例如 DeviceCheck

選用:使用 Crashlytics 監控當機事件

如要進一步瞭解應用程式在推出期間的穩定性,請使用 Firebase Crashlytics 監控當機次數和一般錯誤。

6. 恭喜!

您已成功透過遠端設定推出 App Check 🎉?

其他資源: