firebase-unity-sdk icon indicating copy to clipboard operation
firebase-unity-sdk copied to clipboard

Auth: How to solve user credential merge conflict?

Open initfusion opened this issue 6 years ago • 18 comments

Hi,

Anyone can please help me to solve merge conflicts when link account. How to link account when the credentials already have an existing account.

Thanks.

initfusion avatar Jun 07 '19 14:06 initfusion

Hi @initfusion, you can start from this doc, which introduces how to link multiple auth providers to one account in Unity. You can also find related docs for other platforms under iOS / Android / Web and C++.

Hope this helps.

cynthiajoan avatar Jun 10 '19 22:06 cynthiajoan

Hi @cynthiajoan, this doc is not enough for solving conflicts. Because the credentials which I linking is having an existing account so we have to delete the new account first. After that, we can link.

I have followed the doc https://firebase.google.com/docs/auth/web/account-linking

// Get reference to the currently signed-in user
var prevUser = auth.currentUser;
// Sign in user with another account
auth.signInWithCredential(credential).then(function(user) {
  console.log("Sign In Success", user);
  var currentUser = user;
  // Merge prevUser and currentUser data stored in Firebase.
  // Note: How you handle this is specific to your application

  // After data is migrated delete the duplicate user
  return user.delete().then(function() {
    // Link the OAuth Credential to original account
    return prevUser.linkWithCredential(credential);
  }).then(function() {
    // Sign in with the newly linked credential
    return auth.signInWithCredential(credential);
  });
}).catch(function(error) {
  console.log("Sign In Error", error);
});

But the above code is in javascript.

In c# how to store prevUser? when I have signInWithCredantial I am lost the current user.

initfusion avatar Jun 17 '19 13:06 initfusion

Hi @initfusion, if I understand correctly, in Unity/C#, we have this example maybe can solve your problem: Unity account linking

// Gather data for the currently signed in User. string currentUserId = auth.CurrentUser.UserId; string currentEmail = auth.CurrentUser.Email; string currentDisplayName = auth.CurrentUser.DisplayName; System.Uri currentPhotoUrl = auth.CurrentUser.PhotoUrl;

// Sign in with the new credentials. auth.SignInWithCredentialAsync(credential).ContinueWith(task => { if (task.IsCanceled) { Debug.LogError("SignInWithCredentialAsync was canceled."); return; } if (task.IsFaulted) { Debug.LogError("SignInWithCredentialAsync encountered an error: " + task.Exception); return; }

Firebase.Auth.FirebaseUser newUser = task.Result; Debug.LogFormat("User signed in successfully: {0} ({1})", newUser.DisplayName, newUser.UserId);

// TODO: Merge app specific details using the newUser and values from the // previous user, saved above. });

You can grab prevUser by auth.CurrentUser before signInWithCredantial.

Let me know if that works for you.

cynthiajoan avatar Jun 17 '19 17:06 cynthiajoan

Hi @cynthiajoan Yes, I have done that way but after doing signInWithCredantial the prevUser which I have grabbed previously is also changed to the new user.

I think its sync new user to all its existing user instances.

initfusion avatar Jun 18 '19 06:06 initfusion

Hi This is I am trying

        var credential = EmailAuthProvider.GetCredential(email, password);

        auth.CurrentUser.LinkWithCredentialAsync(credential).ContinueWithOnMainThread(task =>
        {
            if (task.IsCanceled)
            {
                Debug.Log("Cancel");
            }

            if (task.IsFaulted)
            {
                Debug.Log("Faulted");

                var currentUser = auth.CurrentUser;

                otherAuth.SignInWithCredentialAsync(credential).ContinueWithOnMainThread(linkTask =>
                {
                    Debug.Log("Current: " + currentUser.UserId);
                    Debug.Log("New: " + linkTask.Result.UserId);

                    // Here both current and new have the same user.
                });
            }

            if (task.IsCompleted)
            {
                Debug.Log("Completed");
            }
        });

initfusion avatar Jun 20 '19 04:06 initfusion

HI @initfusion ,

as per @cynthiajoan suggestion, have you tried caching the individual fields you need before executing the SignInWithCredentialAsync? The JavaScript and Unity APIs will be slightly different.

--Patrick

patm1987 avatar Jun 20 '19 16:06 patm1987

Hi @patm1987,

Yes, I can cache the individual fields but I need the previous user to link the user. Without a previous user, I can't call the LinkWithCredantials.

Screenshot 2019-06-21 at 10 08 55 AM

initfusion avatar Jun 21 '19 04:06 initfusion

With the Unity API, you just need the credential of the account you want to link with the current one (ie: if your continuation from LinkWithCredentialAsync succeeds, you should have linked accounts). You can get more information here: https://firebase.google.com/docs/reference/unity/class/firebase/auth/firebase-user#linkandretrievedatawithcredentialasync

Let me know if it's not the case, but you should be able to verify that the merger worked in your firebase authentication dashboard (in console.firebase.google.com).

patm1987 avatar Jun 21 '19 16:06 patm1987

Hi @patm1987

