How to force logout firebase auth user from app remotely

12,534

Solution 1

You also cannot remotely force a user to be signed out. Any sign out will have to happen from the device that the user is signed in on.

There is no way to revoke an access token once that is minted. This means that even if you disable the user's account, they may continue to have access for up to an hour.

If that is too long, the trick (as also mentioned in my answer to the question you linked) is to maintain a list of blocked users in your database (or elsewhere) and then check against that in your security rules (or other authorization layer).

For example in the realtime database, you could create a list of blocked user's UIDs:

banned_uids: {
  "uid1": true
  "uid2": true
}

And then check against that in your security rules with:

".read": "auth.uid !== null && !root.child('banned_uids').child(auth.uid).exists()"

Solution 2

You can send a message data with FCM to force to log out.

For example, if the users use android application.

  1. Save the FCM token in a collection in firebase Realtime.
  2. configure the Android client app, in the service. LINK You have to make when receive a message with especial string, force to log out.
  3. make the trigger you need in cloud functions, to send the data LINK when you need the user log out.

SUCCESS!

Solution 3

As per your scenarios, i assume that you need to make user logout when user is disabled.

Use One global variable to store TokenNo (might be in shared preference or sqlite):

Add following code to your manifest:

<service android:name=".YourFirebaseMessagingService">
 <intent-filter>
     <action android:name="com.google.firebase.MESSAGING_EVENT" />
 </intent-filter>
</service>

Add following code in your

public class LogoutOntokenchange extends FirebaseMessagingService{
   @Override
   public void onNewToken (String token){
     if(TokenNo=>1){ //if tokenNo >=1 means he already logged in
       TokenNo=0;
       FirebaseAuth.getInstance().signOut(); //Then call signout method
     }
     else{
       TokenNo=1; //store token no in db
     }
   }
}

What Happens here:
When user logged in first time onNewToken is called then It goes into else then TokenNo is updated to 1 from 0.
When You disable any user then automatically token is refreshed.Then OnNewToken is called then TokenNo>=1 so user will be logged out.

NOTE: When user log in for first time i.e if TokenNo variable is not stored then store it as 0.

For reference: https://firebase.google.com/docs/reference/android/com/google/firebase/messaging/FirebaseMessagingService

Solution 4

What I've done is I created for each user upon registration a Firestore document with the UID as document ID. In this document I store an array which stores all fcm tokens the individual user receives when logging into a new device. That way I always keep track where the user is logged in. When the user logs out manually the fcm token will be deleted from the document in Firestore as well as on the device.

In order to be able to log out the user everywhere they are signed in I did the following. When starting the app and once the user is logged in I start a snapshot listener that listens to all changes in the users document. As soon as there is a change I retrieve the new array of fcm tokens, search inside the array for the local current device fcm token. If found, I do nothing. If the fcm token is no longer in the array I will call the local logout method and go back to the login screen.

Here are the methods I used in swift on iOS. The closures (passOnMethod) will just trigger an unwind segue to the login view controller.

import Foundation
import Firebase

class FB_Auth_Methods {

let db = Firestore.firestore()
var listener: ListenerRegistration?

func trackLoginStatus(passOnMethod: @escaping () -> () ) {
    listener?.remove()
    if let loggedInUserA_UID = Auth.auth().currentUser?.uid {
        listener = db.collection(K.FStore.collectionOf_RegisteredUsers_Name)
            .document(loggedInUserA_UID)
            .addSnapshotListener { (snapshotDocument, error) in
                if let error = error {
                    print(error)
                } else {
                    if let document = snapshotDocument {
                        if let data = document.data() {
                            if let fcmTokens = data[K.FStore.Users.fcmTokens] as? [String] {
                                print("Found the following tokens: \(fcmTokens)")
                                self.compareTokensAgainstCurrentDeviceToken(fcmTokens: fcmTokens, passOnMethod: { () in
                                    passOnMethod()
                                })
                            }
                        }
                    }
                }
        }
    }
}

func compareTokensAgainstCurrentDeviceToken(fcmTokens: [String], passOnMethod: @escaping () -> () ) {
    InstanceID.instanceID().instanceID { (result, error) in
        if let error = error {
            print(error)
        } else if let result = result {
            if fcmTokens.contains(result.token) {
                print("Token found, doing nothing")
            } else {
                print("Token no longer found, logout user")
                do {
                    try Auth.auth().signOut()
                    InstanceID.instanceID().deleteID { error in
                        if let error = error {
                            print(error)
                        } else {
                            passOnMethod()
                        }


                    }
                    } catch let signOutError as NSError {
                        print (signOutError)
                    }
                }
            }
        }
    }
}

And here is the method I use when logging out the user everywhere but at the current device.

func deleteAllFcmTokensExceptCurrent(loggedInUserA: User, passOnMethod: @escaping () -> () )  {

    InstanceID.instanceID().instanceID { (result, error) in
        if let error = error {
            print(error)
        } else if let result = result {
            let batch = self.db.batch()

            let deleteAllFcmRef = self.db.collection(K.FStore.collectionOf_RegisteredUsers_Name).document(loggedInUserA.uid)
            batch.updateData([K.FStore.Users.fcmTokens: FieldValue.delete()], forDocument: deleteAllFcmRef)

            let updateFcmTokenRef = self.db.collection(K.FStore.collectionOf_RegisteredUsers_Name).document(loggedInUserA.uid)
            batch.updateData([K.FStore.Users.fcmTokens: FieldValue.arrayUnion([result.token])], forDocument: updateFcmTokenRef)

            batch.commit { (error) in
                if let error = error {
                    print(error)
                } else {
                    passOnMethod()
                }
            }
        }
    }
}
Share:
12,534
Ashwin Valento
Author by

Ashwin Valento

Updated on June 06, 2022

Comments

  • Ashwin Valento
    Ashwin Valento about 2 years

    I have a project which uses firebase auth with firebaseUI to authenticate users. I have enabled Google, Facebook and email providers. What I need is to remotely logout or disable some of the users.

    I want the users to logout from the app on doing so. I tried disabling the user in the firebase console and also used the firebase admin SDK (https://firebase.google.com/docs/auth/admin/manage-sessions) to revoke the refresh tokens.

    I waited for more than 2 days and still noticed that the user was logged in and could access the firestore data.

    I have also gone through and tried Firebase still retrieving authData after deletion

    Can anyone point to what I am doing wrong ?