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

Kus*_*han 5 android firebase firebase-authentication

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

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

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

getIdToken(boolean forceRefresh)
Run Code Online (Sandbox Code Playgroud)

例如令牌是...

 01-18 17:20:08.904 15947-15947/? D/FragmentCreate: Token found without force refresh from a single thread eyJhbGciOiJSUzI1NiIsImtpZCI6ImExNTAxNjY5NTNiYTFhMjBjY2FhOTdmOTM4M2NiMDg3OTYyODBkZDcifQ.eyJpc3MiOiJodHR....JTXpA
Run Code Online (Sandbox Code Playgroud)

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

例如。

01-18 17:22:16.990 15947-15947/? D/FragmentCreate: Token found single thread after force refresh eyJhbGciOiJSUzI1NiIsImtpZCI6ImExNTAxNjY5NTNiYTFhMjBjY2FhOTdmOTM4M2NiMDg3OTYyODBkZDcifQ.eyJpc3MiOiJod.......xQCA
Run Code Online (Sandbox Code Playgroud)

现在,当我在最后一次显示的强制刷新一个小时后调用getIdToken(false)时,令牌确实随着过期而发生了变化,并且从深入研究Firebase代码中发现了一些类似isValid()的检查,我猜测即使强制刷新为false,令牌到期并刷新它。

为了使事情变得有趣,我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
Run Code Online (Sandbox Code Playgroud)

令我惊讶的是,该令牌确实确实如我所预料的那样得到了刷新,但是它们两个都给出了相同的结果。这种暗示表明,用于内部获取令牌的方法可能已同步,并且至少在从两个不同的线程将强制刷新设置为false的情况下调用该方法至少会返回相同的结果。如果将forceRefresh设置为true,则同时线程打印具有相同值的新令牌时也会显示相似的结果

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

PS用于获取令牌而无需从两个线程刷新的代码是

                        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();
Run Code Online (Sandbox Code Playgroud)

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

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

                            }
                        });

                    }
Run Code Online (Sandbox Code Playgroud)

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

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

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

Fab*_*ese 5

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

来自官方文档

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

而(来自官方文档):

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

本节介绍如何检索令牌以及如何监视对令牌的更改。由于初始启动后令牌可能会轮换,因此强烈建议您检索最新更新的注册令牌。

在以下情况下,注册令牌可能会更改:

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

因此,这一点似乎有点偏离重点:为什么要拥有一个线程安全且唯一的身份验证令牌?在您的后端服务器上,您可以uid通过调用适当的函数(例如 Node.js 代码)来简单地获取用户信息:

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

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