Use App Check to secure Navigation SDK for iOS

Firebase App Check provides protection for calls from your app to Google Maps Platform by blocking traffic that comes from sources other than your legitimate apps. It does this by requesting an evaluation of the app or device's authenticity from an attestation provider like App Attest. When you integrate your app with App Check, you add protection against malicious and unauthorized requests, which in turn protects your billing from unauthorized charges. App Check also significantly improves real-time disruption reporting in your fleet app ecosystem, thereby benefiting all the drivers who use your app. For more information, see Disruption reporting.

Why use App Check?

App Check protects two distinct areas of the Navigation SDK for iOS: the main navigation functionality and disruption reporting.

App Check helps block main Navigation SDK for iOS requests from malicious or unauthorized sources. This directly benefits you by protecting your project from billing fraud and quota exhaustion.

Disruption reporting

App Check is highly recommended if your app supports real-time disruption reporting and voting capabilities. Enabling App Check ensures your drivers are provided the most accurate routes accounting for all real-time feedback.

Why App Check matters for reporting:

  • The high trust bar for closures: Route-impacting events, such as road closures, can significantly alter routing behavior for all drivers. To protect the map from potential vandalism, spam, or inaccurate reporting, Google's moderation infrastructure for road closure disruptions relies on strong device and app integrity signals.
  • How App Check validates reports: User reports and votes sent from Navigation SDK for iOS include the App Check token, which is used by Google's backend to validate the legitimacy of the feedback, raising its trust level and allowing it to be evaluated for live map impact.
  • The impact of omitting App Check: Reports submitted without a valid App Check token are evaluated under a lower-trust model and may be processed solely as silent signals. This means that while your users can still report and vote, their reports are less likely to be visible on the map or affect other drivers' routes and will require additional validation.

Is App Check right for me?

App Check is recommended in most cases; however, App Check isn't needed or isn't supported in the following scenarios:

  • Private or experimental apps: If your app isn't publicly accessible, App Check isn't needed.
  • Compromised devices: The recommended attestation providers prevent Navigation SDK for iOS from running on untrustworthy devices, such as rooted or jailbroken phones. To support these devices, deploy a custom attestation provider.

Overview of implementation steps

At a high level, you'll follow these steps to integrate your app with App Check:

  1. Add Firebase to your app.
  2. Add the App Check library and initialize App Check.
  3. Add a token provider. This step invokes the attestation provider of your choice to verify the integrity of the device or app.
  4. Initialize the Navigation and App Check APIs.
  5. Enable debugging. This is useful during development or in continuous integration (CI) environments.
  6. Monitor your app requests before enabling enforcement. This way, you seamlessly enforce App Check without disrupting your users.

Considerations when planning an App Check integration

  • Attestation Provider Quotas: The attestation providers we recommend, DeviceCheck or App Attest, are subject to quotas and limitations set by Apple.
  • Startup Latency: In most situations, your users won't experience latency during regular use, because App Check tokens are cached on the device. The system automatically refreshes App Check tokens in the background before expiration to maintain seamless performance. However, if a valid App Check token isn't present, your app users will experience some latency on startup. For example, this latency occurs during cold starts when a cached token is expired or missing.
  • Token TTL: Time to live (TTL) determines the amount of time for which the App Check token is valid before it needs to be refreshed. You can configure this duration from 30 minutes to 7 days in the Firebase console. A duration of 1 hour is recommended as a secure baseline, but the SDK automatically attempts background refreshes at approximately half the TTL duration. For step-by-step console instructions, see the Firebase App Check documentation.

Integrate your app with App Check

Prerequisites and requirements

  • An app with the Navigation SDK for iOS version 11.0 or later installed.
  • The app's bundle ID.
  • Your Team ID from the Membership tab in your Apple Developer console.
  • If you plan to use DeviceCheck, your private key file and key ID.
  • You must be the owner of the app in the Google Cloud console.
  • Your app's project ID from the Google Cloud console.

Step 1: Add Firebase to your app

Follow the instructions in the Firebase developer documentation to add Firebase to your app. Add your GoogleService-Info.plist file, unmodified, to the root level of your project.

In your AppDelegate file, import the following modules:

Swift

import FirebaseCore
import FirebaseAppCheck
import GoogleNavigation

Objective-C

@import FirebaseCore;
@import FirebaseAppCheck;
@import GoogleNavigation;

Step 2: Add the App Check library and initialize App Check

Firebase provides instructions for each default attestation provider. These instructions show you how to set up a Firebase project and add the App Check library to your app. Follow the code samples provided to initialize App Check.

  1. Follow the Firebase instructions to add the App Check library:
  2. Initialize App Check in your AppDelegate:
    • If you are using App Attest, create an implementation of AppCheckProviderFactory and register it before calling FirebaseApp.configure():

      Swift

      class YourAppCheckProviderFactory: NSObject, AppCheckProviderFactory {
          func createProvider(with app: FirebaseApp) -> AppCheckProvider? {
              return AppAttestProvider(app: app)
          }
      }
      // In application(_:didFinishLaunchingWithOptions:)
      let providerFactory = YourAppCheckProviderFactory()
      AppCheck.setAppCheckProviderFactory(providerFactory)
      FirebaseApp.configure()

      Objective-C

      #import <FirebaseCore/FirebaseCore.h>
      #import <FirebaseAppCheck/FirebaseAppCheck.h>
      
      @interface YourAppCheckProviderFactory : NSObject <FIRAppCheckProviderFactory>
      @end
      
      @implementation YourAppCheckProviderFactory
      - (nullable id<FIRAppCheckProvider>)createProviderWithApp:(FIRApp *)app {
          return [[FIRAppAttestProvider alloc] initWithApp:app];
      }
      @end
      
      // In application:didFinishLaunchingWithOptions:
      YourAppCheckProviderFactory *providerFactory = [[YourAppCheckProviderFactory alloc] init];
      [FIRAppCheck setAppCheckProviderFactory:providerFactory];
      [FIRApp configure];
    • If you are using DeviceCheck instead, set the factory using the following:

      Swift

      AppCheck.setAppCheckProviderFactory(DeviceCheckProviderFactory())

      Objective-C

      FIRDeviceCheckProviderFactory *providerFactory = [[FIRDeviceCheckProviderFactory alloc] init];
      [FIRAppCheck setAppCheckProviderFactory:providerFactory];

