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.
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.
AppDelegate
to use the App Attest provider even for SwiftUI.AppDelegate
class: class AppDelegate: NSObject, NSApplicationDelegate {
func applicationDidFinishLaunching(_ notification: Notification) {
}
}
AppDelegate
as the delegate:@main
struct YourApp: App {
@UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
AppDelegate
, add the following code:import FirebaseAppCheck
class YourAppCheckProviderFactory: NSObject, AppCheckProviderFactory {
func createProvider(with app: FirebaseApp) -> AppCheckProvider? {
return AppAttestProvider(app: app)
}
}
AppDelegate
, set the provider factory:func applicationDidFinishLaunching(_ notification: Notification) {
#if DEBUG
let providerFactory = AppCheckDebugProviderFactory()
AppCheck.setAppCheckProviderFactory(providerFactory)
#else
AppCheck.setAppCheckProviderFactory(YourAppCheckProviderFactory())
#endif
}
App Attest
capability in the Xcode project from Signing & Capabilities.x-firebase-appcheck
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
}
}
Firebase App Check
in the logs.Apps
section.Manage debug tokens
Add debug token
Save
Setup the firebase functions and express like you normally do. Setup Firebase Functions
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);
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
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;
That’s it! Now you can use the app check token in your firebase functions.
AppDelegate
setup for device check from the iOS setup.Hope this helps!