Firebase Phone authentication cant link with Email authentication - android

I want to link a Phone account with an Email account, but I have trouble linking it with the linkWithAuth() method.
Here is my method to link two accounts.
private void linkPhone(PhoneAuthCredential credential) {
AuthFireSingleTon.instanceOfAuth().linkWithCredential(credential)
.addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
#Override
public void onComplete(#NonNull Task<AuthResult> task) {
if (task.isSuccessful()) {
Log.wtf(TAG, "linked: SUCCESSFULLY");
} else {
Log.wtf(TAG, "link failed PHONE AUTH " + task.getException().getMessage());
}
}
});
}
And the logcat output
link failed PHONE AUTH User has already been linked to the given provider.
Even though I deleted all the accounts on my Firebase dashboard it still says this error.
user has already been linked to the given provider

Related

Google Facebook and email Auth in android app with Firebase

I am developing an android app using Firebase and I want to add 3 Login option: Facebook, Google and email (and password).
I want to give the user 3 options when he wants to login: sign in with email and password or login with Facebook or Google.
If the user chooses Facebook or Google, I want to check if the email of the Facebook/Google belongs to another credential, and if it is, I want to link the user with the new credential. Else, sign-in in the normal way.
For example, let's assume that I have Facebook and Google accounts with the same email, and I have already signed-in through email and password auth. When I will click the Facebook login button, the app will notice that my email belongs to existing user and will connect my facebook account to my existing user (probably with user.linkWithCredential()). The same if I tries to sign-in via Google, or if I create new user using email and password if he already logged-in via Google...
I have tried to do something like this, but I still not sure how to get the other credential (without asking the user to sign-in from different provider) or the firebase existing user, in order to link it with the new credential.
authResultTask = mAuth.signInWithCredential(credential).addOnCompleteListener(new OnCompleteListener<AuthResult>() {
#Override
public void onComplete(#NonNull Task<AuthResult> task) {
if (task.isSuccessful()) {
// Sign in success, update UI with the signed-in user's information
Log.d(TAG, "signInWithCredential:success");
loggedIn(LoginType.FACEBOOK);
} else {
// If sign in fails, display a message to the user.
Log.w(TAG, "signInWithCredential:failure", task.getException());
if (task.getException() instanceof FirebaseAuthUserCollisionException) {
mAuth.fetchProvidersForEmail(email /*not sure how to get the email also*/).addOnCompleteListener(new OnCompleteListener<ProviderQueryResult>() {
#Override
public void onComplete(#NonNull Task<ProviderQueryResult> task) {
if (task.isSuccessful()) {
if (task.getResult().getProviders().contains(
GoogleAuthProvider.PROVIDER_ID)) {
mAuth.signInWithCredential(credential)
.addOnCompleteListener(new OnCompleteListener<AuthResult>() {
#Override
public void onComplete(#NonNull Task<AuthResult> task) {
if (task.isSuccessful()) {
// Link initial credential to existing account.
getExistingUser()/*somehow get the existing user*/.linkWithCredential(credential).addOnCompleteListener(new OnCompleteListener<AuthResult>() {
#Override
public void onComplete(#NonNull Task<AuthResult> task) {
}
});
} else {
makeToast(R.string.error_occurred);
disable.enableAndRemoveProgress();
}
}
});
}
} else {
makeToast(R.string.error_occurred);
}
}
});
} else {
makeToast(R.string.error_occurred);
}
}
}
});
Do you have any idea how it can be done?

How to handle FirebaseAuthUserCollisionException

