1
votes

I am using firebase authentication with anonymous users. When the user sign up on the site, we link their credential.

Here is the code:

//Create the email and password credential, to upgrade the anonymous user.    
let credential = firebase.auth.EmailAuthProvider.credential(email, visitorId);

//Link the credential to the currently signed in user (the anonymous user).
auth.currentUser.link(credential).then((user) => {

     console.log("anonymous visitor successfully upgraded");

    }, 

    (error) => {

        //error upgrading the anonymouse user
        console.log("Error upgrading anonymous visitor", error);
});

Here is documentation of this approach

This has been working great until today. Now I am suddenly receiving an error code: auth/network-request-failed.

If I inspect the failed request in Fiddler, I find a response of:

{
 "error": {
  "errors": [
   {
    "domain": "global",
    "reason": "invalid",
    "message": "TOKEN_EXPIRED"
   }
  ],
  "code": 400,
  "message": "TOKEN_EXPIRED"
 }

}

The odd thing is that if I decode the token on the request, the expiration date falls in the future (about 1 hour ahead which is the default time length for their tokens). There is almost no delay between the time I generate the token, and the time I try linking it to the user. So why the failure?

My research:

The issue here sounds similar, but I has not resolved it for me.

1
More information could be helpful. I can only speculate. The following things could be happening: the anonymous user was disabled/ deleted or the password/email was updated on that same account. This is typically when a TOKEN_EXPIRED error is thrown by the backend. Are you doing any of the following?bojeil
No on both accounts. Here is what I'm doing:Kevin Pidduck
No on both accounts. The first thing I do upon a visit, is sign the user in anonymously. This works. I see that userId is output in the console and the same userId shows up in the Authentication dashboard. The next step, upon accepting user input of username (I can guarantee uniqueness here - ensuring the same email was never used before), I get the credential and try to link it with the anonymous user. This fails with the "auth/network-request-failed" error. I should also mention the same thing happens across different networks, browsers and devices.Kevin Pidduck
I'd like to add that I've also tried setting up a new project in order to use a completely new authentication store. Same thing happens.Kevin Pidduck
I tried to do the same in my test app. I sign in an anonymous user and then link an email/password credential to it successfully. This is very strange. Can you provide more code or details? It is very strange the token would expire here.bojeil

1 Answers

0
votes

The first thing the support team at firebase asked for was a simplified demo of the issue. I should have done this first. My bad. For other's benefit, I'm including the demo code below. It worked without a problem. So our problem was clearly with our implementation.

I went back to the network requests. The 1st clue came when I realized that the network request that is sent in response to firebase.auth().currentUser.link(credential) was showing as cancelled. Using chrome://net-internals/#events, I found --> net_error = -3 (ERR_ABORTED).

This meant something was redirecting the page before the firebase request could be returned. Stepping through the code I found that a Google Tag Manager HTML snippet was firing that included a redirect when a specific element was clicked.

Removed that and the issue resolved itself. I hope this post-mortem proves useful for somebody else.

<html>
    <head>
        <meta charset="utf-8">
        <script src="https://www.gstatic.com/firebasejs/3.6.2/firebase-app.js"></script>
        <script src="https://www.gstatic.com/firebasejs/3.6.2/firebase-auth.js"></script>
        <script>
        
        window.config = {
            apiKey: "<apikey>",
            authDomain: "<authDomain>",
            databaseURL: "<databaseUrl>",
          };
          

          //initialize the app
           firebase.initializeApp(window.config);
    
            firebase.auth().onAuthStateChanged((user) => {
              if (user) {
                // User is signed in.
                console.log("User is signed in:" + user.uid);
              } else {
                // No user is signed in.
                console.log("no user so sign in anonymously");
                firebase.auth().signInAnonymously().catch((error) => {
            			console.log("marketing.visitor:" + error.code + " " + error.message);
            	});
              }
            });
    
            function signInWithEmailAndPassword(){
                if(event.preventDefault){
                    event.preventDefault();
                }else{
                    event.returnValue = false; // for IE as dont support preventDefault;
                }
                
                var myElement = document.getElementById("lead_email");
                console.log(myElement.value);
                
                //Create the email and password credential, to upgrade the anonymous user.
                let credential = firebase.auth.EmailAuthProvider.credential(myElement.value, firebase.auth().currentUser.uid);
                
                //Links the credential to the currently signed in user (the anonymous user).
                firebase.auth().currentUser.link(credential).then((user) => {
                    
                    console.log("anonymous visitor successfully upgraded");

                }, 
                
                (error) => {
                   console.log("error upgrading anonymous user", error);
                });
                
                
                return false;
            }
    
            
        </script>
        
        
    </head>
    <body>
        <form onsubmit="signInWithEmailAndPassword()">
          <label class="form-field" for="lead_email">Email Address:</label>
        <input data-name="lead_email" id="lead_email" maxlength="256" name="lead_email" required="required" type="email">
        
        <input type="submit" value="Submit">
        </form>   
    </body>
</html>