【问题标题】:How to force logout firebase auth user from app remotely如何从应用程序远程强制注销firebase auth用户
【发布时间】:2019-04-04 21:17:52
【问题描述】:

我有一个项目,它使用 firebase auth 和 firebaseUI 对用户进行身份验证。我已启用 Google、Facebook 和电子邮件提供商。我需要的是远程注销或禁用一些用户。

我希望用户在这样做时退出应用。我尝试在 firebase 控制台中禁用用户,并使用 firebase admin SDK (https://firebase.google.com/docs/auth/admin/manage-sessions) 撤销刷新令牌。

我等了 2 天多,仍然注意到用户已登录并且可以访问 firestore 数据。

我也经历过并尝试过 Firebase still retrieving authData after deletion

谁能指出我做错了什么?

【问题讨论】:

    标签: firebase firebase-authentication firebase-admin


    【解决方案1】:

    您也不能远程强制用户退出。任何注销都必须在用户登录的设备上进行。

    访问令牌一旦生成,就无法撤销。这意味着即使您禁用该用户的帐户,他们也可以继续访问长达一个小时。

    如果这太长,诀窍(正如我对您链接的问题的回答中所提到的)是在您的数据库(或其他地方)中维护一个被阻止用户的列表,然后在您的安全规则中检查该列表(或其他授权层)。

    例如在实时数据库中,您可以创建一个被屏蔽用户的 UID 列表:

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

    然后在您的安全规则中检查:

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

    【讨论】:

    • 谢谢,@frank-van-puffelen。在我们的案例中,我们尝试禁用用户并尝试撤销他/她的刷新令牌,但即使等待 1 小时后用户仍然能够访问数据(我们也等待了 2 天 :))。我们还在 Firestore 中添加了规则,只允许经过身份验证的用户。如果我的理解有误,请帮助我理解何时撤销用户刷新令牌?
    • 我注意到我说的是 ID 令牌,我的意思是访问令牌,所以我解决了这个问题。撤销访问令牌是不够的,这就是安全规则的用武之地。如果这些步骤对您不起作用,请准确显示您所做的事情。没有代码或精确的重现步骤,任何人都很难说出你在哪里犯了错误。
    • @FrankvanPuffelen 在 Firebase 身份验证控制台中禁用用户后,用户需要多长时间才能注销?有这方面的文档吗?
    • "如果您禁用该用户的帐户,他们最多可以继续访问一个小时"
    • 这是一个愚蠢的问题,但是您实际上是如何在 Firebase 规则 DSL 中定义banned_uuids 的?只是复制粘贴是行不通的?
    【解决方案2】:

    您可以使用 FCM 发送消息数据以强制注销。

    例如,如果用户使用 android 应用程序。

    1. 将 FCM 令牌保存在 firebase Realtime 的集合中。
    2. 在服务中配置 Android 客户端应用程序。 LINK你必须在收到带有特殊字符串的消息时强制退出。
    3. 在云功能中做你需要的触发器,当你需要用户退出时发送数据LINK

    成功!

    【讨论】:

    • 我们如何使用 nodejs sdk 来实现这一点,我也没有使用 firebase db
    • 只需保存令牌并使用api发送数据firebase.google.com/docs/cloud-messaging/…
    • 不知道Android App在后台运行的情况下是否有效?
    【解决方案3】:

    根据您的情况,我假设您需要在用户被禁用时让用户注销。

    使用一个全局变量来存储 TokenNo(可能在共享偏好或 sqlite 中):

    将以下代码添加到您的清单中:

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

    在您的

    中添加以下代码
    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
         }
       }
    }
    

    这里发生了什么:
    当用户第一次登录时调用 onNewToken 然后它进入 else 然后 TokenNo 从 0 更新为 1。
    当您禁用任何用户时,会自动刷新令牌。然后调用 OnNewToken 然后 TokenNo>=1 ,因此用户将被注销。

    注意: 当用户第一次登录时,即如果没有存储 TokenNo 变量,则将其存储为 0。

    供参考:https://firebase.google.com/docs/reference/android/com/google/firebase/messaging/FirebaseMessagingService

    【讨论】:

    • 据我所知,以上onNewToken属于FCM。您能否解释一下为什么我们在刷新 FCM 令牌时将用户注销?
    【解决方案4】:

    尚未测试,因为我们负责设置 Firestore 规则的后端程序员已经离开了一天,但理论上这应该可以工作:(明天我会测试)

    有一个 FirebaseAuth.AuthStateListener 负责根据用户的状态为 UI 提供服务

    这与firestore中的规则相结合

    match /collection
    allow read: if isAuth();
    

    isAuth 在哪里:

    function isAuth() {
      return request.auth.uid != null;
    }
    

    如果用户随后被禁用,则在登录时,每当用户尝试从集合中读取数据时,他都应该被拒绝,并且应该进行 signOut() 调用。 然后 AuthStateListener 将检测到它,并将用户注销。

    【讨论】:

      【解决方案5】:

      我能想到的唯一方法是在您的开始活动中添加一个 if-else 块。 将用户的状态(已验证/禁止/删除)存储在 Firebase 实时数据库中。然后在应用程序启动时检索用户的状态并添加代码:

      if (currentUserStatus.equals("banned"))
      {
      currentUser.logout();
      }
      

      【讨论】:

        【解决方案6】:

        我所做的是在注册时为每个用户创建一个以 UID 作为文档 ID 的 Firestore 文档。在本文档中,我存储了一个数组,该数组存储了单个用户在登录新设备时收到的所有 fcm 令牌。这样,我始终跟踪用户登录的位置。当用户手动注销时,fcm 令牌将从 Firestore 以及设备上的文档中删除。

        为了能够在用户登录的任何地方注销用户,我执行了以下操作。启动应用程序时,一旦用户登录,我就会启动一个快照侦听器来侦听用户文档中的所有更改。一旦发生更改,我就会检索新的 fcm 令牌数组,在数组中搜索本地当前设备 fcm 令牌。如果找到了,我什么也不做。如果 fcm 令牌不再在数组中,我将调用本地注销方法并返回登录屏幕。

        这是我在 iOS 上 swift 中使用的方法。闭包(passOnMethod)只会触发登录视图控制器的展开segue。

        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)
                            }
                        }
                    }
                }
            }
        }
        

        这是我在除当前设备之外的任何地方注销用户时使用的方法。

        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()
                        }
                    }
                }
            }
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2015-09-27
          • 2022-01-11
          • 2011-04-27
          • 2019-07-31
          • 2019-09-20
          • 2021-07-30
          • 2018-07-08
          相关资源
          最近更新 更多