I started getting a FirebaseAuthUserCollisionException exception when I try to sign in with Facebook in my Android application.
com.google.firebase.auth.FirebaseAuthUserCollisionException: An
account already exists with the same email address but different
sign-in credentials. Sign in using a provider associated with this
email address.
I am using Firebase to handle the registration and Facebook to deliver a "one-click" login method, using a com.facebook.login.widget.LoginButton view as a trigger.
These sign-in method was already working. I was able to register a account with Facebook, and use the same method to log-in this account. But now have start to throwing this exception.
Here is the code where I register a account from Facebook and proceed with login:
private void handleFacebookAccessToken(AccessToken token) {
final ProgressDialog dialog = new ProgressDialog(this);
dialog.show(getString(R.string.dialog_wait));
firebaseAuth.signInWithCredential(FacebookAuthProvider.getCredential(token.getToken()))
.addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
#SuppressWarnings("ThrowableResultOfMethodCallIgnored")
#Override
public void onComplete(#NonNull Task<AuthResult> task) {
if (task.isSuccessful()) {
dialog.close();
registerNewUserFromSocialLogin(firebaseAuth.getCurrentUser());
} else {
if(task.getException() instanceof FirebaseAuthUserCollisionException) {
//TODO: handle sign-in with different credentials
} else {
dialog.close();
LoginManager.getInstance().logOut();
Toast.makeText(LoginActivity.this,
R.string.error_login,
Toast.LENGTH_SHORT).show();
}
}
}
});
}
And my Gradle file with current use library:
compile 'com.google.firebase:firebase-auth:10.2.1'
compile 'com.facebook.android:facebook-android-sdk:[4,5)'
So my problem is: I don't know how to handle FirebaseAuthUserCollisionException exception.
None of the solutions in StackOverflow or Firebase Documentation help me. I am looking for a solution that is able to login the user although the duplicated credential, to stil deliver the "one-click" login method.
You will get that error when the user had previously signed in with the same email using a different provider. For example, the user signs in with email user#gmail.com using Google. The user then tries to sign in with the same email but using Facebook. The Firebase Auth backend will return that error (account exists with different credential). In that case, you should use the fetchProvidersForEmail to look up the existing providers associated with email user#gmail.com, in this case google.com. You signInWithCredential to the existing google account to prove ownership of that account, and then linkWithCredential the Facebook credential the user originally was trying to sign in with. This merges both accounts so in the future the user can sign in with either.
This happens when you use the single accounts per email. If you want to allow different accounts per email, you can switch to multiple accounts per email in the Firebase console.
Here is an example:
mAuth.signInWithCredential(authCredential)
.addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
#Override
public void onComplete(#NonNull Task<AuthResult> task) {
// Account exists with different credential. Assume the developer wants to
// continue and link new credential to existing account.
if (!task.isSuccessful() &&
task.getException() instanceof FirebaseAuthUserCollisionException) {
FirebaseAuthUserCollisionException exception =
(FirebaseAuthUserCollisionException)task.getException();
if (exception.getErrorCode() ==
ERROR_ACCOUNT_EXISTS_WITH_DIFFERENT_CREDENTIAL) {
// Lookup existing account’s provider ID.
mAuth.fetchProvidersForEmail(existingAcctEmail)
.addOnCompleteListener(new OnCompleteListener<ProviderQueryResult> {
#Override
public void onComplete(#NonNull Task<ProviderQueryResult> task) {
if (task.isSuccessful()) {
if (task.getResult().getProviders().contains(
EmailAuthProvider.PROVIDER_ID)) {
// Password account already exists with the same email.
// Ask user to provide password associated with that account.
...
// Sign in with email and the provided password.
// If this was a Google account, call signInWithCredential instead.
mAuth.signInWithEmailAndPassword(existingAcctEmail, password)
addOnCompleteListener(new OnCompleteListener<AuthResult> {
#Override
public void onComplete(#NonNull Task<AuthResult> task) {
if (task.isSuccessful()) {
// Link initial credential to existing account.
mAuth.getCurrentUser().linkWithCredential(authCredential);
}
}
});
}
}
}
});
}
}
});
There is no need for this, you can just allow multiple accounts merge under firebaase->authentication-> sign in method -> Advanced - > change (multiple accounts per email address.
Firebase will merge the same email address but will give you different user UID.
See sample below.
AuthCredential authCredential = FacebookAuthProvider.getCredential(token.getToken());
mAuth.signInWithCredential(authCredential)
.addOnCompleteListener(this, task -> {
if (task.isSuccessful()) {
// Sign in success, update UI with the signed-in user's information
Log.d(TAG, "signInWithCredential:success");
FirebaseUser user = mAuth.getCurrentUser();
LoginFacebookGoogleActivity.this.updateUI(user);
} else {
// If sign in fails, display a message to the user.
Log.w(TAG, "signInWithCredential:failure", task.getException());
if(task.getException() instanceof FirebaseAuthUserCollisionException){
FirebaseAuthUserCollisionException exception = (FirebaseAuthUserCollisionException) task.getException();
//log this bundle into the analytics to analyze which details you want to collect
}
Toast.makeText(LoginFacebookGoogleActivity.this, "Authentication failed " + task.getException(), Toast.LENGTH_SHORT).show();
LoginFacebookGoogleActivity.this.updateUI(null);
}
});

Firebase createUserWithEmailAndPassword does not work

I am following the basic tutorial in order to create an e-mail / password authentication using Firebase.
mAuth.createUserWithEmailAndPassword(email, password)
.addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
#Override
public void onComplete(#NonNull Task<AuthResult> task) {
Log.d(TAG, "createUserWithEmail:onComplete:" + task.isSuccessful());
if (!task.isSuccessful()) {
Toast.makeText(MainActivity.this, R.string.auth_failed,
Toast.LENGTH_SHORT).show();
}
hideProgressDialog();
}
});
But whenever I try to create an account (with a valid e-mail address and a diversity of passwords, with caps, numbers, high length) it displays my failure Toast.
What could I be doing wrong?
As this answer says: Firebase Android: An internal error has occurred. [ OPERATION_NOT_ALLOWED ]
You need to have the authentication method enabled in the Firebase console, so go to the authentication option on Firebase and then, to the sign in method to activate the email provider.
I Hope it helps!
Go to Firebase console -> Authentication -> SIGN-IN METHOD -> enable Email/Password

