我无法在 Firebase 中重新验证或撤销用户的访问令牌

Abh*_*han 3 android firebase firebase-authentication

要求

1 . 我想让用户选择删除他/她的帐户,而现在用户可以使用 Google 和手机登录。

我阅读了一些文档,结果证明如果我可以重新验证用户身份,我可以轻松删除该帐户,但我无法这样做。

这是我用来重新验证帐户的代码

目前我只是在尝试使用 Google。

 final FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
    GoogleSignInAccount account = GoogleSignIn.getLastSignedInAccount(getActivity());
    if(account != null && user != null) {
        AuthCredential credential = GoogleAuthProvider.getCredential(account.getIdToken(),null);
        user.reauthenticate(credential)
                .addOnSuccessListener(new OnSuccessListener<Void>() {
                    @Override
                    public void onSuccess(Void aVoid) {
                        Log.d(TAG,"reauthenticated");
                    }
                })
Run Code Online (Sandbox Code Playgroud)

它会产生一个错误,即

com.google.firebase.auth.FirebaseAuthInvalidCredentialsException: The supplied auth credential is malformed or has expired. [ ID Token issued at 1587271042 is stale to sign-in.

通过阅读一些文档,我也明白如果我没有错,这是因为令牌的有效期为一小时,而我正在尝试在一小时后访问它。也就是说,为什么我会收到此错误?

我包含了此代码,以便您可以告诉我另一种方法。

我也知道另一种方式,我试过:

通过Delete account点击按钮,我可以通过弹出一个谷歌帐户对话框来启动谷歌登录流程,以便用户可以再次登录,因为这将是一个全新的登录,那么我可以说它user.delete()会删除帐户,但由于三个原因,它不是一个好的选择:

1用户会思考为什么他/她必须再次选择一个帐户

2我无法更改该对话框的标题。它将始终具有标题选择帐户以继续“我的应用程序名称”,这并不反映我删除帐户的意图。

3用户不知道他/她必须选择当前登录的帐户,他/她可能会选择其他帐户

我不想通过将他/她带到登录流程来打扰用户。我可以刷新令牌并立即删除帐户吗?

或者,如果没有任何方法并且用户必须再次登录,我是否可以使用 AuthUI 以某种方式进行操作,因为这对用户和我来说都更方便,因为我不必为所有人实现自定义 UI供应商?

与此相关的问题很多,答案为零。我希望这个不会属于这一类。

sam*_*man 5

使用过期的身份验证令牌将不允许您通过 Firebase 进行身份验证。因此,首先您必须获得一个新的 ID 令牌。

如果GoogleSignInAccount您的设备上存储的支持(您有存储的刷新令牌),您应该能够使用它silentSignIn()来获取新的 ID 令牌,然后将其传递到 Firebase。

下面的流程是从 JavaScript 中粗略地删除的。预计会有拼写错误和错误,但它应该为您(或其他人)指明正确的方向。

public void deleteCurrentFirebaseUser() {
  final FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
  if (user == null) {
    // TODO: Throw error or show message to user
    return;
  }

  // STEP 1: Get a new ID token (using cached user info)
  Task<GoogleSignInAccount> task = mGoogleSignInClient.silentSignIn();
  task
    .continueWithTask(Continuation<GoogleSignInAccount, Task<AuthResult>>() {
      @Override
      public void then(Task<GoogleSignInAccount> silentSignInTask) {
        GoogleSignInAccount acct = silentSignInTask.getResult();
        // STEP 2: Use the new token to reauthenticate with Firebase
        AuthCredential credential = GoogleAuthProvider.getCredential(acct.getIdToken(), null);
        return mAuth.reauthenticate(credential);
      }
    })
    .continueWithTask(Continuation<AuthResult, Task<Void>>() {
      @Override
      public void then(Task<AuthResult> firebaseSignInTask) {
        AuthResult result = firebaseSignInTask.getResult();
        // STEP 3: If successful, delete the user
        FirebaseUser user = result.getUser();
        return user.delete();
      }
    })
    .addOnCompleteListener(this, new OnCompleteListener<Void>() {
      @Override
      public void onComplete(@NonNull Task<Void> deleteUserTask) {
        // STEP 4: Handle success/errors
        if (task.isSuccessful()) {
          // The user was successfully deleted
          Log.d(TAG, "deleteCurrentFirebaseUser:success");
          // TODO: Go to sign-in screen
        } else {
          // The user was not deleted
          // Google sign in, Firebase sign in or Firebase delete user operation failed.
          Log.w(TAG, "deleteCurrentFirebaseUser:failure", task.getException());
          Snackbar.make(mBinding.mainLayout, "Failed to delete user.", Snackbar.LENGTH_SHORT).show();

          final Exception taskEx = task.getException();
          if (taskEx instanceof ApiException) {
            ApiException apiEx = (ApiException) taskEx;
            int googleSignInStatusCode = apiEx.getStatusCode();
            // TODO: Handle Google sign-in exception based on googleSignInStatusCode
            // e.g. GoogleSignInStatusCodes.SIGN_IN_REQUIRED means the user needs to do something to allow background sign-in.
          } else if (taskEx instanceof FirebaseAuthException) {
            // One of:
            //  - FirebaseAuthInvalidUserException (disabled/deleted user)
            //  - FirebaseAuthInvalidCredentialsException (token revoked/stale)
            //  - FirebaseAuthUserCollisionException (does the user already exist? - it is likely that Google Sign In wasn't originally used to create the matching account)
            //  - FirebaseAuthRecentLoginRequiredException (need to reauthenticate user - it shouldn't occur with this flow)

            FirebaseAuthException firebaseAuthEx = (FirebaseAuthException) taskEx;
            String errorCode = firebaseAuthEx.getErrorCode(); // Contains the reason for the exception
            String message = firebaseAuthEx.getMessage();
            // TODO: Handle Firebase Auth exception based on errorCode or more instanceof checks
          } else {
            // TODO: Handle unexpected exception
          }
        }
      }
    });
}
Run Code Online (Sandbox Code Playgroud)

上述方法的替代方法是使用可调用云函数,该函数使用Admin SDK 的删除用户函数,如@example所评论。这是一个简单的实现(没有任何确认步骤):

public void deleteCurrentFirebaseUser() {
  final FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
  if (user == null) {
    // TODO: Throw error or show message to user
    return;
  }

  // STEP 1: Get a new ID token (using cached user info)
  Task<GoogleSignInAccount> task = mGoogleSignInClient.silentSignIn();
  task
    .continueWithTask(Continuation<GoogleSignInAccount, Task<AuthResult>>() {
      @Override
      public void then(Task<GoogleSignInAccount> silentSignInTask) {
        GoogleSignInAccount acct = silentSignInTask.getResult();
        // STEP 2: Use the new token to reauthenticate with Firebase
        AuthCredential credential = GoogleAuthProvider.getCredential(acct.getIdToken(), null);
        return mAuth.reauthenticate(credential);
      }
    })
    .continueWithTask(Continuation<AuthResult, Task<Void>>() {
      @Override
      public void then(Task<AuthResult> firebaseSignInTask) {
        AuthResult result = firebaseSignInTask.getResult();
        // STEP 3: If successful, delete the user
        FirebaseUser user = result.getUser();
        return user.delete();
      }
    })
    .addOnCompleteListener(this, new OnCompleteListener<Void>() {
      @Override
      public void onComplete(@NonNull Task<Void> deleteUserTask) {
        // STEP 4: Handle success/errors
        if (task.isSuccessful()) {
          // The user was successfully deleted
          Log.d(TAG, "deleteCurrentFirebaseUser:success");
          // TODO: Go to sign-in screen
        } else {
          // The user was not deleted
          // Google sign in, Firebase sign in or Firebase delete user operation failed.
          Log.w(TAG, "deleteCurrentFirebaseUser:failure", task.getException());
          Snackbar.make(mBinding.mainLayout, "Failed to delete user.", Snackbar.LENGTH_SHORT).show();

          final Exception taskEx = task.getException();
          if (taskEx instanceof ApiException) {
            ApiException apiEx = (ApiException) taskEx;
            int googleSignInStatusCode = apiEx.getStatusCode();
            // TODO: Handle Google sign-in exception based on googleSignInStatusCode
            // e.g. GoogleSignInStatusCodes.SIGN_IN_REQUIRED means the user needs to do something to allow background sign-in.
          } else if (taskEx instanceof FirebaseAuthException) {
            // One of:
            //  - FirebaseAuthInvalidUserException (disabled/deleted user)
            //  - FirebaseAuthInvalidCredentialsException (token revoked/stale)
            //  - FirebaseAuthUserCollisionException (does the user already exist? - it is likely that Google Sign In wasn't originally used to create the matching account)
            //  - FirebaseAuthRecentLoginRequiredException (need to reauthenticate user - it shouldn't occur with this flow)

            FirebaseAuthException firebaseAuthEx = (FirebaseAuthException) taskEx;
            String errorCode = firebaseAuthEx.getErrorCode(); // Contains the reason for the exception
            String message = firebaseAuthEx.getMessage();
            // TODO: Handle Firebase Auth exception based on errorCode or more instanceof checks
          } else {
            // TODO: Handle unexpected exception
          }
        }
      }
    });
}
Run Code Online (Sandbox Code Playgroud)

这将被调用使用:

FirebaseFunctions.getInstance()
  .getHttpsCallable("deleteMe")
  .call()
  .continueWith(new Continuation<HttpsCallableResult, Void>() {
    @Override
    public void then(@NonNull Task<HttpsCallableResult> task) {
      if (task.isSuccessful()) {
        // deleted user!
      } else {
        // failed!
      }
    }
  });
Run Code Online (Sandbox Code Playgroud)

如果您使用 Cloud Functions 方法,我强烈建议您在删除用户的帐户之前向用户的链接电子邮件地址发送一封确认电子邮件,以确保这不是坏人。以下是实现这一目标所需的粗略草案:

exports.deleteMe = functions.https.onCall((data, context) => {
  if (!context.auth) {
    throw new functions.https.HttpsError('failed-precondition', 'The function must be called while authenticated.');
  }

  const uid = context.auth.uid;

  return admin.auth().deleteUser(uid)
    .then(() => {
      console.log('Successfully deleted user');
      return 'Success!';
    })
    .catch(error => {
      console.error('Error deleting user: ', error);
      throw new functions.https.HttpsError('internal', 'Failed to delete user.', error.code);
    });
});
Run Code Online (Sandbox Code Playgroud)