Repository: firebase/firebase-admin-java
Pull request #463 (2020-07-30 18:02:55)

chore: Support for specifying query parameters in HttpRequestInfo
A collection of somewhat related cleanup and refactoring actions:

* Updated to `FirebaseUserManager.Builder` and introducing a new `createUserManager()` factory method so it looks similar to how other helper classes are initialized in the auth package.
* `HttpRequestInfo` now supports setting URL query parameters. Calling classes no need to use `GenericUrl`.
* `JsonObjectParser` attribute is removed from the `ErrorHandlingHttpClient`. It's not needed there due to the way we parse response payloads in the SDK.


Patch:
diff --git a/src/main/java/com/google/firebase/auth/AbstractFirebaseAuth.java b/src/main/java/com/google/firebase/auth/AbstractFirebaseAuth.java
index ee3e67b..8fb20c5 100644
--- a/src/main/java/com/google/firebase/auth/AbstractFirebaseAuth.java
+++ b/src/main/java/com/google/firebase/auth/AbstractFirebaseAuth.java
@@ -96,11 +96,7 @@ public abstract class AbstractFirebaseAuth {
             new Supplier<FirebaseUserManager>() {
               @Override
               public FirebaseUserManager get() {
-                return FirebaseUserManager
-                    .builder()
-                    .setFirebaseApp(app)
-                    .setTenantId(tenantId)
-                    .build();
+                return FirebaseUserManager.createUserManager(app, tenantId);
               }
             });
   }
