@@ -15,6 +15,9 @@ import 'package:meta/meta.dart';
1515import  'package:retry/retry.dart' ;
1616import  'package:rxdart/subjects.dart' ;
1717
18+ import  'broadcast_stub.dart'  if  (dart.library.html) './broadcast_web.dart' 
19+     as  web;
20+ 
1821part  'gotrue_mfa_api.dart' ;
1922
2023/// {@template gotrue_client} 
@@ -84,6 +87,11 @@ class GoTrueClient {
8487
8588  final  AuthFlowType  _flowType;
8689
90+   /// Proxy to the web BroadcastChannel API. Should be null on non-web platforms. 
91+    BroadcastChannel ?  _broadcastChannel;
92+ 
93+   StreamSubscription ?  _broadcastChannelSubscription;
94+ 
8795  /// {@macro gotrue_client} 
8896   GoTrueClient ({
8997    String ?  url,
@@ -116,6 +124,8 @@ class GoTrueClient {
116124    if  (_autoRefreshToken) {
117125      startAutoRefresh ();
118126    }
127+ 
128+     _mayStartBroadcastChannel ();
119129  }
120130
121131  /// Getter for the headers 
@@ -1128,6 +1138,63 @@ class GoTrueClient {
11281138    _currentUser =  null ;
11291139  }
11301140
1141+   void  _mayStartBroadcastChannel () {
1142+     if  (const  bool .fromEnvironment ('dart.library.html' )) {
1143+       // Used by the js library as well 
1144+       final  broadcastKey = 
1145+           "sb-${Uri .parse (_url ).host .split ("." ).first }-auth-token" ;
1146+ 
1147+       assert (_broadcastChannel ==  null ,
1148+           'Broadcast channel should not be started more than once.' );
1149+       try  {
1150+         _broadcastChannel =  web.getBroadcastChannel (broadcastKey);
1151+         _broadcastChannelSubscription = 
1152+             _broadcastChannel? .onMessage.listen ((messageEvent) {
1153+           final  rawEvent =  messageEvent['event' ];
1154+           final  event =  switch  (rawEvent) {
1155+             // This library sends the js name of the event to be comptabile with 
1156+             // the js library, so we need to convert it back to the dart name 
1157+             'INITIAL_SESSION'  =>  AuthChangeEvent .initialSession,
1158+             'PASSWORD_RECOVERY'  =>  AuthChangeEvent .passwordRecovery,
1159+             'SIGNED_IN'  =>  AuthChangeEvent .signedIn,
1160+             'SIGNED_OUT'  =>  AuthChangeEvent .signedOut,
1161+             'TOKEN_REFRESHED'  =>  AuthChangeEvent .tokenRefreshed,
1162+             'USER_UPDATED'  =>  AuthChangeEvent .userUpdated,
1163+             'MFA_CHALLENGE_VERIFIED'  =>  AuthChangeEvent .mfaChallengeVerified,
1164+             // This case should never happen though 
1165+             _ =>  AuthChangeEvent .values
1166+                 .firstWhereOrNull ((event) =>  event.name ==  rawEvent),
1167+           };
1168+ 
1169+           if  (event !=  null ) {
1170+             Session ?  session;
1171+             if  (messageEvent['session' ] !=  null ) {
1172+               session =  Session .fromJson (messageEvent['session' ]);
1173+             }
1174+             if  (session !=  null ) {
1175+               _saveSession (session);
1176+             } else  {
1177+               _removeSession ();
1178+             }
1179+             notifyAllSubscribers (event, session:  session, broadcast:  false );
1180+           }
1181+         });
1182+       } catch  (e) {
1183+         // Ignoring 
1184+       }
1185+     }
1186+   }
1187+ 
1188+   @mustCallSuper 
1189+   void  dispose () {
1190+     _onAuthStateChangeController.close ();
1191+     _onAuthStateChangeControllerSync.close ();
1192+     _broadcastChannel? .close ();
1193+     _broadcastChannelSubscription? .cancel ();
1194+     _refreshTokenCompleter? .completeError (AuthException ('Disposed' ));
1195+     _autoRefreshTicker? .cancel ();
1196+   }
1197+ 
11311198  /// Generates a new JWT. 
11321199  /// 
11331200  /// To prevent multiple simultaneous requests it catches an already ongoing request by using the global [_refreshTokenCompleter] . 
@@ -1181,9 +1248,23 @@ class GoTrueClient {
11811248  }
11821249
11831250  /// For internal use only. 
1251+   /// 
1252+   /// [broadcast]  is used to determine if the event should be broadcasted to 
1253+   /// other tabs. 
11841254   @internal 
1185-   void  notifyAllSubscribers (AuthChangeEvent  event) {
1186-     final  state =  AuthState (event, currentSession);
1255+   void  notifyAllSubscribers (
1256+     AuthChangeEvent  event, {
1257+     Session ?  session,
1258+     bool  broadcast =  true ,
1259+   }) {
1260+     session ?? =  currentSession;
1261+     if  (broadcast &&  event !=  AuthChangeEvent .initialSession) {
1262+       _broadcastChannel? .postMessage ({
1263+         'event' :  event.jsName,
1264+         'session' :  session? .toJson (),
1265+       });
1266+     }
1267+     final  state =  AuthState (event, session, fromBroadcast:  ! broadcast);
11871268    _onAuthStateChangeController.add (state);
11881269    _onAuthStateChangeControllerSync.add (state);
11891270  }
0 commit comments