Skip to content
Closed
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 @@ -4,6 +4,7 @@
// found in the LICENSE file.

import 'dart:async';
import 'dart:io' show Platform;

import 'package:_flutterfire_internals/_flutterfire_internals.dart';
import 'package:firebase_auth_platform_interface/src/method_channel/method_channel_multi_factor.dart';
Expand All @@ -18,6 +19,25 @@ import 'method_channel_user.dart';
import 'method_channel_user_credential.dart';
import 'utils/exception.dart';

/// Returns `true` when the `id-token` [EventChannel] subscription should be
/// skipped. Only effective on Windows when the host app has explicitly opted
/// in via [FirebaseAuthPlatform.disableIdTokenChannelOnWindows]. Never
/// returns `true` on web or on platforms other than Windows.
///
/// See [FirebaseAuthPlatform.disableIdTokenChannelOnWindows] for the
/// rationale (upstream Flutter engine threading bug tracked in
/// https://github.com/firebase/flutterfire/issues/18210 /
/// https://github.com/flutter/flutter/issues/134346).
bool _shouldSkipIdTokenChannel() {
if (kIsWeb) return false;
if (!FirebaseAuthPlatform.disableIdTokenChannelOnWindows) return false;
try {
return Platform.isWindows;
} on Object catch (_) {
return false;
}
}

/// Method Channel delegate for [FirebaseAuthPlatform].
class MethodChannelFirebaseAuth extends FirebaseAuthPlatform {
/// The [MethodChannelFirebaseAuth] method channel.
Expand Down Expand Up @@ -74,16 +94,21 @@ class MethodChannelFirebaseAuth extends FirebaseAuthPlatform {
/// Creates a new instance with a given [FirebaseApp].
MethodChannelFirebaseAuth({required FirebaseApp app})
: super(appInstance: app) {
_api.registerIdTokenListener(pigeonDefault).then((channelName) {
final events = EventChannel(channelName, channel.codec);
events
.receiveGuardedBroadcastStream(onError: convertPlatformException)
.listen(
(arguments) {
_handleIdTokenChangesListener(app.name, arguments);
},
);
});
// Skip the `id-token` EventChannel subscription on Windows when the host
// app has opted in via `FirebaseAuthPlatform.disableIdTokenChannelOnWindows`.
// See `_shouldSkipIdTokenChannel()` for the rationale.
if (!_shouldSkipIdTokenChannel()) {
_api.registerIdTokenListener(pigeonDefault).then((channelName) {
final events = EventChannel(channelName, channel.codec);
events
.receiveGuardedBroadcastStream(onError: convertPlatformException)
.listen(
(arguments) {
_handleIdTokenChangesListener(app.name, arguments);
},
);
});
}

_api.registerAuthStateListener(pigeonDefault).then((channelName) {
final events = EventChannel(channelName, channel.codec);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,31 @@ abstract class FirebaseAuthPlatform extends PlatformInterface {
/// If not `null`, this value will supersede `authDomain` property set in `initializeApp`.
String? customAuthDomain;

/// Opt-in workaround for the Windows-only native threading bug tracked in:
/// - https://github.com/firebase/flutterfire/issues/18210
/// - https://github.com/flutter/flutter/issues/134346
///
/// When set to `true` **before** calling [Firebase.initializeApp], the
/// method-channel implementation skips registering the `id-token`
/// [EventChannel] subscription on Windows. This avoids the
/// `FAST_FAIL_INVALID_ARG` (`0xc0000409` / `STATUS_STACK_BUFFER_OVERRUN`)
/// process abort produced when the native Windows `firebase_auth` plugin
/// dispatches id-token events from a background thread.
///
/// Trade-off: with this flag enabled, `idTokenChanges()` and
/// `userChanges()` on Windows only emit on sign-in / sign-out (via the
/// separate `auth-state` channel). Mid-session token-refresh events
/// (e.g. custom-claims updates) are not observed until the underlying
/// engine fix ships. `authStateChanges()` and all imperative APIs
/// (`getIdToken`, `getIdTokenResult`, sign-in, sign-up, sign-out, etc.)
/// are unaffected.
///
/// Has no effect on web or on platforms other than Windows.
///
/// This flag is a temporary mitigation and will be removed in a future
/// release once the underlying engine bug is resolved.
static bool disableIdTokenChannelOnWindows = false;

/// Create an instance using [app]
FirebaseAuthPlatform({this.appInstance}) : super(token: _token);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Copyright 2025, the Chromium project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

import 'package:firebase_auth_platform_interface/firebase_auth_platform_interface.dart';
import 'package:flutter_test/flutter_test.dart';

/// Tests for the Windows-only opt-in
/// [FirebaseAuthPlatform.disableIdTokenChannelOnWindows] flag that works
/// around the upstream engine threading bug
/// (https://github.com/firebase/flutterfire/issues/18210 /
/// https://github.com/flutter/flutter/issues/134346).
void main() {
TestWidgetsFlutterBinding.ensureInitialized();

tearDown(() {
FirebaseAuthPlatform.disableIdTokenChannelOnWindows = false;
});

group('FirebaseAuthPlatform.disableIdTokenChannelOnWindows', () {
test('defaults to false so existing apps see no behavior change', () {
expect(FirebaseAuthPlatform.disableIdTokenChannelOnWindows, isFalse);
});

test('is a plain mutable static bool toggle', () {
FirebaseAuthPlatform.disableIdTokenChannelOnWindows = true;
expect(FirebaseAuthPlatform.disableIdTokenChannelOnWindows, isTrue);

FirebaseAuthPlatform.disableIdTokenChannelOnWindows = false;
expect(FirebaseAuthPlatform.disableIdTokenChannelOnWindows, isFalse);
});
});
}
Loading