Softotic
Back to insights
10 August 202617 min readFlutterFirebaseApp CheckMobile SecurityTroubleshooting

Flutter Firebase App Check Fails After Enforcement: How to Diagnose 403s and Play Integrity Errors

A diagnosis-first guide to Flutter Firebase App Check failures after enforcement, covering request metrics, Play Integrity, debug tokens, emulators, CI, and custom backends.

Flutter Firebase App Check Fails After Enforcement: How to Diagnose 403s and Play Integrity Errors

If a Flutter app works normally until Firebase App Check enforcement is enabled and then Firestore, Storage, Authentication, Functions, or a custom backend starts rejecting requests, do not begin by weakening Firestore rules or disabling authentication. First prove whether App Check is actually the failing layer.

Firebase's current App Check model is simple: before enforcement, clients can send App Check tokens without the backend requiring them; after enforcement, requests without acceptable attestation are rejected. Firebase therefore recommends monitoring App Check request metrics before enforcing it on an existing app.

The fastest troubleshooting sequence is:

  1. check Firebase App Check metrics
  2. verify App Check initialises before dependent Firebase calls
  3. determine whether the failing build should use a production attestation provider or the debug provider
  4. validate Android Play Integrity configuration, especially the Firebase project link and SHA-256 signing certificate
  5. only then investigate Firebase Security Rules, Authentication, or application logic

This article walks through that sequence and explains why each step matters.

What changes when Firebase App Check enforcement is enabled?

Firebase App Check and Firebase Authentication solve different problems.

Firebase Authentication answers: who is the user?

Firebase App Check answers: is this request coming from an app or device that Firebase is prepared to trust?

Firebase's App Check overview describes App Check as an attestation layer for protecting Firebase and custom backend resources from unauthorised clients. On Flutter, Firebase's default providers are Play Integrity on Android, DeviceCheck on Apple platforms, and reCAPTCHA on web.

The crucial behaviour change happens at enforcement.

According to Firebase's Flutter App Check setup guide, adding and activating App Check causes the client to start attaching tokens to requests, but Firebase products do not require valid tokens until enforcement is enabled.

That means this sequence can occur:

text
App works
   ↓
App Check SDK added
   ↓
App still works because enforcement is OFF
   ↓
Enforcement enabled
   ↓
Requests begin failing

When that happens, the failure is often not "Firebase suddenly broke". Enforcement exposed a client or attestation path that was never producing acceptable App Check requests.

Step 1: read App Check metrics before changing code

The most useful first diagnostic is Firebase Console:

text
Security
  → App Check
    → APIs
      → select the affected Firebase product

Firebase's request metrics documentation separates requests into categories that are directly useful for troubleshooting.

Metric categoryWhat Firebase says it meansWhat to investigate
VerifiedValid App Check tokenApp Check is probably not the failing layer for those requests
Outdated clientNo App Check token, likely an older app versionUsers may still be running builds released before App Check was added
Unknown originNo token and request does not look like it came from a Firebase SDKDirect API calls, unexpected clients, scripts, or abuse
InvalidInvalid App Check tokenProvider/configuration problem, emulator, invalid attestation, or unauthorised environment
Reused tokenToken already used where replay protection appliesLimited-use token/replay-protection flow

Firebase explicitly warns that enabling enforcement while a meaningful share of users are still on older clients can break those app versions.

This gives you a better debugging question than:

Why is Firestore returning an error?

Ask:

What does App Check classify the failing request as?

That narrows the problem immediately.

A useful rule: if requests are Verified, look beyond App Check

Suppose the App Check console reports the affected requests as Verified, but the application still receives __INLINE_CODE_0__, an authentication error, a failed Cloud Function response, or an API-specific error.

At that point, App Check may already be doing its job.

Check the next layer:

text
Request
  ↓
App Check attestation      ← verified
  ↓
Firebase Authentication
  ↓
Firestore / Storage rules
  ↓
Cloud Function / API logic

It is easy to lose time changing App Check configuration when the actual failure is a Security Rules condition, expired user session, missing custom claim, incorrect document path, or backend validation rule.

Treat the layers separately.

Step 2: verify Flutter App Check initialisation order

Firebase's Flutter documentation says App Check should be activated after __INLINE_CODE_0__ but before using Firebase services such as Storage.

A clean startup sequence looks like this:

dart
import 'package:firebase_app_check/firebase_app_check.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();

  await Firebase.initializeApp();

  await FirebaseAppCheck.instance.activate(
    androidProvider: AndroidProvider.playIntegrity,
    appleProvider: AppleProvider.appAttestWithDeviceCheckFallback,
  );

  runApp(const MyApp());
}

