Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -394,15 +402,20 @@ 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();
Expand Down
Original file line number Diff line number Diff line change
@@ -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 = <String>[];

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']),
);
},
);
});
}
Loading