【问题标题】:Firebase: How to check if a user auth was overriden by another provider?Firebase:如何检查用户身份验证是否被另一个提供商覆盖?
【发布时间】:2021-10-10 10:42:34
【问题描述】:

目前,我有四种不同的提供商方法可以在我的应用中登录/创建帐户:

  • 电子邮件和密码
  • 谷歌
  • 脸书
  • 微软

当用户第一次通过其中一个提供商登录(或通过电子邮件和密码创建帐户)时,我会自动创建一个带有附加信息的 用户文档在云火库中。这是我的一个提供商(电子邮件和密码)的问题:

当用户使用提供商“电子邮件和密码”创建帐户时,他可以选择他/她喜欢的任何电子邮件,因此也可以使用 Google 或 Microsoft 电子邮件并提供错误的附加信息(错误的名称等)。然后,此错误信息将存储在用户创建帐户后我创建的 user-document 中。

如果上述用户未验证他的电子邮件并通过 Google 或 Microsoft 提供商登录,则旧的“电子邮件和密码”提供商将被覆盖,但不是我之前创建的错误 用户文档。

我的问题是:有没有办法观察或知道一个提供者被另一个提供者覆盖,因此也覆盖了错误的用户文档?

通用代码

// Check if user-document already exists, so it does not get overridden 
// when logging in via Google, Microsoft or Facebook-auth
private suspend fun isUserRegistered(): Boolean {
    val document = dbFirestore.collection(FIREBASE_USER_BASE_PATH).document(dbAuth.currentUser!!.uid).get().await()
    return document.exists()
}

private suspend fun createUserIfNotExist(user: UserNetworkEntity) {
    if (isUserRegistered()) return
    createUserAccount(dbAuth.currentUser!!.uid, user)
}

// Function to check whether user is verified and delete old doc if not verified 
// Here the user-data and the created user-doc will be verified via firebase-admin-sdk
private suspend fun callUserOnCheck(email: String): HttpsCallableResult? {
    return dbFunctions
        .getHttpsCallable(FUNCTION_USER_ON_CHECK_EMAIL)
        .call(hashMapOf<String, String?>("email" to email))
        .await()
}

Google 代码

override fun signInWithGoogle(account: GoogleSignInAccount): Flow<LoginStateEvent> = flow {
    val credential = GoogleAuthProvider.getCredential(account.idToken, null)

    // Delete old doc if not verified. Getting email from GoogleSignInAccount
    // IMPORTANT: We have to check the old document here, before calling dbAuth.signInWithCredential()
    // because #signInWithCredential() would override the old email-provider
    val email = account.email!!
    callUserOnCheck(email)

    dbAuth.signInWithCredential(credential).await()

    createUserIfNotExist(UserNetworkEntity(fullName = fullName))
}

Facebook 代码

override fun signInWithFacebook(): Flow<LoginStateEvent> = flow {
    val result = fLoginManager.registerMCallback(callbackManager)

    val credential = FacebookAuthProvider.getCredential(result.accessToken.token)

    // Delete old doc if not verified. Getting email from Facebook-GraphResponse
    // IMPORTANT: We have to check the old document here, before calling dbAuth.signInWithCredential()
    // because #signInWithCredential() would override the old email-provider
    callUserOnCheckFacebook(result.accessToken)

    dbAuth.signInWithCredential(credential).await()

    createUserIfNotExist(UserNetworkEntity(fullName = dbAuth.currentUser!!.displayName!!))
}

// Function to check whether user is verified. Special case: Get Facebook email via GraphResponce 
private suspend fun callUserOnCheckFacebook(accessToken: AccessToken) {
    val email = getFacebookEmail(accessToken)
    if (email != null) callUserOnCheck(email)
}

// Get Facebook email via GraphResponce without the need to login via #signInWithCredential()
private suspend fun getFacebookEmail(accessToken: AccessToken) = suspendCancellableCoroutine<String?> { cont ->
    val callback = GraphRequest.GraphJSONObjectCallback { json, response ->
        // OnError
        response?.error?.let { cont.resumeWithException(Throwable(it.errorMessage)) }

        // OnSucess
        cont.resume(json?.optString("email"), cont::resumeWithException)
    }

    GraphRequest.newMeRequest(accessToken, callback)
}

Microsoft 代码(如何解决 TODO?)

override fun signInWithMicrosoft(activity: Activity): Flow<LoginStateEvent> = flow {

   // TODO: Check whether old created user-doc is valid or not before calling
   // dbAuth.pendingAuthResult or ..startActivityForSignIn... as this would
   // override the old existing email-provider.
   // Question: How to get microsoft-email without calling #pendingAuthResult or #startActivityForSignIn ?
    
    val result = dbAuth.pendingAuthResult?.await() ?: dbAuth.startActivityForSignInWithProvider(
        activity,
        microsoftOAuth.build()
    ).await()

    createUserIfNotExist(UserNetworkEntity(fullName = result.user!!.displayName!!))
}

【问题讨论】:

    标签: android firebase firebase-authentication


    【解决方案1】:

    我想您可能正在寻找fetchSignInMethodsForEmail function。您可以将此用户的电子邮件传递给此用户,然后检查该电子邮件是否已与电子邮件+密码提供商一起使用(在这种情况下,您需要清理该文档)。

    【讨论】:

    • 是的,这可能有效。但是我需要进一步检查给定电子邮件的用户是否经过验证(如果用户经过验证,我不想清理文档)。结合上面给定的函数fetchSignInMethodsForEmail,我怎样才能做到这一点?
    • 无法在客户端 SDK 中检索您没有凭据的用户配置文件,因为这会带来安全风险。如果您需要这种检查,您通常希望使用 Admin SDK 在受信任的环境(例如您的开发机器、您控制的服务器或云功能)中执行它:firebase.google.com/docs/auth/admin
    • 好的,那么我将为此使用 firebase admin sdk 并为此编写一个 firebase 函数。您知道我是否可以将fetchSignInMethodsForEmail 用于上述所有提供者?我知道如何获取电子邮件和密码、google 和 facebook 的电子邮件,但不知道如何获取 microsoft。
    • 在 Admin SDK 中你会使用 getUserByEmail: firebase.google.com/docs/reference/admin/node/…
    • 是的,我知道这一点,但是如何在用户登录并因此覆盖之前检索用户的电子邮件。我已经扩展了我的代码示例
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-01-10
    • 2020-09-17
    • 1970-01-01
    • 2018-12-28
    • 2017-05-15
    • 2023-01-31
    • 2022-01-21
    相关资源
    最近更新 更多