Search code examples
firebaseunity-game-enginefirebase-realtime-databasefirebase-authenticationapple-sign-in

Firebase Authentication Duplicate Credential Error, Apple Sign In


I am working on a game where i need to transfer data between devices, regardless of OS type. I have imported the Apple Sign In Asset from Unity Asset Store and Firebase Authentication package to my project.

Apple Sign-In and Firebase Authentication using apple as a provider is working, i am able to read and write data to my database from my game after signing in with my Apple ID.

However, when I try to sign out from Firebase authentication and sign back in using the same sing in method i get the duplicate credential error.

This is how i am authenticating the user after a successful Apple Sign-In.

private void SignInWithAppleSuccessful()
{ 
  auth = Firebase.Auth.FirebaseAuth.DefaultInstance;
    
  var appleIdToken = PlayerPrefs.GetString(AppleUserTokenKey);
  var rawNonce = Guid.NewGuid().ToString();
    
  var user = auth.CurrentUser;
            
  if (auth.CurrentUser != null) 
  {
    userId.text = user.UserId;
    AuthenticationSuccessful(auth.CurrentUser);
    AddListenersToDatabase();
  }
  else
  {
    resultText.text = "No current user found";
    Firebase.Auth.Credential credential = Firebase.Auth.OAuthProvider.GetCredential("apple.com", appleIdToken, rawNonce, null);

    auth.SignInAndRetrieveDataWithCredentialAsync(credential)
    .ContinueWith(task => {
      if (task.IsCanceled) 
      {
      Debug.LogError("BB 1 SignInAndRetrieveDataWithCredentialAsync was canceled.");

        return;
      }
      if (task.IsFaulted) {
        Debug.LogError("BB 2 SignInAndRetrieveDataWithCredentialAsync encountered an error: " + task.Exception);
    
        return;
      }
    
     Firebase.Auth.SignInResult result = task.Result;
                resultText.text = "User signed in successfully : " + 
     result.User.UserId;
     userId.text = result.User.UserId;
                
     AuthenticationSuccessful(result.User);
     AddListenersToDatabase();
    }
}

Google document mentions that Firebase Authentication tokens are long lived. Meaning it will expire at some point?

In order to simulate token expiration i added sign out method to my project.

if (auth.CurrentUser != null)
{
  auth.SignOut();
  RemoveListenerFromDatabase();         
  resultText.text = "User signed out";
}

After signing out, i then tried to sign back in using the same method above SignInWithAppleSuccessful()

And received the following error

SignInAndRetrieveDataWithCredentialAsync encountered an error: System.AggregateException: One or more errors occurred. (One or more errors occurred. (Duplicate credential received. Please try again with a new credential.)) ---> System.AggregateException: One or more errors occurred. (Duplicate credential received. Please try again with a new credential.) ---> Firebase.FirebaseException: Duplicate credential received. Please try again with a new credential.
   --- End of inner exception stack trace ---
   --- End of inner exception stack trace ---
---> (Inner Exception #0) System.AggregateException: One or more errors occurred. (Duplicate credential received. Please try again with a new credential.) ---> Firebase.FirebaseException: Duplicate credential received. Please try again with a new credential.
   --- End of inner exception stack trace ---
---> (Inner Exception #0) Firebase.FirebaseException: Duplicate credential received. Please try again with a new credential.<---
<---
<>c__DisplayClass33_0:<SignInWithAppleSuccessful>b__0(Task`1)
System.Threading.Tasks.ContinuationTaskFromResultTask`1:InnerInvoke()
System.Threading.Tasks.Task:Execute()
System.Threading.ExecutionContext:RunInternal(ExecutionContext, ContextCallback, Object, Boolean)
System.Threading.Tasks.Task:ExecuteWithThreadLocal(Task&)
System.Threading.Tasks.Task:ExecuteEntry(Boolean)
System.Threading.ThreadPoolWorkQueue:Dispatch()

I have tried generating new Nonce string for every sign in attempt and nothing changed. Still get the same error message.


Solution

  • I solved the issue by deleting Apple user id and Apple user token which i saved in PlayerPrefs. It re-shows the Apple Sign In prompt but i am now able to re-authenticate the same user.

    At first i thought i was getting the same Apple User Token for each Apple Sign In but turns out that wasn't the case. Every time a user signs in with Apple Sign In it returns a new Apple User Token. I am no longer getting duplicate credential errors.

    Note that if you ever sign out from Firebase Authentication, you need to erase your tokens and re-prompt user to sign in to whichever provider you are using in order to get that newly generated token.

    I have spent too much time on this 🥲