The current FlutterFire API exposes Play Integrity as the default Android provider and supports App Attest, DeviceCheck, debug providers, and fallback options on Apple platforms. See the current __INLINE_CODE_0__ API.

The important part is not merely that __INLINE_CODE_0__ exists somewhere in the codebase.

It must run before code begins making protected requests.

Watch for startup races

A common architecture is:

text
main()
  ↓
Firebase.initializeApp()
  ↓
runApp()
  ↓
auth listener / repository / provider starts immediately
  ↓
Firestore query

If App Check activation happens later inside a widget, service locator, or asynchronous bootstrap that is not awaited, a protected Firebase call can start before App Check is ready.

Prefer one explicit bootstrap sequence.

Step 3: confirm whether this build should use Play Integrity or the debug provider

Production attestation providers are not designed for every local development environment.

Firebase's Flutter debug-provider documentation states that emulators and CI environments normally do not qualify as valid devices after App Check is enforced. Firebase provides a debug provider specifically for development and testing.

For Android Flutter development:

dart
await FirebaseAppCheck.instance.activate(
  androidProvider: AndroidProvider.debug,
);

Run the app, obtain the debug token from logs, then register that token in:

text
Firebase Console
  → Security
    → App Check
      → Apps
        → your app
          → Manage debug tokens

After registration, Firebase will accept that debug token.

Do not ship the debug provider

Firebase warns that a valid debug token allows access from an unverified device. Do not:

  • commit debug tokens to source control
  • embed a debug secret in a public repository
  • ship a production build configured with __INLINE_CODE_0__
  • distribute debug builds to untrusted users

A better application configuration makes the provider choice explicit.

For example:

dart
const useAppCheckDebug = bool.fromEnvironment(
  'APP_CHECK_DEBUG',
  defaultValue: false,
);

await FirebaseAppCheck.instance.activate(
  androidProvider: useAppCheckDebug
      ? AndroidProvider.debug
      : AndroidProvider.playIntegrity,
  appleProvider: useAppCheckDebug
      ? AppleProvider.debug
      : AppleProvider.appAttestWithDeviceCheckFallback,
);

Local development can then opt in deliberately:

bash
flutter run --dart-define=APP_CHECK_DEBUG=true

Production builds should omit that flag.

This is safer than casually switching the whole project to the debug provider because one emulator stopped working.

Step 4: test whether the Flutter client can obtain an App Check token

The FlutterFire API provides __INLINE_CODE_0__ and __INLINE_CODE_1__.

A useful diagnostic is to force a refresh and log only whether acquisition succeeded, not the token itself:

dart
import 'package:firebase_app_check/firebase_app_check.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/foundation.dart';

Future<void> diagnoseAppCheck() async {
  try {
    final token = await FirebaseAppCheck.instance.getToken(true);

    debugPrint(
      token == null
          ? 'App Check: no token returned'
          : 'App Check: token acquired successfully',
    );
  } on FirebaseException catch (e, stackTrace) {
    debugPrint('App Check failed: ${e.code} ${e.message}');
    debugPrintStack(stackTrace: stackTrace);
  }
}

Do not print the actual token into production logs.

The purpose of this test is to separate:

text
Cannot obtain App Check token

from:

text
Token exists, but the protected service rejects the request

Those are different debugging paths.

Step 5: for Android, validate the Play Integrity chain

When Android production or release builds fail, Play Integrity configuration deserves its own checklist.

Firebase's current Play Integrity App Check guide requires several pieces to line up.

1. Is the Google Play app linked to the correct Firebase project?

In Google Play Console:

text
Release
  → App integrity
    → Play Integrity API
      → Link Cloud project

Firebase says the linked Google Cloud project must be the same Firebase project in which the Android app is registered for App Check.

A project mismatch can leave the application looking correctly configured in two consoles while attestation is being evaluated against the wrong project.

2. Is the correct SHA-256 signing certificate registered?

Firebase requires the SHA-256 fingerprint of the Android app's signing certificate when registering Play Integrity for App Check.

This is especially important because the certificate used locally may not be the certificate users receive from Google Play.

Check the certificate for the build path you are actually testing.

Useful places include:

  • your local signing keystore
  • Firebase Android app settings
  • Google Play App Signing
  • CI/release signing configuration

Do not assume that an existing SHA-1 entry is enough for App Check registration. Firebase's current Play Integrity setup specifically requires SHA-256.

3. Is the build installed through the distribution path your App Check settings expect?

This is a subtle source of failures.

Firebase documents different recommended Play Integrity settings depending on whether the application is:

  • exclusively distributed on Google Play
  • exclusively distributed outside Google Play
  • distributed both through Google Play and outside it

