Firebase & App Integrity

X-Firebase-AppCheck: Validate the Token on Your Backend

X-Firebase-AppCheck carries an App Check token to a custom backend. Reading the header is not verification: the server must validate the token with a supported verification library before trusting its app-authenticity claims.

By , FounderUpdated 4 min read

Keep App Check separate from user identity

A request can contain both a user-authentication token and an App Check token. They answer different questions. The first identifies the authenticated principal; the second contributes evidence about the calling app environment.

The custom backend guide describes token verification. Resource ownership, roles and business authorization still need explicit checks.

Define the server decision order

A robust handler should validate the request shape, verify required credentials, check authorization, enforce relevant abuse controls and only then perform the protected action. Do not trust a client-provided app ID or decoded token payload without verification.

Input Server responsibility
App Check header Verify token and expected app/project context
User token Verify authentication under the correct service
Resource ID Check that this user may access the resource
Action data Validate types, limits and business rules

Handle missing and invalid tokens deliberately

Return an appropriate error rather than continuing with elevated privileges. Avoid exposing detailed verification internals to an untrusted caller, while keeping useful redacted diagnostics for operations.

Do not log full App Check or authentication tokens. They can be replayable credentials during their validity window.

Consider replay protection where supported

The App Check architecture is not a substitute for transaction-level replay or idempotency controls. For sensitive custom actions, evaluate the documented limited-use/replay-protection options and their SDK support, plus server-side action identifiers.

A valid token does not prove that a transfer, purchase or deletion request has never been submitted before.

Test the enforcement boundary

Send one legitimate request, one with a missing header, one with an invalid token and one authenticated user requesting an unauthorized resource. These cases test different controls.

APKLint can help inspect Firebase configuration, but it cannot verify your custom server middleware. The useful completion evidence is a server-side test matrix showing the intended accept/reject decisions without leaking credentials in the report.

Send a token for the request being made

For a concrete example, suppose an Android app calls your own /orders endpoint. Configure the app's App Check provider during initialization, then request a token before constructing the order request. The following Kotlin fragment shows the boundary; sendOrder and showRetry are application functions that you implement, rather than Firebase APIs.

FirebaseAppCheck.getInstance().getAppCheckToken(false)
    .addOnSuccessListener { result ->
        sendOrder(appCheckHeader = result.token)
    }
    .addOnFailureListener { error ->
        showRetry(error)
    }

Inside sendOrder, put that string in the X-Firebase-AppCheck HTTP header of the HTTPS request. Do not put it in a URL or persist it as a permanent application secret. The SDK can refresh an expired cached token. A token-acquisition failure must have its own retry path; silently sending the protected request without the header obscures the actual failure. See the Android custom-resource integration for the client contract.

Put verification before the protected operation

In a Node server already initialized with Firebase Admin and configured for the intended project, a minimal Express-style gate can be written as follows. The example deliberately returns the same public error for missing and rejected credentials. Its final next() passes to your separate user-authentication and authorization middleware; it does not itself authorize an order.

import { getAppCheck } from "firebase-admin/app-check";

async function requireVerifiedApp(req, res, next) {
  const token = req.get("X-Firebase-AppCheck");
  if (!token) return res.status(401).json({ error: "app_check_required" });
  try {
    const claims = await getAppCheck().verifyToken(token);
    res.locals.verifiedAppId = claims.app_id;
  } catch {
    return res.status(401).json({ error: "app_check_rejected" });
  }
  return next();
}

Use the verified app identity when your server deliberately accepts only a subset of the project's applications. Keep user IDs and resource ownership checks independent. For example, a valid app token for Alice's device must not allow Alice to read an order belonging to Bob. The server verification documentation explains the Admin SDK boundary; merely decoding a JWT is insufficient.

Exercise decisions, not just the happy path

Use a test Firebase project and synthetic order data. Make one request without the header, one with an arbitrary non-token string, one with a valid token plus an unauthorized order ID, and one with both a valid token and an authorized user. Record whether the order handler was invoked in each case. The first two should stop at the App Check gate; the third should stop at your authorization layer. A successful HTTP response alone cannot show which protection ran.

Also exercise a token acquisition failure on the client, a verification-service failure on the server and an expired session for the user credential. Return a recoverable error without creating the order twice. If the client retries a state-changing request, give the order operation its own idempotency design; app attestation is not duplicate-submission protection. Ordinary tokens can be reused within their validity period. Where replay protection is required, use the documented limited-use token and server-consumption mechanism together, not an invented check against the header's text.

Log a request correlation ID and the decision category during development, while excluding raw App Check tokens, user credentials and private order contents. This makes a missing-header bug distinguishable from an authorization denial without turning diagnostic logs into a credential store.

Sources and further reading

  1. Firebase: App Check for custom backends
  2. Firebase: App Check overview

Reference review: 29 September 2026. Examples illustrate the workflow; check your installed versions, release artifact and account-specific Console requirements before applying them. This guide is not a claim that APKLint executed your project or verified your private account.

APKLint

Android inspection tools and practical release guides. About APKLint · Report a correction