android - FirebaseUser 无法重新验证以删除帐户
问题描述
正如标题所示,我无法删除 Firebase 用户。我在 Firebase 控制台中启用了 2 种登录类型:
- 匿名的
- 谷歌
这些提供程序类型在应用程序中镜像,使用登录不是问题firebase-ui-auth
listOf(
IdpConfig.AnonymousBuilder().build(),
IdpConfig.GoogleBuilder().build())
我希望用户能够删除他们的帐户,这对于匿名用户来说很好,但对于使用GoogleAuthCredential
. 为了做到这一点,文档说明您需要“重新验证”:FirebaseUser::reauthenticate
。这是我遇到麻烦的地方,重新身份验证总是失败:
FirebaseAuthInvalidCredentialsException
ERROR_INVALID_CREDENTIAL
The supplied auth credential is malformed or has expired. [ Invalid id_token in IdP response: <token provided in request>, error: id token is not issued by Google. ]
我已检查令牌是否在 UTC 到期时间内,并且我的设备时钟设置正确。
当前代码(使用协程):
class UserActions internal constructor(
private val context: Context,
private val authUI: AuthUI,
private val auth: FirebaseAuth) {
suspend fun signOut(): Boolean = suspendCoroutine { cont -> cont.suspendCompletableTask(authUI.signOut(context)) }
suspend fun delete(): Boolean {
auth.currentUser
?.takeIf { user -> !user.isAnonymous }
?.let { user ->
val tokenResult: GetTokenResult = suspendCoroutine { cont -> cont.suspendTask(user.getIdToken(true)) }
val credential : AuthCredential = GoogleAuthProvider.getCredential(tokenResult.token, null)
// Point of failure - always returns false with above error.
val success: Boolean = suspendCoroutine { cont -> cont.suspendCompletableTask(user.reauthenticate(credential)) }
if (!success) return false
}
return suspendCoroutine { cont -> cont.suspendCompletableTask(authUI.delete(context)) }
}
private fun <R> Continuation<R>.suspendTask(task: Task<R>) {
task.addOnSuccessListener { this.success(it) }
.addOnFailureListener { this.failure(it) }
}
private fun Continuation<Boolean>.suspendCompletableTask(task: Task<Void>) {
task.addOnSuccessListener { this.success() }
.addOnFailureListener { this.failure() }
}
private fun Continuation<Boolean>.success() = resume(true)
private fun Continuation<Boolean>.failure() = resume(false)
private fun <R> Continuation<R>.success(r : R) = resume(r)
private fun <R> Continuation<R>.failure(t : Exception) = resumeWithException(t)
}
我想可能是我错误地将令牌添加为参数argumentnt:
GoogleAuthProvider.getCredential(tokenResult.token, null)
所以换成:
GoogleAuthProvider.getCredential(null, tokenResult.token)
但是说我在错误描述中有一个无效的值,所以据我AuthCredential
所知,我有一个正确的参数和一个“有效”的 id 令牌。
我在这里做错了什么?
解决方案
已解决:最后对这个简单的答案。
两者都FirebaseUser
指GoogleSigInAccount
具有idToken
. 我在这里使用前者作为令牌:
val tokenResult: GetTokenResult = suspendCoroutine { cont -> cont.suspendTask(user.getIdToken(true)) }
val credential : AuthCredential = GoogleAuthProvider.getCredential(tokenResult.token, null)
现在AuthCredential
正在使用不正确的令牌。我应该使用的是:
val token = GoogleSignIn.getLastSignedInAccount(context)?.idToken.orEmpty()
val credential : AuthCredential = GoogleAuthProvider.getCredential(token, null)
所以错误是正确的 - 我FirebaseUser
在重新验证GoogleAuthCredential
应该使用令牌时使用了GoogleSigInAccount
令牌。
更新
由于令牌是短暂的,如果令牌变得陈旧,上述方法仍然会失败。解决方案是执行“静默登录”以刷新令牌。这无法完成,FirebaseAuth::silentSignin
因为如果 aFirebaseUser
已经登录,这将失败。它需要调用GoogleSignInClient::silentSignIn
。
修改后的完整代码:
class UserActions internal constructor(
private val context: Context,
private val authUI: AuthUI,
private val auth: FirebaseAuth) {
suspend fun signOut(): Boolean = suspendCoroutine { cont -> cont.suspendCompletableTask(authUI.signOut(context)) }
suspend fun delete(): Boolean {
auth.currentUser
?.takeIf { user -> !user.isAnonymous }
?.let { user ->
val credential: AuthCredential = GoogleAuthProvider.getCredential(getFreshGoogleIdToken(), null)
val success: Boolean = suspendCoroutine { cont -> cont.suspendCompletableTask(user.reauthenticate(credential)) }
if (!success) return false
}
return suspendCoroutine { cont -> cont.suspendCompletableTask(authUI.delete(context)) }
}
private suspend fun getFreshGoogleIdToken(): String = suspendCoroutine<GoogleSignInAccount> { cont ->
cont.suspendTask(
GoogleSignIn.getClient(
context,
GoogleSignInOptions.Builder()
.requestProfile()
.requestId()
.requestIdToken(context.getString(R.string.default_web_client_id))
.build())
.silentSignIn())
}.idToken.orEmpty()
private fun <R> Continuation<R>.suspendTask(task: Task<R>) {
task.addOnSuccessListener { this.success(it) }
.addOnFailureListener { this.failure(it) }
}
private fun Continuation<Boolean>.suspendCompletableTask(task: Task<Void>) {
task.addOnSuccessListener { this.success() }
.addOnFailureListener { this.failure() }
}
private fun Continuation<Boolean>.success() = resume(true)
private fun Continuation<Boolean>.failure() = resume(false)
private fun <R> Continuation<R>.success(r: R) = resume(r)
private fun <R> Continuation<R>.failure(t: Exception) = resumeWithException(t)
}
推荐阅读
- node-red - 使用 node-red 从活动中获取数据或有效负载
- java - while(true) 循环中的 switch 语句,文本出现两次
- powershell - 如何使 io.systemfileswatcher 无限期运行
- android - Ionic - 如何将证书添加到网络安全配置中?
- javascript - 如何比较 2 个 iframe 并在视觉上获得差异?
- java - Java中的@Value与单例
- c# - 使用 Caliburn Micro 在 WPF 中更新 DataGrid
- asp.net-mvc - ORA-12537 实现 Hangfire.FluentNHibernate 时出错
- c# - 如何使用实体框架和 linq 仅获取具有唯一属性的记录?
- python - 根据用户输入绘制一条线