Skip to content

Commit 093b5fe

Browse files
fix(app-check, web): fixed broken onTokenChanged and ensured it is properly cleaned up. Streams are also cleaned up on "hot restart" (#12933)
* chore: reinsert debug token initialiser * fix(app-check): broken stream handling * fix: app check stream clean up on hot restart * chore: format
1 parent f2fc902 commit 093b5fe

File tree

3 files changed

+34
-12
lines changed

3 files changed

+34
-12
lines changed

packages/firebase_app_check/firebase_app_check/example/web/index.html

+3
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@
3333
<link rel="manifest" href="manifest.json">
3434
</head>
3535
<body>
36+
<script>
37+
self.FIREBASE_APPCHECK_DEBUG_TOKEN = true;
38+
</script>
3639
<script src="flutter_bootstrap.js" async></script>
3740
</body>
3841
</html>

packages/firebase_app_check/firebase_app_check_web/lib/firebase_app_check_web.dart

+20-10
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ import 'src/interop/app_check.dart' as app_check_interop;
1919
class FirebaseAppCheckWeb extends FirebaseAppCheckPlatform {
2020
static const recaptchaTypeV3 = 'recaptcha-v3';
2121
static const recaptchaTypeEnterprise = 'enterprise';
22-
2322
static Map<String, StreamController<String?>> _tokenChangesListeners = {};
2423

2524
/// Stub initializer to allow the [registerWith] to create an instance without
@@ -121,17 +120,25 @@ class FirebaseAppCheckWeb extends FirebaseAppCheckPlatform {
121120
return convertWebExceptions<Future<void>>(() async {
122121
_webAppCheck ??= app_check_interop.getAppCheckInstance(
123122
core_interop.app(app.name), webProvider);
124-
if (_tokenChangesListeners[app.name] == null) {
125-
_tokenChangesListeners[app.name] =
126-
StreamController<String?>.broadcast();
127-
128-
_delegate!.onTokenChanged().map((event) {
129-
_tokenChangesListeners[app.name]!.add(event.token.toDart);
130-
});
131-
}
123+
_initialiseStreamController();
132124
});
133125
}
134126

127+
void _initialiseStreamController() {
128+
if (_tokenChangesListeners[app.name] == null) {
129+
_tokenChangesListeners[app.name] = StreamController<String?>.broadcast(
130+
onCancel: () {
131+
_tokenChangesListeners[app.name]!.close();
132+
_tokenChangesListeners.remove(app.name);
133+
_delegate!.idTokenChangedController?.close();
134+
},
135+
);
136+
_delegate!.onTokenChanged(app.name).listen((event) {
137+
_tokenChangesListeners[app.name]!.add(event.token.toDart);
138+
});
139+
}
140+
}
141+
135142
@override
136143
Future<String?> getToken(bool forceRefresh) async {
137144
return convertWebExceptions<Future<String?>>(() async {
@@ -162,6 +169,9 @@ class FirebaseAppCheckWeb extends FirebaseAppCheckPlatform {
162169

163170
@override
164171
Stream<String?> get onTokenChange {
165-
return _tokenChangesListeners[app.name]!.stream;
172+
_initialiseStreamController();
173+
return convertWebExceptions(
174+
() => _tokenChangesListeners[app.name]!.stream,
175+
);
166176
}
167177
}

packages/firebase_app_check/firebase_app_check_web/lib/src/interop/app_check.dart

+11-2
Original file line numberDiff line numberDiff line change
@@ -69,11 +69,18 @@ class AppCheck extends JsObjectWrapper<app_check_interop.AppCheckJsImpl> {
6969

7070
JSFunction? _idTokenChangedUnsubscribe;
7171

72+
StreamController<app_check_interop.AppCheckTokenResult>?
73+
get idTokenChangedController => _idTokenChangedController;
74+
7275
StreamController<app_check_interop.AppCheckTokenResult>?
7376
// ignore: close_sinks
7477
_idTokenChangedController;
7578

76-
Stream<app_check_interop.AppCheckTokenResult> onTokenChanged() {
79+
String _appCheckWindowsKey(String appName) =>
80+
'flutterfire-${appName}_onTokenChanged';
81+
Stream<app_check_interop.AppCheckTokenResult> onTokenChanged(String appName) {
82+
final appCheckWindowsKey = _appCheckWindowsKey(appName);
83+
unsubscribeWindowsListener(appCheckWindowsKey);
7784
if (_idTokenChangedController == null) {
7885
final nextWrapper = ((app_check_interop.AppCheckTokenResult result) {
7986
_idTokenChangedController!.add(result);
@@ -83,17 +90,19 @@ class AppCheck extends JsObjectWrapper<app_check_interop.AppCheckJsImpl> {
8390
((JSError e) => _idTokenChangedController!.addError(e)).toJS;
8491

8592
void startListen() {
86-
assert(_idTokenChangedUnsubscribe == null);
8793
_idTokenChangedUnsubscribe = app_check_interop.onTokenChanged(
8894
jsObject,
8995
nextWrapper,
9096
errorWrapper,
9197
);
98+
setWindowsListener(appCheckWindowsKey, _idTokenChangedUnsubscribe!);
9299
}
93100

94101
void stopListen() {
95102
_idTokenChangedUnsubscribe?.callAsFunction();
96103
_idTokenChangedUnsubscribe = null;
104+
_idTokenChangedController = null;
105+
removeWindowsListener(appCheckWindowsKey);
97106
}
98107

99108
_idTokenChangedController =

0 commit comments

Comments
 (0)