Skip to content

fix(app-check, ios): do not default to the debug provider in release builds - #9117

Open
kolezka wants to merge 1 commit into
invertase:mainfrom
kolezka:fix/app-check-ios-release-debug-provider-default
Open

fix(app-check, ios): do not default to the debug provider in release builds#9117
kolezka wants to merge 1 commit into
invertase:mainfrom
kolezka:fix/app-check-ios-release-debug-provider-default

Conversation

@kolezka

@kolezka kolezka commented Jul 28, 2026

Copy link
Copy Markdown

Description

On iOS, release builds using the Expo config plugin exchange App Check debug tokens against firebaseappcheck.googleapis.com, getting 403 App attestation failed and then 429 once the per-project debug-token quota (60/min, not adjustable) saturates.

The config plugin emits RNFBAppCheckModule.sharedInstance() before FirebaseApp.configure() in AppDelegate. sharedInstance() registers the provider factory with a nil providers dictionary; configure() then instantiates FIRAppCheckInterop eagerly (FIRInstantiationTimingAlwaysEager), which calls createProviderWithApp: while providers[app.name] is still nil. The factory defaults that case to @"debug", so release builds get FIRAppCheckDebugProvider and exchange an unregistered token.

This changes the pre-configure default to appAttestWithDeviceCheckFallback outside DEBUG builds, matching what the JS layer configures moments later anyway. DEBUG builds keep the debug default, which local development with debug tokens relies on. RNFBAppCheckProvider already handles the iOS 14 / tvOS 15 availability fallback for that provider name.

Fixing the factory rather than the plugin's line order means the outcome holds regardless of who calls the factory or in what sequence, so it survives future plugin and SDK changes. The ordering is arguably the deeper fix, but changing the emitted AppDelegate affects every consumer, and I can't test that blast radius across Objective-C and Swift AppDelegates and older plugin versions — so I've left it alone and described it in the issue instead. Happy to follow up if you'd prefer that direction, or a different non-debug default.

Android is unaffected — ReactNativeFirebaseAppCheckProviderFactory.create() throws rather than defaulting to debug.

The debug default dates to ee7df855 ("feat(app-check): add custom factory/provider", 2023-01-19) and is unchanged on main.

On tests: there is no existing jest or e2e coverage for RNFBAppCheckProviderFactory — it is native Objective-C with no test target in this package, and the behaviour needs a release-configuration build to observe. I have left the test checkboxes unticked rather than claim coverage that does not exist; happy to add one if you can point me at where it should live.

Related issues

Fixes #9116

Previously reported, uncaused, in discussion #7518 (open since 2023-12-19) and StackOverflow 77673428. Both threads assume a race condition; the misconfiguration is deterministic — only the exchange timing varies, which is why it presents as intermittent.

Release Summary

iOS: App Check no longer installs the debug provider in release builds, which caused exchangeDebugToken 403/429 traffic and could saturate the project's debug-token quota.

Checklist

  • I read the Contributor Guide and followed the process outlined there for submitting PRs.
    • Yes
  • My change supports the following platforms;
    • Android
    • iOS
    • Other (macOS, web)
  • My change includes tests;
    • e2e tests added or updated in packages/**/e2e
    • jest tests added or updated in packages/**/__tests__
  • I have updated TypeScript types that are affected by my change.
  • This is a breaking change;
    • Yes
    • No

…builds

The Expo config plugin emits `RNFBAppCheckModule.sharedInstance()` before
`FirebaseApp.configure()` in AppDelegate. `sharedInstance()` registers the
provider factory with a nil `providers` dictionary, and `configure()` then
instantiates FIRAppCheckInterop eagerly (FIRInstantiationTimingAlwaysEager),
which calls `createProviderWithApp:` while `providers[app.name]` is still nil.

That pre-configure branch defaulted to `@"debug"`, so release builds installed
FIRAppCheckDebugProvider and exchanged an unregistered debug token against
firebaseappcheck.googleapis.com — 403 "App attestation failed", and 429 once
the per-project debug-token quota saturated.

Default to `appAttestWithDeviceCheckFallback` outside DEBUG builds, matching
what the JS layer configures moments later. DEBUG builds keep the debug
default, which local development with debug tokens relies on.

Reported without a root cause in discussion invertase#7518 since 2023-12-19.
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Caution

The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased.

@CLAassistant

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

@mikehardy

Copy link
Copy Markdown
Collaborator

Just acknowledging this is on our radar 👋

We are just finishing up with some massive internal refactors and that will give us space to fix things like this up

For anyone else affected, and fo you @kolezka I can say this an obviously okay temporary fix - if you set the default provider to whatever you intend to configure anyway, it can't possibly be wrong, can it? So doing this while waiting for a real fix is absolutely a fine temporary fix to carry locally via patch-package or yarn patches

@mikehardy mikehardy added the Workflow: Needs Review Pending feedback or review from a maintainer. label Jul 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Workflow: Needs Review Pending feedback or review from a maintainer.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

🔥 [iOS] App Check installs the debug provider in release builds — config plugin registers the factory before FirebaseApp.configure()

3 participants