Android Studio Kotlin Firebase Authentication issues with email formatting - firebase

First of all I'm very new to coding, so perhaps this will be easy to explain, but I've been stuck on it for several weeks and I can't seem to find the solution.
I'm doing a tutorial on Firebase authentication, and I'm trying to create a user with email and password. I keep getting this Log: "com.google.firebase.auth.FirebaseAuthInvalidCredentialsException: The email address is badly formatted." I'm using a pixel5 emulator, latest version of Android Studio and compileSdk 33.
I will show you my code below:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
auth = Firebase.auth
val email = binding.enterEmailtxt.toString().trim()
val password = binding.enterPasstxt.toString().trim()
binding.CreateAcctBtn.setOnClickListener {
Log.d("EMAIL STRING:", email)
auth.createUserWithEmailAndPassword(email, password)
.addOnCompleteListener(this) {
task -\>
if (task.isSuccessful) {
var user: FirebaseUser? = auth.currentUser
if (user != null) {
Log.d("USER:", user.email.toString())
}
} else {
Log.d("USER FAIL:", task.exception.toString())
}
}
}
//I have this user in firebase already
auth.signInWithEmailAndPassword("email#gmail.com", "password123")
.addOnCompleteListener(this) {
task -\>
if (task.isSuccessful) {
//Sign is was successful
Log.d("SUCCESS:", "User signed in")
Toast.makeText(this, "Signed in Successfully", Toast.LENGTH_LONG).show()
}else {
//Not successful
Log.d("FAILURE:", "User not signed in")
Toast.makeText(this, "Sign in not successful", Toast.LENGTH_LONG).show()
}
}
}
public override fun onStart() {
super.onStart()
// Check if user is signed in (non-null) and update UI accordingly.
val currentUser = auth.currentUser
if (currentUser != null) {
Toast.makeText(this, "User is logged in", Toast.LENGTH_LONG).show()
Log.d("LOGIN SUCCESS:", "User is logged in")
}else {
Toast.makeText(this, "User is logged out", Toast.LENGTH_LONG).show()
Log.d("LOGIN FAILURE:", "User is logged out")
}
the "EMAIL STRING:" log is so I could see what email string Firebase is actually getting, and in logcat this is what I am seeing:
EMAIL STRING: com.example.introtokotlinfirebase D androidx.appcompat.widget.AppCompatEditText{8c1ff5b VFED..CL. ......I. 0,0-0,0 #7f0800ba app:id/enterEmailtxt}
Maybe that's normal?? Like I said I'm very new at this. I do have the input type as textEmailAddress in the XML.
I tried creating a user with several different email addresses that I know are legit, 1 is a gmail address.
I did have a lot of extra code commented out that I deleted just FYI
Thank you! in advance :)

Related

Apple sign in causes FIRAuthErrorUserInfoNameKey=ERROR_EMAIL_ALREADY_IN_USE (Code = 17007)

Using SwiftUI, Xcode12.5.1, Swift5.4.2, iOS14.7.1,
My Firebase-Email/Password Login-page shall be extended with other Login possibilities such as Apple-Login (eventually Google-login, Facebook-login etc).
My steps:
log in with Email/Password to Firebase
log out
log in with "Sign in with Apple"
--> Then I get the following error:
Error Domain=FIRAuthErrorDomain Code=17007
"The email address is already in use by another account."
UserInfo={NSLocalizedDescription=The email address is already in use by another account.,
FIRAuthErrorUserInfoNameKey=ERROR_EMAIL_ALREADY_IN_USE}
What I intended to do is to link the existing Email/Password-Firebase-Account to the Sign in with Apple-Account (as described here and here).
But for doing that I would need the error FIRAuthErrorUserInfoUpdatedCredentialKey that allows to retrieve the old user eventually.
In my case, I get ERROR_EMAIL_ALREADY_IN_USE which does not lead to any old user to be linked.
What do I have to do ?
Here is my code:
let credential = OAuthProvider.credential(withProviderID: "apple.com", idToken: idTokenString, rawNonce: nonce)
Auth.auth().signIn(with: credential) { (authResult, error) in
if (error != nil) {
print(error?.localizedDescription as Any)
return
}
print("signed in with Apple...")
do {
// if user did log in with Email/Password previously
if let email = try THKeychain.getEmail(),
let password = try THKeychain.getPassword() {
let credential = EmailAuthProvider.credential(withEmail: email, password: password)
if let user = authResult?.user {
// here I am trying to link the existing Firebase-Email/Password account to the just signed-in with Apple account
user.link(with: credential) { (result, linkError) in
print(linkError) // this is where I get FIRAuthErrorUserInfoNameKey=ERROR_EMAIL_ALREADY_IN_USE
// unfortunately, the two accounts are not linked as expected due to this error !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// What is missing ??????????????????
loginStatus = true
}
}
} else {
loginStatus = true
}
} catch {
print(error.localizedDescription)
}
}
On the Firebase-documentation it sais:
Sign in with Apple will not allow you to reuse an auth credential to link to an existing account. If you want to link a Sign in with Apple credential to another account, you must first attempt to link the accounts using the old Sign in with Apple credential and then examine the error returned to find a new credential. The new credential will be located in the error's userInfo dictionary and can be accessed via the FIRAuthErrorUserInfoUpdatedCredentialKey key.
What does the part "...If you want to link a Sign in with Apple credential to another account, you must first attempt to link the accounts using the old Sign in with Apple credential..." exactly mean ? WHAT IS THE old Sign in with Apple credential ????????
And how would I do that ?
In fact, at the linking-call, I actually expected some sort of linkError.userInfo with an updated user to sign in with. But the linkError in my example only gives me the ERROR_EMAIL_ALREADY_IN_USE error without further userInfo.
As Peter Friese mentions in his Blog, I should somehow be able to retrieve a AuthErrorUserInfoUpdatedCredentialKey from the error.userInfo. But in my case, the linkError does not have any kind of such information - unfortunately!
Here is an excerpt of Peter's example: (again not applicable in my case for some unknown reason?????)
currentUser.link(with: credential) { (result, error) in // (1)
if let error = error, (error as NSError).code == AuthErrorCode.credentialAlreadyInUse.rawValue { // (2)
print("The user you're signing in with has already been linked, signing in to the new user and migrating the anonymous users [\(currentUser.uid)] tasks.")
if let updatedCredential = (error as NSError).userInfo[AuthErrorUserInfoUpdatedCredentialKey] as? OAuthCredential {
print("Signing in using the updated credentials")
Auth.auth().signIn(with: updatedCredential) { (result, error) in
if let user = result?.user {
// TODO: handle data migration
self.doSignIn(appleIDCredential: appleIDCredential, user: user) // (3)
}
}
}
}
}
Reversing the order of linking made me advance a tiny bit.
If I press the Sign in with Apple button, my code now logs in with Firebase-Email/Password first (i.e. the necessary credentials are taken from the Keychain). And on a second step, links with the Apple-credentials. And by doing so, the linking finally gives me the desired AuthErrorUserInfoUpdatedCredentialKey in the link-callback.
There I retrieve the updatedCredential to log in with Apple.
See code below.
HOWEVER, I STILL DON'T KNOW WHY AFTER LOGIN THIS WAY, MY DATA IS STILL MISSING ???????
HOW DOES THIS DATA-MIGRATION STEP WORK ???
Shouldn't the user.link(with: appleCredentials) { ... } do the job ?
What do I need to do in order to get the very same Firebase-Data, no matter the login method ???
let appleCredentials = OAuthProvider.credential(withProviderID: "apple.com", idToken: idTokenString, rawNonce: nonce)
do {
// if user did log in with Email/Password anytime before
if let email = try THKeychain.getEmail(),
let password = try THKeychain.getPassword() {
let firebaseEmailCredentials = EmailAuthProvider.credential(withEmail: email, password: password)
Auth.auth().signIn(with: firebaseEmailCredentials) { (authResult, error) in
if let user = authResult?.user {
user.link(with: appleCredentials) { (result, linkError) in
if let linkError = linkError, (linkError as NSError).code == AuthErrorCode.credentialAlreadyInUse.rawValue {
print("The user you're signing in with has been linked.")
print("Signing in to Apple and migrating the email/pw-firebase-users [\(user.uid)]` data.")
if let updatedCredential = (linkError as NSError).userInfo[AuthErrorUserInfoUpdatedCredentialKey] as? OAuthCredential {
print("Signing in using the updated credentials")
Auth.auth().signIn(with: updatedCredential) { (result, error) in
if let _ = result?.user {
print("signed in with Apple...")
// TODO: handle data migration
print("Data-migration takes place now...")
loginStatus = true
}
}
}
}
else if let error = error {
print("Error trying to link user: \(error.localizedDescription)")
}
else {
if let _ = result?.user {
loginStatus = true
}
}
}
}
}
} else {
// case where user never logged in with firebase-Email/Password before
Auth.auth().signIn(with: appleCredentials) { (result, error) in
if let _ = result?.user {
print("signed in with Apple...")
loginStatus = true
}
}
}
} catch {
print(error.localizedDescription)
}

Firebase Login and Login with Apple not linking to same user account

Using SwiftUI, Xcode12.5.1, Swift5.4.2, iOS14.7.1,
My Firebase-Login page shall be extended with other Login possibilities such as Apple-Login (eventually Google-login, Facebook-login etc).
I have an implementation of Firebase-Login that works well.
I extended the LoginView with the Sign in with Apple Button.
And this new Apple Login in its basic implementation also works.
Now the problem:
If I log in with Apple, I need to access the corresponding Firebase-user in order to query the correct user-data. Right now, login in with Apple works but the retrieved data is not the user-data of the corresponding Firebase-user.
What I want to achieve:
From a logout-state, I want to
a) Being able to log in with Firebase Email/Password and sometimes later want to log-out and log in again with Apple.
--> and for both cases, I would like to get the same user-data
b) Being able to log in with Apple and sometimes later want to log-out and log in again with Firebase Email/Password
--> and for both cases, I would like to get the same user-data
--- THE IDEA ----------
I learned from the Firebase documentation that there is a way to link two login-accounts that we are able to know that these two accounts are corresponding.
--- THE IMPLEMENTATION -----------
Below is my current implementation for the Apple login:
I learned that you can get userInformation of the corresponding other account in the error of the link-callback. But in my case, I get the wrong linkError:
My linkError:
The email address is already in use by another account.
Instead of:
AuthErrorCode.credentialAlreadyInUse
For me this doesn't make sense. Especially since I know that I already did log in before with Firebase-Email/Password. Then I logged out and now I tried to log in with Apple.
Shouldn't the link method recognise that I am allowed to have been logged in via Firebase-Email/Password before and shouldn't it be ok to have that email being used before ?? I don't understand this linkError.
Questions:
In the link-callback, why do I get the linkError The email address is already in use by another account. instead of AuthErrorCode.credentialAlreadyInUse ??
What do I need to change in order to make a) work ??
How does the implementation look for the b) workflow (i.e. if user logs in to Apple, then logs-out and logs in again with Firebase-Email/Password ??). How do I link the two accounts then ??
Here my code:
switch state {
case .signIn:
Auth.auth().signIn(with: credential) { (authResult, error) in
if let error = error {
print("Error authenticating: \(error.localizedDescription)")
return
}
do {
if let email = try THKeychain.getEmail(),
let password = try THKeychain.getPassword() {
let credential = EmailAuthProvider.credential(withEmail: email, password: password)
if let user = authResult?.user {
user.link(with: credential) { (result, linkError) in
if let linkError = linkError, (linkError as NSError).code == AuthErrorCode.credentialAlreadyInUse.rawValue {
print("The user you're signing in with has already been linked, signing in to the new user and migrating the anonymous users [\(user.uid)] tasks.")
if let updatedCredential = (linkError as NSError).userInfo[AuthErrorUserInfoUpdatedCredentialKey] as? OAuthCredential {
print("Signing in using the updated credentials")
Auth.auth().signIn(with: updatedCredential) { (result, error) in
if let user = result?.user {
// eventually do a data-migration
user.getIDToken { (token, error) in
if let _ = token {
// do data migration here with the token....
self.doSignIn(appleIDCredential: appleIDCredential, user: user)
}
}
}
}
}
}
else if let linkError = linkError {
// I END UP HERE !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// WHY WHY WHY WHY WHY WHY WHY WHY ????????????????????????
print("Error trying to link user: \(linkError.localizedDescription)")
}
else {
if let user = result?.user {
self.doSignIn(appleIDCredential: appleIDCredential, user: user)
}
}
}
}
}
} catch {
print(error.localizedDescription)
}
if let user = authResult?.user {
if let onSignedInHandler = self.onSignedInHandler {
onSignedInHandler(user)
}
}
}
case .link:
// t.b.d.
case .reauth:
// t.b.d.
}

