Repository: firebase/firebase-admin-java
Pull request #64 (2017-09-05 19:45:56)

Proper handling of exceptions when verifying ID tokens


Patch:
diff --git a/src/main/java/com/google/firebase/auth/internal/FirebaseTokenVerifier.java b/src/main/java/com/google/firebase/auth/internal/FirebaseTokenVerifier.java
index b945781..5a3d6dd 100644
--- a/src/main/java/com/google/firebase/auth/internal/FirebaseTokenVerifier.java
+++ b/src/main/java/com/google/firebase/auth/internal/FirebaseTokenVerifier.java
@@ -58,7 +58,8 @@ public final class FirebaseTokenVerifier extends IdTokenVerifier {
   private static final String ISSUER_PREFIX = "https://securetoken.google.com/";
   private static final String FIREBASE_AUDIENCE =
       "https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit";
-  private static final String ERROR_CODE = "ERROR_INVALID_CREDENTIAL";
+  private static final String ERROR_INVALID_CREDENTIAL = "ERROR_INVALID_CREDENTIAL";
+  private static final String ERROR_RUNTIME_EXCEPTION = "ERROR_RUNTIME_EXCEPTION";
   private static final String PROJECT_ID_MATCH_MESSAGE =
       " Make sure the ID token comes from the same Firebase project as the service account used to "
           + "authenticate this SDK.";
@@ -138,18 +139,18 @@ public final class FirebaseTokenVerifier extends IdTokenVerifier {
 
     if (errorMessage != null) {
       errorMessage += VERIFY_ID_TOKEN_DOCS_MESSAGE;
-      throw new FirebaseAuthException(ERROR_CODE, errorMessage);
+      throw new FirebaseAuthException(ERROR_INVALID_CREDENTIAL, errorMessage);
     }
 
     try {
       if (!verifySignature(token)) {
         throw new FirebaseAuthException(
-            ERROR_CODE,
+            ERROR_INVALID_CREDENTIAL,
             "Firebase ID token isn't signed by a valid public key." + VERIFY_ID_TOKEN_DOCS_MESSAGE);
       }
     } catch (IOException | GeneralSecurityException e) {
       throw new FirebaseAuthException(
-          ERROR_CODE, "Firebase ID token has invalid signature." + VERIFY_ID_TOKEN_DOCS_MESSAGE);
+          ERROR_RUNTIME_EXCEPTION, "Error while verifying token signature.", e);
     }
 
     return true;


Test patch:
diff --git a/src/test/java/com/google/firebase/auth/internal/FirebaseTokenVerifierTest.java b/src/test/java/com/google/firebase/auth/internal/FirebaseTokenVerifierTest.java
index 808340a..e0df09c 100644
--- a/src/test/java/com/google/firebase/auth/internal/FirebaseTokenVerifierTest.java
+++ b/src/test/java/com/google/firebase/auth/internal/FirebaseTokenVerifierTest.java
@@ -21,6 +21,7 @@ import static org.junit.Assert.assertTrue;
 
 import com.google.api.client.auth.openidconnect.IdToken;
 import com.google.api.client.googleapis.auth.oauth2.GooglePublicKeysManager;
+import com.google.api.client.http.LowLevelHttpRequest;
 import com.google.api.client.json.JsonFactory;
 import com.google.api.client.json.gson.GsonFactory;
 import com.google.api.client.json.webtoken.JsonWebSignature;
@@ -31,6 +32,7 @@ import com.google.api.client.testing.http.FixedClock;
 import com.google.api.client.testing.http.MockHttpTransport;
 import com.google.api.client.testing.http.MockLowLevelHttpResponse;
 import com.google.common.io.BaseEncoding;
+import com.google.firebase.auth.FirebaseAuthException;
 import com.google.firebase.auth.FirebaseToken;
 import com.google.firebase.auth.TestOnlyImplFirebaseAuthTrampolines;
 import com.google.firebase.testing.ServiceAccount;
@@ -42,6 +44,7 @@ import java.security.PrivateKey;
 import java.security.spec.InvalidKeySpecException;
 import java.security.spec.KeySpec;
 import java.security.spec.PKCS8EncodedKeySpec;
+import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -265,6 +268,36 @@ public class FirebaseTokenVerifierTest {
     verifier.verifyTokenAndSignature(TestOnlyImplFirebaseAuthTrampolines.getToken(token));
   }
 
+  @Test
+  public void verifyTokenCertificateError() throws Exception {
+    FirebaseToken token =
+        TestOnlyImplFirebaseAuthTrampolines.parseToken(
+            FACTORY, createToken(createHeader(), createPayload()));
+
+    MockHttpTransport mockTransport = new MockHttpTransport() {
+      @Override
+      public LowLevelHttpRequest buildRequest(String method, String url) throws IOException {
+        throw new IOException("Expected error");
+      }
+    };
+    FirebaseTokenVerifier verifier = new FirebaseTokenVerifier.Builder()
+        .setClock(CLOCK)
+        .setPublicKeysManager(
+            new GooglePublicKeysManager.Builder(mockTransport, FACTORY)
+                .setClock(CLOCK)
+                .setPublicCertsEncodedUrl(FirebaseTokenVerifier.CLIENT_CERT_URL)
+                .build())
+        .setProjectId(PROJECT_ID)
+        .build();
+    try {
+      verifier.verifyTokenAndSignature(TestOnlyImplFirebaseAuthTrampolines.getToken(token));
+      Assert.fail("No exception thrown");
+    } catch (FirebaseAuthException expected) {
+      assertTrue(expected.getCause() instanceof IOException);
+      assertEquals("Expected error", expected.getCause().getMessage());
+    }
+  }
+
   @Test
   public void legacyCustomToken() throws Exception {
     initCrypto(ServiceAccount.OWNER.getPrivateKey(), ServiceAccount.NONE.getCert());