什么是FirebaseAuth ID令牌,它是线程安全的getIdToken方法吗?

时间:2018-01-18 13:25:46

标签: android firebase firebase-authentication

我一直在使用FirebaseAuth登录用户,并注意到FirebaseUser的getToken方法返回的ID令牌与FCM FirebaseInstanceIdService不同。

FirebaseAuth ID令牌和FCM实例ID onTokenRefresh返回令牌之间究竟有什么区别?实例ID令牌和ID令牌的名称相似,因此对我来说似乎有点混乱。

根据我的观察,FirebaseUser对象上通过getIdToken方法获得的FirebaseAuth令牌将在一小时内到期。

getIdToken(boolean forceRefresh)

例如,令牌是......

 01-18 17:20:08.904 15947-15947/? D/FragmentCreate: Token found without force refresh from a single thread eyJhbGciOiJSUzI1NiIsImtpZCI6ImExNTAxNjY5NTNiYTFhMjBjY2FhOTdmOTM4M2NiMDg3OTYyODBkZDcifQ.eyJpc3MiOiJodHR....JTXpA

我观察到将forceRefresh设置为true会立即更改令牌。

例如

01-18 17:22:16.990 15947-15947/? D/FragmentCreate: Token found single thread after force refresh eyJhbGciOiJSUzI1NiIsImtpZCI6ImExNTAxNjY5NTNiYTFhMjBjY2FhOTdmOTM4M2NiMDg3OTYyODBkZDcifQ.eyJpc3MiOiJod.......xQCA

现在当我在最后一次显示的强制刷新一小时后调用getIdToken(false)时,令牌确实会因为已经过期而改变,并且从一点点挖掘到firebase代码,我可以看到一些像isValid()这样的检查我猜测检查令牌到期并刷新它,即使强制刷新是假的。

为了让事情变得有趣,我在令牌到期后稍微同时从两个线程调用了getIdToken(false),以查看是否两者都打印了不同的令牌,因为两者都会看到令牌同时到期并且因此两者都应该尝试刷新令牌

例如

01-18 18:25:29.479 29849-29849/com.foodiniq.waitlist.katana D/FragmentCreate: Token found from thread1 after expiry eyJhbGciOiJSUzI1NiIsImtpZCI6ImExNTAxNjY5NTNiYTFhMjBjY2FhOTdmOTM4M2NiMDg3OTYyODBkZDcifQ.eyJpc3MiOiJod........GpdbA


01-18 18:25:30.071 29849-29849/com.foodiniq.waitlist.katana D/FragmentCreate: Token found from thread1 after expiry eyJhbGciOiJSUzI1NiIsImtpZCI6ImExNTAxNjY5NTNiYTFhMjBjY2FhOTdmOTM4M2NiMDg3OTYyODBkZDcifQ.eyJpc3MiOiJod........GpdbA

令我惊讶的是,正如我预测的那样,令牌确实得到了更新,但两者都给出了相同的结果。这种暗示,用于在内部获取令牌的方法可能是同步的,并且至少在从两个不同线程将force refresh设置为false时调用返回相同的结果。对于forceRefresh设置为true,同时线程打印具有相同值的新标记

也显示类似的结果

我假设getIdToken(false)方法是线程安全的并且在同时调用时始终只返回所有线程中的相同值,这是正确的吗?这种行为会不同于getIdToken(true)?

P.S用于获取令牌而不刷新两个线程的代码是

                        Thread testThread1 = new Thread(new Runnable() {
                        @Override
                        public void run() {

                            if(FirebaseAuth.getInstance().getCurrentUser()!=null){

                                FirebaseAuth.getInstance().getCurrentUser().getIdToken(false).addOnCompleteListener(new OnCompleteListener<GetTokenResult>() {
                                    @Override
                                    public void onComplete(@NonNull Task<GetTokenResult> task) {

                                        if (task.isSuccessful()) {
                                            Log.d("FragmentCreate","Token found from thread1 after expiry "+task.getResult().getToken());
                                        }

                                    }
                                }).addOnFailureListener(new OnFailureListener() {
                                    @Override
                                    public void onFailure(@NonNull Exception e) {

                                        Log.d("FragmentCreate","Token failed from main thread single "+e.toString());

                                    }
                                });

                            }

                        }
                    });

                    Thread testThread2 = new Thread(new Runnable() {
                        @Override
                        public void run() {

                            if(FirebaseAuth.getInstance().getCurrentUser()!=null){

                                FirebaseAuth.getInstance().getCurrentUser().getIdToken(false).addOnCompleteListener(new OnCompleteListener<GetTokenResult>() {
                                    @Override
                                    public void onComplete(@NonNull Task<GetTokenResult> task) {

                                        if (task.isSuccessful()) {
                                            Log.d("FragmentCreate","Token found from thread2 after expiry "+task.getResult().getToken());
                                        }

                                    }
                                }).addOnFailureListener(new OnFailureListener() {
                                    @Override
                                    public void onFailure(@NonNull Exception e) {

                                        Log.d("FragmentCreate","Token failed from main thread single "+e.toString());

                                    }
                                });

                            }

                        }
                    });

                    testThread1.start();

                    testThread2.start();