Yes, it's not the case, Link account is working properly when there is no error when link. But if there is an error (ie: if the credential which is I am going to merge is already having an account).

In that case, I have to delete the existing account. for delete existing account I have to SignIn so I can get the user and using that user I can delete.

if there is another way to solve merge conflicts then let me know.

Let me know if you still not understand my problem.

initfusion avatar Jun 24 '19 05:06 initfusion

@initfusion it looks like you've found a problem with both the C++ and Unity SDKs here. You're right that it's not currently possible to copy the FirebaseUser object in the C# API which is an issue in the case you've described where you want to:

  • Cache the current user's auth token
  • Attempt to sign in with credentials (e.g email / password) and if that results in a different user, delete the other user's account.
  • Link the credentials (email / password) with the current user's account

However, I think it's possible to do this with multiple FirebaseApp objects instead:

  • Create a secondary FirebaseApp object and a FirebaseAuth object from that (e.g otherAuth)
  • Then if credential linking fails with FirebaseAuth.DefaultInstance.CurrentUser, sign-in to otherAuth with the credentials
  • Delete otherAuth.CurrentUser
  • Try linking credentials on FirebaseAuth.DefaultInstance.CurrentUser again

stewartmiles avatar Jun 25 '19 23:06 stewartmiles

Hi @stewartmiles

Thanks for understanding my problem. I was also tried with multiple Firebase objects but still not able to get or cache the current user's auth token.

Here is my code

otherAuth = FirebaseAuth.GetAuth(FirebaseApp.Create(AppOptions.LoadFromJsonConfig(conf.text)));

Now I am trying this but still, both are the same.

var credential = EmailAuthProvider.GetCredential(email, password);
  auth.CurrentUser.LinkAndRetrieveDataWithCredentialAsync(credential).ContinueWithOnMainThread(task =>
        {
            if (task.IsCanceled)
            {
                Debug.Log("Cancel");
            }

            if (task.IsFaulted)
            {
                Debug.Log("Faulted");
                
                otherAuth.SignInWithCredentialAsync(credential).ContinueWithOnMainThread(linkTask =>
                {
                    Debug.Log("Current: " + auth.CurrentUser.UserId);
                    Debug.Log("New: " + otherAuth.CurrentUser.UserId);

                    // Here both current and new have same user..
                });
            }

            if (task.IsCompleted)
            {
                Debug.Log("Completed");
            }
        });

initfusion avatar Jun 26 '19 06:06 initfusion

FirebaseApp.Create() will create the default app, you need to create a named secondary app using FirebaseApp.Create(options, name) see https://firebase.google.com/docs/reference/unity/class/firebase/firebase-app#class_firebase_1_1_firebase_app_1a39a702e1912ab6b708600b03bd955f4d

Here's an example: https://github.com/firebase/quickstart-unity/blob/master/auth/testapp/Assets/Firebase/Sample/Auth/UIHandler.cs#L97

stewartmiles avatar Jun 26 '19 20:06 stewartmiles

Hi @stewartmiles

Thank you very much, using a secondary app solve my problem in all the methods except phone auth.

Now I am facing a new issue when link phone credentials. If phone auth link fails then I have used the same way to solve merge conflict.

  • First, I copy the credentials of phone auth after auto verification completed
var phoneAuthProvider = PhoneAuthProvider.GetInstance(_auth);
        phoneAuthProvider.VerifyPhoneNumber(phoneNumber, PhoneAuthTimeoutMs, null,
            cred =>
            {
                _phoneAuthCredentials = cred;
            },
  • After that I linked the credentials to current user.

_user.LinkWithCredentialAsync(_phoneAuthCredentials).ContinueWithOnMainThread(HandleLinkWithUser);

  • Now I get merge error
  • After that, if I sign with that credentials I am getting "Phone auth code expire" error.

_otherAuth.SignInWithCredentialAsync(_phoneAuthCredentials).ContinueWithOnMainThread(HandleMergeConflict);

initfusion avatar Jun 27 '19 09:06 initfusion

After deleting the other phone auth account could you run through the VerifyPhoneNumber() step again ? I know it's a little irritating for the user but I'm sure you can pop up a message explaining what happened (e.g there was a duplicate account).

stewartmiles avatar Jun 27 '19 16:06 stewartmiles

Hi @stewartmiles

This is not a proper way, because I need to VerifyPhoneNumber() 3 times.

  • First, When linking the account first time.
  • Second, When sigh in and delete the account
  • Third, When linking again.

It's very irritating for the user. My testing phone numbers are work but others do not work.

initfusion avatar Jun 28 '19 04:06 initfusion

@initfusion I see what you mean, that is a pain. We'll try to get a fix out soon.

stewartmiles avatar Jun 28 '19 23:06 stewartmiles

Hi @stewartmiles,

May I know ETA for this issue?

initfusion avatar Jul 01 '19 13:07 initfusion

@initfusion we don't have a timeline for the change yet. I took a quick look at it last week and it's a non-trivial change to the way we store user data at the moment in the C++ SDK.

stewartmiles avatar Jul 01 '19 15:07 stewartmiles