diff --git a/android/src/main/java/com/frontegg/reactnative/FronteggRNModule.kt b/android/src/main/java/com/frontegg/reactnative/FronteggRNModule.kt index a88993d..19190fe 100644 --- a/android/src/main/java/com/frontegg/reactnative/FronteggRNModule.kt +++ b/android/src/main/java/com/frontegg/reactnative/FronteggRNModule.kt @@ -108,8 +108,12 @@ class FronteggRNModule(val reactContext: ReactApplicationContext) : } @ReactMethod - fun logout() { - auth.logout() + fun logout(promise: Promise) { + // Resolve only after the SDK reports the logout finished, so JS can + // await the actual end of the session (parity with the iOS bridge). + auth.logout { + promise.resolve("Success") + } } @ReactMethod diff --git a/ios/FronteggRN.m b/ios/FronteggRN.m index 7b91721..159da8c 100644 --- a/ios/FronteggRN.m +++ b/ios/FronteggRN.m @@ -5,7 +5,10 @@ @interface RCT_EXTERN_MODULE(FronteggRN, RCTEventEmitter) RCT_EXTERN_METHOD(subscribe) -RCT_EXTERN_METHOD(logout) +RCT_EXTERN_METHOD( + logout: (RCTPromiseResolveBlock)resolve + rejecter: (RCTPromiseRejectBlock)reject + ) RCT_EXTERN_METHOD( login: (NSString *)loginHint resolver: (RCTPromiseResolveBlock)resolve diff --git a/ios/FronteggRN.swift b/ios/FronteggRN.swift index a3c530f..0e5531c 100644 --- a/ios/FronteggRN.swift +++ b/ios/FronteggRN.swift @@ -109,11 +109,29 @@ class FronteggRN: RCTEventEmitter { } @objc - func logout() -> [AnyHashable : Any]! { - DispatchQueue.main.sync { - fronteggApp.auth.logout() + func logout(_ resolve: @escaping RCTPromiseResolveBlock, rejecter: RCTPromiseRejectBlock) -> Void { + DispatchQueue.main.async { + // Use the completion overload so JS can await the actual end of + // the session, and push the final state when it lands. The + // fire-and-forget call relies solely on Combine-driven events, + // which can be lost when logout coincides with app-level + // teardown — leaving the JS state authenticated forever and + // breaking the next login's state-transition detection. The SDK + // completion is success-only, so there is no reject path. + self.fronteggApp.auth.logout { _ in + // Read/write hasListeners + pendingObservingState on main — + // RCTEventEmitter mutates them on main (start/stopObserving), + // so touching them off-main would be a data race. + DispatchQueue.main.async { + if self.hasListeners { + self.sendEventToJS() + } else { + self.pendingObservingState = true + } + resolve("Success") + } + } } - return ["status": "OK"] } @objc diff --git a/src/FronteggNative.ts b/src/FronteggNative.ts index 8783ee2..104b839 100644 --- a/src/FronteggNative.ts +++ b/src/FronteggNative.ts @@ -31,7 +31,7 @@ export function login(loginHint?: string) { }); } -export function logout() { +export function logout(): Promise { return FronteggRN.logout(); }