You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过Firebase实现Android应用账号单设备同时登录限制?

Implement Single-Device Login for Android Apps with Firebase

Great question! Enforcing single-device login for your Android app using Firebase is totally feasible—here’s a practical, production-ready approach I’ve implemented in several projects:

Core Idea

The goal is to track each user’s active device session. When a new login occurs, we either reject it or invalidate the existing session on the old device, ensuring only one device stays logged in at a time.

Step 1: Track Device & User Sessions

First, we need a reliable way to identify each device. Firebase Installations ID is perfect here—it’s a stable, privacy-compliant identifier for your app’s instance on a device.

Get the Device ID

val installations = FirebaseInstallations.getInstance()
installations.id.addOnCompleteListener { task ->
    if (task.isSuccessful) {
        val deviceId = task.result
        // Use this ID for session tracking later
    } else {
        // Handle failure (e.g., network issues)
    }
}

Store Session Data in Firestore

We’ll use Firestore for this example (Realtime Database works similarly). Create a user_sessions collection where each document ID matches the user’s Firebase Auth UID. The document will store:

  • device_id: The current active device’s ID
  • last_login_timestamp: Server-side timestamp for freshness
  • fcm_token: (Optional) FCM token to send logout alerts to old devices
// After successful Firebase Auth login
FirebaseAuth.getInstance().signInWithEmailAndPassword(email, password)
    .addOnCompleteListener { authTask ->
        if (authTask.isSuccessful) {
            val user = authTask.result.user ?: return@addOnCompleteListener
            val userSessionRef = FirebaseFirestore.getInstance()
                .collection("user_sessions")
                .document(user.uid)

            val sessionData = hashMapOf(
                "device_id" to deviceId,
                "last_login_timestamp" to FieldValue.serverTimestamp(),
                "fcm_token" to yourFcmToken // Fetch this via Firebase Messaging
            )

            userSessionRef.set(sessionData)
                .addOnSuccessListener {
                    // Session saved, proceed to app home
                }
                .addOnFailureListener { e ->
                    // Handle session save failure
                }
        }
    }

Step 2: Validate Login & Invalidate Old Sessions

When a user tries to log in, check if they already have an active session on another device:

// After login, validate the session
userSessionRef.get().addOnSuccessListener { document ->
    if (document.exists()) {
        val storedDeviceId = document.getString("device_id")
        if (storedDeviceId != deviceId) {
            // Option 1: Reject new login (notify user)
            FirebaseAuth.getInstance().signOut()
            Toast.makeText(this, "Your account is already logged in on another device", Toast.LENGTH_LONG).show()

            // Option 2: Force old device offline (update session to current device)
            userSessionRef.update(
                "device_id", deviceId,
                "last_login_timestamp", FieldValue.serverTimestamp(),
                "fcm_token", yourFcmToken
            )
            // Notify old device via FCM (see next step)
        }
    } else {
        // No existing session, create new one (as shown in Step 1)
        userSessionRef.set(sessionData)
    }
}

Step 3: Force Old Device Logout

Set up a real-time listener on the user’s session document to auto-logout the old device when the session changes:

// In your main activity or base activity
val currentUser = FirebaseAuth.getInstance().currentUser
currentUser?.let { user ->
    val userSessionRef = FirebaseFirestore.getInstance()
        .collection("user_sessions")
        .document(user.uid)

    userSessionRef.addSnapshotListener { snapshot, error ->
        if (snapshot != null && snapshot.exists()) {
            val storedDeviceId = snapshot.getString("device_id")
            if (storedDeviceId != currentDeviceId) {
                // Account logged in elsewhere—force logout
                FirebaseAuth.getInstance().signOut()
                startActivity(Intent(this, LoginActivity::class.java))
                Toast.makeText(this, "Your account was logged in on another device", Toast.LENGTH_LONG).show()
                finish()
            }
        }
    }
}

Optional: Notify Old Device via FCM

If you stored the old device’s FCM token, send a push notification to alert the user:

// Cloud Function (Node.js) to send logout alerts
const functions = require("firebase-functions");
const admin = require("firebase-admin");
admin.initializeApp();

exports.sendLogoutNotification = functions.firestore
    .document("user_sessions/{userId}")
    .onUpdate(async (change, context) => {
        const oldData = change.before.data();
        const newData = change.after.data();
        
        if (oldData.device_id !== newData.device_id && oldData.fcm_token) {
            const message = {
                notification: {
                    title: "Session Expired",
                    body: "Your account has been logged in on another device."
                },
                data: {
                    action: "sign_out"
                },
                token: oldData.fcm_token
            };
            
            await admin.messaging().send(message);
        }
    });

Step 4: Secure Your Data

Add Firestore security rules to ensure only the user can access their session data:

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /user_sessions/{userId} {
      allow read, write: if request.auth != null && request.auth.uid == userId;
    }
  }
}

Key Notes

  • Skip Android ID: Firebase Installations ID is more reliable and privacy-friendly than Android ID.
  • Offline Handling: If a user logs in offline, the session update will sync once they’re online—make sure your listener handles this gracefully.
  • Server-Side Validation: For stricter control, use Cloud Functions to validate logins server-side (prevents client-side tampering).

内容的提问来源于stack exchange,提问作者Muhammad Faizan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 12:35:35