从一个线程强制刷新调用令牌的方法是:

        if(FirebaseAuth.getInstance().getCurrentUser()!=null){

                        FirebaseAuth.getInstance().getCurrentUser().getIdToken(true).addOnCompleteListener(new OnCompleteListener<GetTokenResult>() {
                            @Override
                            public void onComplete(@NonNull Task<GetTokenResult> task) {

                                if (task.isSuccessful()) {
                                    Log.d("FragmentCreate","Token found single thread after force refresh "+task.getResult().getToken());
                                }

                            }
                        }).addOnFailureListener(new OnFailureListener() {
                            @Override
                            public void onFailure(@NonNull Exception e) {

                                Log.d("FragmentCreate","Token failed from main thread single "+e.toString());

                            }
                        });

                    }

由于以下原因,我想要一个线程安全的实现:

在每次推出应用时,我都会在不强制刷新的情况下获取令牌。并在所有后续请求中使用此令牌。(因此可能存在此令牌可能已过期的点,但用户保持应用程序运行一小时的情况不太可能)

我在后端做的就像你说验证id令牌一样。这里可能存在令牌可能已过期的点。所以我发送一个响应代码,告诉客户端手动刷新令牌。现在,这是在后端的所有Servlet上完成的,因此其中许多可以同时返回过期的响应代码。在这个响应中,我从任何获得响应代码的线程刷新客户端上的令牌,从而导致许多不同的线程在客户端上调用刷新方法。我基本上担心可能是令牌

刷新时的配额

1 个答案:

答案 0 :(得分:4)

Firebase ID令牌和FCM令牌是两个完全不同的东西:第一个是身份验证令牌,需要在您的后端服务器上验证请求,后者是唯一标识应用程序安装的实例,以了解发送给谁正确的信息。还要考虑Firebase用户身份验证ID - 这是特定用户帐户的线程安全和唯一ID。

来自官方doc

  

验证ID令牌
  如果Firebase客户端应用程序与自定义后端服务器通信,则可能需要标识该服务器上当前已登录的用户。为了安全地这样做,在成功登录后,使用HTTPS将用户的ID令牌发送到您的服务器。然后,在服务器上,验证ID令牌的完整性和真实性,并从中检索uid。您可以使用以这种方式传输的uid来安全地识别服务器上当前登录的用户。

while(from official doc):

  

访问设备注册令牌
在应用初次启动时,   FCM SDK为客户端应用程序生成注册令牌   实例。如果要定位单个设备或创建设备   组,您需要通过扩展来访问此令牌   FirebaseInstanceIdService。

     

本节介绍如何检索令牌以及如何监控   更改令牌。因为令牌可以在初始后旋转   启动时,强烈建议您检索最新更新   注册令牌。

     

注册令牌可能会在以下情况下发生变化:

     
      
  • 该应用删除实例ID
  •   
  • 该应用已在新设备上恢复
  •   
  • 用户卸载/重新安装应用
  •   
  • 用户清除应用数据。
  •   

因此,这一点似乎略微偏离焦​​点:为什么你想要一个线程安全且唯一的身份验证令牌?在您的后端服务器上,您可以通过调用正确的函数(例如Node.js代码)来获取用户的uid

admin.auth().verifyIdToken(idToken)
  .then(function(decodedToken) {
    var uid = decodedToken.uid;
    // ...
  }).catch(function(error) {
    // Handle error
  });

如果这些信息改变了对代码中发生的事情的理解,请随意详细说明/改进问题。