1. Introduction
You can use Firebase App Check with App Attest to protect your backend services and verify that requests to Firebase services are coming from your authentic app.
It's generally recommended to onboard users gradually to the App Attest service to avoid hitting quota limits. For more information, see Apple's "Preparing to Use the App Attest Service" documentation.
The ability to release app updates incrementally using Apple's App Store Connect feature, as described in "Releasing a version update in phases," can make the App Check rollout smoother. This is a straightforward, simple solution. However, releasing an app version update in stages doesn't allow you to control the rollout or change behavior of existing, updated apps without publishing a new app version.
One way to have more control over your App Check with App Attest rollout is to use Firebase Remote Config to enable App Check with App Attest for a percentage of your app's users at a time. This may help avoid throttling from the attestation servers. Google Analytics can be used to observe the impact of the rollout on users.
What you'll learn
In this multistep codelab, you'll learn how to use Firebase Remote Config to roll out App Check for your app.
This codelab uses a Firebase project based on the DatabaseExample quickstart app and integrated with Firebase App Check, as described in the Firebase App Check for Apple Platforms codelab. The DatabaseExample quickstart app allows users to log in and add posts using the features of Firebase Realtime Database.
You can also adapt the steps in this codelab to test your own app.
Prerequisites
What you'll need
- Xcode 12.5+
- For App Attest testing:
- An Apple Developer account that allows you to create new app identifiers
- An application with an explicit App ID with App Attest capability enabled. See Register an App ID and Enable app capabilities articles if you need help with the process.
- An iOS/iPadOS device that supports App Attest
- Firebase project with:
- An iOS app configured (learn more)
- Google Analytics, Remote Config, and App Check enabled
- Access to your app's associated Firebase project, with permissions to create and manage Remote Config and to view Google Analytics
2. Create a custom attestation provider
In this step, we'll create a custom provider class to provide a token only when App Attest is enabled. Remote Config relies on a configured Firebase app instance, and the custom provider you implement in this step acts as the placeholder to finish configuration.
To complete the following steps, you'll need to add Firebase
, FirebaseRemoteConfig
, and FirebaseAnalytics
in the Frameworks, Libraries, and Embedded Content section of your app in Xcode. For an example of how to do this, refer to the Firebase App check for Apple platforms codelab.
- Create a file "MyAppCheckProvider" that is a subclass of
NSObject
conforming to theAppCheckProvider
protocol. - Include an empty
getToken()
method that you'll fill out later.
See the following example code for the custom provider class with empty getToken()
method.
// MyAppCheckProvider.swift
import Firebase
import FirebaseAnalytics
import FirebaseAppCheck
import FirebaseRemoteConfig
class MyAppCheckProvider: NSObject, AppCheckProvider {
func getToken(completion handler: @escaping (AppCheckToken?, Error?) -> Void) {}
}
To instantiate AppAttestProvider
, you will need to pass an instance of the corresponding FirebaseApp
. Create a stored property for it and accept it as an initializer parameter:
// 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) {}
}
Forward the token request to the App Attest provider
Now you have everything to forward the token request to the App Attest provider in your getToken()
method.
Note: Learn more about the getToken()
method in the FirebaseAppCheck Framework Reference.
Add the following code to your getToken()
method:
// 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)
}
The previous code checks a Remote Config AppAttestEnabled
boolean parameter (this Remote Config parameter will be created later in the codelab). If the value is false, the code fails, indicating that App Check is not rolled out on the current device. If the value is true, the code tries to obtain an App Attest provider and fails if it cannot. If these error checks are passed, the code will forward the token request to the App Attest provider.
Add Analytics events
By adding Analytics events, you get better insights into how successful the App Check rollout is. Analytics will help determine whether App Attest should be enabled for a larger audience.
Log two Analytics events: AppAttestSuccess on success, and AppAttestFailure on failure. These two Analytics events can help track the success of your App Check rollout and help you decide if a larger rollout should proceed.
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. Update the Provider Factory class
After you've implemented the logic to forward the token request to the App Attest provider and added some Analytics events, you need to update MyAppCheckProviderFactory.class
which you created in the App Check for Apple Platforms codelab. This class will target the App Check debug provider for simulators, and otherwise target your custom provider.
Edit the following code in the MyAppCheckProviderFactory
class you created in the Firebase App Check for Apple platforms codelab:
// 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
}
}
Confirm you've set the AppCheckProviderFactory
before configuring FirebaseApp
:
// DatabaseExampleApp.swift
import SwiftUI
import Firebase
import FirebaseAppCheck
@main
struct DatabaseExampleApp: App {
init() {
AppCheck.setAppCheckProviderFactory(MyAppCheckProviderFactory())
FirebaseApp.configure()
}
// ...
}
4. Add a Remote Config parameter in the Firebase console
You will now add the Remote Config parameter AppAttestEnabled to the Firebase console . Your getToken
method requires this parameter.
To create a Remote Config parameter in the Firebase console :
- Open Remote Config for your project and click Add parameter. If this is your first time using Remote Config, click Create configuration.
- In the Parameter name (key) field, enter
AppAttestEnabled
. - From the Data type drop-down, select Boolean.
- From the Default value drop-down, select false.
Before clicking Save, create a conditional value for 10% of users:
- Click Add new > Conditional value > Create new condition.
- In the Name field, enter a condition name.
- Under Applies if..., select User in random percentile, <=, and then enter 10 in the % field.
- Click Create condition.
Set the conditional value to true so that App Attest will roll out to 10% of your users.
- Set the value to true for the condition you just created.
- Click Save.
When you're done, publish the Remote Config changes.
Test the rollout on your device
To test the different Remote Config flag values on your device without modifying the app code, configure an experiment on the AppAttestEnabled parameter following the Create Firebase Remote Config Experiments with A/B Testing tutorial. The tutorial section "Validate your experiment on a test device" explains how to assign different values for your test device.
The final step is to use Google Analytics to monitor the success of your App Attest rollout.
5. Review the success of your AppCheck rollout
You can measure the success of your rollout on the Analytics Events dashboard. Watch for AppAttestSuccess and AppAttestFailure events. It may take up to 24 hours to see events in the dashboard. Alternatively, you can enabled debugging and use DebugView to see debug events more quickly.
Optionally, you can monitor the Crashlytics dashboard for increases in crash rates. For more information about adding Crashlytics to your app, see Get started with Firebase Crashlytics.
Once you see mostly AppAttestSuccess events and few AppAttestFailure events, it is a good sign that you can increase the percentage of users with App Attest enabled by modifying the condition in the Remote Config parameter AppAttestEnabled.
Optional: Leverage Google Analytics Audience
If you want to further leverage the AppAttestEnabled Analytics event, you can create an Analytics Audience to track users with AppAttestEnabled set to true.
App Attest was released with iOS 14.0. Some of your users may not be on this release, and thus not eligible for App Attest. You can log another Analytics event to track these users, then target that audience for another attestation method, such as DeviceCheck.
Optional: Use Crashlytics to monitor crashes
To better understand the stability of your app during rollout, use Firebase Crashlytics to monitor crashes and non-fatals.
6. Congratulations!
You successfully rolled out App Check with Remote Config 🎉