I am implementing google smart lock on my app, for now, we are only implementing on the app side, to log in the user automatically once he allows saving the credentials, on a reinstall for example.
But when I removed the password from password.google.com OR when I run the app on a device where the google account doesn't have credentials stored for that app, the library shows a dialog suggesting others sites and apps emails. I need to disable this behavior, I just want to suggest credentials and emails if they belong to my app.
I'm requesting credentials with the following code:
private void requestCredentials() {
CredentialRequest request = new CredentialRequest.Builder()
.setPasswordLoginSupported(true)
.setIdTokenRequested(true)
.build();
mProgressSmartLock.show();
credentialsClient.request(request).addOnCompleteListener(credentialsApiRequestCompleteListener());
}
and the listener:
public OnCompleteListener<CredentialRequestResponse> credentialsApiRequestCompleteListener(){
return new OnCompleteListener<CredentialRequestResponse>() {
#Override
public void onComplete(#NonNull Task<CredentialRequestResponse> task) {
// Successfully read the credential without any user interaction, this
// means there was only a single credential and the user has auto
// sign-in enabled.
mProgressSmartLock.dismiss();
if (task.isSuccessful()) {
processRetrievedCredential(task.getResult().getCredential());
return;
}
// This is most likely the case where the user has multiple saved
// credentials and needs to pick one. This requires showing UI to
// resolve the read request.
Exception e = task.getException();
if (e instanceof ResolvableApiException) {
ResolvableApiException rae = (ResolvableApiException) e;
resolveResult(rae, RC_READ);
return;
}
// This means only a hint is available
if (e instanceof ApiException) {
Crashlytics.logException(e);
}
}
};
}
saving credentials :
private void saveCredentials(String email, String password) {
final Credential credential = new Credential.Builder(email)
.setPassword(password)
.build();
mProgress.show();
credentialsClient.save(credential).addOnCompleteListener(credentialsApiSaveCompleteListener());
}
listener:
public OnCompleteListener<Void> credentialsApiSaveCompleteListener(){
return new OnCompleteListener<Void>() {
#Override
public void onComplete(#NonNull Task<Void> task) {
if (task.isSuccessful()) {
mProgress.dismiss();
return;
}
Exception e = task.getException();
if (e instanceof ResolvableApiException) {
// The first time a credential is saved, the user is shown UI
// to confirm the action. This requires resolution.
ResolvableApiException rae = (ResolvableApiException) e;
resolveResult(rae, RC_SAVE);
} else {
// Save failure cannot be resolved.
mProgress.dismiss();
}
}
};
}
To avoid this dialog (which lists all email addresses in order to help fill a form, even if there is no passwords saved), do not resolve if the task's getStatusCode() returns SIGN_IN_REQUIRED.
Sorry, this detail was lost in a recent doc change, thanks for reporting. Will get that updated ASAP, sorry for the confusion.
If the credential is not from the app or the app did not save any credential, thestatusCode will be the SIGN_IN_REQUIRED. But if you had saved any credential before, you will receive another INT value from statusCode. You can judge in the Resolveable Exception.
Related
Is there a way to pass custom query parameters in Firebase Dynamic Links that are created using the console?
My workflow is as follows:
Reset Password screen that takes email and executes sendPasswordResetEmail(email, settings)
Go to email and select link which opens app to screen to reset password: https://xxxxx.page.link?link=https://xxxx-00000.firebaseapp.com/__/auth/action?apiKey%3DAIzaSyDxTJUhYNwbMpoRhRWde74tAqV0CMKHh_o%26mode%3DresetPassword%26oobCode%3DccgIWg7D-FPtRTp2OXon8UaIB1AL0_qpktnAL--P-eMAAAFsgjDmkw%26continueUrl%3Dhttps://example.com/%26lang%3Den&apn=com.example&amv
App is launched and we go to the screen to enter a new password.
Call FirebaseDynamicLinks.getInstance().getDynamicLink(getIntent())
Use pendingDynamicLinkData.getLink().getQueryParameter("oobCode")); to get query parameters from the dynamic link (in this case get our password code that we use to reset the password.
Use oobCode in previous step and call FirebaseAuth.getInstance().confirmPasswordReset(actionCode, password) on button click (grabbing password from use inputted editText field) to reset password.
Ideally I would like to login my user after the password reset. In order to do this I need to have the email address used in the password reset (I need to get firestore document information from the user trying to login).
So I need to be able to pass the email address to the screen to reset the password.
Here are my relevant code snippets:
Initial "Forgot password screen": User enters email here.
First build my action code settings, then execute the sendPasswordResetEmail() method.
String url = "https://example.com"; //my deep link set in Firebase console
ActionCodeSettings settings = ActionCodeSettings.newBuilder()
.setAndroidPackageName(
getPackageName(),
true, /* install if not available? */
null /* minimum app version */)
.setHandleCodeInApp(true)
.setUrl(url)
.build();
mAuth.sendPasswordResetEmail(email, settings)
.addOnCompleteListener(new OnCompleteListener<Void>() {
#Override
public void onComplete(#NonNull Task<Void> task) {
if (task.isSuccessful()) {
Log.d(TAG, "Email sent.");
}
else {
Exception e = task.getException();
Log.w(TAG, "passwordResetRequest:failure " + e.getMessage(), task.getException());
if (e instanceof FirebaseAuthInvalidCredentialsException) {
}
}
}
});
Then after selecting link in email we go to the reset password screen.
in here we call the following to get parameters from the dynamic link:
FirebaseDynamicLinks.getInstance()
.getDynamicLink(getIntent())
.addOnSuccessListener(this, new OnSuccessListener<PendingDynamicLinkData>() {
#Override
public void onSuccess(PendingDynamicLinkData pendingDynamicLinkData) {
// Get deep link from result (may be null if no link is found)
Uri deepLink = null;
if (pendingDynamicLinkData != null && pendingDynamicLinkData.getLink() != null) {
deepLink = pendingDynamicLinkData.getLink();
actionCode = deepLink.getQueryParameter("oobCode");
actionMode = deepLink.getQueryParameter("mode");
}
}
})
.addOnFailureListener(this, new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Log.w(TAG, "getDynamicLink:onFailure", e);
}
});
Finally we use the oobCode (action code to permit a password reset), and on button click and user input for a new password, we reset the password using:
resetPass.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (actionCode != null && !actionCode.equals(""))
FirebaseAuth.getInstance().confirmPasswordReset(actionCode, newPassword.getText().toString()).addOnCompleteListener(new OnCompleteListener<Void>() {
#Override
public void onComplete(#NonNull Task<Void> task) {
if (task.isSuccessful() && task.getResult() != null)
Log.d(TAG, "Deep Link confirmPassReset: " + task.getResult().toString());
}
});
}
});
Here's where I'm struggling. I'm trying to pass an email address when building the ActionCodeSettings. For example:
String url = "https://example.com/?email=jsmith#gmail.com"; And then try to get them in the reset password screen using:
deepLink.getQueryParameter("email");
But everytime I try this I keep getting null. What am I missing. Is it even possible to pass custom query parameters with a dynamic link that was created in the Firebase console? If not, what is the best way to accomplish this?
Any help would be greatly appreciated. Thanks in advance!
You need get the link first
val actionUrl = deepLink.getQueryParameter("continueUrl")
And get the email value with subString
actionUrl.substring(actionURL.lastIndexOf("=") + 1, actionURL.length)
I have made an Android application using Firebase Phone Authentication and Realtime Database.
My requirement is to first check if the user's no. is in my database or not and then send him the OTP.
My database:
My code:
rootRef= FirebaseDatabase.getInstance().getReference().child("Employees").child("mobno");
//---------------1----------------
getcodeBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Log.d("abcd","get code button clicked");
if (!cugET.getText().toString().trim().isEmpty() && cugValidFlag==true) {
Log.d("abcd","entered no. is valid");
enteredcugno = cugET.getText().toString().trim();
Log.d("abcd","Entered cugno: "+enteredcugno);
rootRef.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
Log.d("abcd","rootRef listener reached");
if(dataSnapshot.child(enteredcugno).exists()){
Log.d("abcd","cugno exists in db");
startPhoneNumberVerification(cugET.getText().toString().trim());
mVerified = false;
otpTV.setVisibility(View.VISIBLE);
otpET.setVisibility(View.VISIBLE);
otpValTV.setVisibility(View.VISIBLE);
verifycodeBtn.setVisibility(View.VISIBLE);
}
else{
Log.d("abcd","cugno doesn't exists in db");
Toast.makeText(cugLogin.this,"No such CUG No. found",Toast.LENGTH_SHORT).show();
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
}
});
//------------------------2--------------------
private void startPhoneNumberVerification(String phoneNumber) {
Log.d("abcd","startPhoneNumberVerification");
// [START start_phone_auth]
PhoneAuthProvider.getInstance().verifyPhoneNumber(
"+91"+phoneNumber, // Phone number to verify
60, // Timeout duration
TimeUnit.SECONDS, // Unit of timeout
this, // Activity (for callback binding)
mCallbacks); // OnVerificationStateChangedCallbacks
// [END start_phone_auth]
}
//--------------3--------------------
mCallbacks = new PhoneAuthProvider.OnVerificationStateChangedCallbacks() {
#Override
public void onVerificationCompleted(PhoneAuthCredential credential) {
// This callback will be invoked in two situations:
// 1 - Instant verification. In some cases the phone number can be instantly
// verified without needing to send or enter a verification code.
// 2 - Auto-retrieval. On some devices Google Play services can automatically
// detect the incoming verification SMS and perform verificaiton without
// user action.
Log.d("abcd", "onVerificationCompleted:" + credential);
signInWithPhoneAuthCredential(credential);
}
#Override
public void onVerificationFailed(FirebaseException e) {
// This callback is invoked in an invalid request for verification is made,
// for instance if the the phone number format is not valid.
Log.w("abcd", "onVerificationFailed", e);
if (e instanceof FirebaseAuthInvalidCredentialsException) {
Log.d("abcd","verification failed cz of FirebaseAuthInvalidCredentialsException");
Toast.makeText(cugLogin.this,"Verification Failed !! Invalied verification Code",Toast.LENGTH_SHORT).show();
}
else if (e instanceof FirebaseTooManyRequestsException) {
Log.d("abcd","verification failed cz FirebaseTooManyRequestsException");
Toast.makeText(cugLogin.this,"Verification Failed !! Too many request. Try after some time. ",Toast.LENGTH_SHORT);
}
}
#Override
public void onCodeSent(String verificationId,
PhoneAuthProvider.ForceResendingToken token) {
// The SMS verification code has been sent to the provided phone number, we
// now need to ask the user to enter the code and then construct a credential
// by combining the code with a verification ID.
Log.d("abcd", "onCodeSent:" + verificationId);
// Save verification ID and resending token so we can use them later
mVerificationId = verificationId;
mResendToken = token;
}
};
//----------------4---------------
private void signInWithPhoneAuthCredential(PhoneAuthCredential credential) {
Log.d("abcd","signInWithPhoneAuthCredential reached");
mAuth.signInWithCredential(credential)
.addOnCompleteListener(this, 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("abcd", "signInWithCredential:success");
FirebaseUser user = task.getResult().getUser();
mVerified = true;
timer.cancel();
timerTV.setVisibility(View.INVISIBLE);
cugET.setEnabled(false);
cugTV.setVisibility(View.INVISIBLE);
cugET.setVisibility(View.INVISIBLE);
getcodeBtn.setVisibility(View.INVISIBLE);
otpTV.setVisibility(View.INVISIBLE);
otpET.setVisibility(View.INVISIBLE);
otpValTV.setVisibility(View.INVISIBLE);
verifycodeBtn.setVisibility(View.INVISIBLE);
Intent intent = new Intent(cugLogin.this,Login.class);
intent.putExtra("cugnotoLogin",enteredcugno);
startActivity(intent);
Toast.makeText(cugLogin.this,"Successfully verified",Toast.LENGTH_SHORT).show();
// ...
} else {
// Sign in failed, display a message and update the UI
Log.w("abcd", "signInWithCredential:failure", task.getException());
if (task.getException() instanceof FirebaseAuthInvalidCredentialsException) {
// The verification code entered was invalid
Toast.makeText(cugLogin.this,"Invalid OTP ! Please enter correct OTP",Toast.LENGTH_SHORT).show();
}
}
}
});
}
The last log I see is "Entered cugno: 9876543210"
No error being shown, I am really confused how can I correct it?
If I remove the rootRef listener my code is working but I want to check the user in the database first. Also I am giving the correct path to the rootRef then why isn't it working?
EDIT
I got my code working but I am still a bit confused, I just moved my rootRef initialization in the getcodeBtn listener. But I already had it in my onStart() function so it should have been initialized there as well, if anyone knows the reason behind it please let me know.
LATEST EDIT
Now when I signed out, my code is again stuck at the rootRef listener, it isn't getting passed into it. Even after closing the app it isn't letting me in. It's the same thing happening again. It's like something got refreshed overnight and the app let me sign in and when I signed out, I am stuck again. Why is this happening?
I solved it, it wasn't the coding problem but the logic was faulty. I set the rules of the database for the authenticated users only, and was checking my database first to let the user to sign in, so when I logged out, the code went into a loop as it couldn't access the database to let the user sign in.
I have tested this on my Moto G5+ (works) and Nexus 6 (doesn't work), and my firebase authentication only works on one of them:
mAuth = FirebaseAuth.getInstance();
if(mAuth.getCurrentUser() == null) { //No existing user
mAuth.signInAnonymously().addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Log.v("myTag", "Cannot authenticate user" + e);
}
});
}
if(mAuth.getCurrentUser() != null) {
Log.v("myTag", "Boutta take photos");
takePhoto(this, 0);//back camera
}else{
Log.v("myTag", "Cannot take photos, user not authenticated");
}
What's odd is that I only get the log Cannot take photos, the user not authenticated, but not the log Cannot authenticate user. This means I am able to authenticate the user, but for some reason, it does not work
How come this only works on some devices?
Assuming your user starts off unauthenticated, once the first if statement is called and it attempts the anonymous sign in, that anonymous sign in happens asynchronously as it is waiting on a callback. So in that state the user isn't authenticated until the call back completes. The code then jumps to your second set of if/else statements where you check
mAuth.getCurrentUser() != null
but the callback for authentication still may have not returned, and your mAuth.getCurrentUser() is still null therefore jumping to the else statement and logging the Log.v("myTag", "Cannot take photos, user not authenticated");
Your callback may then return authenticating the user but at this point, its too late. You already run logic assuming the use wasn't authenticated.
#martinomburajr brought me to the right answer! Just to elaborate on his solution, in case anyone else has the same problem in the future, I needed to wait until the authentication was successful:
mAuth = FirebaseAuth.getInstance();
if(mAuth.getCurrentUser() == null) { //No existing user
Log.v("myTag", "Boutta authenticate");
mAuth.signInAnonymously().addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
}
}).addOnCompleteListener(new OnCompleteListener<AuthResult>() {
#Override
public void onComplete(#NonNull Task<AuthResult> task) {
if(mAuth.getCurrentUser() != null) {
//TAKE PHOTO HERE INSTEAD
takePhoto(c, 0);//back camera
}else{
Log.v("myTag", "Cannot take photos, user not authenticated");
}
}
});
}else {
Log.v("myTag", "aalready authenticated!");
takePhoto(c, 0);//back camera
frontOrBack = 0;
}
}
When I put my authentication-dependent code (in this case, to take a picture) I wasn't giving my app enough time to get a response from Firebase. Instead of just assuming that the user was authenticated, however, I fixed this issue by putting the authentication-dependent code in an OnCompleteListener. Many thanks to #martinomburajr for his helpful answer!
I want to implement change password functionality for my application.
I included com.google.firebase:firebase-auth:9.0.2 in my build.gradle file and so far everything has been working fine until I tried to implement change password functionality.
I found that the FirebaseUser object has a updatePassword method that takes a new password as the parameter. I could use this method and implement validation myself. However, I need the user's current password for comparing with the inputted one and I can't find a way to get that password.
I also found another method on the Firebase object that takes the old password, new password, and a handler. The problem is that I need to also include com.firebase:firebase-client-android:2.5.2+ to access this class and when I am trying this method I'm getting to following error:
Projects created at console.firebase.google.com must use the new Firebase Authentication SDKs available from firebase.google.com/docs/auth/
Feel like I'm missing something here. What's the recommended approach for implementing this? And when to use what dependency?
I found a handy example of this in the Firebase docs:
Some security-sensitive actions—such as deleting an account, setting a
primary email address, and changing a password—require that the user
has recently signed in. If you perform one of these actions, and the
user signed in too long ago, the action fails and throws
FirebaseAuthRecentLoginRequiredException. When this happens,
re-authenticate the user by getting new sign-in credentials from the
user and passing the credentials to reauthenticate. For example:
FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
// Get auth credentials from the user for re-authentication. The example below shows
// email and password credentials but there are multiple possible providers,
// such as GoogleAuthProvider or FacebookAuthProvider.
AuthCredential credential = EmailAuthProvider
.getCredential("user#example.com", "password1234");
// Prompt the user to re-provide their sign-in credentials
user.reauthenticate(credential)
.addOnCompleteListener(new OnCompleteListener<Void>() {
#Override
public void onComplete(#NonNull Task<Void> task) {
if (task.isSuccessful()) {
user.updatePassword(newPass).addOnCompleteListener(new OnCompleteListener<Void>() {
#Override
public void onComplete(#NonNull Task<Void> task) {
if (task.isSuccessful()) {
Log.d(TAG, "Password updated");
} else {
Log.d(TAG, "Error password not updated")
}
}
});
} else {
Log.d(TAG, "Error auth failed")
}
}
});
Changing password in firebase is bit tricky. it's not like what we usually do for changing password in server side scripting and database. to implement change password functionality in your app, first you need to get the user's email from FirebaseAuth or prompt user to input email and after that prompt the user to input old password because you can't retrieve user's password as Frank van Puffelen said. After that you need to reauthenticate that. Once reauthentication is done, if successful, you can use updatePassword(). I have added a sample below that i used for my own app. Hope, it will help you.
private FirebaseUser user;
user = FirebaseAuth.getInstance().getCurrentUser();
final String email = user.getEmail();
AuthCredential credential = EmailAuthProvider.getCredential(email,oldpass);
user.reauthenticate(credential).addOnCompleteListener(new OnCompleteListener<Void>() {
#Override
public void onComplete(#NonNull Task<Void> task) {
if(task.isSuccessful()){
user.updatePassword(newPass).addOnCompleteListener(new OnCompleteListener<Void>() {
#Override
public void onComplete(#NonNull Task<Void> task) {
if(!task.isSuccessful()){
Snackbar snackbar_fail = Snackbar
.make(coordinatorLayout, "Something went wrong. Please try again later", Snackbar.LENGTH_LONG);
snackbar_fail.show();
}else {
Snackbar snackbar_su = Snackbar
.make(coordinatorLayout, "Password Successfully Modified", Snackbar.LENGTH_LONG);
snackbar_su.show();
}
}
});
}else {
Snackbar snackbar_su = Snackbar
.make(coordinatorLayout, "Authentication Failed", Snackbar.LENGTH_LONG);
snackbar_su.show();
}
}
});
}
}
There is no way to retrieve the current password of a user from Firebase Authentication.
One way to allow your users to change their password is to show a dialog where they enter their current password and the new password they'd like. You then sign in (or re-authenticate) the user with the current passwordand call updatePassword() to update it.
I googled something about resetting Firebase passwords and got to this page. It was helpful but didn't get me all the way to the finish line: I still had to Google around for five or ten minutes. So I'm back to improve the answer for VueJS users.
I see lots of code here using "FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();" in the top line. That's a piece of the puzzle mentioned in the most popular two answers.
But I couldn't get that to work in my project, which is written in VueJS. So I had to go exploring.
What I found was another page of the Firebase documentation. It's the same page people are getting the quoted code from (I think), but with the documentation written for Web instead of Android/Java.
So check out the first link if you're here using VueJS. I think it'll be helpful. "Get the currently signed-in user" might contain the appropriate code for your project. The code I found there says:
firebase.auth().onAuthStateChanged(function(user) {
if (user) {
// User is signed in.
} else {
// No user is signed in.
}
});
That page I linked up above ("another page") brought me eventually to the "Set a user's password" part of the Web docs. Posters here correctly state that the user must have been authenticated recently to initiate a password update. Try this link for more on re-authenticating users.
"Set a user's password":
// You can set a user's password with the updatePassword method. For example:
var user = firebase.auth().currentUser;
var newPassword = getASecureRandomPassword();
user.updatePassword(newPassword).then(function() {
// Update successful.
}).catch(function(error) {
// An error happened.
});
"Re-authenticate a user"
var user = firebase.auth().currentUser;
var credential;
// Prompt the user to re-provide their sign-in credentials
user.reauthenticateWithCredential(credential).then(function() {
// User re-authenticated.
}).catch(function(error) {
// An error happened.
});
Query revolves around users forgetting their passwords or wishing to reset their passwords via an email letter. Which can be attained by Auth.sendPasswordResetEmail("email#gmail.com");
begin by initializing
private FirebaseAuth mAuth;
private FirebaseAuth.AuthStateListener mAuthListener;
private String DummyEmail = "Dummy#gmail.com"
mAuth = FirebaseAuth.getInstance();
mAuthListener = new FirebaseAuth.AuthStateListener() {
#Override
public void onAuthStateChanged(#NonNull FirebaseAuth firebaseAuth) {
if (firebaseAuth.getCurrentUser() == null) {
}
}
};
Somewhere else when a user requests to update or reset their passwords simply access the mAuth,
private void PassResetViaEmail(){
if(mAuth != null) {
Log.w(" if Email authenticated", "Recovery Email has been sent to " + DummyEmail);
mAuth.sendPasswordResetEmail(DummyEmail);
} else {
Log.w(" error ", " bad entry ");
}
}
Now, needless to burden yourself querying around your database to find whether the Email exits or not, Firebase mAuth will handle that for you.
Is the Email authenticated? Is it active in your Authentication list? Then send a password-reset Email.
The content will look something like this
the reset link will prompt the following dialog on a new web page
Extra
if you're bit nerved by the reset-template "devised" by Firebase. You can easily access and customize your own letter from the Firebase Console.
Authentication > Email templates > Password reset
A simple approach to handle changing a password is to send a password reset email to the user.
FirebaseAuth.getInstance().sendPasswordResetEmail("user#example.org")
.addOnCompleteListener(new OnCompleteListener<Void>() {
#Override
public void onComplete(#NonNull Task<Void> task) {
if (task.isSuccessful()) {
Toast.makeText(Activity.this, "Password Reset Email Sent!"), Toast.LENGTH_LONG).show();
}
else {
Toast.makeText(Activity.this, task.getException().getLocalizedMessage(), Toast.LENGTH_LONG).show();
}
});
This is a kotlin solution to the problem I am putting the method here Hope it helps
// The method takes current users email (currentUserEmail), current users old password (oldUserPassword), new users password (newUserPassword) as parameter and change the user password to newUserPassword
private fun fireBasePasswordChange(
currentUserEmail: String,
oldUserPassword: String,
newUserPassword: String
) {
// To re authenticate the user credentials getting current sign in credentials
val credential: AuthCredential =
EmailAuthProvider.getCredential(currentUserEmail, oldUserPassword)
// creating current users instance
val user: FirebaseUser? = FirebaseAuth.getInstance().currentUser
// creating after successfully re authenticating update password will be called else it will provide a toast about the error ( makeToast is a user defined function here for providing a toast to the user)
user?.reauthenticate(credential)?.addOnCompleteListener { task ->
when {
task.isSuccessful -> {
user.updatePassword(newUserPassword).addOnCompleteListener {
if (it.isSuccessful) {
makeToast("Password updated")
// This part is optional
// it is signing out the user from the current status once changing password is successful
// it is changing the activity and going to the sign in page while clearing the backstack so the user cant come to the current state by back pressing
FirebaseAuth.getInstance().signOut()
val i = Intent(activity, SignInActivity::class.java)
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
startActivity(i)
(activity as Activity?)!!.overridePendingTransition(0, 0)
} else
makeToast("Error password not updated")
}
}
else -> {
makeToast("Incorrect old password")
}
}
}
}
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.