fix: stop push notifications after session expires without explicit logout - #312
Merged
deaflynx merged 15 commits intoSep 10, 2026
Conversation
…logout-304' into fix/push-notifications-after-logout-304
…board#304) - await Firebase init in main() so the apps.isNotEmpty guards are deterministic, and move the Firebase-configured guard into NotificationService instead of copying it at every call site - persist the push-registration flag via DatabaseKeys and ILocalDatabaseService, checked with containsKey instead of a string sentinel - rename handleSessionExpired to cleanUpStalePushRegistration and split the local teardown out of logout() into _cleanupPushRegistration() - handle failures of the remaining fire-and-forget removeMobileSession calls (token refresh listener, _resetToken) and clear the cached FCM token after deletion - fix TbContext.logout() calling NotificationService.init() instead of logout() - drive the real cleanup path in tests via injectable Firebase collaborators and pin the persisted key contract
…logout-304' into fix/push-notifications-after-logout-304 # Conflicts: # lib/main.dart
…d#304) - move the teardown's try/catch into _tearDownLocalPushState() so an offline logout still clears the client session; the flag stays set and the next launch retries - serialize the stale cleanup: concurrent calls share one run and init() awaits it, so a fast auto-login cannot register a token that is being deleted - save a refreshed FCM token even when no previous token was resolved - give each _getAndSaveToken() path a single owner of the flag write - read the registration flag by value, camelCase its persisted key - declare dio under dependencies (imported by production code) - tests: cover init() flag writes, token rotation, token refresh, cleanup ordering/deduplication and offline logout; helper registers its own getIt teardown
- catch a failing registration-flag read inside the stale cleanup so the shared future never fails and init() still registers afterwards - document that init() only waits for a cleanup already in flight, and that the ordering relies on the service being a locator singleton - tests: rotation without a fresh token, provisional permission, failed save in the refresh listener, retry after an interrupted teardown, unreadable flag; extract the repeated stub/verify helpers
…logout-304' into fix/push-notifications-after-logout-304
…t-switch fix: make QR-code login and endpoint switch reliable
…external-url-link fix(dashboard): display dashboard images configured via external URL link
…ifications-after-logout-304 # Conflicts: # lib/utils/services/notification_service.dart
…ifications-after-logout-304 # Conflicts: # lib/core/auth/login/provider/login_provider.g.dart # lib/utils/services/notification_service.dart
…logout-304' into fix/push-notifications-after-logout-304 # Conflicts: # lib/core/auth/login/provider/login_provider.g.dart
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #304
Merge of the CE fix (thingsboard/flutter_thingsboard_app#248) into PE.
Problem
When the session ends without an explicit logout (refresh token expires while the app is unused), the FCM token stays registered on the platform and the device keeps receiving alarm push notifications after the automatic logout.
Solution
push_notifications_registeredflag (TbStorage) whenever the FCM token is registered with the platform — on a freshsaveMobileSessionand wheninit()finds an existing valid session (covers installs upgrading to this version).NotificationService.handleSessionExpired(): if the flag is set, runs the full push cleanup and clears the flag. Deleting the local FCM token is what actually stops delivery — the JWT is already invalid at this point, so the server-sideremoveMobileSessioncall usually cannot succeed; subsequent pushes to the deleted token bounce withUNREGISTEREDand the platform purges the mobile session on the next delivery attempt.Login.handleUserLoaded()triggers the cleanup whenever it detects an unauthenticated client with Firebase configured. Because the trigger is the persisted flag (not the in-memory login state), it works on cold start — the main reported scenario (app killed, token expired days ago) — as well as when the expiry happens while the app is running. It is a no-op on fresh installs, after manual logout, and on subsequent logged-out launches.NotificationService.logout()now awaitsremoveMobileSessioninside a try/catch: previously the call was fire-and-forget and produced an unhandled async error exactly in the expired-token case.Limitations
Pushes delivered between the token expiry and the next app launch cannot be stopped from the client — closing that gap requires a platform-side change (tying mobile session lifetime to the auth session), which is being discussed separately.
Testing
handleSessionExpiredflag logic (test/utils/services/notification_service_test.dart), passing on the PE tree.flutter analyze— no new findings on touched files.