diff --git a/src/main/java/com/google/firebase/auth/AuthErrorCode.java b/src/main/java/com/google/firebase/auth/AuthErrorCode.java
index c162518..bea067e 100644
--- a/src/main/java/com/google/firebase/auth/AuthErrorCode.java
+++ b/src/main/java/com/google/firebase/auth/AuthErrorCode.java
@@ -26,6 +26,11 @@ public enum AuthErrorCode {
    */
   CERTIFICATE_FETCH_FAILED,
 
+  /**
+   * No IdP configuration found for the given identifier.
+   */
+  CONFIGURATION_NOT_FOUND,
+
   /**
    * A user already exists with the provided email.
    */
@@ -71,8 +76,16 @@ public enum AuthErrorCode {
    */
   REVOKED_SESSION_COOKIE,
 
+  /**
+   * Tenant ID in the JWT does not match.
+   */
   TENANT_ID_MISMATCH,
 
+  /**
+   * No tenant found for the given identifier.
+   */
+  TENANT_NOT_FOUND,
+
   /**
    * A user already exists with the provided UID.
    */
diff --git a/src/main/java/com/google/firebase/auth/FirebaseAuth.java b/src/main/java/com/google/firebase/auth/FirebaseAuth.java
index f44bd23..264111a 100644
--- a/src/main/java/com/google/firebase/auth/FirebaseAuth.java
+++ b/src/main/java/com/google/firebase/auth/FirebaseAuth.java
@@ -19,14 +19,12 @@ package com.google.firebase.auth;
 import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Preconditions.checkNotNull;
 
-import com.google.api.client.util.Clock;
 import com.google.api.core.ApiFuture;
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Strings;
 import com.google.common.base.Supplier;
 import com.google.firebase.FirebaseApp;
 import com.google.firebase.ImplFirebaseTrampolines;
-import com.google.firebase.auth.internal.FirebaseTokenFactory;
 import com.google.firebase.auth.multitenancy.TenantManager;
 import com.google.firebase.internal.CallableOperation;
 import com.google.firebase.internal.FirebaseService;
@@ -214,37 +212,7 @@ public final class FirebaseAuth extends AbstractFirebaseAuth {
   protected void doDestroy() { }
 
   private static FirebaseAuth fromApp(final FirebaseApp app) {
-    return new FirebaseAuth(
-        AbstractFirebaseAuth.builder()
-          .setFirebaseApp(app)
-          .setTokenFactory(
-              new Supplier<FirebaseTokenFactory>() {
-                @Override
-                public FirebaseTokenFactory get() {
-                  return FirebaseTokenUtils.createTokenFactory(app, Clock.SYSTEM);
-                }
-              })
-          .setIdTokenVerifier(
-              new Supplier<FirebaseTokenVerifier>() {
-                @Override
-                public FirebaseTokenVerifier get() {
-                  return FirebaseTokenUtils.createIdTokenVerifier(app, Clock.SYSTEM);
-                }
-              })
-          .setCookieVerifier(
-              new Supplier<FirebaseTokenVerifier>() {
-                @Override
-                public FirebaseTokenVerifier get() {
-                  return FirebaseTokenUtils.createSessionCookieVerifier(app, Clock.SYSTEM);
-                }
-            })
-          .setUserManager(
-              new Supplier<FirebaseUserManager>() {
-                @Override
-                public FirebaseUserManager get() {
-                  return FirebaseUserManager.builder().setFirebaseApp(app).build();
-                }
-              }));
+    return new FirebaseAuth(AbstractFirebaseAuth.builderFromAppAndTenantId(app, null));
   }
 
   private static class FirebaseAuthService extends FirebaseService<FirebaseAuth> {
diff --git a/src/main/java/com/google/firebase/auth/FirebaseUserManager.java b/src/main/java/com/google/firebase/auth/FirebaseUserManager.java
index d00cd24..554d017 100644
--- a/src/main/java/com/google/firebase/auth/FirebaseUserManager.java
+++ b/src/main/java/com/google/firebase/auth/FirebaseUserManager.java
@@ -19,8 +19,6 @@ package com.google.firebase.auth;
 import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Preconditions.checkNotNull;
 
-import com.google.api.client.http.GenericUrl;
-import com.google.api.client.http.HttpMethods;
 import com.google.api.client.http.HttpRequestFactory;
 import com.google.api.client.http.HttpResponseInterceptor;
 import com.google.api.client.json.GenericJson;
@@ -44,9 +42,9 @@ import com.google.firebase.auth.internal.ListOidcProviderConfigsResponse;
 import com.google.firebase.auth.internal.ListSamlProviderConfigsResponse;
 import com.google.firebase.auth.internal.UploadAccountResponse;
 import com.google.firebase.internal.ApiClientUtils;
+import com.google.firebase.internal.HttpRequestInfo;
 import com.google.firebase.internal.NonNull;
 import com.google.firebase.internal.Nullable;
-
 import java.util.Collection;
 import java.util.HashSet;
 import java.util.List;
@@ -60,7 +58,7 @@ import java.util.Set;
  * @see <a href="https://developers.google.com/identity/toolkit/web/reference/relyingparty">
  *   Google Identity Toolkit</a>
  */
-class FirebaseUserManager {
+final class FirebaseUserManager {
 
   static final int MAX_LIST_PROVIDER_CONFIGS_RESULTS = 100;
   static final int MAX_GET_ACCOUNTS_BATCH_SIZE = 100;
@@ -81,13 +79,12 @@ class FirebaseUserManager {
   private final AuthHttpClient httpClient;
 
   private FirebaseUserManager(Builder builder) {
-    FirebaseApp app = checkNotNull(builder.app, "FirebaseApp must not be null");
-    String projectId = ImplFirebaseTrampolines.getProjectId(app);
+    String projectId = builder.projectId;
     checkArgument(!Strings.isNullOrEmpty(projectId),
         "Project ID is required to access the auth service. Use a service account credential or "
             + "set the project ID explicitly via FirebaseOptions. Alternatively you can also "
             + "set the project ID via the GOOGLE_CLOUD_PROJECT environment variable.");
-    this.jsonFactory = app.getOptions().getJsonFactory();
+    this.jsonFactory = checkNotNull(builder.jsonFactory, "JsonFactory must not be null");
     final String idToolkitUrlV1 = String.format(ID_TOOLKIT_URL, "v1", projectId);
     final String idToolkitUrlV2 = String.format(ID_TOOLKIT_URL, "v2", projectId);
     final String tenantId = builder.tenantId;
@@ -100,9 +97,7 @@ class FirebaseUserManager {
       this.idpConfigMgtBaseUrl = idToolkitUrlV2 + "/tenants/" + tenantId;
     }
 
-    HttpRequestFactory requestFactory = builder.requestFactory == null
-        ? ApiClientUtils.newAuthorizedRequestFactory(app) : builder.requestFactory;
-    this.httpClient = new AuthHttpClient(jsonFactory, requestFactory);
+    this.httpClient = new AuthHttpClient(jsonFactory, builder.requestFactory);
   }
 
   @VisibleForTesting
@@ -182,9 +177,10 @@ class FirebaseUserManager {
       builder.put("nextPageToken", pageToken);
     }
 
-    GenericUrl url = new GenericUrl(userMgtBaseUrl + "/accounts:batchGet");
-    url.putAll(builder.build());
-    return httpClient.sendRequest(HttpMethods.GET, url, null, DownloadAccountResponse.class);
+    String url = userMgtBaseUrl + "/accounts:batchGet";
+    HttpRequestInfo requestInfo = HttpRequestInfo.buildGetRequest(url)
+        .addAllParameters(builder.build());
+    return httpClient.sendRequest(requestInfo, DownloadAccountResponse.class);
   }
 
   UserImportResult importUsers(UserImportRequest request) throws FirebaseAuthException {
@@ -218,8 +214,9 @@ class FirebaseUserManager {
 
   private UserRecord lookupUserAccount(
       Map<String, Object> payload, String identifier) throws FirebaseAuthException {
-    IncomingHttpResponse response = httpClient.sendRequest(
-        HttpMethods.POST, new GenericUrl(userMgtBaseUrl + "/accounts:lookup"), payload);
+    HttpRequestInfo requestInfo = HttpRequestInfo.buildJsonPostRequest(
+        userMgtBaseUrl + "/accounts:lookup", payload);
+    IncomingHttpResponse response = httpClient.sendRequest(requestInfo);
     GetAccountInfoResponse parsed = httpClient.parse(response, GetAccountInfoResponse.class);
     if (parsed.getUsers() == null || parsed.getUsers().isEmpty()) {
       throw new FirebaseAuthException(ErrorCode.NOT_FOUND,
@@ -234,44 +231,46 @@ class FirebaseUserManager {
 
   OidcProviderConfig createOidcProviderConfig(
       OidcProviderConfig.CreateRequest request) throws FirebaseAuthException {
-    GenericUrl url = new GenericUrl(idpConfigMgtBaseUrl + "/oauthIdpConfigs");
-    url.set("oauthIdpConfigId", request.getProviderId());
-    return httpClient.sendRequest("POST", url, request.getProperties(), OidcProviderConfig.class);
+    String url = idpConfigMgtBaseUrl + "/oauthIdpConfigs";
+    HttpRequestInfo requestInfo = HttpRequestInfo.buildJsonPostRequest(url, request.getProperties())
+        .addParameter("oauthIdpConfigId", request.getProviderId());
+    return httpClient.sendRequest(requestInfo, OidcProviderConfig.class);
   }
 
   SamlProviderConfig createSamlProviderConfig(
       SamlProviderConfig.CreateRequest request) throws FirebaseAuthException {
-    GenericUrl url = new GenericUrl(idpConfigMgtBaseUrl + "/inboundSamlConfigs");
-    url.set("inboundSamlConfigId", request.getProviderId());
-    return httpClient.sendRequest("POST", url, request.getProperties(), SamlProviderConfig.class);
+    String url = idpConfigMgtBaseUrl + "/inboundSamlConfigs";
+    HttpRequestInfo requestInfo = HttpRequestInfo.buildJsonPostRequest(url, request.getProperties())
+        .addParameter("inboundSamlConfigId", request.getProviderId());
+    return httpClient.sendRequest(requestInfo, SamlProviderConfig.class);
   }
 
   OidcProviderConfig updateOidcProviderConfig(OidcProviderConfig.UpdateRequest request)
       throws FirebaseAuthException {
     Map<String, Object> properties = request.getProperties();
-    GenericUrl url =
-        new GenericUrl(idpConfigMgtBaseUrl + getOidcUrlSuffix(request.getProviderId()));
-    url.put("updateMask", Joiner.on(",").join(AuthHttpClient.generateMask(properties)));
-    return httpClient.sendRequest("PATCH", url, properties, OidcProviderConfig.class);
+    String url = idpConfigMgtBaseUrl + getOidcUrlSuffix(request.getProviderId());
+    HttpRequestInfo requestInfo = HttpRequestInfo.buildJsonPatchRequest(url, properties)
+        .addParameter("updateMask", Joiner.on(",").join(AuthHttpClient.generateMask(properties)));
+    return httpClient.sendRequest(requestInfo, OidcProviderConfig.class);
   }
 
   SamlProviderConfig updateSamlProviderConfig(SamlProviderConfig.UpdateRequest request)
       throws FirebaseAuthException {
     Map<String, Object> properties = request.getProperties();
-    GenericUrl url =
-        new GenericUrl(idpConfigMgtBaseUrl + getSamlUrlSuffix(request.getProviderId()));
-    url.put("updateMask", Joiner.on(",").join(AuthHttpClient.generateMask(properties)));
-    return httpClient.sendRequest("PATCH", url, properties, SamlProviderConfig.class);
+    String url = idpConfigMgtBaseUrl + getSamlUrlSuffix(request.getProviderId());
+    HttpRequestInfo requestInfo = HttpRequestInfo.buildJsonPatchRequest(url, properties)
+        .addParameter("updateMask", Joiner.on(",").join(AuthHttpClient.generateMask(properties)));
+    return httpClient.sendRequest(requestInfo, SamlProviderConfig.class);
   }
 
   OidcProviderConfig getOidcProviderConfig(String providerId) throws FirebaseAuthException {
-    GenericUrl url = new GenericUrl(idpConfigMgtBaseUrl + getOidcUrlSuffix(providerId));
-    return httpClient.sendRequest("GET", url, null, OidcProviderConfig.class);
+    String url = idpConfigMgtBaseUrl + getOidcUrlSuffix(providerId);
+    return httpClient.sendRequest(HttpRequestInfo.buildGetRequest(url), OidcProviderConfig.class);
   }
 
   SamlProviderConfig getSamlProviderConfig(String providerId) throws FirebaseAuthException {
-    GenericUrl url = new GenericUrl(idpConfigMgtBaseUrl + getSamlUrlSuffix(providerId));
-    return httpClient.sendRequest("GET", url, null, SamlProviderConfig.class);
+    String url = idpConfigMgtBaseUrl + getSamlUrlSuffix(providerId);
+    return httpClient.sendRequest(HttpRequestInfo.buildGetRequest(url), SamlProviderConfig.class);
   }
 
   ListOidcProviderConfigsResponse listOidcProviderConfigs(int maxResults, String pageToken)
@@ -284,9 +283,10 @@ class FirebaseUserManager {
       builder.put("nextPageToken", pageToken);
     }
 
-    GenericUrl url = new GenericUrl(idpConfigMgtBaseUrl + "/oauthIdpConfigs");
-    url.putAll(builder.build());
-    return httpClient.sendRequest("GET", url, null, ListOidcProviderConfigsResponse.class);
+    String url = idpConfigMgtBaseUrl + "/oauthIdpConfigs";
+    HttpRequestInfo requestInfo = HttpRequestInfo.buildGetRequest(url)
+        .addAllParameters(builder.build());
+    return httpClient.sendRequest(requestInfo, ListOidcProviderConfigsResponse.class);
   }
 
   ListSamlProviderConfigsResponse listSamlProviderConfigs(int maxResults, String pageToken)
@@ -299,19 +299,20 @@ class FirebaseUserManager {
       builder.put("nextPageToken", pageToken);
     }
 
-    GenericUrl url = new GenericUrl(idpConfigMgtBaseUrl + "/inboundSamlConfigs");
-    url.putAll(builder.build());
-    return httpClient.sendRequest("GET", url, null, ListSamlProviderConfigsResponse.class);
+    String url = idpConfigMgtBaseUrl + "/inboundSamlConfigs";
+    HttpRequestInfo requestInfo = HttpRequestInfo.buildGetRequest(url)
+        .addAllParameters(builder.build());
+    return httpClient.sendRequest(requestInfo, ListSamlProviderConfigsResponse.class);
   }
 
   void deleteOidcProviderConfig(String providerId) throws FirebaseAuthException {
-    GenericUrl url = new GenericUrl(idpConfigMgtBaseUrl + getOidcUrlSuffix(providerId));
-    httpClient.sendRequest("DELETE", url, null, GenericJson.class);
+    String url = idpConfigMgtBaseUrl + getOidcUrlSuffix(providerId);
+    httpClient.sendRequest(HttpRequestInfo.buildDeleteRequest(url));
   }
 
   void deleteSamlProviderConfig(String providerId) throws FirebaseAuthException {
-    GenericUrl url = new GenericUrl(idpConfigMgtBaseUrl + getSamlUrlSuffix(providerId));
-    httpClient.sendRequest("DELETE", url, null, GenericJson.class);
+    String url = idpConfigMgtBaseUrl + getSamlUrlSuffix(providerId);
+    httpClient.sendRequest(HttpRequestInfo.buildDeleteRequest(url));
   }
 
   private static String getOidcUrlSuffix(String providerId) {
@@ -327,8 +328,8 @@ class FirebaseUserManager {
   private <T> T post(String path, Object content, Class<T> clazz) throws FirebaseAuthException {
     checkArgument(!Strings.isNullOrEmpty(path), "path must not be null or empty");
     checkNotNull(content, "content must not be null for POST requests");
-    GenericUrl url = new GenericUrl(userMgtBaseUrl + path);
-    return httpClient.sendRequest(HttpMethods.POST, url, content, clazz);
+    String url = userMgtBaseUrl + path;
+    return httpClient.sendRequest(HttpRequestInfo.buildJsonPostRequest(url, content), clazz);
   }
 
   static class UserImportRequest extends GenericJson {
@@ -371,18 +372,30 @@ class FirebaseUserManager {
     PASSWORD_RESET,
   }
 
+  static FirebaseUserManager createUserManager(FirebaseApp app, String tenantId) {
+    return FirebaseUserManager.builder()
+        .setProjectId(ImplFirebaseTrampolines.getProjectId(app))
+        .setTenantId(tenantId)
+        .setHttpRequestFactory(ApiClientUtils.newAuthorizedRequestFactory(app))
+        .setJsonFactory(app.getOptions().getJsonFactory())
+        .build();
+  }
+
   static Builder builder() {
     return new Builder();
   }
 
   static class Builder {
 
-    private FirebaseApp app;
+    private String projectId;
     private String tenantId;
     private HttpRequestFactory requestFactory;
+    private JsonFactory jsonFactory;
+
+    private Builder() { }
 
-    Builder setFirebaseApp(FirebaseApp app) {
-      this.app = app;
+    public Builder setProjectId(String projectId) {
+      this.projectId = projectId;
       return this;
     }
 
@@ -396,6 +409,11 @@ class FirebaseUserManager {
       return this;
     }
 
+    public Builder setJsonFactory(JsonFactory jsonFactory) {
+      this.jsonFactory = jsonFactory;
+      return this;
+    }
+
     FirebaseUserManager build() {
       return new FirebaseUserManager(this);
     }
diff --git a/src/main/java/com/google/firebase/auth/internal/AuthErrorHandler.java b/src/main/java/com/google/firebase/auth/internal/AuthErrorHandler.java
index 0fc5ef3..e989114 100644
--- a/src/main/java/com/google/firebase/auth/internal/AuthErrorHandler.java
+++ b/src/main/java/com/google/firebase/auth/internal/AuthErrorHandler.java
@@ -36,6 +36,12 @@ final class AuthErrorHandler extends AbstractHttpErrorHandler<FirebaseAuthExcept
 
   private static final Map<String, AuthError> ERROR_CODES =
       ImmutableMap.<String, AuthError>builder()
+          .put(
+              "CONFIGURATION_NOT_FOUND",
+              new AuthError(
+                  ErrorCode.NOT_FOUND,
+                  "No IdP configuration found corresponding to the provided identifier",
+                  AuthErrorCode.CONFIGURATION_NOT_FOUND))
           .put(
               "DUPLICATE_EMAIL",
               new AuthError(
@@ -67,6 +73,12 @@ final class AuthErrorHandler extends AbstractHttpErrorHandler<FirebaseAuthExcept
                   ErrorCode.ALREADY_EXISTS,
                   "The user with the provided phone number already exists",
                   AuthErrorCode.PHONE_NUMBER_ALREADY_EXISTS))
+          .put(
+              "TENANT_NOT_FOUND",
+              new AuthError(
+                  ErrorCode.NOT_FOUND,
+                  "No tenant found for the given identifier",
+                  AuthErrorCode.TENANT_NOT_FOUND))
           .put(
               "UNAUTHORIZED_DOMAIN",
               new AuthError(
diff --git a/src/main/java/com/google/firebase/auth/internal/AuthHttpClient.java b/src/main/java/com/google/firebase/auth/internal/AuthHttpClient.java
index cc6cfc9..e8413f1 100644
--- a/src/main/java/com/google/firebase/auth/internal/AuthHttpClient.java
+++ b/src/main/java/com/google/firebase/auth/internal/AuthHttpClient.java
@@ -16,7 +16,6 @@
 
 package com.google.firebase.auth.internal;
 
-import com.google.api.client.http.GenericUrl;
 import com.google.api.client.http.HttpRequestFactory;
 import com.google.api.client.http.HttpResponseInterceptor;
 import com.google.api.client.json.JsonFactory;
@@ -25,7 +24,6 @@ import com.google.firebase.IncomingHttpResponse;
 import com.google.firebase.auth.FirebaseAuthException;
 import com.google.firebase.internal.ErrorHandlingHttpClient;
 import com.google.firebase.internal.HttpRequestInfo;
-import com.google.firebase.internal.Nullable;
 import com.google.firebase.internal.SdkUtils;
 import java.util.Map;
 import java.util.Set;
@@ -40,10 +38,12 @@ public final class AuthHttpClient {
   private static final String CLIENT_VERSION = "Java/Admin/" + SdkUtils.getVersion();
 
   private final ErrorHandlingHttpClient<FirebaseAuthException> httpClient;
+  private final JsonFactory jsonFactory;
 
   public AuthHttpClient(JsonFactory jsonFactory, HttpRequestFactory requestFactory) {
     AuthErrorHandler authErrorHandler = new AuthErrorHandler(jsonFactory);
     this.httpClient = new ErrorHandlingHttpClient<>(requestFactory, jsonFactory, authErrorHandler);
+    this.jsonFactory = jsonFactory;
   }
 
   public static Set<String> generateMask(Map<String, Object> properties) {
@@ -65,21 +65,14 @@ public final class AuthHttpClient {
     this.httpClient.setInterceptor(interceptor);
   }
 
-  public IncomingHttpResponse sendRequest(
-      String method, GenericUrl url, @Nullable Object content) throws FirebaseAuthException {
-    HttpRequestInfo request = HttpRequestInfo.buildJsonRequest(method, url, content)
-        .addHeader(CLIENT_VERSION_HEADER, CLIENT_VERSION);
-    return httpClient.send(request);
+  public <T> T sendRequest(HttpRequestInfo request, Class<T> clazz) throws FirebaseAuthException {
+    IncomingHttpResponse response = this.sendRequest(request);
+    return this.parse(response, clazz);
   }
 
-  public <T> T sendRequest(
-      String method,
-      GenericUrl url,
-      @Nullable Object content,
-      Class<T> clazz) throws FirebaseAuthException {
-
-    IncomingHttpResponse response = this.sendRequest(method, url, content);
-    return this.parse(response, clazz);
+  public IncomingHttpResponse sendRequest(HttpRequestInfo request) throws FirebaseAuthException {
+    request.addHeader(CLIENT_VERSION_HEADER, CLIENT_VERSION);
+    return httpClient.send(request);
   }
 
   public <T> T parse(IncomingHttpResponse response, Class<T> clazz) throws FirebaseAuthException {
diff --git a/src/main/java/com/google/firebase/auth/multitenancy/FirebaseTenantClient.java b/src/main/java/com/google/firebase/auth/multitenancy/FirebaseTenantClient.java
index 1f6256c..5b776a4 100644
--- a/src/main/java/com/google/firebase/auth/multitenancy/FirebaseTenantClient.java
+++ b/src/main/java/com/google/firebase/auth/multitenancy/FirebaseTenantClient.java
@@ -19,7 +19,6 @@ package com.google.firebase.auth.multitenancy;
 import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Preconditions.checkNotNull;
 
-import com.google.api.client.http.GenericUrl;
 import com.google.api.client.http.HttpRequestFactory;
 import com.google.api.client.http.HttpResponseInterceptor;
 import com.google.api.client.json.GenericJson;
@@ -33,6 +32,7 @@ import com.google.firebase.auth.FirebaseAuthException;
 import com.google.firebase.auth.internal.AuthHttpClient;
 import com.google.firebase.auth.internal.ListTenantsResponse;
 import com.google.firebase.internal.ApiClientUtils;
+import com.google.firebase.internal.HttpRequestInfo;
 import java.util.Map;
 
 final class FirebaseTenantClient {
@@ -46,15 +46,19 @@ final class FirebaseTenantClient {
   private final AuthHttpClient httpClient;
 
   FirebaseTenantClient(FirebaseApp app) {
-    checkNotNull(app, "FirebaseApp must not be null");
-    String projectId = ImplFirebaseTrampolines.getProjectId(app);
+    this(
+        ImplFirebaseTrampolines.getProjectId(checkNotNull(app)),
+        app.getOptions().getJsonFactory(),
+        ApiClientUtils.newAuthorizedRequestFactory(app));
+  }
+
+  FirebaseTenantClient(
+      String projectId, JsonFactory jsonFactory, HttpRequestFactory requestFactory) {
     checkArgument(!Strings.isNullOrEmpty(projectId),
         "Project ID is required to access the auth service. Use a service account credential or "
             + "set the project ID explicitly via FirebaseOptions. Alternatively you can also "
             + "set the project ID via the GOOGLE_CLOUD_PROJECT environment variable.");
     this.tenantMgtBaseUrl = String.format(ID_TOOLKIT_URL, "v2", projectId);
-    JsonFactory jsonFactory = app.getOptions().getJsonFactory();
-    HttpRequestFactory requestFactory = ApiClientUtils.newAuthorizedRequestFactory(app);
     this.httpClient = new AuthHttpClient(jsonFactory, requestFactory);
   }
 
@@ -63,25 +67,28 @@ final class FirebaseTenantClient {
   }
 
   Tenant getTenant(String tenantId) throws FirebaseAuthException {
-    GenericUrl url = new GenericUrl(tenantMgtBaseUrl + getTenantUrlSuffix(tenantId));
-    return httpClient.sendRequest("GET", url, null, Tenant.class);
+    String url = tenantMgtBaseUrl + getTenantUrlSuffix(tenantId);
+    return httpClient.sendRequest(HttpRequestInfo.buildGetRequest(url), Tenant.class);
   }
 
   Tenant createTenant(Tenant.CreateRequest request) throws FirebaseAuthException {
-    GenericUrl url = new GenericUrl(tenantMgtBaseUrl + "/tenants");
-    return httpClient.sendRequest("POST", url, request.getProperties(), Tenant.class);
+    String url = tenantMgtBaseUrl + "/tenants";
+    return httpClient.sendRequest(
+        HttpRequestInfo.buildJsonPostRequest(url, request.getProperties()),
+        Tenant.class);
   }
 
   Tenant updateTenant(Tenant.UpdateRequest request) throws FirebaseAuthException {
     Map<String, Object> properties = request.getProperties();
-    GenericUrl url = new GenericUrl(tenantMgtBaseUrl + getTenantUrlSuffix(request.getTenantId()));
-    url.put("updateMask", Joiner.on(",").join(AuthHttpClient.generateMask(properties)));
-    return httpClient.sendRequest("PATCH", url, properties, Tenant.class);
+    String url = tenantMgtBaseUrl + getTenantUrlSuffix(request.getTenantId());
+    HttpRequestInfo requestInfo = HttpRequestInfo.buildJsonPatchRequest(url, properties)
+        .addParameter("updateMask", Joiner.on(",").join(AuthHttpClient.generateMask(properties)));
+    return httpClient.sendRequest(requestInfo, Tenant.class);
   }
 
   void deleteTenant(String tenantId) throws FirebaseAuthException {
-    GenericUrl url = new GenericUrl(tenantMgtBaseUrl + getTenantUrlSuffix(tenantId));
-    httpClient.sendRequest("DELETE", url, null, GenericJson.class);
+    String url = tenantMgtBaseUrl + getTenantUrlSuffix(tenantId);
+    httpClient.sendRequest(HttpRequestInfo.buildDeleteRequest(url), GenericJson.class);
   }
 
   ListTenantsResponse listTenants(int maxResults, String pageToken)
@@ -94,9 +101,9 @@ final class FirebaseTenantClient {
       builder.put("pageToken", pageToken);
     }
 
-    GenericUrl url = new GenericUrl(tenantMgtBaseUrl + "/tenants");
-    url.putAll(builder.build());
-    return httpClient.sendRequest("GET", url, null, ListTenantsResponse.class);
+    HttpRequestInfo requestInfo = HttpRequestInfo.buildGetRequest(tenantMgtBaseUrl + "/tenants")
+        .addAllParameters(builder.build());
+    return httpClient.sendRequest(requestInfo, ListTenantsResponse.class);
   }
 
   private static String getTenantUrlSuffix(String tenantId) {
diff --git a/src/main/java/com/google/firebase/auth/multitenancy/TenantManager.java b/src/main/java/com/google/firebase/auth/multitenancy/TenantManager.java
index 11f26b0..febe666 100644
--- a/src/main/java/com/google/firebase/auth/multitenancy/TenantManager.java
+++ b/src/main/java/com/google/firebase/auth/multitenancy/TenantManager.java
@@ -54,8 +54,13 @@ public final class TenantManager {
    * @hide
    */
   public TenantManager(FirebaseApp firebaseApp) {
-    this.firebaseApp = firebaseApp;
-    this.tenantClient = new FirebaseTenantClient(firebaseApp);
+    this(firebaseApp, new FirebaseTenantClient(firebaseApp));
+  }
+
+  @VisibleForTesting
+  TenantManager(FirebaseApp firebaseApp, FirebaseTenantClient tenantClient) {
+    this.firebaseApp = checkNotNull(firebaseApp);
+    this.tenantClient = checkNotNull(tenantClient);
     this.tenantAwareAuths = new HashMap<>();
   }
 
diff --git a/src/main/java/com/google/firebase/internal/ErrorHandlingHttpClient.java b/src/main/java/com/google/firebase/internal/ErrorHandlingHttpClient.java
index e449568..5efdd0e 100644
--- a/src/main/java/com/google/firebase/internal/ErrorHandlingHttpClient.java
+++ b/src/main/java/com/google/firebase/internal/ErrorHandlingHttpClient.java
@@ -24,7 +24,6 @@ import com.google.api.client.http.HttpResponse;
 import com.google.api.client.http.HttpResponseException;
 import com.google.api.client.http.HttpResponseInterceptor;
 import com.google.api.client.json.JsonFactory;
-import com.google.api.client.json.JsonObjectParser;
 import com.google.api.client.json.JsonParser;
 import com.google.common.io.CharStreams;
 import com.google.firebase.FirebaseException;
@@ -42,7 +41,6 @@ public final class ErrorHandlingHttpClient<T extends FirebaseException> {
   private final HttpRequestFactory requestFactory;
   private final JsonFactory jsonFactory;
   private final HttpErrorHandler<T> errorHandler;
-  private final JsonObjectParser jsonParser;
 
   private HttpResponseInterceptor interceptor;
 
@@ -53,7 +51,6 @@ public final class ErrorHandlingHttpClient<T extends FirebaseException> {
     this.requestFactory = checkNotNull(requestFactory, "requestFactory must not be null");
     this.jsonFactory = checkNotNull(jsonFactory, "jsonFactory must not be null");
     this.errorHandler = checkNotNull(errorHandler, "errorHandler must not be null");
-    this.jsonParser = new JsonObjectParser(jsonFactory);
   }
 
   public ErrorHandlingHttpClient<T> setInterceptor(HttpResponseInterceptor interceptor) {
@@ -138,7 +135,6 @@ public final class ErrorHandlingHttpClient<T extends FirebaseException> {
   private HttpRequest createHttpRequest(HttpRequestInfo requestInfo) throws T {
     try {
       return requestInfo.newHttpRequest(requestFactory, jsonFactory)
-          .setParser(jsonParser)
           .setResponseInterceptor(interceptor);
     } catch (IOException e) {
       // Handle request initialization errors (credential loading and other config errors)
diff --git a/src/main/java/com/google/firebase/internal/HttpRequestInfo.java b/src/main/java/com/google/firebase/internal/HttpRequestInfo.java
index cb687b7..375e332 100644
--- a/src/main/java/com/google/firebase/internal/HttpRequestInfo.java
+++ b/src/main/java/com/google/firebase/internal/HttpRequestInfo.java
@@ -61,6 +61,16 @@ public final class HttpRequestInfo {
     return this;
   }
 
+  public HttpRequestInfo addParameter(String name, Object value) {
+    this.url.put(name, value);
+    return this;
+  }
+
+  public HttpRequestInfo addAllParameters(Map<String, Object> params) {
+    this.url.putAll(params);
+    return this;
+  }
+
   public static HttpRequestInfo buildGetRequest(String url) {
     return buildRequest(HttpMethods.GET, url, null);
   }
@@ -71,26 +81,20 @@ public final class HttpRequestInfo {
 
   public static HttpRequestInfo buildRequest(
       String method, String url, @Nullable HttpContent content) {
-    return buildRequest(method, new GenericUrl(url), content);
-  }
-
-  public static HttpRequestInfo buildRequest(
-      String method, GenericUrl url, @Nullable HttpContent content) {
-    return new HttpRequestInfo(method, url, content, null);
+    return new HttpRequestInfo(method, new GenericUrl(url), content, null);
   }
 
   public static HttpRequestInfo buildJsonPostRequest(String url, @Nullable Object content) {
     return buildJsonRequest(HttpMethods.POST, url, content);
   }
 
-  public static HttpRequestInfo buildJsonRequest(
-      String method, String url, @Nullable Object content) {
-    return buildJsonRequest(method, new GenericUrl(url), content);
+  public static HttpRequestInfo buildJsonPatchRequest(String url, @Nullable Object content) {
+    return buildJsonRequest(HttpMethods.PATCH, url, content);
   }
 
   public static HttpRequestInfo buildJsonRequest(
-      String method, GenericUrl url, @Nullable Object content) {
-    return new HttpRequestInfo(method, url, null, content);
+      String method, String url, @Nullable Object content) {
+    return new HttpRequestInfo(method, new GenericUrl(url), null, content);
   }
 
   HttpRequest newHttpRequest(
diff --git a/src/main/java/com/google/firebase/projectmanagement/HttpHelper.java b/src/main/java/com/google/firebase/projectmanagement/HttpHelper.java
index f9ec62a..fb29f7b 100644
--- a/src/main/java/com/google/firebase/projectmanagement/HttpHelper.java
+++ b/src/main/java/com/google/firebase/projectmanagement/HttpHelper.java
@@ -94,7 +94,7 @@ final class HttpHelper {
         requestIdentifierDescription);
   }
 
-  <T> IncomingHttpResponse makeRequest(
+  private <T> IncomingHttpResponse makeRequest(
       HttpRequestInfo baseRequest,
       T parsedResponseInstance,
       String requestIdentifier,


Test patch:
diff --git a/src/test/java/com/google/firebase/auth/FirebaseAuthTest.java b/src/test/java/com/google/firebase/auth/FirebaseAuthTest.java
index bfbb773..66c004c 100644
--- a/src/test/java/com/google/firebase/auth/FirebaseAuthTest.java
+++ b/src/test/java/com/google/firebase/auth/FirebaseAuthTest.java
@@ -499,7 +499,7 @@ public class FirebaseAuthTest {
   private FirebaseAuth getAuthForIdTokenVerification(
       FirebaseApp app,
       Supplier<? extends FirebaseTokenVerifier> tokenVerifierSupplier) {
-    FirebaseUserManager userManager = FirebaseUserManager.builder().setFirebaseApp(app).build();
+    FirebaseUserManager userManager = FirebaseUserManager.createUserManager(app, null);
     return new FirebaseAuth(
         AbstractFirebaseAuth.builder()
           .setFirebaseApp(app)
@@ -526,7 +526,7 @@ public class FirebaseAuthTest {
   private FirebaseAuth getAuthForSessionCookieVerification(
       FirebaseApp app,
       Supplier<? extends FirebaseTokenVerifier> tokenVerifierSupplier) {
-    FirebaseUserManager userManager = FirebaseUserManager.builder().setFirebaseApp(app).build();
+    FirebaseUserManager userManager = FirebaseUserManager.createUserManager(app, null);
     return new FirebaseAuth(
           AbstractFirebaseAuth.builder()
           .setFirebaseApp(app)
diff --git a/src/test/java/com/google/firebase/auth/FirebaseUserManagerTest.java b/src/test/java/com/google/firebase/auth/FirebaseUserManagerTest.java
index 38c3166..5f45b89 100644
--- a/src/test/java/com/google/firebase/auth/FirebaseUserManagerTest.java
+++ b/src/test/java/com/google/firebase/auth/FirebaseUserManagerTest.java
@@ -45,14 +45,12 @@ import com.google.firebase.FirebaseApp;
 import com.google.firebase.FirebaseOptions;
 import com.google.firebase.TestOnlyImplFirebaseTrampolines;
 import com.google.firebase.auth.FirebaseUserManager.EmailLinkType;
-import com.google.firebase.auth.internal.AuthHttpClient;
 import com.google.firebase.auth.multitenancy.TenantAwareFirebaseAuth;
 import com.google.firebase.auth.multitenancy.TenantManager;
 import com.google.firebase.internal.SdkUtils;
 import com.google.firebase.testing.MultiRequestMockHttpTransport;
 import com.google.firebase.testing.TestResponseInterceptor;
 import com.google.firebase.testing.TestUtils;
-
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.math.BigDecimal;
@@ -92,6 +90,10 @@ public class FirebaseUserManagerTest {
 
   private static final String TENANTS_BASE_URL = PROJECT_BASE_URL + "/tenants";
 
+  private static final String SAML_RESPONSE = TestUtils.loadResource("saml.json");
+
+  private static final String OIDC_RESPONSE = TestUtils.loadResource("oidc.json");
+
   @After
   public void tearDown() {
     TestOnlyImplFirebaseTrampolines.clearInstancesForTest();
@@ -202,7 +204,7 @@ public class FirebaseUserManagerTest {
 
   @Test
   public void testGetUsersExceeds100() throws Exception {
-    FirebaseApp.initializeApp(new FirebaseOptions.Builder()
+    FirebaseApp.initializeApp(FirebaseOptions.builder()
             .setCredentials(credentials)
             .build());
     List<UserIdentifier> identifiers = new ArrayList<>();
@@ -220,7 +222,7 @@ public class FirebaseUserManagerTest {
 
   @Test
   public void testGetUsersNull() throws Exception {
-    FirebaseApp.initializeApp(new FirebaseOptions.Builder()
+    FirebaseApp.initializeApp(FirebaseOptions.builder()
             .setCredentials(credentials)
             .build());
     try {
@@ -279,7 +281,7 @@ public class FirebaseUserManagerTest {
         ).replace("'", "\""));
 
     UidIdentifier doesntExist = new UidIdentifier("this-uid-doesnt-exist");
-    List<UserIdentifier> ids = ImmutableList.<UserIdentifier>of(
+    List<UserIdentifier> ids = ImmutableList.of(
         new UidIdentifier("uid1"),
         new EmailIdentifier("user2@example.com"),
         new PhoneIdentifier("+15555550003"),
@@ -301,7 +303,7 @@ public class FirebaseUserManagerTest {
   }
 
   @Test
-  public void testInvalidUidIdentifier() throws Exception {
+  public void testInvalidUidIdentifier() {
     try {
       new UidIdentifier("too long " + Strings.repeat(".", 128));
       fail("No error thrown for invalid uid");
@@ -311,7 +313,7 @@ public class FirebaseUserManagerTest {
   }
 
   @Test
-  public void testInvalidEmailIdentifier() throws Exception {
+  public void testInvalidEmailIdentifier() {
     try {
       new EmailIdentifier("invalid email addr");
       fail("No error thrown for invalid email");
@@ -321,7 +323,7 @@ public class FirebaseUserManagerTest {
   }
 
   @Test
-  public void testInvalidPhoneIdentifier() throws Exception {
+  public void testInvalidPhoneIdentifier() {
     try {
       new PhoneIdentifier("invalid phone number");
       fail("No error thrown for invalid phone number");
@@ -331,7 +333,7 @@ public class FirebaseUserManagerTest {
   }
 
   @Test
-  public void testInvalidProviderIdentifier() throws Exception {
+  public void testInvalidProviderIdentifier() {
     try {
       new ProviderIdentifier("", "valid-uid");
       fail("No error thrown for invalid provider id");
@@ -454,8 +456,8 @@ public class FirebaseUserManagerTest {
   }
 
   @Test
-  public void testDeleteUsersExceeds1000() throws Exception {
-    FirebaseApp.initializeApp(new FirebaseOptions.Builder()
+  public void testDeleteUsersExceeds1000() {
+    FirebaseApp.initializeApp(FirebaseOptions.builder()
             .setCredentials(credentials)
             .build());
     List<String> ids = new ArrayList<>();
@@ -471,8 +473,8 @@ public class FirebaseUserManagerTest {
   }
 
   @Test
-  public void testDeleteUsersInvalidId() throws Exception {
-    FirebaseApp.initializeApp(new FirebaseOptions.Builder()
+  public void testDeleteUsersInvalidId() {
+    FirebaseApp.initializeApp(FirebaseOptions.builder()
             .setCredentials(credentials)
             .build());
     try {
@@ -1481,8 +1483,7 @@ public class FirebaseUserManagerTest {
 
   @Test
   public void testCreateOidcProvider() throws Exception {
-    TestResponseInterceptor interceptor = initializeAppForUserManagement(
-        TestUtils.loadResource("oidc.json"));
+    TestResponseInterceptor interceptor = initializeAppForUserManagement(OIDC_RESPONSE);
     OidcProviderConfig.CreateRequest createRequest =
         new OidcProviderConfig.CreateRequest()
             .setProviderId("oidc.provider-id")
@@ -1507,8 +1508,7 @@ public class FirebaseUserManagerTest {
 
   @Test
   public void testCreateOidcProviderAsync() throws Exception {
-    TestResponseInterceptor interceptor = initializeAppForUserManagement(
-        TestUtils.loadResource("oidc.json"));
+    TestResponseInterceptor interceptor = initializeAppForUserManagement(OIDC_RESPONSE);
     OidcProviderConfig.CreateRequest createRequest =
         new OidcProviderConfig.CreateRequest()
             .setProviderId("oidc.provider-id")
@@ -1534,8 +1534,7 @@ public class FirebaseUserManagerTest {
 
   @Test
   public void testCreateOidcProviderMinimal() throws Exception {
-    TestResponseInterceptor interceptor = initializeAppForUserManagement(
-        TestUtils.loadResource("oidc.json"));
+    TestResponseInterceptor interceptor = initializeAppForUserManagement(OIDC_RESPONSE);
     // Only the 'enabled' and 'displayName' fields can be omitted from an OIDC provider config
     // creation request.
     OidcProviderConfig.CreateRequest createRequest =
@@ -1557,25 +1556,32 @@ public class FirebaseUserManagerTest {
   }
 
   @Test
-  public void testCreateOidcProviderError() throws Exception {
-    TestResponseInterceptor interceptor =
-        initializeAppForUserManagementWithStatusCode(404,
-            "{\"error\": {\"message\": \"INTERNAL_ERROR\"}}");
+  public void testCreateOidcProviderError() {
+    String message = "{\"error\": {\"message\": \"INTERNAL_ERROR\"}}";
+    MockLowLevelHttpResponse response = new MockLowLevelHttpResponse()
+        .setContent(message)
+        .setStatusCode(500);
+    FirebaseAuth auth = getRetryDisabledAuth(response);
     OidcProviderConfig.CreateRequest createRequest =
         new OidcProviderConfig.CreateRequest().setProviderId("oidc.provider-id");
+
     try {
-      FirebaseAuth.getInstance().createOidcProviderConfig(createRequest);
+      auth.createOidcProviderConfig(createRequest);
       fail("No error thrown for invalid response");
     } catch (FirebaseAuthException e) {
-      assertEquals(ErrorCode.NOT_FOUND, e.getErrorCode());
+      assertEquals(ErrorCode.INTERNAL, e.getErrorCode());
+      assertEquals(
+          "Unexpected HTTP response with status: 500\n" + message,
+          e.getMessage());
+      assertTrue(e.getCause() instanceof HttpResponseException);
+      assertNotNull(e.getHttpResponse());
+      assertNull(e.getAuthErrorCode());
     }
-
-    checkUrl(interceptor, "POST", PROJECT_BASE_URL + "/oauthIdpConfigs");
   }
 
   @Test
   public void testCreateOidcProviderMissingId() throws Exception {
-    initializeAppForUserManagement(TestUtils.loadResource("oidc.json"));
+    initializeAppForUserManagement(OIDC_RESPONSE);
     OidcProviderConfig.CreateRequest createRequest =
         new OidcProviderConfig.CreateRequest()
             .setDisplayName("DISPLAY_NAME")
@@ -1593,8 +1599,7 @@ public class FirebaseUserManagerTest {
   @Test
   public void testTenantAwareCreateOidcProvider() throws Exception {
     TestResponseInterceptor interceptor = initializeAppForTenantAwareUserManagement(
-        "TENANT_ID",
-        TestUtils.loadResource("oidc.json"));
+        "TENANT_ID", OIDC_RESPONSE);
     OidcProviderConfig.CreateRequest createRequest =
         new OidcProviderConfig.CreateRequest()
             .setProviderId("oidc.provider-id")
@@ -1613,8 +1618,7 @@ public class FirebaseUserManagerTest {
 
   @Test
   public void testUpdateOidcProvider() throws Exception {
-    TestResponseInterceptor interceptor = initializeAppForUserManagement(
-        TestUtils.loadResource("oidc.json"));
+    TestResponseInterceptor interceptor = initializeAppForUserManagement(OIDC_RESPONSE);
     OidcProviderConfig.UpdateRequest request =
         new OidcProviderConfig.UpdateRequest("oidc.provider-id")
             .setDisplayName("DISPLAY_NAME")
@@ -1638,8 +1642,7 @@ public class FirebaseUserManagerTest {
 
   @Test
   public void testUpdateOidcProviderAsync() throws Exception {
-    TestResponseInterceptor interceptor = initializeAppForUserManagement(
-        TestUtils.loadResource("oidc.json"));
+    TestResponseInterceptor interceptor = initializeAppForUserManagement(OIDC_RESPONSE);
     OidcProviderConfig.UpdateRequest request =
         new OidcProviderConfig.UpdateRequest("oidc.provider-id")
             .setDisplayName("DISPLAY_NAME")
@@ -1664,8 +1667,7 @@ public class FirebaseUserManagerTest {
 
   @Test
   public void testUpdateOidcProviderMinimal() throws Exception {
-    TestResponseInterceptor interceptor = initializeAppForUserManagement(
-        TestUtils.loadResource("oidc.json"));
+    TestResponseInterceptor interceptor = initializeAppForUserManagement(OIDC_RESPONSE);
     OidcProviderConfig.UpdateRequest request =
         new OidcProviderConfig.UpdateRequest("oidc.provider-id").setDisplayName("DISPLAY_NAME");
 
@@ -1683,7 +1685,7 @@ public class FirebaseUserManagerTest {
 
   @Test
   public void testUpdateOidcProviderConfigNoValues() throws Exception {
-    initializeAppForUserManagement(TestUtils.loadResource("oidc.json"));
+    initializeAppForUserManagement(OIDC_RESPONSE);
     try {
       FirebaseAuth.getInstance().updateOidcProviderConfig(
           new OidcProviderConfig.UpdateRequest("oidc.provider-id"));
@@ -1695,25 +1697,32 @@ public class FirebaseUserManagerTest {
 
   @Test
   public void testUpdateOidcProviderConfigError() {
-    TestResponseInterceptor interceptor =
-        initializeAppForUserManagementWithStatusCode(404,
-            "{\"error\": {\"message\": \"INTERNAL_ERROR\"}}");
+    String message = "{\"error\": {\"message\": \"INTERNAL_ERROR\"}}";
+    MockLowLevelHttpResponse response = new MockLowLevelHttpResponse()
+        .setContent(message)
+        .setStatusCode(500);
+    FirebaseAuth auth = getRetryDisabledAuth(response);
     OidcProviderConfig.UpdateRequest request =
         new OidcProviderConfig.UpdateRequest("oidc.provider-id").setDisplayName("DISPLAY_NAME");
+
     try {
-      FirebaseAuth.getInstance().updateOidcProviderConfig(request);
+      auth.updateOidcProviderConfig(request);
       fail("No error thrown for invalid response");
     } catch (FirebaseAuthException e) {
-      assertEquals(ErrorCode.NOT_FOUND, e.getErrorCode());
+      assertEquals(ErrorCode.INTERNAL, e.getErrorCode());
+      assertEquals(
+          "Unexpected HTTP response with status: 500\n" + message,
+          e.getMessage());
+      assertTrue(e.getCause() instanceof HttpResponseException);
+      assertNotNull(e.getHttpResponse());
+      assertNull(e.getAuthErrorCode());
     }
-    checkUrl(interceptor, "PATCH", PROJECT_BASE_URL + "/oauthIdpConfigs/oidc.provider-id");
   }
 
   @Test
   public void testTenantAwareUpdateOidcProvider() throws Exception {
     TestResponseInterceptor interceptor = initializeAppForTenantAwareUserManagement(
-        "TENANT_ID",
-        TestUtils.loadResource("oidc.json"));
+        "TENANT_ID", OIDC_RESPONSE);
     TenantAwareFirebaseAuth tenantAwareAuth =
         FirebaseAuth.getInstance().getTenantManager().getAuthForTenant("TENANT_ID");
     OidcProviderConfig.UpdateRequest request =
@@ -1740,8 +1749,7 @@ public class FirebaseUserManagerTest {
 
   @Test
   public void testGetOidcProviderConfig() throws Exception {
-    TestResponseInterceptor interceptor = initializeAppForUserManagement(
-        TestUtils.loadResource("oidc.json"));
+    TestResponseInterceptor interceptor = initializeAppForUserManagement(OIDC_RESPONSE);
 
     OidcProviderConfig config =
         FirebaseAuth.getInstance().getOidcProviderConfig("oidc.provider-id");
@@ -1753,8 +1761,7 @@ public class FirebaseUserManagerTest {
 
   @Test
   public void testGetOidcProviderConfigAsync() throws Exception {
-    TestResponseInterceptor interceptor = initializeAppForUserManagement(
-        TestUtils.loadResource("oidc.json"));
+    TestResponseInterceptor interceptor = initializeAppForUserManagement(OIDC_RESPONSE);
 
     OidcProviderConfig config =
         FirebaseAuth.getInstance().getOidcProviderConfigAsync("oidc.provider-id").get();
@@ -1766,7 +1773,7 @@ public class FirebaseUserManagerTest {
 
   @Test
   public void testGetOidcProviderConfigMissingId() throws Exception {
-    initializeAppForUserManagement(TestUtils.loadResource("oidc.json"));
+    initializeAppForUserManagement(OIDC_RESPONSE);
 
     try {
       FirebaseAuth.getInstance().getOidcProviderConfig(null);
@@ -1778,7 +1785,7 @@ public class FirebaseUserManagerTest {
 
   @Test
   public void testGetOidcProviderConfigInvalidId() throws Exception {
-    initializeAppForUserManagement(TestUtils.loadResource("oidc.json"));
+    initializeAppForUserManagement(OIDC_RESPONSE);
 
     try {
       FirebaseAuth.getInstance().getOidcProviderConfig("saml.invalid-oidc-provider-id");
@@ -1789,7 +1796,7 @@ public class FirebaseUserManagerTest {
   }
 
   @Test
-  public void testGetOidcProviderConfigWithNotFoundError() throws Exception {
+  public void testGetOidcProviderConfigWithNotFoundError() {
     TestResponseInterceptor interceptor =
         initializeAppForUserManagementWithStatusCode(404,
             "{\"error\": {\"message\": \"CONFIGURATION_NOT_FOUND\"}}");
@@ -1798,6 +1805,13 @@ public class FirebaseUserManagerTest {
       fail("No error thrown for invalid response");
     } catch (FirebaseAuthException e) {
       assertEquals(ErrorCode.NOT_FOUND, e.getErrorCode());
+      assertEquals(
+          "No IdP configuration found corresponding to the provided identifier "
+              + "(CONFIGURATION_NOT_FOUND).",
+          e.getMessage());
+      assertTrue(e.getCause() instanceof HttpResponseException);
+      assertNotNull(e.getHttpResponse());
+      assertEquals(AuthErrorCode.CONFIGURATION_NOT_FOUND, e.getAuthErrorCode());
     }
     checkUrl(interceptor, "GET", PROJECT_BASE_URL + "/oauthIdpConfigs/oidc.provider-id");
   }
@@ -1805,8 +1819,7 @@ public class FirebaseUserManagerTest {
   @Test
   public void testGetTenantAwareOidcProviderConfig() throws Exception {
     TestResponseInterceptor interceptor = initializeAppForTenantAwareUserManagement(
-        "TENANT_ID",
-        TestUtils.loadResource("oidc.json"));
+        "TENANT_ID", OIDC_RESPONSE);
     TenantAwareFirebaseAuth tenantAwareAuth =
         FirebaseAuth.getInstance().getTenantManager().getAuthForTenant("TENANT_ID");
 
@@ -1856,18 +1869,25 @@ public class FirebaseUserManagerTest {
   }
 
   @Test
-  public void testListOidcProviderConfigsError() throws Exception {
-    TestResponseInterceptor interceptor =
-        initializeAppForUserManagementWithStatusCode(404,
-            "{\"error\": {\"message\": \"INTERNAL_ERROR\"}}");
+  public void testListOidcProviderConfigsError() {
+    String message = "{\"error\": {\"message\": \"INTERNAL_ERROR\"}}";
+    MockLowLevelHttpResponse response = new MockLowLevelHttpResponse()
+        .setContent(message)
+        .setStatusCode(500);
+    FirebaseAuth auth = getRetryDisabledAuth(response);
 
     try {
-      FirebaseAuth.getInstance().listOidcProviderConfigs(null, 99);
+      auth.listOidcProviderConfigs(null, 99);
       fail("No error thrown for invalid response");
     } catch (FirebaseAuthException e) {
-      assertEquals(ErrorCode.NOT_FOUND, e.getErrorCode());
+      assertEquals(ErrorCode.INTERNAL, e.getErrorCode());
+      assertEquals(
+          "Unexpected HTTP response with status: 500\n" + message,
+          e.getMessage());
+      assertTrue(e.getCause() instanceof HttpResponseException);
+      assertNotNull(e.getHttpResponse());
+      assertNull(e.getAuthErrorCode());
     }
-    checkUrl(interceptor, "GET", PROJECT_BASE_URL + "/oauthIdpConfigs");
   }
 
   @Test
@@ -1975,6 +1995,13 @@ public class FirebaseUserManagerTest {
       fail("No error thrown for invalid response");
     } catch (FirebaseAuthException e) {
       assertEquals(ErrorCode.NOT_FOUND, e.getErrorCode());
+      assertEquals(
+          "No IdP configuration found corresponding to the provided identifier "
+              + "(CONFIGURATION_NOT_FOUND).",
+          e.getMessage());
+      assertTrue(e.getCause() instanceof HttpResponseException);
+      assertNotNull(e.getHttpResponse());
+      assertEquals(AuthErrorCode.CONFIGURATION_NOT_FOUND, e.getAuthErrorCode());
     }
     checkUrl(interceptor, "DELETE", PROJECT_BASE_URL + "/oauthIdpConfigs/oidc.UNKNOWN");
   }
@@ -1996,8 +2023,7 @@ public class FirebaseUserManagerTest {
 
   @Test
   public void testCreateSamlProvider() throws Exception {
-    TestResponseInterceptor interceptor = initializeAppForUserManagement(
-        TestUtils.loadResource("saml.json"));
+    TestResponseInterceptor interceptor = initializeAppForUserManagement(SAML_RESPONSE);
     SamlProviderConfig.CreateRequest createRequest =
         new SamlProviderConfig.CreateRequest()
           .setProviderId("saml.provider-id")
@@ -2042,8 +2068,7 @@ public class FirebaseUserManagerTest {
 
   @Test
   public void testCreateSamlProviderAsync() throws Exception {
-    TestResponseInterceptor interceptor = initializeAppForUserManagement(
-        TestUtils.loadResource("saml.json"));
+    TestResponseInterceptor interceptor = initializeAppForUserManagement(SAML_RESPONSE);
     SamlProviderConfig.CreateRequest createRequest =
         new SamlProviderConfig.CreateRequest()
           .setProviderId("saml.provider-id")
@@ -2089,8 +2114,7 @@ public class FirebaseUserManagerTest {
 
   @Test
   public void testCreateSamlProviderMinimal() throws Exception {
-    TestResponseInterceptor interceptor = initializeAppForUserManagement(
-        TestUtils.loadResource("saml.json"));
+    TestResponseInterceptor interceptor = initializeAppForUserManagement(SAML_RESPONSE);
     // Only the 'enabled', 'displayName', and 'signRequest' fields can be omitted from a SAML
     // provider config creation request.
     SamlProviderConfig.CreateRequest createRequest =
@@ -2130,23 +2154,31 @@ public class FirebaseUserManagerTest {
 
   @Test
   public void testCreateSamlProviderError() {
-    TestResponseInterceptor interceptor =
-        initializeAppForUserManagementWithStatusCode(404,
-            "{\"error\": {\"message\": \"INTERNAL_ERROR\"}}");
+    String message = "{\"error\": {\"message\": \"INTERNAL_ERROR\"}}";
+    MockLowLevelHttpResponse response = new MockLowLevelHttpResponse()
+        .setContent(message)
+        .setStatusCode(500);
+    FirebaseAuth auth = getRetryDisabledAuth(response);
     SamlProviderConfig.CreateRequest createRequest =
         new SamlProviderConfig.CreateRequest().setProviderId("saml.provider-id");
+
     try {
-      FirebaseAuth.getInstance().createSamlProviderConfig(createRequest);
+      auth.createSamlProviderConfig(createRequest);
       fail("No error thrown for invalid response");
     } catch (FirebaseAuthException e) {
-      assertEquals(ErrorCode.NOT_FOUND, e.getErrorCode());
+      assertEquals(ErrorCode.INTERNAL, e.getErrorCode());
+      assertEquals(
+          "Unexpected HTTP response with status: 500\n" + message,
+          e.getMessage());
+      assertTrue(e.getCause() instanceof HttpResponseException);
+      assertNotNull(e.getHttpResponse());
+      assertNull(e.getAuthErrorCode());
     }
-    checkUrl(interceptor, "POST", PROJECT_BASE_URL + "/inboundSamlConfigs");
   }
 
   @Test
   public void testCreateSamlProviderMissingId() throws Exception {
-    initializeAppForUserManagement(TestUtils.loadResource("saml.json"));
+    initializeAppForUserManagement(SAML_RESPONSE);
     SamlProviderConfig.CreateRequest createRequest =
         new SamlProviderConfig.CreateRequest()
           .setDisplayName("DISPLAY_NAME")
@@ -2168,8 +2200,7 @@ public class FirebaseUserManagerTest {
   @Test
   public void testTenantAwareCreateSamlProvider() throws Exception {
     TestResponseInterceptor interceptor = initializeAppForTenantAwareUserManagement(
-        "TENANT_ID",
-        TestUtils.loadResource("saml.json"));
+        "TENANT_ID", SAML_RESPONSE);
     SamlProviderConfig.CreateRequest createRequest =
         new SamlProviderConfig.CreateRequest()
           .setProviderId("saml.provider-id")
@@ -2192,8 +2223,7 @@ public class FirebaseUserManagerTest {
 
   @Test
   public void testUpdateSamlProvider() throws Exception {
-    TestResponseInterceptor interceptor = initializeAppForUserManagement(
-        TestUtils.loadResource("saml.json"));
+    TestResponseInterceptor interceptor = initializeAppForUserManagement(SAML_RESPONSE);
     SamlProviderConfig.UpdateRequest updateRequest =
         new SamlProviderConfig.UpdateRequest("saml.provider-id")
           .setDisplayName("DISPLAY_NAME")
@@ -2240,8 +2270,7 @@ public class FirebaseUserManagerTest {
 
   @Test
   public void testUpdateSamlProviderAsync() throws Exception {
-    TestResponseInterceptor interceptor = initializeAppForUserManagement(
-        TestUtils.loadResource("saml.json"));
+    TestResponseInterceptor interceptor = initializeAppForUserManagement(SAML_RESPONSE);
     SamlProviderConfig.UpdateRequest updateRequest =
         new SamlProviderConfig.UpdateRequest("saml.provider-id")
           .setDisplayName("DISPLAY_NAME")
@@ -2289,8 +2318,7 @@ public class FirebaseUserManagerTest {
 
   @Test
   public void testUpdateSamlProviderMinimal() throws Exception {
-    TestResponseInterceptor interceptor = initializeAppForUserManagement(
-        TestUtils.loadResource("saml.json"));
+    TestResponseInterceptor interceptor = initializeAppForUserManagement(SAML_RESPONSE);
     SamlProviderConfig.UpdateRequest request =
         new SamlProviderConfig.UpdateRequest("saml.provider-id").setDisplayName("DISPLAY_NAME");
 
@@ -2308,7 +2336,7 @@ public class FirebaseUserManagerTest {
 
   @Test
   public void testUpdateSamlProviderConfigNoValues() throws Exception {
-    initializeAppForUserManagement(TestUtils.loadResource("saml.json"));
+    initializeAppForUserManagement(SAML_RESPONSE);
     try {
       FirebaseAuth.getInstance().updateSamlProviderConfig(
           new SamlProviderConfig.UpdateRequest("saml.provider-id"));
@@ -2319,26 +2347,33 @@ public class FirebaseUserManagerTest {
   }
 
   @Test
-  public void testUpdateSamlProviderConfigError() throws Exception {
-    TestResponseInterceptor interceptor =
-        initializeAppForUserManagementWithStatusCode(404,
-            "{\"error\": {\"message\": \"INTERNAL_ERROR\"}}");
+  public void testUpdateSamlProviderConfigError() {
+    String message = "{\"error\": {\"message\": \"INTERNAL_ERROR\"}}";
+    MockLowLevelHttpResponse response = new MockLowLevelHttpResponse()
+        .setContent(message)
+        .setStatusCode(500);
+    FirebaseAuth auth = getRetryDisabledAuth(response);
     SamlProviderConfig.UpdateRequest request =
         new SamlProviderConfig.UpdateRequest("saml.provider-id").setDisplayName("DISPLAY_NAME");
+
     try {
-      FirebaseAuth.getInstance().updateSamlProviderConfig(request);
+      auth.updateSamlProviderConfig(request);
       fail("No error thrown for invalid response");
     } catch (FirebaseAuthException e) {
-      assertEquals(ErrorCode.NOT_FOUND, e.getErrorCode());
+      assertEquals(ErrorCode.INTERNAL, e.getErrorCode());
+      assertEquals(
+          "Unexpected HTTP response with status: 500\n" + message,
+          e.getMessage());
+      assertTrue(e.getCause() instanceof HttpResponseException);
+      assertNotNull(e.getHttpResponse());
+      assertNull(e.getAuthErrorCode());
     }
-    checkUrl(interceptor, "PATCH", PROJECT_BASE_URL + "/inboundSamlConfigs/saml.provider-id");
   }
 
   @Test
   public void testTenantAwareUpdateSamlProvider() throws Exception {
     TestResponseInterceptor interceptor = initializeAppForTenantAwareUserManagement(
-        "TENANT_ID",
-        TestUtils.loadResource("saml.json"));
+        "TENANT_ID", SAML_RESPONSE);
     TenantAwareFirebaseAuth tenantAwareAuth =
         FirebaseAuth.getInstance().getTenantManager().getAuthForTenant("TENANT_ID");
     SamlProviderConfig.UpdateRequest updateRequest =
@@ -2370,8 +2405,7 @@ public class FirebaseUserManagerTest {
 
   @Test
   public void testGetSamlProviderConfig() throws Exception {
-    TestResponseInterceptor interceptor = initializeAppForUserManagement(
-        TestUtils.loadResource("saml.json"));
+    TestResponseInterceptor interceptor = initializeAppForUserManagement(SAML_RESPONSE);
 
     SamlProviderConfig config =
         FirebaseAuth.getInstance().getSamlProviderConfig("saml.provider-id");
@@ -2383,8 +2417,7 @@ public class FirebaseUserManagerTest {
 
   @Test
   public void testGetSamlProviderConfigAsync() throws Exception {
-    TestResponseInterceptor interceptor = initializeAppForUserManagement(
-        TestUtils.loadResource("saml.json"));
+    TestResponseInterceptor interceptor = initializeAppForUserManagement(SAML_RESPONSE);
 
     SamlProviderConfig config =
         FirebaseAuth.getInstance().getSamlProviderConfigAsync("saml.provider-id").get();
@@ -2408,7 +2441,7 @@ public class FirebaseUserManagerTest {
 
   @Test
   public void testGetSamlProviderConfigInvalidId() throws Exception {
-    initializeAppForUserManagement(TestUtils.loadResource("saml.json"));
+    initializeAppForUserManagement(SAML_RESPONSE);
 
     try {
       FirebaseAuth.getInstance().getSamlProviderConfig("oidc.invalid-saml-provider-id");
@@ -2428,6 +2461,13 @@ public class FirebaseUserManagerTest {
       fail("No error thrown for invalid response");
     } catch (FirebaseAuthException e) {
       assertEquals(ErrorCode.NOT_FOUND, e.getErrorCode());
+      assertEquals(
+          "No IdP configuration found corresponding to the provided identifier "
+              + "(CONFIGURATION_NOT_FOUND).",
+          e.getMessage());
+      assertTrue(e.getCause() instanceof HttpResponseException);
+      assertNotNull(e.getHttpResponse());
+      assertEquals(AuthErrorCode.CONFIGURATION_NOT_FOUND, e.getAuthErrorCode());
     }
     checkUrl(interceptor, "GET", PROJECT_BASE_URL + "/inboundSamlConfigs/saml.provider-id");
   }
@@ -2435,8 +2475,7 @@ public class FirebaseUserManagerTest {
   @Test
   public void testGetTenantAwareSamlProviderConfig() throws Exception {
     TestResponseInterceptor interceptor = initializeAppForTenantAwareUserManagement(
-        "TENANT_ID",
-        TestUtils.loadResource("saml.json"));
+        "TENANT_ID", SAML_RESPONSE);
     TenantAwareFirebaseAuth tenantAwareAuth =
         FirebaseAuth.getInstance().getTenantManager().getAuthForTenant("TENANT_ID");
 
@@ -2487,18 +2526,25 @@ public class FirebaseUserManagerTest {
   }
 
   @Test
-  public void testListSamlProviderConfigsError() throws Exception {
-    TestResponseInterceptor interceptor =
-        initializeAppForUserManagementWithStatusCode(404,
-            "{\"error\": {\"message\": \"INTERNAL_ERROR\"}}");
+  public void testListSamlProviderConfigsError() {
+    String message = "{\"error\": {\"message\": \"INTERNAL_ERROR\"}}";
+    MockLowLevelHttpResponse response = new MockLowLevelHttpResponse()
+        .setContent(message)
+        .setStatusCode(500);
+    FirebaseAuth auth = getRetryDisabledAuth(response);
 
     try {
-      FirebaseAuth.getInstance().listSamlProviderConfigs(null, 99);
+      auth.listSamlProviderConfigs(null, 99);
       fail("No error thrown for invalid response");
     } catch (FirebaseAuthException e) {
-      assertEquals(ErrorCode.NOT_FOUND, e.getErrorCode());
+      assertEquals(ErrorCode.INTERNAL, e.getErrorCode());
+      assertEquals(
+          "Unexpected HTTP response with status: 500\n" + message,
+          e.getMessage());
+      assertTrue(e.getCause() instanceof HttpResponseException);
+      assertNotNull(e.getHttpResponse());
+      assertNull(e.getAuthErrorCode());
     }
-    checkUrl(interceptor, "GET", PROJECT_BASE_URL + "/inboundSamlConfigs");
   }
 
   @Test
@@ -2606,6 +2652,13 @@ public class FirebaseUserManagerTest {
       fail("No error thrown for invalid response");
     } catch (FirebaseAuthException e) {
       assertEquals(ErrorCode.NOT_FOUND, e.getErrorCode());
+      assertEquals(
+          "No IdP configuration found corresponding to the provided identifier "
+              + "(CONFIGURATION_NOT_FOUND).",
+          e.getMessage());
+      assertTrue(e.getCause() instanceof HttpResponseException);
+      assertNotNull(e.getHttpResponse());
+      assertEquals(AuthErrorCode.CONFIGURATION_NOT_FOUND, e.getAuthErrorCode());
     }
     checkUrl(interceptor, "DELETE", PROJECT_BASE_URL + "/inboundSamlConfigs/saml.UNKNOWN");
   }
@@ -2627,7 +2680,7 @@ public class FirebaseUserManagerTest {
 
   private static TestResponseInterceptor initializeAppForUserManagementWithStatusCode(
       int statusCode, String response) {
-    FirebaseApp.initializeApp(new FirebaseOptions.Builder()
+    FirebaseApp.initializeApp(FirebaseOptions.builder()
         .setCredentials(credentials)
         .setHttpTransport(
           new MockHttpTransport.Builder().setLowLevelHttpResponse(
@@ -2663,7 +2716,7 @@ public class FirebaseUserManagerTest {
       mocks.add(new MockLowLevelHttpResponse().setContent(response));
     }
     MockHttpTransport transport = new MultiRequestMockHttpTransport(mocks);
-    FirebaseApp.initializeApp(new FirebaseOptions.Builder()
+    FirebaseApp.initializeApp(FirebaseOptions.builder()
         .setCredentials(credentials)
         .setHttpTransport(transport)
         .setProjectId("test-project-id")
@@ -2681,9 +2734,8 @@ public class FirebaseUserManagerTest {
     final MockHttpTransport transport = new MockHttpTransport.Builder()
         .setLowLevelHttpResponse(response)
         .build();
-    final FirebaseApp app = FirebaseApp.initializeApp(new FirebaseOptions.Builder()
+    final FirebaseApp app = FirebaseApp.initializeApp(FirebaseOptions.builder()
         .setCredentials(credentials)
-        .setProjectId("test-project-id")
         .setHttpTransport(transport)
         .build());
     return new FirebaseAuth(
@@ -2694,8 +2746,9 @@ public class FirebaseUserManagerTest {
             public FirebaseUserManager get() {
               return FirebaseUserManager
                 .builder()
-                .setFirebaseApp(app)
+                .setProjectId("test-project-id")
                 .setHttpRequestFactory(transport.createRequestFactory())
+                .setJsonFactory(JSON_FACTORY)
                 .build();
             }
           }));
diff --git a/src/test/java/com/google/firebase/auth/ProviderConfigTestUtils.java b/src/test/java/com/google/firebase/auth/ProviderConfigTestUtils.java
index 588ce33..e027882 100644
--- a/src/test/java/com/google/firebase/auth/ProviderConfigTestUtils.java
+++ b/src/test/java/com/google/firebase/auth/ProviderConfigTestUtils.java
@@ -36,8 +36,9 @@ public class ProviderConfigTestUtils {
       fail("No error thrown for getting a deleted OIDC provider config.");
     } catch (ExecutionException e) {
       assertTrue(e.getCause() instanceof FirebaseAuthException);
-      assertEquals(ErrorCode.NOT_FOUND,
-          ((FirebaseAuthException) e.getCause()).getErrorCode());
+      FirebaseAuthException authException = (FirebaseAuthException) e.getCause();
+      assertEquals(ErrorCode.NOT_FOUND, authException.getErrorCode());
+      assertEquals(AuthErrorCode.CONFIGURATION_NOT_FOUND, authException.getAuthErrorCode());
     }
   }
 
@@ -48,8 +49,9 @@ public class ProviderConfigTestUtils {
       fail("No error thrown for getting a deleted SAML provider config.");
     } catch (ExecutionException e) {
       assertTrue(e.getCause() instanceof FirebaseAuthException);
-      assertEquals(ErrorCode.NOT_FOUND,
-          ((FirebaseAuthException) e.getCause()).getErrorCode());
+      FirebaseAuthException authException = (FirebaseAuthException) e.getCause();
+      assertEquals(ErrorCode.NOT_FOUND, authException.getErrorCode());
+      assertEquals(AuthErrorCode.CONFIGURATION_NOT_FOUND, authException.getAuthErrorCode());
     }
   }
 
diff --git a/src/test/java/com/google/firebase/auth/multitenancy/FirebaseTenantClientTest.java b/src/test/java/com/google/firebase/auth/multitenancy/FirebaseTenantClientTest.java
index ec9cbe1..c03d205 100644
--- a/src/test/java/com/google/firebase/auth/multitenancy/FirebaseTenantClientTest.java
+++ b/src/test/java/com/google/firebase/auth/multitenancy/FirebaseTenantClientTest.java
@@ -18,6 +18,7 @@ package com.google.firebase.auth.multitenancy;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
@@ -26,6 +27,7 @@ import com.google.api.client.googleapis.util.Utils;
 import com.google.api.client.http.GenericUrl;
 import com.google.api.client.http.HttpHeaders;
 import com.google.api.client.http.HttpRequest;
+import com.google.api.client.http.HttpResponseException;
 import com.google.api.client.json.GenericJson;
 import com.google.api.client.json.JsonFactory;
 import com.google.api.client.testing.http.MockHttpTransport;
@@ -37,6 +39,7 @@ import com.google.firebase.ErrorCode;
 import com.google.firebase.FirebaseApp;
 import com.google.firebase.FirebaseOptions;
 import com.google.firebase.TestOnlyImplFirebaseTrampolines;
+import com.google.firebase.auth.AuthErrorCode;
 import com.google.firebase.auth.FirebaseAuth;
 import com.google.firebase.auth.FirebaseAuthException;
 import com.google.firebase.auth.MockGoogleCredentials;
@@ -91,6 +94,11 @@ public class FirebaseTenantClientTest {
       fail("No error thrown for invalid response");
     } catch (FirebaseAuthException e) {
       assertEquals(ErrorCode.NOT_FOUND, e.getErrorCode());
+      assertEquals(
+          "No tenant found for the given identifier (TENANT_NOT_FOUND).", e.getMessage());
+      assertTrue(e.getCause() instanceof HttpResponseException);
+      assertNotNull(e.getHttpResponse());
+      assertEquals(AuthErrorCode.TENANT_NOT_FOUND, e.getAuthErrorCode());
     }
     checkUrl(interceptor, "GET", TENANTS_BASE_URL + "/UNKNOWN");
   }
@@ -183,16 +191,21 @@ public class FirebaseTenantClientTest {
 
   @Test
   public void testCreateTenantError() {
-    TestResponseInterceptor interceptor =
-        initializeAppForTenantManagementWithStatusCode(404,
-            "{\"error\": {\"message\": \"INTERNAL_ERROR\"}}");
+    String message = "{\"error\": {\"message\": \"INTERNAL_ERROR\"}}";
+    TenantManager tenantManager = createRetryDisabledTenantManager(new MockLowLevelHttpResponse()
+        .setStatusCode(500)
+        .setContent(message));
+
     try {
-      FirebaseAuth.getInstance().getTenantManager().createTenant(new Tenant.CreateRequest());
+      tenantManager.createTenant(new Tenant.CreateRequest());
       fail("No error thrown for invalid response");
     } catch (FirebaseAuthException e) {
-      assertEquals(ErrorCode.NOT_FOUND, e.getErrorCode());
+      assertEquals(ErrorCode.INTERNAL, e.getErrorCode());
+      assertEquals("Unexpected HTTP response with status: 500\n" + message, e.getMessage());
+      assertTrue(e.getCause() instanceof HttpResponseException);
+      assertNotNull(e.getHttpResponse());
+      assertNull(e.getAuthErrorCode());
     }
-    checkUrl(interceptor, "POST", TENANTS_BASE_URL);
   }
 
   @Test
@@ -252,18 +265,23 @@ public class FirebaseTenantClientTest {
 
   @Test
   public void testUpdateTenantError() {
-    TestResponseInterceptor interceptor =
-        initializeAppForTenantManagementWithStatusCode(404,
-            "{\"error\": {\"message\": \"INTERNAL_ERROR\"}}");
+    String message = "{\"error\": {\"message\": \"INTERNAL_ERROR\"}}";
+    TenantManager tenantManager = createRetryDisabledTenantManager(new MockLowLevelHttpResponse()
+        .setStatusCode(500)
+        .setContent(message));
     Tenant.UpdateRequest request =
         new Tenant.UpdateRequest("TENANT_1").setDisplayName("DISPLAY_NAME");
+
     try {
-      FirebaseAuth.getInstance().getTenantManager().updateTenant(request);
+      tenantManager.updateTenant(request);
       fail("No error thrown for invalid response");
     } catch (FirebaseAuthException e) {
-      assertEquals(ErrorCode.NOT_FOUND, e.getErrorCode());
+      assertEquals(ErrorCode.INTERNAL, e.getErrorCode());
+      assertEquals("Unexpected HTTP response with status: 500\n" + message, e.getMessage());
+      assertTrue(e.getCause() instanceof HttpResponseException);
+      assertNotNull(e.getHttpResponse());
+      assertNull(e.getAuthErrorCode());
     }
-    checkUrl(interceptor, "PATCH", TENANTS_BASE_URL + "/TENANT_1");
   }
 
   @Test
@@ -286,6 +304,10 @@ public class FirebaseTenantClientTest {
       fail("No error thrown for invalid response");
     } catch (FirebaseAuthException e) {
       assertEquals(ErrorCode.NOT_FOUND, e.getErrorCode());
+      assertEquals("No tenant found for the given identifier (TENANT_NOT_FOUND).", e.getMessage());
+      assertTrue(e.getCause() instanceof HttpResponseException);
+      assertNotNull(e.getHttpResponse());
+      assertEquals(AuthErrorCode.TENANT_NOT_FOUND, e.getAuthErrorCode());
     }
     checkUrl(interceptor, "DELETE", TENANTS_BASE_URL + "/UNKNOWN");
   }
@@ -313,7 +335,17 @@ public class FirebaseTenantClientTest {
   }
 
   private static TestResponseInterceptor initializeAppForTenantManagement(String... responses) {
-    initializeAppWithResponses(responses);
+    List<MockLowLevelHttpResponse> mocks = new ArrayList<>();
+    for (String response : responses) {
+      mocks.add(new MockLowLevelHttpResponse().setContent(response));
+    }
+    MockHttpTransport transport = new MultiRequestMockHttpTransport(mocks);
+    FirebaseApp.initializeApp(FirebaseOptions.builder()
+        .setCredentials(credentials)
+        .setHttpTransport(transport)
+        .setProjectId("test-project-id")
+        .build());
+
     TestResponseInterceptor interceptor = new TestResponseInterceptor();
     FirebaseAuth.getInstance().getTenantManager().setInterceptor(interceptor);
     return interceptor;
@@ -321,7 +353,7 @@ public class FirebaseTenantClientTest {
 
   private static TestResponseInterceptor initializeAppForTenantManagementWithStatusCode(
       int statusCode, String response) {
-    FirebaseApp.initializeApp(new FirebaseOptions.Builder()
+    FirebaseApp.initializeApp(FirebaseOptions.builder()
         .setCredentials(credentials)
         .setHttpTransport(
             new MockHttpTransport.Builder()
@@ -335,17 +367,16 @@ public class FirebaseTenantClientTest {
     return interceptor;
   }
 
-  private static void initializeAppWithResponses(String... responses) {
-    List<MockLowLevelHttpResponse> mocks = new ArrayList<>();
-    for (String response : responses) {
-      mocks.add(new MockLowLevelHttpResponse().setContent(response));
-    }
-    MockHttpTransport transport = new MultiRequestMockHttpTransport(mocks);
-    FirebaseApp.initializeApp(new FirebaseOptions.Builder()
+  private static TenantManager createRetryDisabledTenantManager(MockLowLevelHttpResponse response) {
+    MockHttpTransport transport = new MockHttpTransport.Builder()
+        .setLowLevelHttpResponse(response)
+        .build();
+    FirebaseApp app = FirebaseApp.initializeApp(FirebaseOptions.builder()
         .setCredentials(credentials)
-        .setHttpTransport(transport)
-        .setProjectId("test-project-id")
         .build());
+    FirebaseTenantClient tenantClient = new FirebaseTenantClient(
+        "test-project-id", Utils.getDefaultJsonFactory(), transport.createRequestFactory());
+    return new TenantManager(app, tenantClient);
   }
 
   private static GenericJson parseRequestContent(TestResponseInterceptor interceptor)
diff --git a/src/test/java/com/google/firebase/auth/multitenancy/TenantManagerIT.java b/src/test/java/com/google/firebase/auth/multitenancy/TenantManagerIT.java
index 1949dc5..44d75e8 100644
--- a/src/test/java/com/google/firebase/auth/multitenancy/TenantManagerIT.java
+++ b/src/test/java/com/google/firebase/auth/multitenancy/TenantManagerIT.java
@@ -28,6 +28,7 @@ import com.google.api.core.ApiFutureCallback;
 import com.google.api.core.ApiFutures;
 import com.google.common.util.concurrent.MoreExecutors;
 import com.google.firebase.ErrorCode;
+import com.google.firebase.auth.AuthErrorCode;
 import com.google.firebase.auth.FirebaseAuth;
 import com.google.firebase.auth.FirebaseAuthException;
 import com.google.firebase.testing.IntegrationTestUtils;
@@ -81,8 +82,9 @@ public class TenantManagerIT {
       fail("No error thrown for getting a deleted tenant");
     } catch (ExecutionException e) {
       assertTrue(e.getCause() instanceof FirebaseAuthException);
-      assertEquals(ErrorCode.NOT_FOUND,
-          ((FirebaseAuthException) e.getCause()).getErrorCode());
+      FirebaseAuthException authException = (FirebaseAuthException) e.getCause();
+      assertEquals(ErrorCode.NOT_FOUND, authException.getErrorCode());
+      assertEquals(AuthErrorCode.TENANT_NOT_FOUND, authException.getAuthErrorCode());
     }
   }