From 067e465837bf0ff5d8e3e176f40c9a6c5a9b769d Mon Sep 17 00:00:00 2001 From: Jude Kwashie Date: Mon, 10 Aug 2026 11:52:41 +0000 Subject: [PATCH 1/2] fix(core, web): keep App Check registered for secondary Firebase apps The #11625 startup path removed app-check from the service registry after the first initializeApp, so named apps never reactivated App Check before Auth on reload. --- .../lib/src/firebase_core_web.dart | 27 ++++++-- ...ase_core_web_app_check_multi_app_test.dart | 67 +++++++++++++++++++ 2 files changed, 88 insertions(+), 6 deletions(-) create mode 100644 packages/firebase_core/firebase_core_web/test/firebase_core_web_app_check_multi_app_test.dart diff --git a/packages/firebase_core/firebase_core_web/lib/src/firebase_core_web.dart b/packages/firebase_core/firebase_core_web/lib/src/firebase_core_web.dart index 192e673e7bd0..3a9f7ec650e8 100644 --- a/packages/firebase_core/firebase_core_web/lib/src/firebase_core_web.dart +++ b/packages/firebase_core/firebase_core_web/lib/src/firebase_core_web.dart @@ -57,6 +57,14 @@ class FirebaseCoreWeb extends FirebasePlatform { ); } + /// Whether [service] is still registered for per-app initialization. + /// + /// Used by tests to ensure App Check is not removed after the first + /// [initializeApp] (secondary apps must re-run ensurePluginInitialized). + @visibleForTesting + static bool isServiceRegistered(String service) => + _services.containsKey(service); + static const String _libraryName = 'flutter-fire-core'; /// Registers that [FirebaseCoreWeb] is the platform implementation. @@ -394,15 +402,22 @@ class FirebaseCoreWeb extends FirebasePlatform { } } - final appCheck = _services.remove('app-check'); - if (appCheck != null) { - // Activate app check first - await appCheck.ensurePluginInitialized!(app!); + // Activate App Check before other services so Auth/etc. attach tokens. + // Do NOT remove 'app-check' from [_services]: secondary (named) apps also + // need ensurePluginInitialized on each Firebase.initializeApp() call. + // Removing it caused App Check to skip reactivation for named apps on + // reload, so accounts:lookup ran without X-Firebase-AppCheck (#18556). + final appCheck = _services['app-check']; + final appCheckEnsureInitialized = appCheck?.ensurePluginInitialized; + if (appCheckEnsureInitialized != null) { + await appCheckEnsureInitialized(app!); } await Future.wait( - _services.values.map((service) { - final ensureInitializedFunction = service.ensurePluginInitialized; + _services.entries + .where((entry) => entry.key != 'app-check') + .map((entry) { + final ensureInitializedFunction = entry.value.ensurePluginInitialized; if (ensureInitializedFunction == null || app == null) { return Future.value(); diff --git a/packages/firebase_core/firebase_core_web/test/firebase_core_web_app_check_multi_app_test.dart b/packages/firebase_core/firebase_core_web/test/firebase_core_web_app_check_multi_app_test.dart new file mode 100644 index 000000000000..88bfad5eb7a1 --- /dev/null +++ b/packages/firebase_core/firebase_core_web/test/firebase_core_web_app_check_multi_app_test.dart @@ -0,0 +1,67 @@ +// Copyright 2026 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +@TestOn('browser') +library; + +import 'package:firebase_core_platform_interface/firebase_core_platform_interface.dart'; +import 'package:firebase_core_web/firebase_core_web.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + group('App Check multi-app initialization', () { + setUp(() async { + FirebasePlatform.instance = FirebaseCoreWeb(); + }); + + test( + 'keeps app-check registered and re-runs ensurePluginInitialized for named apps', + () async { + final initializedAppNames = []; + + FirebaseCoreWeb.registerService( + 'app-check', + productNameOverride: 'app_check', + ensurePluginInitialized: (firebaseApp) async { + initializedAppNames.add(firebaseApp.name); + }, + ); + + expect(FirebaseCoreWeb.isServiceRegistered('app-check'), isTrue); + + const options = FirebaseOptions( + apiKey: 'fake-api-key', + appId: '1:1234567890:web:fake', + messagingSenderId: '1234567890', + projectId: 'fake-project', + authDomain: 'fake-project.firebaseapp.com', + ); + + final coreWeb = FirebaseCoreWeb(); + final version = coreWeb.firebaseSDKVersion; + await coreWeb.injectSrcScript( + 'https://www.gstatic.com/firebasejs/$version/firebase-app.js', + 'firebase_core', + ); + + await FirebasePlatform.instance.initializeApp(options: options); + + // Must still be registered so a secondary app can reactivate App Check. + expect(FirebaseCoreWeb.isServiceRegistered('app-check'), isTrue); + expect(initializedAppNames, contains('[DEFAULT]')); + + await FirebasePlatform.instance.initializeApp( + name: 'prod', + options: options, + ); + + expect(FirebaseCoreWeb.isServiceRegistered('app-check'), isTrue); + expect( + initializedAppNames, + containsAll(['[DEFAULT]', 'prod']), + ); + }, + ); + }); +} From f6e601faa9cec93609460aaf8f316f96dbf17771 Mon Sep 17 00:00:00 2001 From: Jude Kwashie Date: Mon, 10 Aug 2026 12:08:05 +0000 Subject: [PATCH 2/2] style(core, web): format App Check service initialization chain --- .../firebase_core_web/lib/src/firebase_core_web.dart | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/firebase_core/firebase_core_web/lib/src/firebase_core_web.dart b/packages/firebase_core/firebase_core_web/lib/src/firebase_core_web.dart index 3a9f7ec650e8..c1c83936a222 100644 --- a/packages/firebase_core/firebase_core_web/lib/src/firebase_core_web.dart +++ b/packages/firebase_core/firebase_core_web/lib/src/firebase_core_web.dart @@ -414,9 +414,7 @@ class FirebaseCoreWeb extends FirebasePlatform { } await Future.wait( - _services.entries - .where((entry) => entry.key != 'app-check') - .map((entry) { + _services.entries.where((entry) => entry.key != 'app-check').map((entry) { final ensureInitializedFunction = entry.value.ensurePluginInitialized; if (ensureInitializedFunction == null || app == null) {