How to setup Firebase App Check for iOS and macOS

29th of January 2025

What is Firebase App Check?

Firebase App Check is a service that helps you protect your app from abuse by verifying that requests originate from an app that you have configured.

How does it work?

Firebase App Check uses a token-based approach to verify that requests originate from an app that you have configured. The token is generated by the Firebase SDK and contains information about the app and the device it is running on.

How to setup for iOS using App Attest?

  1. Setup the Firebase SDK in your app like you normally do.
  2. Add the App Check SDK to your app using CocoaPods or Swift Package Manager.
  3. Setup AppDelegate to use the App Attest provider even for SwiftUI.
    • Step 1: Create a new AppDelegate class:
     class AppDelegate: NSObject, NSApplicationDelegate {
         func applicationDidFinishLaunching(_ notification: Notification) {
         }
     }
    
    • Step 2: In your SwiftUI app, set the AppDelegate as the delegate:
    @main
    struct YourApp: App {
        @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
        var body: some Scene {
            WindowGroup {
                ContentView()
            }
        }
    }
    
  4. Configure the App Check SDK to use the App Attest provider.
    • Step 1: In your AppDelegate, add the following code:
      import FirebaseAppCheck
      
      class YourAppCheckProviderFactory: NSObject, AppCheckProviderFactory {
         func createProvider(with app: FirebaseApp) -> AppCheckProvider? {
             return AppAttestProvider(app: app)
         }
      }
      
    • Step 2: In your AppDelegate, set the provider factory:
      func applicationDidFinishLaunching(_ notification: Notification) {
         #if DEBUG
             let providerFactory = AppCheckDebugProviderFactory()
             AppCheck.setAppCheckProviderFactory(providerFactory)
         #else
             AppCheck.setAppCheckProviderFactory(YourAppCheckProviderFactory())
         #endif
      }
      
  5. Enable App Attest capability in the Xcode project from Signing & Capabilities.
  6. Now you can use token to send to your firebase functions.
    • Step 1: create header in your firebase function to send the token.
      • header name: x-firebase-appcheck
    • Step 2: send the token in your firebase function.
         import Foundation
         import FirebaseAppCheck
      
         final class YourAPIService {
             func apiCall() {
                 guard let url = URL(string: baseURL + "/your-endpoint") else {
                     throw APIError.invalidURL
                 }
      
                 let token = try await AppCheck.appCheck().limitedUseToken()
                 let tokenString = token.token
      
                 // Set up the request
                 var request = URLRequest(url: url)
                 request.httpMethod = "POST"
                 request.setValue("application/json", forHTTPHeaderField: "Content-Type")
                 request.setValue("Bearer \(apiKey)", forHTTPHeaderField: "Authorization") // apiKey is your normal key
                 request.setValue(tokenString, forHTTPHeaderField: "x-firebase-appcheck") // tokenString is the token you got from the App Check
                 request.httpBody = "some data".data(using: .utf8)
      
                 let (data, response) = try await URLSession.shared.data(for: request)
                 guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else {
                     throw APIError.invalidResponse
                 }
                 return data
             }
         }
      
  7. During development, you will have to register your debug token in the firebase console.
    • Step 1: In Xcode enable firebase debug mode. Firebase DebugView
    • Step 2: Search for Firebase App Check in the logs.
    • Step 3: Copy the token from the logs.
    • Step 4: In the firebase console, go to the App Check section.
    • Step 5: Click on Apps section.
    • Step 6: Hover over your app and click on 3 dots
    • Step 7: Click on Manage debug tokens
    • Step 8: Click on Add debug token
    • Step 9: Add your debug token and click on Save

How to setup firebase functions for app check using express

  1. Setup the firebase functions and express like you normally do. Setup Firebase Functions

  2. Here’s my index.ts file for the firebase functions.

     import * as functions from 'firebase-functions';
     import express from 'express';
     import * as admin from 'firebase-admin';
     import firebaseConfig from './config/firebaseConfig';
     import authenticateToken from './middleware/authMiddleware';
     import { v1Routes } from './route/routesV1';
    
     // Initialize Firebase Admin
     admin.initializeApp(firebaseConfig);
    
     // Initialize Express
     const app = express();
     const v1Router = express.Router();
    
     // Add the Bearer token authentication middleware to all routes
     app.use(authenticateToken);
    
     // Add routes
     v1Router.use('/', v1Routes);
    
     // Add the router to the main app
     app.use('/v1', v1Router);
    
     const runtimeOpts = {
         timeoutSeconds: 300,
         memory: "512MiB" as MemoryOption,
     };
    
     // Create a Firebase Cloud Function
     export const yourFunctionName = functions.https.onRequest(runtimeOpts, app);
    
  3. Setup firebase config in the config/firebaseConfig.ts file.

     import { credential } from "firebase-admin";
    
     const serviceAccount = require("../../src/config/serviceAccountKey.json");
     const firebaseConfig = {
         credential: credential.cert(serviceAccount)
     };
    
     export default firebaseConfig;
    

    Get the service account key from the firebase console. Firebase Console

  4. Setup the middleware in the middleware/authMiddleware.ts file.

     import { Request, Response, NextFunction } from 'express';
     import admin from 'firebase-admin';
    
     const AUTH_VALID_TOKEN = process.env.AUTH_SECRET_TOKEN; // This can be your jwt token or any other token
     const VALID_APP_CHECK_TOKEN = process.env.APP_CHECK_SECRET; // This is for testing in the postman, set in the .env file
    
     async function authenticateToken(req: Request, res: Response, next: NextFunction) {
         const authHeader = req.headers['authorization'];
         const token1 = authHeader && authHeader.split(' ')[1];
         const appCheckToken = req.headers['x-firebase-appcheck'];
    
         if (token1 == null) {
             console.log('No token provided');
             res.status(403).send({ message: 'Something went wrong' });
             return;
         }
    
         if (token1 !== AUTH_VALID_TOKEN) {
             console.log('Invalid token');
             res.status(403).send({ message: 'Something went wrong' });
             return;
         }
    
         if (appCheckToken == null) {
             console.log('No App Check token provided');
             res.status(401).send({ message: 'Something went wrong' });
             return;
         }
    
         if (appCheckToken === VALID_APP_CHECK_TOKEN) {
             console.log('Valid App Check testing token'); // This is for testing in the postman, set in the .env file
             return next();
         }
    
         try {
             const appCheckClaims = await admin.appCheck().verifyToken(appCheckToken as string, { consume: true });
             if (appCheckClaims.alreadyConsumed) {
                 console.log('App Check token already consumed');
                 res.status(401).send({ message: 'Something went wrong' });
                 return;
             }
             // Token is not consumed, continue
             return next();
         } catch (error) {
             console.log('Error verifying App Check token:', error);
             res.status(401).send({ message: 'Something went wrong' });
             return;
         }
     };
    
     export default authenticateToken;
    
  5. That’s it! Now you can use the app check token in your firebase functions.

How to setup app check for macOS using device check

  1. The backend is the same as the above, so you can use the same firebase functions and express setup.
  2. For device check you have to generate a p8 key from apple developer account. Device Check Guide
  3. Add the p8 key in app check > apps > your app > device check > keys
  4. You can skip the AppDelegate setup for device check from the iOS setup.
  5. Directly use the token generate logic in the api service.
  6. Also you don’t have to register the debug token in the firebase console for device check.

Hope this helps!