Getting wrong access token from registerCallback method of facebook LoginButton

I can successfully log into facebook by calling this signIn() method.
private fun signIn() {
loginBtn?.registerCallback(callBackManager,object: FacebookCallback<LoginResult>{
override fun onSuccess(result: LoginResult?) {
handleFacebookAccessToken(result!!.accessToken)
}
override fun onCancel() {
}
override fun onError(error: FacebookException?) {
Log.d("MainActivity:", "onError "+error?.message)
}
})
}
private fun handleFacebookAccessToken(accessToken: AccessToken?) {
val authCredential = FacebookAuthProvider.getCredential(accessToken!!.token)
firebaseAuth?.signInWithCredential(authCredential)
?.addOnCompleteListener(this) { task ->
if (task.isSuccessful) {
// Sign in success, update UI with the signed-in user's information
Log.d("MainActivity:", "signInWithCredential:success")
} else {
// If sign in fails, display a message to the user.
Log.w("dsds", "signInWithCredential:failure ", task.exception)
Toast.makeText(baseContext, "Authentication failed.",
Toast.LENGTH_SHORT).show()
}
}
}
But I am not able to authenticate with firebase to sign into firebase by calling handleFacebookAccessToken(accessToken: AccessToken?) method because it is giving invalid access token.
If I manually get an access token from a test user by going to the app in facebook developer console and
hardcode it in the handleFacebookAccessToken(token: String?) method below then it works.
private fun handleFacebookAccessToken(token: String?) {
val authCredential = FacebookAuthProvider.getCredential(token)
firebaseAuth?.signInWithCredential(authCredential)
?.addOnCompleteListener(this) { task ->
if (task.isSuccessful) {
// Sign in success, update UI with the signed-in user's information
Log.d("MainActivity:", "signInWithCredential:success")
} else {
// If sign in fails, display a message to the user.
Log.w("dsds", "signInWithCredential:failure ", task.exception)
Toast.makeText(baseContext, "Authentication failed.",
Toast.LENGTH_SHORT).show()
}
}
}
I am getting this exception below.
com.google.firebase.auth.FirebaseAuthInvalidCredentialsException: The supplied auth credential is malformed or has expired. [ Remote site 5XX from facebook.com for VERIFY_CREDENTIAL ]
at com.google.android.gms.internal.firebase-auth-api.zztt.zza(com.google.firebase:firebase-auth##20.0.4:28)
at com.google.android.gms.internal.firebase-auth-api.zzvb.zza(com.google.firebase:firebase-auth##20.0.4:9)
at com.google.android.gms.internal.firebase-auth-api.zzvc.zzk(com.google.firebase:firebase-auth##20.0.4:1)
at com.google.android.gms.internal.firebase-auth-api.zzuz.zzh(com.google.firebase:firebase-auth##20.0.4:25)
at com.google.android.gms.internal.firebase-auth-api.zztq.zzk(com.google.firebase:firebase-auth##20.0.4:1)
at com.google.android.gms.internal.firebase-auth-api.zzpr.zza(com.google.firebase:firebase-auth##20.0.4:2)
at com.google.android.gms.internal.firebase-auth-api.zzvg.zza(com.google.firebase:firebase-auth##20.0.4:25)
at com.google.android.gms.internal.firebase-auth-api.zzuq.zzd(com.google.firebase:firebase-auth##20.0.4:4)
at com.google.android.gms.internal.firebase-auth-api.zzpy.zzc(com.google.firebase:firebase-auth##20.0.4:4)
at com.google.android.gms.internal.firebase-auth-api.zztu.zzd(com.google.firebase:firebase-auth##20.0.4:5)
at com.google.android.gms.internal.firebase-auth-api.zzsg.zzd(com.google.firebase:firebase-auth##20.0.4:3)
at com.google.android.gms.internal.firebase-auth-api.zzsf.accept(Unknown Source:6)
at com.google.android.gms.common.api.internal.zacj.doExecute(com.google.android.gms:play-services-base##17.1.0:2)
at com.google.android.gms.common.api.internal.zaf.zac(com.google.android.gms:play-services-base##17.1.0:6)
at com.google.android.gms.common.api.internal.GoogleApiManager$zaa.zac(com.google.android.gms:play-services-base##17.1.0:167)
at com.google.android.gms.common.api.internal.GoogleApiManager$zaa.zab(com.google.android.gms:play-services-base##17.1.0:139)
at com.google.android.gms.common.api.internal.GoogleApiManager$zaa.zaa(com.google.android.gms:play-services-base##17.1.0:105)
at com.google.android.gms.common.api.internal.GoogleApiManager.handleMessage(com.google.android.gms:play-services-base##17.1.0:145)
at android.os.Handler.dispatchMessage(Handler.java:103)
at com.google.android.gms.internal.base.zar.dispatchMessage(com.google.android.gms:play-services-base##17.1.0:8)
at android.os.Looper.loop(Looper.java:224)
at android.os.HandlerThread.run(HandlerThread.java:67)
Login to https://developers.facebook.com/apps/
Select app and confirm App Type is Consumer
Solution referred from https://github.com/FirebaseExtended/flutterfire/issues/4524#issuecomment-764610739

How to re-authenticate a Facebook signed-in user with Firebase on Android Studio?

I correctly set up Facebook sign-in with Firebase on my app and now I'd like to make the user able to delete his account. I'd like to re-authenticate the user before deleting the account, but I don't have success.
I followed this example How to re-authenticate a user on Firebase with Google Provider? but the snippet provided by the person who asked the question doesn't help me.
Here is my code
//get the token from the signed in account
val credential = FacebookAuthProvider.getCredential(AccessToken.getCurrentAccessToken().toString())
//reauthenticate the user
user.reauthenticate(credential)
.addOnCompleteListener { task ->
if(task.isSuccessful)
Toast.makeText(activity, "Authentication successful!!!",Toast.LENGTH_LONG).show()
else
Toast.makeText(activity, "Authentication NOT successful", Toast.LENGTH_LONG).show()
}
The problem is, when I click the button "Delete" the string "Authentication NOT successful" always appears on screen.
Thank you all in advance.
I had the same problem as you and this is what worked for me
AuthCredential credential = FacebookAuthProvider.getCredential(AccessToken.getCurrentAccessToken().getToken());
mAuth.getCurrentUser().reauthenticate(credential).addOnCompleteListener(task12 -> {
if (task12.isSuccessful()) {
//end onComplete
mAuth.getCurrentUser().delete()
.addOnCompleteListener(task1 -> {
if (task1.isSuccessful()) {
//YOUR CODE HERE
}
});
}else {
}
});

export data from firebase firestore

I am doing an secret santa app for my university project and I store users into firebase firestore, then I want to pair them randomly. I think one way to do this is to download this database as json file. Can anyone tell me how to download firestore database as json file, or another way to pair them randomly?
Thats how i authenticate and add user into database:
registerButton.setOnClickListener {
auth.createUserWithEmailAndPassword(emailEditText.text.toString(), passwordEditText.text.toString())
.addOnCompleteListener(this) { task ->
if (task.isSuccessful) {
// Sign in success, update UI with the signed-in user's information
Log.d("Jemala", "createUserWithEmail:success")
val data = HashMap<String, Any>()
data["email"] = emailEditText.text.toString()
data["password"] = passwordEditText.text.toString()
data["fullname"] = fullnameEditText.text.toString()
val newCityRef = db.collection("people").document()
newCityRef.set(data)
Toast.makeText(this, "Registered Successfully", Toast.LENGTH_LONG).show()
val user = auth.currentUser
} else {
// If sign in fails, display a message to the user.
Log.w("Jemala", "createUserWithEmail:failure", task.exception)
Toast.makeText(
baseContext, "Authentication failed.",
Toast.LENGTH_SHORT
).show()
}
// ...
}
}

Resources