By default, App Check requires the __INLINE_CODE_0__ app-recognition label. Firebase notes that apps not published on Google Play are not eligible for that label.

So a release APK that is manually installed on a device is not automatically equivalent to the Play-distributed build.

If your production model includes direct APK distribution, enterprise distribution, QA sideloading, or other non-Play channels, review the App Check advanced settings rather than weakening them blindly.

4. Did you make device-integrity requirements stricter than your audience can satisfy?

Firebase supports optional Play Integrity device requirements such as basic, device, and strong integrity.

Stricter is not automatically better.

Firebase specifically warns that some Android 13+ users who did not install or update from Google Play may be unable to satisfy certain integrity requirements.

Configure the requirement for the actual distribution model and threat model of the application.

Step 6: do not use a production provider to "fix" an emulator

If this pattern occurs:

text
Physical Play Store build → works
Android emulator          → fails
CI integration tests      → fail

that is not necessarily evidence that production App Check is broken.

Firebase explicitly expects emulators and CI to use the debug provider when those environments cannot provide production attestation.

The correct architecture is:

text
Production
  → Play Integrity / App Attest / DeviceCheck

Development emulator
  → App Check debug provider

CI
  → App Check debug provider + token stored in CI secret storage

The debug token is a credential. Treat it accordingly.

Step 7: distinguish App Check failure from Firebase Authentication failure

App Check and Firebase Authentication can both participate in the same request.

A valid authenticated user can still have invalid App Check attestation.

A valid App Check request can still come from an unauthenticated user.

Think of the request as having separate gates:

text
Is this our trusted application?
        ↓
      App Check

Who is using it?
        ↓
 Firebase Authentication

May this user perform this operation?
        ↓
 Security Rules / backend authorisation

If App Check metrics say Verified, inspect Authentication and authorisation next.

If App Check says Invalid, solve attestation before rewriting your Firestore rules.

This prevents security debugging from becoming a pile of unrelated configuration changes.

Step 8: check old application versions before enforcing on a live app

A rollout problem can look exactly like a random production bug.

Imagine version __INLINE_CODE_0__ introduces App Check but version __INLINE_CODE_1__ is still installed on thousands of devices.

If enforcement is enabled immediately:

text
5.2.0 → sends App Check token → potentially verified
5.1.0 → no App Check SDK/token → outdated client

Firebase's metrics guide explicitly recommends examining the share of outdated requests before enforcement because old app versions can be disrupted.

A safer rollout is:

  1. ship the App Check-enabled application
  2. leave enforcement off initially
  3. inspect metrics per protected Firebase product
  4. identify outdated/invalid traffic
  5. fix provider and rollout issues
  6. allow user adoption to improve where necessary
  7. enable enforcement when the verified-request picture is acceptable

For a new unreleased application, Firebase recommends enabling enforcement early because there are no installed legacy versions to protect.

Step 9: if a custom backend fails, verify the header and server-side token validation

App Check can also protect a Node.js, Python, Go, or other self-hosted backend.

Firebase's Flutter custom-resource guide instructs the client to obtain an App Check token and send it in the __INLINE_CODE_0__ header.

Client example:

dart
final token = await FirebaseAppCheck.instance.getToken();

final response = await http.get(
  Uri.parse('https://api.example.com/account'),
  headers: {
    if (token != null) 'X-Firebase-AppCheck': token,
  },
);

On a Node.js backend, Firebase recommends verifying the token with the Admin SDK. Its custom backend verification guide uses __INLINE_CODE_0__.

A minimal Express middleware can look like this:

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

export async function requireAppCheck(req, res, next) {
  const token = req.get('X-Firebase-AppCheck');

  if (!token) {
    return res.status(401).json({
      error: 'Missing Firebase App Check token',
    });
  }

  try {
    req.appCheck = await getAppCheck().verifyToken(token);
    return next();
  } catch (error) {
    console.error('App Check verification failed', error);

    return res.status(401).json({
      error: 'Invalid Firebase App Check token',
    });
  }
}

Do not put App Check tokens in query strings.

Firebase recommends a request header because tokens in URLs are more exposed to accidental logging and leakage.

Why you may see an HTTP 403 during token verification

Firebase's App Check token verification REST API documents that an invalid App Check token produces HTTP 403.

That does not mean every Firebase SDK surfaces App Check failures as a literal __INLINE_CODE_0__ string. Different Firebase products and client SDKs can translate backend rejection into product-specific exceptions.

Treat "403 after App Check enforcement" as a symptom to classify, not as a diagnosis by itself.

A diagnosis matrix

Use this before changing configuration.

