From c84b83241277e3bf7fbaeea636a4bd4ffc6afee3 Mon Sep 17 00:00:00 2001 From: Oluwatobi Shokunbi Date: Fri, 17 Jul 2026 10:57:02 +0100 Subject: [PATCH 1/7] fix(app-check): validate provider name and error on unrecognized values Closes #9008 --- .../ReactNativeFirebaseAppCheckProvider.java | 5 +++++ .../ios/RNFBAppCheck/RNFBAppCheckProvider.m | 6 ++++++ packages/app-check/lib/index.ts | 17 +++++++++++++++++ 3 files changed, 28 insertions(+) diff --git a/packages/app-check/android/src/main/java/io/invertase/firebase/appcheck/ReactNativeFirebaseAppCheckProvider.java b/packages/app-check/android/src/main/java/io/invertase/firebase/appcheck/ReactNativeFirebaseAppCheckProvider.java index 730b1ad0aa..eea3b2e634 100644 --- a/packages/app-check/android/src/main/java/io/invertase/firebase/appcheck/ReactNativeFirebaseAppCheckProvider.java +++ b/packages/app-check/android/src/main/java/io/invertase/firebase/appcheck/ReactNativeFirebaseAppCheckProvider.java @@ -89,6 +89,11 @@ public String getDebugSecret() { if ("playIntegrity".equals(providerName)) { delegateProvider = PlayIntegrityAppCheckProviderFactory.getInstance().create(app); } + + if (delegateProvider == null) { + Log.e(LOGTAG, "Unknown provider name \"" + providerName + + "\". Valid providers are: debug, playIntegrity."); + } } catch (Exception e) { // This will bubble up and result in a rejected promise with the underlying message throw new RuntimeException(e.getMessage()); diff --git a/packages/app-check/ios/RNFBAppCheck/RNFBAppCheckProvider.m b/packages/app-check/ios/RNFBAppCheck/RNFBAppCheckProvider.m index 07c3ace612..9fec1d7adf 100644 --- a/packages/app-check/ios/RNFBAppCheck/RNFBAppCheckProvider.m +++ b/packages/app-check/ios/RNFBAppCheck/RNFBAppCheckProvider.m @@ -73,6 +73,12 @@ - (void)configure:(FIRApp *)app self.delegateProvider = [[FIRDeviceCheckProvider alloc] initWithApp:app]; } } + + if (self.delegateProvider == nil) { + NSLog(@"RNFBAppCheck: Unknown provider name \"%@\". Valid providers are: debug, deviceCheck, " + @"appAttest, appAttestWithDeviceCheckFallback.", + providerName); + } } - (void)getTokenWithCompletion:(nonnull void (^)(FIRAppCheckToken *_Nullable, diff --git a/packages/app-check/lib/index.ts b/packages/app-check/lib/index.ts index 6a5d8d1517..d9ddbc798f 100644 --- a/packages/app-check/lib/index.ts +++ b/packages/app-check/lib/index.ts @@ -133,6 +133,12 @@ class FirebaseAppCheckModule extends FirebaseModule { 'Invalid configuration: no android provider configured while on android platform.', ); } + const validAndroidProviders = ['debug', 'playIntegrity']; + if (!validAndroidProviders.includes(provider.providerOptions.android.provider)) { + throw new Error( + `Invalid App Check provider "${provider.providerOptions.android.provider}". Valid android providers are: ${validAndroidProviders.join(', ')}.`, + ); + } return this.native.configureProvider( provider.providerOptions.android.provider, provider.providerOptions.android.debugToken, @@ -144,6 +150,17 @@ class FirebaseAppCheckModule extends FirebaseModule { 'Invalid configuration: no apple provider configured while on apple platform.', ); } + const validAppleProviders = [ + 'debug', + 'deviceCheck', + 'appAttest', + 'appAttestWithDeviceCheckFallback', + ]; + if (!validAppleProviders.includes(provider.providerOptions.apple.provider)) { + throw new Error( + `Invalid App Check provider "${provider.providerOptions.apple.provider}". Valid apple providers are: ${validAppleProviders.join(', ')}.`, + ); + } return this.native.configureProvider( provider.providerOptions.apple.provider, provider.providerOptions.apple.debugToken, From 350e2ad1afef95e0a6e4555f5a411379a48b3d63 Mon Sep 17 00:00:00 2001 From: Oluwatobi Shokunbi Date: Fri, 17 Jul 2026 11:19:44 +0100 Subject: [PATCH 2/7] fix(app-check): validate provider name and error on unrecognized values Closes #9008 --- packages/app-check/__tests__/appcheck.test.ts | 66 ++++++++++++++++++- packages/app-check/lib/index.ts | 44 ++++++++----- 2 files changed, 92 insertions(+), 18 deletions(-) diff --git a/packages/app-check/__tests__/appcheck.test.ts b/packages/app-check/__tests__/appcheck.test.ts index 581cc123c4..d7dae132f2 100644 --- a/packages/app-check/__tests__/appcheck.test.ts +++ b/packages/app-check/__tests__/appcheck.test.ts @@ -1,4 +1,5 @@ -import { describe, expect, it } from '@jest/globals'; +import { describe, expect, it, beforeEach, afterEach } from '@jest/globals'; +import { Platform } from 'react-native'; import { initializeAppCheck, @@ -26,6 +27,69 @@ describe('appCheck()', function () { ); }); + describe('provider name validation', function () { + it('throws on invalid android provider name', function () { + const provider = new ReactNativeFirebaseAppCheckProvider(); + provider.configure({ + android: { provider: 'invalidProvider' as any }, + }); + expect(() => initializeAppCheck(undefined, { provider })).toThrow( + 'Invalid App Check provider "invalidProvider". Valid android providers are: debug, playIntegrity.', + ); + }); + + it('does not throw validation error for valid android provider names', function () { + for (const name of ['debug', 'playIntegrity']) { + const provider = new ReactNativeFirebaseAppCheckProvider(); + provider.configure({ + android: { provider: name as any }, + }); + try { + initializeAppCheck(undefined, { provider }); + } catch (e: any) { + expect(e.message).not.toContain('Invalid App Check provider'); + } + } + }); + + describe('apple platform', function () { + let originalOS: string; + + beforeEach(function () { + originalOS = Platform.OS; + Platform.OS = 'ios' as any; + }); + + afterEach(function () { + Platform.OS = originalOS as any; + }); + + it('throws on invalid apple provider name', function () { + const provider = new ReactNativeFirebaseAppCheckProvider(); + provider.configure({ + apple: { provider: 'appAttestWithDebugProviderFallback' as any }, + }); + expect(() => initializeAppCheck(undefined, { provider })).toThrow( + 'Invalid App Check provider "appAttestWithDebugProviderFallback". Valid apple providers are: debug, deviceCheck, appAttest, appAttestWithDeviceCheckFallback.', + ); + }); + + it('does not throw validation error for valid apple provider names', function () { + for (const name of ['debug', 'deviceCheck', 'appAttest', 'appAttestWithDeviceCheckFallback']) { + const provider = new ReactNativeFirebaseAppCheckProvider(); + provider.configure({ + apple: { provider: name as any }, + }); + try { + initializeAppCheck(undefined, { provider }); + } catch (e: any) { + expect(e.message).not.toContain('Invalid App Check provider'); + } + } + }); + }); + }); + it('`getToken` function is properly exposed to end user', function () { expect(getToken).toBeDefined(); }); diff --git a/packages/app-check/lib/index.ts b/packages/app-check/lib/index.ts index d9ddbc798f..ffe38de99b 100644 --- a/packages/app-check/lib/index.ts +++ b/packages/app-check/lib/index.ts @@ -49,6 +49,9 @@ import { ReactNativeFirebaseAppCheckProvider } from './providers'; const nativeModuleName = 'NativeRNFBTurboAppCheck'; +const VALID_APPLE_PROVIDERS = ['debug', 'deviceCheck', 'appAttest', 'appAttestWithDeviceCheckFallback']; +const VALID_ANDROID_PROVIDERS = ['debug', 'playIntegrity']; + /** * Type guard to check if a provider has providerOptions. * This provides proper type narrowing for providers that support platform-specific configuration. @@ -63,6 +66,29 @@ function hasProviderOptions( ); } +function validateProviderName(options: AppCheckOptions): void { + if (!hasProviderOptions(options.provider)) { + return; + } + const provider = options.provider; + if (Platform.OS === 'android') { + const name = provider.providerOptions?.android?.provider; + if (isString(name) && !VALID_ANDROID_PROVIDERS.includes(name)) { + throw new Error( + `Invalid App Check provider "${name}". Valid android providers are: ${VALID_ANDROID_PROVIDERS.join(', ')}.`, + ); + } + } + if (Platform.OS === 'ios' || Platform.OS === 'macos') { + const name = provider.providerOptions?.apple?.provider; + if (isString(name) && !VALID_APPLE_PROVIDERS.includes(name)) { + throw new Error( + `Invalid App Check provider "${name}". Valid apple providers are: ${VALID_APPLE_PROVIDERS.join(', ')}.`, + ); + } + } +} + class FirebaseAppCheckModule extends FirebaseModule { _listenerCount: number; @@ -133,12 +159,6 @@ class FirebaseAppCheckModule extends FirebaseModule { 'Invalid configuration: no android provider configured while on android platform.', ); } - const validAndroidProviders = ['debug', 'playIntegrity']; - if (!validAndroidProviders.includes(provider.providerOptions.android.provider)) { - throw new Error( - `Invalid App Check provider "${provider.providerOptions.android.provider}". Valid android providers are: ${validAndroidProviders.join(', ')}.`, - ); - } return this.native.configureProvider( provider.providerOptions.android.provider, provider.providerOptions.android.debugToken, @@ -150,17 +170,6 @@ class FirebaseAppCheckModule extends FirebaseModule { 'Invalid configuration: no apple provider configured while on apple platform.', ); } - const validAppleProviders = [ - 'debug', - 'deviceCheck', - 'appAttest', - 'appAttestWithDeviceCheckFallback', - ]; - if (!validAppleProviders.includes(provider.providerOptions.apple.provider)) { - throw new Error( - `Invalid App Check provider "${provider.providerOptions.apple.provider}". Valid apple providers are: ${validAppleProviders.join(', ')}.`, - ); - } return this.native.configureProvider( provider.providerOptions.apple.provider, provider.providerOptions.apple.debugToken, @@ -284,6 +293,7 @@ export function initializeAppCheck(app?: FirebaseApp, options?: AppCheckOptions) if (!isObject(options)) { throw new Error('Invalid configuration: no options defined.'); } + validateProviderName(options); const appCheck = getModularAppCheck(app); void (appCheck as AppCheckInternal).initializeAppCheck(options); return appCheck; From 51379913c41722befbcc75737794931ff0e950a3 Mon Sep 17 00:00:00 2001 From: Oluwatobi Shokunbi Date: Fri, 17 Jul 2026 11:30:48 +0100 Subject: [PATCH 3/7] style(app-check): fix Prettier formatting for provider validation --- packages/app-check/__tests__/appcheck.test.ts | 7 ++++++- packages/app-check/lib/index.ts | 7 ++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/packages/app-check/__tests__/appcheck.test.ts b/packages/app-check/__tests__/appcheck.test.ts index d7dae132f2..496fa6a513 100644 --- a/packages/app-check/__tests__/appcheck.test.ts +++ b/packages/app-check/__tests__/appcheck.test.ts @@ -75,7 +75,12 @@ describe('appCheck()', function () { }); it('does not throw validation error for valid apple provider names', function () { - for (const name of ['debug', 'deviceCheck', 'appAttest', 'appAttestWithDeviceCheckFallback']) { + for (const name of [ + 'debug', + 'deviceCheck', + 'appAttest', + 'appAttestWithDeviceCheckFallback', + ]) { const provider = new ReactNativeFirebaseAppCheckProvider(); provider.configure({ apple: { provider: name as any }, diff --git a/packages/app-check/lib/index.ts b/packages/app-check/lib/index.ts index ffe38de99b..a96de4f1fe 100644 --- a/packages/app-check/lib/index.ts +++ b/packages/app-check/lib/index.ts @@ -49,7 +49,12 @@ import { ReactNativeFirebaseAppCheckProvider } from './providers'; const nativeModuleName = 'NativeRNFBTurboAppCheck'; -const VALID_APPLE_PROVIDERS = ['debug', 'deviceCheck', 'appAttest', 'appAttestWithDeviceCheckFallback']; +const VALID_APPLE_PROVIDERS = [ + 'debug', + 'deviceCheck', + 'appAttest', + 'appAttestWithDeviceCheckFallback', +]; const VALID_ANDROID_PROVIDERS = ['debug', 'playIntegrity']; /** From ebfd8c41ca24071628d0727364470d9343885953 Mon Sep 17 00:00:00 2001 From: Oluwatobi Shokunbi Date: Fri, 17 Jul 2026 11:35:15 +0100 Subject: [PATCH 4/7] style(app-check): reflow Android Log.e to match google-java-format --- .../appcheck/ReactNativeFirebaseAppCheckProvider.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/app-check/android/src/main/java/io/invertase/firebase/appcheck/ReactNativeFirebaseAppCheckProvider.java b/packages/app-check/android/src/main/java/io/invertase/firebase/appcheck/ReactNativeFirebaseAppCheckProvider.java index eea3b2e634..db0f7e8d0f 100644 --- a/packages/app-check/android/src/main/java/io/invertase/firebase/appcheck/ReactNativeFirebaseAppCheckProvider.java +++ b/packages/app-check/android/src/main/java/io/invertase/firebase/appcheck/ReactNativeFirebaseAppCheckProvider.java @@ -91,8 +91,11 @@ public String getDebugSecret() { } if (delegateProvider == null) { - Log.e(LOGTAG, "Unknown provider name \"" + providerName - + "\". Valid providers are: debug, playIntegrity."); + Log.e( + LOGTAG, + "Unknown provider name \"" + + providerName + + "\". Valid providers are: debug, playIntegrity."); } } catch (Exception e) { // This will bubble up and result in a rejected promise with the underlying message From 119c0718e233564fe485546bf739ea549fd08db6 Mon Sep 17 00:00:00 2001 From: Oluwatobi Shokunbi Date: Mon, 20 Jul 2026 11:57:17 +0100 Subject: [PATCH 5/7] fix(app-check, android): throw on unknown provider instead of silent null --- .../appcheck/ReactNativeFirebaseAppCheckProvider.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/app-check/android/src/main/java/io/invertase/firebase/appcheck/ReactNativeFirebaseAppCheckProvider.java b/packages/app-check/android/src/main/java/io/invertase/firebase/appcheck/ReactNativeFirebaseAppCheckProvider.java index db0f7e8d0f..f56d6b628a 100644 --- a/packages/app-check/android/src/main/java/io/invertase/firebase/appcheck/ReactNativeFirebaseAppCheckProvider.java +++ b/packages/app-check/android/src/main/java/io/invertase/firebase/appcheck/ReactNativeFirebaseAppCheckProvider.java @@ -91,11 +91,12 @@ public String getDebugSecret() { } if (delegateProvider == null) { - Log.e( - LOGTAG, + String message = "Unknown provider name \"" + providerName - + "\". Valid providers are: debug, playIntegrity."); + + "\". Valid providers are: debug, playIntegrity."; + Log.e(LOGTAG, message); + throw new IllegalArgumentException(message); } } catch (Exception e) { // This will bubble up and result in a rejected promise with the underlying message From 0843cbaca078c8c5fec77031335b0b6e72428159 Mon Sep 17 00:00:00 2001 From: Oluwatobi Shokunbi Date: Tue, 21 Jul 2026 10:28:42 +0100 Subject: [PATCH 6/7] fix(app-check, ios): throw on unknown provider and reject promise Parity with Android: reset delegateProvider before configuring so a prior factory default does not survive an invalid reconfigure. Throw NSException on unrecognized provider name and catch it in the module to reject the JS promise with a clear error message. --- .../ios/RNFBAppCheck/RNFBAppCheckModule.mm | 16 ++++++++++++---- .../ios/RNFBAppCheck/RNFBAppCheckProvider.m | 14 ++++++++++---- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/packages/app-check/ios/RNFBAppCheck/RNFBAppCheckModule.mm b/packages/app-check/ios/RNFBAppCheck/RNFBAppCheckModule.mm index f3027d9fe9..6d8bcbe890 100644 --- a/packages/app-check/ios/RNFBAppCheck/RNFBAppCheckModule.mm +++ b/packages/app-check/ios/RNFBAppCheck/RNFBAppCheckModule.mm @@ -76,10 +76,18 @@ - (void)configureProvider:(NSString *)appName FIRApp *firebaseApp = [RCTConvert firAppFromString:appName]; DLog(@"appName/providerName/debugToken: %@/%@/%@", firebaseApp.name, providerName, (debugToken == nil ? @"null" : @"(not shown)")); - [[RNFBAppCheckModule sharedInstance].providerFactory configure:firebaseApp - providerName:providerName - debugToken:debugToken]; - resolve([NSNull null]); + @try { + [[RNFBAppCheckModule sharedInstance].providerFactory configure:firebaseApp + providerName:providerName + debugToken:debugToken]; + resolve([NSNull null]); + } @catch (NSException *exception) { + [RNFBSharedUtils rejectPromiseWithUserInfo:reject + userInfo:(NSMutableDictionary *)@{ + @"code" : @"unknown", + @"message" : exception.reason ?: @"internal-error", + }]; + } } - (void)setTokenAutoRefreshEnabled:(NSString *)appName diff --git a/packages/app-check/ios/RNFBAppCheck/RNFBAppCheckProvider.m b/packages/app-check/ios/RNFBAppCheck/RNFBAppCheckProvider.m index 9fec1d7adf..6d5f075bb3 100644 --- a/packages/app-check/ios/RNFBAppCheck/RNFBAppCheckProvider.m +++ b/packages/app-check/ios/RNFBAppCheck/RNFBAppCheckProvider.m @@ -36,7 +36,8 @@ - (void)configure:(FIRApp *)app DLog(@"appName %@", app.name); - // - determine if debugToken is provided via nullable arg + self.delegateProvider = nil; + if ([providerName isEqualToString:@"debug"]) { // The firebase-ios-sdk debug app check provider will take a token from environment if it // exists: @@ -75,9 +76,14 @@ - (void)configure:(FIRApp *)app } if (self.delegateProvider == nil) { - NSLog(@"RNFBAppCheck: Unknown provider name \"%@\". Valid providers are: debug, deviceCheck, " - @"appAttest, appAttestWithDeviceCheckFallback.", - providerName); + NSString *message = [NSString + stringWithFormat:@"Unknown provider name \"%@\". Valid providers are: debug, " + @"deviceCheck, appAttest, appAttestWithDeviceCheckFallback.", + providerName ?: @"(null)"]; + NSLog(@"RNFBAppCheck: %@", message); + @throw [NSException exceptionWithName:@"RNFBAppCheckException" + reason:message + userInfo:nil]; } } From ae75d0801e28c8af16e2d5c50b0587391a12bddd Mon Sep 17 00:00:00 2001 From: Oluwatobi Shokunbi Date: Tue, 21 Jul 2026 14:50:52 +0100 Subject: [PATCH 7/7] style(app-check, ios): fix clang-format Google style violations --- .../ios/RNFBAppCheck/RNFBAppCheckProvider.m | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/packages/app-check/ios/RNFBAppCheck/RNFBAppCheckProvider.m b/packages/app-check/ios/RNFBAppCheck/RNFBAppCheckProvider.m index 6d5f075bb3..12a24541fa 100644 --- a/packages/app-check/ios/RNFBAppCheck/RNFBAppCheckProvider.m +++ b/packages/app-check/ios/RNFBAppCheck/RNFBAppCheckProvider.m @@ -76,14 +76,12 @@ - (void)configure:(FIRApp *)app } if (self.delegateProvider == nil) { - NSString *message = [NSString - stringWithFormat:@"Unknown provider name \"%@\". Valid providers are: debug, " - @"deviceCheck, appAttest, appAttestWithDeviceCheckFallback.", - providerName ?: @"(null)"]; + NSString *message = + [NSString stringWithFormat:@"Unknown provider name \"%@\". Valid providers are: debug, " + "deviceCheck, appAttest, appAttestWithDeviceCheckFallback.", + providerName ?: @"(null)"]; NSLog(@"RNFBAppCheck: %@", message); - @throw [NSException exceptionWithName:@"RNFBAppCheckException" - reason:message - userInfo:nil]; + @throw [NSException exceptionWithName:@"RNFBAppCheckException" reason:message userInfo:nil]; } }