🛡️ Enforcing Single-Session Login in a Flutter App: Auto Session Kill on App Close

🛡️ Enforcing Single-Session Login in a Flutter App: Auto Session Kill on App Close

FlutterPulse

This article was translated specially for the channel FlutterPulseYou'll find lots of interesting things related to Flutter on this channel. Don't hesitate to subscribe!🚀

Scenario: In many applications — especially in financial or enterprise sectors — users are restricted from logging in on multiple devices…

Scenario: In many applications — especially in financial or enterprise sectors — users are restricted from logging in on multiple devices at the same time. This requires robust session management with a crucial need: automatically delete a session when the app is closed or killed.

In this article, we'll walk through:

  • Why this problem exists
  • Challenges with app lifecycle in mobile and web
  • How to implement session cleanup on app termination
  • Using platform-specific approaches for Android and Flutter Web

🚨 The Problem: Concurrent Sessions

Let's say you're building an app where each agent can only be logged in on one device. If a user logs in on another device while still active on the first, the second session fails — unless we clean up the first session.

But here's the challenge:

Most users don't log out manually. They just swipe the app away or close the tab.

If you don't handle this gracefully, users will be locked out by a ghost session. 😬

🧩 The Challenge: Detecting App Termination

Unfortunately, app termination is tricky to detect reliably:

🟢 On Android

  • If a user swipes the app away or removes it from recent tasks, onTaskRemoved() in a Foreground Service is your friend.

🟠 On Flutter Web

  • You can hook into window.onBeforeUnload to trigger last-second cleanup.

🔴 On iOS (not covered here)

  • More restrictive and unreliable for background tasks.

✅ Our Goal

We want to delete the user session on app close (whether it's mobile or web) by calling an API like:

https://api/WebApiRequest/PostRequestinfo/{value}
{
"apiName": "Delete_Session",
...
}

🔧 Implementation: Android Foreground Service

To handle Android app termination, we'll use a Foreground Service:

class SessionKillService : Service() {
private var userId: String? = null
private var sessionId: String? = null
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
startForeground(1, createNotification())
merchantId = intent?.getStringExtra("merchantId")
sessionId = intent?.getStringExtra("sessionId")
return START_STICKY
}
override fun onTaskRemoved(rootIntent: Intent?) {
sendDeleteSessionRequest(merchantId, sessionId)
stopSelf()
super.onTaskRemoved(rootIntent)
}
}

This service is started from Flutter using a MethodChannel, triggered when login is successful.

Flutter Dart Side:

void triggerSessionKillService({String? merchantId, String? sessionId}) {
const platform = MethodChannel('app.lifecycle.channel');
platform.invokeMethod('triggerService', {
"merchantId": merchantId ?? "",
"sessionId": sessionId ?? "",
});
}

Now if the app is swiped away, the session delete request is fired via the native service.

🕸️ Implementation: Flutter Web Using sendBeacon

For web, the sendBeacon API allows us to send non-blocking data even as the browser tab is closing:

html.window.onBeforeUnload.listen((event) {
final data = { ... }; // JSON payload
final blob = html.Blob([jsonEncode(data)], 'application/json');
html.window.navigator.sendBeacon(webhookUrl, blob);
});

This is perfect for short, critical requests that must be sent before unload.

⚠️ Avoid async/await or standard http.post() inside onBeforeUnload — the browser may terminate it.

🔍 Debugging with Webhook.site

To test this behavior, use https://webhook.site:

  • It gives you a temporary endpoint to receive HTTP POST requests
  • You can inspect headers, payload, and status in real time

Just paste the generated URL in your sendBeacon code and trigger a page close. Magic! ✨

🧠 Wrap-Up

This pattern helps you maintain strict session control in Flutter apps by:

  • Running a native background service on Android to detect task removal
  • Leveraging the browser's onBeforeUnload for Flutter Web
  • Ensuring that no orphan sessions are left open

🔒 It's a simple but effective way to enforce single-session login and avoid frustrating login issues due to stale sessions.

💡 Pro Tips

  • For iOS: Consider using server-side session expiry policies, as background execution is limited.
  • For Flutter desktop apps: Lifecycle management APIs are platform-dependent and evolving.
  • Always fallback gracefully — like allowing a "force login" option to invalidate prior sessions.

Report Page