firebase / firebase-unity-sdk

The Firebase SDK for Unity
http://firebase.google.com
Apache License 2.0
215 stars 35 forks source link

Auth: How to solve user credential merge conflict? #329

Open initfusion opened 5 years ago

initfusion commented 5 years ago

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.

cynthiajoan commented 5 years ago

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.

initfusion commented 5 years ago

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.

cynthiajoan commented 5 years ago

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.

initfusion commented 5 years ago

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 commented 5 years ago

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");
            }
        });
patm1987 commented 5 years ago

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

initfusion commented 5 years ago

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

patm1987 commented 5 years ago

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).

initfusion commented 5 years ago

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.

stewartmiles commented 5 years ago

@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:

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

initfusion commented 5 years ago

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");
            }
        });
stewartmiles commented 5 years ago

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

initfusion commented 5 years ago

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.

var phoneAuthProvider = PhoneAuthProvider.GetInstance(_auth);
        phoneAuthProvider.VerifyPhoneNumber(phoneNumber, PhoneAuthTimeoutMs, null,
            cred =>
            {
                _phoneAuthCredentials = cred;
            },

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

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

stewartmiles commented 5 years ago

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).

initfusion commented 5 years ago

Hi @stewartmiles

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

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

stewartmiles commented 5 years ago

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

initfusion commented 5 years ago

Hi @stewartmiles,

May I know ETA for this issue?

stewartmiles commented 5 years ago

@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.