SymptomMost likely next check
App worked until enforcementApp Check metrics first
Emulator fails, production device worksDebug provider and registered debug token
CI fails after enforcementDebug provider with token stored as CI secret
Android release works only from Play StorePlay Integrity recognition/licensing and distribution settings
Android production fails everywhereFirebase project link, App Check app registration, SHA-256 signing fingerprint
Metrics show OutdatedUsers are likely running pre-App-Check builds
Metrics show InvalidAttestation/provider/environment issue
Metrics show Verified but Firestore deniesAuthentication or Firestore Security Rules
Custom API receives no token__INLINE_CODE_0__ client integration
Custom API receives token but rejects itAdmin SDK verification/project configuration
Only older production versions failEnforcement was enabled before rollout adoption was sufficient

What not to do

Do not disable Firebase Security Rules to test App Check

That changes a different security layer and can create a data-exposure risk without proving anything about attestation.

Do not leave App Check unenforced forever because an emulator failed

Use the debug provider for the emulator.

Do not ship a debug provider to production

A debug token explicitly allows unverified devices to satisfy App Check.

Do not log real App Check tokens

Log the error code, provider state, request classification, build type, app version, and whether token acquisition succeeded.

Do not assume a signed release APK is identical to the Play-distributed build

Play Integrity evaluates signals related to application recognition, account/licensing configuration, device integrity, and distribution.

Do not turn every 401, 403, or permission error into an App Check problem

Use App Check metrics to prove the layer first.

A production rollout checklist

Before enabling enforcement on an existing Flutter app:

  1. App Check is activated immediately after Firebase initialisation.
  2. Every Firebase app/platform that reaches the protected backend is registered.
  3. Android Play Integrity is linked to the correct Firebase project.
  4. Correct SHA-256 signing fingerprints are registered.
  5. Apple provider choice matches your supported OS/device range.
  6. Emulator and CI builds use registered debug tokens.
  7. Debug tokens are stored securely and never shipped.
  8. App Check metrics show the expected requests as Verified.
  9. Old versions have been considered before enforcement.
  10. Each Firebase product is reviewed individually before enforcement.
  11. Custom backends require __INLINE_CODE_0__ and verify it server-side.
  12. Monitoring exists so Invalid and Outdated traffic can be detected after rollout.

This turns enforcement from a console switch into a controlled deployment step.

FAQs

Why does Firebase work before App Check enforcement but fail afterwards?

Before enforcement, Firebase can receive App Check tokens without requiring them to be valid for the protected product. Once enforcement is enabled, unverified requests are rejected. Check the App Check metrics screen to determine whether your client is Verified, Outdated, Unknown, or Invalid.

Why does Firebase App Check fail on a Flutter emulator?

Production attestation providers generally cannot treat an emulator like a genuine production device. Firebase provides the App Check debug provider for emulator and CI use. Register the generated debug token in the Firebase console and keep it private.

Does Firebase App Check replace Firebase Authentication?

No. App Check attests the application/device request path; Firebase Authentication identifies the user. A secure application can require both.

Why does my Android App Check work from Google Play but not from a sideloaded APK?

Play Integrity settings can depend on distribution. Firebase documents different recognition/licensing recommendations for apps distributed only through Google Play, only outside Play, or through both channels. Review those settings instead of assuming the sideloaded APK has the same integrity verdict as the Play-installed build.

Do I need SHA-1 or SHA-256 for Firebase App Check with Play Integrity?

Firebase's current App Check setup for Play Integrity requires the SHA-256 fingerprint of the app's signing certificate. Other Firebase features may use SHA-1 for their own setup, but an existing SHA-1 entry does not replace the App Check SHA-256 requirement.

Should I enable App Check enforcement immediately?

For a new unreleased application, Firebase recommends enabling App Check early. For an existing released application, first ship App Check support and inspect request metrics so that older clients or invalid configurations are not accidentally locked out.

Conclusion

A Firebase App Check failure after enforcement should be debugged as a request-attestation problem first, not as a generic Firebase permission problem.

Start with the metrics. They tell you whether Firebase sees the request as Verified, Outdated, Unknown, or Invalid.

Then follow the failing path:

text
Metrics
   ↓
Flutter initialisation
   ↓
Debug vs production provider
   ↓
Play Integrity / Apple configuration
   ↓
Signing identity and distribution
   ↓
Token acquisition
   ↓
Firebase Auth / Security Rules
   ↓
Custom backend verification

That order avoids the most dangerous troubleshooting habit: weakening unrelated security controls until the error disappears.

If you are building or debugging a Flutter application where Firebase security, authentication, backend APIs, and release infrastructure need to work as one system, Softotic's mobile app development service covers the application layer, while custom software development can handle the backend and integration side.

Sources and references