Firebase Auth - with Email and Password - Check user already registered

I want to check when a user attempts to signup with createUserWithEmailAndPassword() in Firebase user Authentication method, this user is already registered with my app.
To detect whether a user with that email address already exists, you can detect when the call to createUserWithEmailAndPassword () fails with auth/email-already-in-use. I see that #Srinivasan just posted an answer for this.
Alternatively, you can detect that an email address is already used by calling fetchSignInMethodsForEmail().
The usual flow for this is that you first ask the user to enter their email address, then call fetchSignInMethodsForEmail, and then move them to a screen that either asks for the rest of their registration details (if they're new), or show them the provider(s) with which they're signed up already.
When the user trying to create an user with same email address, the task response will be "Response: The email address is already in use by another account."
mFirebaseAuth.createUserWithEmailAndPassword(email,password)
.addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
#Override
public void onComplete(#NonNull Task<AuthResult> task) {
if(task.isSuccessful()){
//User registered successfully
}else{
Log.i("Response","Failed to create user:"+task.getException().getMessage());
}
}
});
First of all, you need to make sure you have that restriction enabled in Firebase console (Account and email address settings). Take a look at #Srinivasan's answer.
Then, do this in your java code:
firebaseAuthenticator.createUserWithEmailAndPassword(email, password)
.addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
#Override
public void onComplete(#NonNull Task<AuthResult> task) {
if (!task.isSuccessful()) {
if (task.getException() instanceof FirebaseAuthUserCollisionException) {
Toast.makeText(SignUpActivity.this, "User with this email already exist.", Toast.LENGTH_SHORT).show();
}
} else {
sendVerificationEmail();
startActivity(new Intent(SignUpActivity.this, DetailsCaptureActivity.class));
}
// ...
}
});
This is where the trick happens:
if (task.getException() instanceof FirebaseAuthUserCollisionException) {
Toast.makeText(SignUpActivity.this,
"User with this email already exist.", Toast.LENGTH_SHORT).show();
Several exceptions can be thrown when registering a user with email and password, but the one we are interested in is the FirebaseAuthUserCollisionException. As the name implies, this exception is thrown if the email already exists. If the exception thrown is an instance of this class, let the user know.
As a practice of #Frank's answer here is the code of using fetchProvidersForEmail()
private boolean checkAccountEmailExistInFirebase(String email) {
FirebaseAuth mAuth = FirebaseAuth.getInstance();
final boolean[] b = new boolean[1];
mAuth.fetchProvidersForEmail(email).addOnCompleteListener(new OnCompleteListener<ProviderQueryResult>() {
#Override
public void onComplete(#NonNull Task<ProviderQueryResult> task) {
b[0] = !task.getResult().getProviders().isEmpty();
}
});
return b[0];
}
I was looking into this kind of condition where we can detect if user exists or not and perform registration and login. fetchProvidersForEmail is best option right now. I have found this tutorial. Hope it helps you too!
See : Manage Users
UserRecord userRecord = FirebaseAuth.getInstance().getUserByEmail(email);
System.out.println("Successfully fetched user data: " + userRecord.getEmail());
This method returns a UserRecord object for the user corresponding to the email provided.
If the provided email does not belong to an existing user or the user cannot be fetched for any other reason, the Admin SDK throws an error. For a full list of error codes, including descriptions and resolution steps, see Admin Authentication API Errors.
private ProgressDialog progressDialog;
progressDialog.setMessage("Registering, please Wait...");
progressDialog.show();
mAuth.createUserWithEmailAndPassword(email, password)
.addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
#Override
public void onComplete(#NonNull Task<AuthResult> task) {
//checking if success
if (task.isSuccessful()) {
//Registration was successfull:
Toast.makeText(RegistrationActivity.this, "Successfully registered!", Toast.LENGTH_LONG).show();
} else {
//Registration failed:
//task.getException().getMessage() makes the magic
Toast.makeText(RegistrationActivity.this, "Registration failed! " + "\n" + task.getException().getMessage(), Toast.LENGTH_LONG).show();
}
progressDialog.dismiss();
}
});
Add below code to MainActivity.java file.When user attempt to register with the same email address a message "The email address is already used by another account" will pop up as a Toast
mAuth.createUserWithEmailAndPassword(email,password).addOnCompleteListener(new OnCompleteListener<AuthResult>() {
#Override
public void onComplete(#NonNull Task<AuthResult> task) {
if(!task.isSuccessful()){
Toast.makeText(MainActivity.this, task.getException().getMessage(), Toast.LENGTH_SHORT).show();
}
if(task.isSuccessful()){
Toast.makeText(MainActivity.this, "Sign up successfull", Toast.LENGTH_SHORT).show();
}
}
});
You do not have to do anything because the backend of Firebase will do the job.
Unless you are referring to reauthenticating of the app.
Take a scenario for an example, w

