Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Firebase Verifying ID Tokens

I have a server app and I'm trying to identify the currently signed-in user on that server. I have successfully authenticated the user on my web app like this:

    var provider = new firebase.auth.GoogleAuthProvider();          
    firebase.auth().signInWithPopup(provider).then(function(result) {
        userJsonToken = result.credential.accessToken;
        user = result.user;
    }).catch(function(error) {
        // Handle Errors here.
        var errorCode = error.code;
        var errorMessage = error.message;
        // The email of the user's account used.
        var email = error.email;
        // The firebase.auth.AuthCredential type that was used.
        var credential = error.credential;
    });

I then send the userJsonToken to my server via an ajax call and I can see the token when I debug so the token is being correctly sent to the server.

In my server application I am doing this:

try (InputStream is = FirebaseUtil.class.getClassLoader().getResourceAsStream(FIREBASE_CREDENTIALS_JSON_FILE)) {
    FirebaseOptions options = new FirebaseOptions.Builder().setServiceAccount(is).setDatabaseUrl(FIREBASE_DATABASE_URL).build();        
    FirebaseApp.initializeApp(options);
} catch (IOException e) {
    e.printStackTrace(System.err);
}

Then I do:

    FirebaseAuth.getInstance().verifyIdToken(idToken).addOnFailureListener(new OnFailureListener() {            
        @Override
        public void onFailure(Exception ex) {
            throw new AuthenticationServiceException(ex.getMessage(), ex);
        }
    }).addOnSuccessListener(new OnSuccessListener<FirebaseToken>() {
        @Override
        public void onSuccess(FirebaseToken token) {
            System.out.println("Yeehaw");
        }
    }).addOnCompleteListener(new OnCompleteListener<FirebaseToken>() {          
        @Override
        public void onComplete(Task<FirebaseToken> arg0) {
            System.out.println(arg0.isSuccessful());
        }
    });

At this point, the OnFailureListener is getting called. I am getting a java.lang.IllegalArgumentException with no explanation about why or what has happened. Is there anyway I can see logs on Firebase that will shed some light on what has happened? Thanks

This is the complete error:

Caused by: java.lang.IllegalArgumentException
at com.google.api.client.repackaged.com.google.common.base.Preconditions.checkArgument(Preconditions.java:111)
at com.google.api.client.util.Preconditions.checkArgument(Preconditions.java:37)
at com.google.api.client.json.webtoken.JsonWebSignature$Parser.parse(JsonWebSignature.java:602)
at com.google.firebase.auth.FirebaseToken.parse(FirebaseToken.java:81)
at com.google.firebase.auth.FirebaseAuth$1.call(FirebaseAuth.java:143)
at com.google.firebase.auth.FirebaseAuth$1.call(FirebaseAuth.java:140)
at com.google.firebase.tasks.Tasks$1.run(Tasks.java:63)
like image 999
SME Avatar asked Sep 16 '16 16:09

SME


People also ask

How do I verify a token?

There are two ways to verify a token: locally or remotely with Okta. The token is signed with a JSON Web Key (JWK) using the RS256 algorithm. To validate the signature, Okta provides your application with a public key that can be used.

Does Firebase Auth use tokens?

Firebase gives you complete control over authentication by allowing you to authenticate users or devices using secure JSON Web Tokens (JWTs). You generate these tokens on your server, pass them back to a client device, and then use them to authenticate via the signInWithCustomToken() method.

What is the difference between ID token and access token?

Access tokens are what the OAuth client uses to make requests to an API. The access token is meant to be read and validated by the API. An ID token contains information about what happened when a user authenticated, and is intended to be read by the OAuth client.

Does Firebase automatically refresh token?

Every time a user signs in, the user credentials are sent to the Firebase Authentication backend and exchanged for a Firebase ID token (a JWT) and refresh token. Firebase ID tokens are short lived and last for an hour; the refresh token can be used to retrieve new ID tokens.


1 Answers

Ran into this problem myself, and after not finding an answer here, decided to look into the source-code itself.

/**
 * Parses a JWS token into a parsed {@link JsonWebSignature}.
 *
 * @param tokenString JWS token string
 * @return parsed {@link JsonWebSignature}
 */
public JsonWebSignature parse(String tokenString) throws IOException {
  // split on the dots
  int firstDot = tokenString.indexOf('.');
  Preconditions.checkArgument(firstDot != -1);
  byte[] headerBytes = Base64.decodeBase64(tokenString.substring(0, firstDot));
  int secondDot = tokenString.indexOf('.', firstDot + 1);
  Preconditions.checkArgument(secondDot != -1);
  ...

In particular, this was the line that triggered the error in your case:

  Preconditions.checkArgument(secondDot != -1);

Apparently, if your token does not have at least 2 dots, it will produce an IllegalArgumentException here, without any error-message clarifying why this error was thrown.

In my case I hit this error because I was using a fake token that I made up by hand, for testing purposes. I was expecting to see a "token decode failed" type error. Sure enough, after adding 2 dots to my fake-token, I then run into a far more descriptive error message:

Decoding Firebase ID token failed. Make sure you passed the entire string JWT which represents an ID token. See https://firebase.google.com/docs/auth/admin/verify-id-tokens for details on how to retrieve an ID token.

It's unfortunate that the Firebase team didn't include a more descriptive error message for the first 2 preconditions, because that would better alert users to the fact that the token is malformed/incomplete, and that there is nothing wrong with the way they initialized or invoked FirebaseAuth.

like image 56
RvPr Avatar answered Oct 17 '22 10:10

RvPr