1. はじめに
Firebase App Check と App Attest を使用して、バックエンド サービスを保護し、Firebase サービスに対するリクエストが正規のアプリから送信されたリクエストであることを確認できます。
一般的に、割り当て上限に達するのを回避するため、App Attest サービスにユーザーを段階的にオンボーディングすることをおすすめします。詳細については、Apple の「App Attest サービスを使用する準備」のドキュメントをご覧ください。
「バージョン アップデートを段階的にリリースする」で説明されているように、Apple の App Store Connect 機能を使用してアプリのアップデートを段階的にリリースすることで、App Check のロールアウトをスムーズに行うことができます。これはシンプルでわかりやすい解決策です。ただし、アプリのバージョン アップデートを段階的にリリースしても、新しいアプリのバージョンを公開せずに、既存の更新済みアプリのロールアウトを制御したり、動作を変更したりすることはできません。
App Attest を使用した App Check のロールアウトをより細かく制御する方法の一つに、Firebase Remote Config を使用して、アプリのユーザーの一定の割合に対して App Attest を使用した App Check を一度に有効にする方法があります。これにより、構成証明サーバーからのスロットリングを回避できる可能性があります。Google アナリティクスを使用して、ロールアウトがユーザーに与える影響を観察できます。
学習内容
このマルチステップの Codelab では、Firebase Remote Config を使用してアプリの App Check をロールアウトする方法について学習します。
この Codelab では、Apple プラットフォーム向け Firebase App Check Codelab で説明されているように、DatabaseExample クイックスタート アプリに基づいて Firebase App Check と統合された Firebase プロジェクトを使用します。DatabaseExample クイックスタート アプリでは、Firebase Realtime Database の機能を使用してログインし、投稿を追加できます。
この Codelab の手順を応用して、独自のアプリをテストすることもできます。
前提条件
必要なもの
- Xcode 12.5 以降
- App Attest のテストの場合:
- 新しいアプリ ID を作成できる Apple Developer アカウント
- App Attest 機能が有効になっている明示的なアプリ ID を持つアプリケーション。このプロセスについて詳しくは、アプリ ID を登録するとアプリの機能を有効にするの記事をご覧ください。
- App Attest に対応している iOS/iPadOS デバイス
- 次のものを含む Firebase プロジェクト:
- 構成済みの iOS アプリ(詳細)
- Google アナリティクス、Remote Config、App Check が有効になっている
- アプリに関連付けられた Firebase プロジェクトへのアクセス権(Remote Config の作成と管理、Google アナリティクスを表示するための権限)
2. カスタム証明書プロバイダを作成する
このステップでは、App Attest が有効な場合にのみトークンを提供するカスタム プロバイダ クラスを作成します。Remote Config は構成済みの Firebase アプリ インスタンスに依存しており、このステップで実装するカスタム プロバイダは、構成を完了するためのプレースホルダとして機能します。
以下の手順を完了するには、Xcode のアプリの [Frameworks, Libraries, and Embedded Content] セクションに Firebase
、FirebaseRemoteConfig
、FirebaseAnalytics
を追加する必要があります。この方法の例については、Apple プラットフォーム向け Firebase App Check Codelab を参照してください。
AppCheckProvider
プロトコルに準拠するNSObject
のサブクラスである「MyAppCheckProvider」というファイルを作成します。- 後で入力する空の
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 Framework Reference をご覧ください。
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 プロバイダの取得を試み、取得できない場合は失敗します。これらのエラーチェックに合格すると、コードはトークン リクエストを App Attest プロバイダに転送します。
アナリティクス イベントを追加する
アナリティクス イベントを追加すると、App Check のロールアウトの成功度合いをより詳しく把握できます。アナリティクスは、より多くのユーザーに対して App Attest を有効にするかどうかを判断するのに役立ちます。
成功した場合は AppAttestSuccess、失敗した場合は AppAttestFailure の 2 つのアナリティクス イベントをログに記録します。この 2 つのアナリティクス イベントは、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 プロバイダに転送するロジックを実装し、アナリティクス イベントを追加したら、Apple プラットフォーム向け App Check の Codelab で作成した MyAppCheckProviderFactory.class
を更新する必要があります。このクラスは、シミュレータの場合は App Check デバッグ プロバイダをターゲットとし、それ以外の場合はカスタム プロバイダをターゲットとします。
Firebase App Check for Apple プラットフォーム 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 パラメータを追加する
次に、Remote Config パラメータ AppAttestEnabled を Firebase コンソールに追加します。getToken
メソッドにはこのパラメータが必要です。
Firebase コンソールで Remote Config パラメータを作成するには :
- プロジェクトの Remote Config を開き、[パラメータを追加] をクリックします。Remote Config を初めて使用する場合は、[構成を作成] をクリックします。
- [Parameter name (key)] フィールドに「
AppAttestEnabled
」と入力します。 - [データ型] プルダウンから [ブール値] を選択します。
- [デフォルト値] プルダウンから、[false] を選択します。
[保存] をクリックする前に、ユーザーの 10% の条件付き値を作成します。
- [新規追加] > [条件付きの値] > [新しい条件を作成] をクリックします。
- [名前] フィールドに、条件名を入力します。
- [条件] で、[ユーザーがランダムなパーセンタイルに属する]、[<=] を選択し、[%] フィールドに「10」と入力します。
- [条件を作成] をクリックします。
条件付きの値を true に設定して、App Attest をユーザーの 10% にロールアウトします。
- 作成した条件の値を true に設定します。
- [保存] をクリックします。
完了したら、Remote Config の変更を公開します。
デバイスでロールアウトをテストする
アプリコードを変更せずにデバイスでさまざまな Remote Config フラグ値をテストするには、A/B テストで Firebase Remote Config テストを作成するチュートリアルに沿って、AppAttestEnabled パラメータでテストを設定します。チュートリアル セクションの「テストデバイスでテストを検証する」では、テストデバイスに異なる値を割り当てる方法について説明しています。
最後のステップは、Google アナリティクスを使用してアプリ証明書のロールアウトの成功をモニタリングすることです。
5. AppCheck のロールアウトの成功を確認する
ロールアウトの成功度は、アナリティクス イベントのダッシュボードで測定できます。AppAttestSuccess イベントと AppAttestFailure イベントを監視します。ダッシュボードにイベントが表示されるまでに、最大で 24 時間ほどかかることがあります。また、デバッグを有効にして DebugView を使用すると、デバッグ イベントをより迅速に確認できます。
必要に応じて、Crashlytics ダッシュボードでクラッシュ率の増加をモニタリングできます。アプリに Crashlytics を追加する方法について詳しくは、Firebase Crashlytics を使ってみるをご覧ください。
AppAttestSuccess イベントがほとんどで、AppAttestFailure イベントが少ない場合は、Remote Config パラメータ AppAttestEnabled の条件を変更して、アプリ証明書を有効にしたユーザーの割合を増やすことができます。
省略可: Google アナリティクスのオーディエンスを活用する
AppAttestEnabled アナリティクス イベントをさらに活用したい場合は、AppAttestEnabled が true に設定されているユーザーをトラッキングする アナリティクス オーディエンスを作成できます。
App Attest は iOS 14.0 でリリースされました。一部のユーザーはこのリリースを利用していないため、アプリ証明書の対象外となる可能性があります。これらのユーザーをトラッキングするために別のアナリティクス イベントを記録し、そのオーディエンスを DeviceCheck などの別の証明書発行方法のターゲットにすることができます。
省略可: Crashlytics を使用してクラッシュをモニタリングする
ロールアウト中のアプリの安定性をよりよく把握するには、Firebase Crashlytics を使用してクラッシュと非致命的なエラーをモニタリングします。
6. 完了
Remote Config を使用して App Check を正常にロールアウトしました 🎉