How to link Multiple Auth Providers to an Firebase Account on Android?

I referenced the documentation in Firebase page. My problem is in this paragraph:
The call to linkWithCredential will fail if the credentials are already linked to another user account. In this situation, you must handle merging the accounts and associated data as appropriate for your app:
FirebaseUser prevUser = currentUser; currentUser = auth.signInWithCredential(credential).await().getUser(); // Merge
prevUser and currentUser accounts and data // ...
I can't figure out how to add this code into my project. When and where do I put this code
auth.signInWithCredential(credential).await().getUser();
into my java file? Android Studio announced me that it can't resolve await() method.
What should I do to resolve that problem. Thank you in advance!
You should handle this part of code when the linkWithCredentials call fails.
Here is a small example :
mAuth.getCurrentUser().linkWithCredential(credentials).addOnCompleteListener(new OnCompleteListener<AuthResult>() {
#Override
public void onComplete(#NonNull Task<AuthResult> task) {
if (!task.isSuccessful()) {
FirebaseUser prevUser = currentUser;
try {
currentUser = Tasks.await(auth.signInWithCredential(credential)).getUser();
} catch (Exception e) {
e.printStackStrace();
}
// Merge prevUser and currentUser accounts and data
// ...
} else {
}
}
});
NB: Task.await() doesn't exist anymore you should use Tasks.await(...) static method instead.
Suppose that:
One account per email address setting is enabled on firebase authentication
User are logging in with anonymous authentication
The linkWithCredential usually fails with FirebaseAuthUserCollisionException, it could happen either because this credential is used to login before (you can check by searching this email on firebase console) or this email account is used to login before but with different providers ( such as a user logged in with abc#gmail.com by using google sign in before, now he/she uses abc#gmail.com but with Facebook login ) To handle it, you need 2 steps:
Step 1: link with anonymous account
private void linkWithAnonymousAccount(final AuthCredential credential) {
mFirebaseAuth.getCurrentUser().linkWithCredential(credential)
.addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
#Override
public void onComplete(#NonNull Task<AuthResult> task) {
if (task.isSuccessful()) {
getFirebaseToken();
} else {
Exception exception = task.getException();
if (exception instanceof FirebaseAuthUserCollisionException) {
signInWithFirebase(credential);
} else {
Utils.showDialogMessage(mContext, task.getException().getLocalizedMessage());
}
}
}
});
}
Step 2: sign with firebase
private void signInWithFirebase(AuthCredential credential) {
mFirebaseAuth.signInWithCredential(credential)
.addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
#Override
public void onComplete(#NonNull Task<AuthResult> task) {
if (task.isSuccessful()) {
getFirebaseToken();
} else {
Utils.showDialogMessage(mContext, task.getException().getLocalizedMessage());
}
}
});
}
Google says the step 2 is merging because after signed in, you need to get information of the previous user and put into the new user.
I hope it helpful.

Categories

Resources