Step 3: Add the token provider

Create a file called AppCheckTokenProvider (or, if you are using Objective-C, two files called AppCheckTokenProvider.h and AppCheckTokenProvider.m) at the root level of your app.

This class must conform to the GMSAppCheckTokenProvider protocol and implement the method fetchAppCheckTokenWithCompletion.

Swift

import Foundation
import FirebaseAppCheck
import GoogleNavigation

class AppCheckTokenProvider: NSObject, GMSAppCheckTokenProvider {
    func fetchAppCheckToken(completion: @escaping (String?, Error?) -> Void) {
        AppCheck.appCheck().token(forcingRefresh: false) { token, error in
            if let error = error {
                print("App Check Error: \(error)")
                completion(nil, error)
                return
            }
            guard let token = token else {
                completion(nil, NSError(domain: "AppCheck", code: -1, userInfo: [NSLocalizedDescriptionKey: "Token is nil"]))
                return
            }
            print("App Check Token: \(token.token)")
            completion(token.token, nil)
        }
    }
}

Objective-C

// AppCheckTokenProvider.h
#import <Foundation/Foundation.h>
#import <GoogleNavigation/GoogleNavigation.h>

NS_ASSUME_NONNULL_BEGIN

@interface AppCheckTokenProvider : NSObject <GMSAppCheckTokenProvider>
@end

NS_ASSUME_NONNULL_END
// AppCheckTokenProvider.m
#import "AppCheckTokenProvider.h"
#import <FirebaseAppCheck/FirebaseAppCheck.h>

@implementation AppCheckTokenProvider
- (void)fetchAppCheckTokenWithCompletion:(void (^)(NSString * _Nullable token, NSError * _Nullable error))completion {
    [[FIRAppCheck appCheck] tokenForcingRefresh:NO
                                     completion:^(FIRAppCheckToken * _Nullable token, NSError * _Nullable error) {
        if (token) {
            completion(token.token, nil);
        } else {
            completion(nil, error);
        }
    }];
}
@end

Step 4: Initialize the Navigation and App Check APIs

Initialize Navigation SDK for iOS (using GMSServices) and register your token provider instance by casting the shared opaque services handle to GMSServices:

Swift

// In application(_:didFinishLaunchingWithOptions:)
GMSServices.provideAPIKey("YOUR_API_KEY")

// Register your App Check token provider before initializing Navigation SDK for iOS
if let services = GMSServices.sharedServices() as? GMSServices {
    services.appCheckTokenProvider = AppCheckTokenProvider()
}

Objective-C

// In application:didFinishLaunchingWithOptions:
[GMSServices provideAPIKey:@"YOUR_API_KEY"];

// Register your App Check token provider before initializing Navigation SDK for iOS
((GMSServices *)[GMSServices sharedServices]).appCheckTokenProvider = [[AppCheckTokenProvider alloc] init];

Step 5: Enable debugging (optional)

After App Check is enforced for Navigation SDK for iOS, your app's features that depend on Navigation SDK for iOS won't run in a simulator or from a continuous integration (CI) environment because these environments don't qualify as valid devices. To run your app in these environments during development and testing, you need to create a debug build of your app that uses the App Check debug provider instead of a production attestation provider.

  1. Configure the debug provider factory in your AppDelegate:

    Swift

    #if targetEnvironment(simulator)
    let providerFactory = AppCheckDebugProviderFactory()
    #else
    let providerFactory = YourAppCheckProviderFactory()
    #endif
    AppCheck.setAppCheckProviderFactory(providerFactory)

    Objective-C

    #if TARGET_OS_SIMULATOR
    id<FIRAppCheckProviderFactory> providerFactory = [[FIRAppCheckDebugProviderFactory alloc] init];
    #else
    id<FIRAppCheckProviderFactory> providerFactory = [[YourAppCheckProviderFactory alloc] init];
    #endif
    [FIRAppCheck setAppCheckProviderFactory:providerFactory];
  2. Enable logging on your Xcode project, launch the app, and locate the local debug token in the console log.
  3. Copy and add this debug token to the Firebase Console. For more details, consult the Firebase App Check debug provider documentation.

Step 6: Monitor your app requests and decide on enforcement

Before enabling enforcement, monitor your app requests to make sure that you won't disrupt legitimate users.

  1. Visit the App Check metrics screen in the Firebase console to see the percentage of verified versus unverified traffic.
  2. Once you are sure that the majority of your traffic is verified and legitimate users have updated to a version of your app containing your App Check implementation, enable enforcement.
  3. Once enforcement is on, App Check will reject all traffic without a valid App Check token.