Reputation: 4712
Currently, I have four different provider-methods to sign-in/create an account in my app:
When the user logs in the first time via one of the providers (or creates an account via email and password) I automatically create a user-document with additional info in cloud-firestore. Here comes the problem with one of my providers (email and password):
When the user creates an account with provider "email and password" he can choose whatever email he/she likes and therefore could also use a Google or Microsoft email and provide wrong additional info (wrong name etc.). This wrong information is then stored in the user-document that I create ONCE the user creates his account.
If the above user then not verifies his email and logs in via Google or Microsoft provider, the old "email and password" provider gets overridden BUT NOT THE WRONG user-document I created before.
My question is: Is there a way to observer, or know, that a provider was overridden by another provider and therefore also override the wrong user-document?
// 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()
}
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))
}
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)
}
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!!))
}
Upvotes: 0
Views: 295
Reputation: 599696
I think you may be looking for the fetchSignInMethodsForEmail
function. You can pass this the email of the user, and then check if the email is already in use with the email+password provider (in which case you need to clean up that document).
Upvotes: 2