Repository: firebase/firebase-admin-java
Pull request #411 (2020-05-12 23:16:00)

Rename OIDC delete operation and refactor integration tests.
I've renamed the delete operation since we need separate methods for deleting OIDC and SAML provider configs.

I've also created a ProviderConfigTestUtils class to house shared logic between `TenantAwareFirebaseAuthIT` and `FirebaseAuthIT`, and I've added a `TemporaryProviderConfig` class to make it easier for provider configs to be cleaned up automatically. I've structured this class in such a way that we can easily tack on logic to cleanup SAML provider configs as well.

I've also decided to remove `testGetUserWithMultipleTenantIds`. I initially wrote this test to understand how tenant-aware auths and tenant-agnostic auths interacted with one another. In its existing state, it appears to be leaking created users, and in order to resolve this with `TemporaryUser`, we would need to declare multiple `TemporaryUser` objects as rules. I think it's better to avoid this complexity and remove this test. After all, this integration test is mainly making assertions about the server's behavior and not our behavior.

NOTE: I also plan on creating a UserTestUtils class to reduce some code duplication between integration tests, but this will be in a separate PR.


Patch:
diff --git a/src/main/java/com/google/firebase/auth/AbstractFirebaseAuth.java b/src/main/java/com/google/firebase/auth/AbstractFirebaseAuth.java
index a90dcdb..4315a26 100644
--- a/src/main/java/com/google/firebase/auth/AbstractFirebaseAuth.java
+++ b/src/main/java/com/google/firebase/auth/AbstractFirebaseAuth.java
@@ -970,7 +970,7 @@ public abstract class AbstractFirebaseAuth {
   private CallableOperation<OidcProviderConfig, FirebaseAuthException>
       createOidcProviderConfigOp(final OidcProviderConfig.CreateRequest request) {
     checkNotDestroyed();
-    checkNotNull(request, "create request must not be null");
+    checkNotNull(request, "Create request must not be null.");
     final FirebaseUserManager userManager = getUserManager();
     return new CallableOperation<OidcProviderConfig, FirebaseAuthException>() {
       @Override
@@ -1010,7 +1010,7 @@ public abstract class AbstractFirebaseAuth {
   private CallableOperation<OidcProviderConfig, FirebaseAuthException> updateOidcProviderConfigOp(
       final OidcProviderConfig.UpdateRequest request) {
     checkNotDestroyed();
-    checkNotNull(request, "update request must not be null");
+    checkNotNull(request, "Update request must not be null.");
     final FirebaseUserManager userManager = getUserManager();
     return new CallableOperation<OidcProviderConfig, FirebaseAuthException>() {
       @Override
@@ -1051,7 +1051,7 @@ public abstract class AbstractFirebaseAuth {
   private CallableOperation<OidcProviderConfig, FirebaseAuthException>
       getOidcProviderConfigOp(final String providerId) {
     checkNotDestroyed();
-    checkArgument(!Strings.isNullOrEmpty(providerId), "provider ID must not be null or empty");
+    checkArgument(!Strings.isNullOrEmpty(providerId), "Provider ID must not be null or empty.");
     final FirebaseUserManager userManager = getUserManager();
     return new CallableOperation<OidcProviderConfig, FirebaseAuthException>() {
       @Override
@@ -1149,18 +1149,18 @@ public abstract class AbstractFirebaseAuth {
   }
 
   /**
-   * Deletes the provider config identified by the specified provider ID.
+   * Deletes the OIDC Auth provider config identified by the specified provider ID.
    *
    * @param providerId A provider ID string.
    * @throws IllegalArgumentException If the provider ID string is null or empty.
    * @throws FirebaseAuthException If an error occurs while deleting the provider config.
    */
-  public void deleteProviderConfig(@NonNull String providerId) throws FirebaseAuthException {
-    deleteProviderConfigOp(providerId).call();
+  public void deleteOidcProviderConfig(@NonNull String providerId) throws FirebaseAuthException {
+    deleteOidcProviderConfigOp(providerId).call();
   }
 
   /**
-   * Similar to {@link #deleteProviderConfig} but performs the operation asynchronously.
+   * Similar to {@link #deleteOidcProviderConfig} but performs the operation asynchronously.
    *
    * @param providerId A provider ID string.
    * @return An {@code ApiFuture} which will complete successfully when the specified provider
@@ -1168,19 +1168,19 @@ public abstract class AbstractFirebaseAuth {
    *     throws a {@link FirebaseAuthException}.
    * @throws IllegalArgumentException If the provider ID string is null or empty.
    */
-  public ApiFuture<Void> deleteProviderConfigAsync(String providerId) {
-    return deleteProviderConfigOp(providerId).callAsync(firebaseApp);
+  public ApiFuture<Void> deleteOidcProviderConfigAsync(String providerId) {
+    return deleteOidcProviderConfigOp(providerId).callAsync(firebaseApp);
   }
 
-  private CallableOperation<Void, FirebaseAuthException> deleteProviderConfigOp(
+  private CallableOperation<Void, FirebaseAuthException> deleteOidcProviderConfigOp(
       final String providerId) {
     checkNotDestroyed();
-    checkArgument(!Strings.isNullOrEmpty(providerId), "provider ID must not be null or empty");
+    checkArgument(!Strings.isNullOrEmpty(providerId), "Provider ID must not be null or empty.");
     final FirebaseUserManager userManager = getUserManager();
     return new CallableOperation<Void, FirebaseAuthException>() {
       @Override
       protected Void execute() throws FirebaseAuthException {
-        userManager.deleteProviderConfig(providerId);
+        userManager.deleteOidcProviderConfig(providerId);
         return null;
       }
     };
diff --git a/src/main/java/com/google/firebase/auth/FirebaseUserManager.java b/src/main/java/com/google/firebase/auth/FirebaseUserManager.java
index 2605d62..690a832 100644
--- a/src/main/java/com/google/firebase/auth/FirebaseUserManager.java
+++ b/src/main/java/com/google/firebase/auth/FirebaseUserManager.java
@@ -319,7 +319,7 @@ class FirebaseUserManager {
       OidcProviderConfig.CreateRequest request) throws FirebaseAuthException {
     GenericUrl url = new GenericUrl(idpConfigMgtBaseUrl + "/oauthIdpConfigs");
     String providerId = request.getProviderId();
-    checkArgument(!Strings.isNullOrEmpty(providerId), "provider ID must not be null or empty");
+    checkArgument(!Strings.isNullOrEmpty(providerId), "Provider ID must not be null or empty.");
     url.set("oauthIdpConfigId", providerId);
     return sendRequest("POST", url, request.getProperties(), OidcProviderConfig.class);
   }
@@ -328,7 +328,7 @@ class FirebaseUserManager {
       throws FirebaseAuthException {
     Map<String, Object> properties = request.getProperties();
     checkArgument(!properties.isEmpty(),
-        "provider config update must have at least one property set");
+        "Provider config update must have at least one property set.");
     GenericUrl url =
         new GenericUrl(idpConfigMgtBaseUrl + getOidcUrlSuffix(request.getProviderId()));
     url.put("updateMask", generateMask(properties));
@@ -346,7 +346,7 @@ class FirebaseUserManager {
         ImmutableMap.<String, Object>builder().put("pageSize", maxResults);
     if (pageToken != null) {
       checkArgument(!pageToken.equals(
-          ListTenantsPage.END_OF_LIST), "invalid end of list page token");
+          ListTenantsPage.END_OF_LIST), "Invalid end of list page token.");
       builder.put("nextPageToken", pageToken);
     }
 
@@ -360,7 +360,7 @@ class FirebaseUserManager {
     return response;
   }
 
-  void deleteProviderConfig(String providerId) throws FirebaseAuthException {
+  void deleteOidcProviderConfig(String providerId) throws FirebaseAuthException {
     GenericUrl url = new GenericUrl(idpConfigMgtBaseUrl + getOidcUrlSuffix(providerId));
     sendRequest("DELETE", url, null, GenericJson.class);
   }
@@ -374,12 +374,12 @@ class FirebaseUserManager {
   }
 
   private static String getTenantUrlSuffix(String tenantId) {
-    checkArgument(!Strings.isNullOrEmpty(tenantId), "tenant ID must not be null or empty");
+    checkArgument(!Strings.isNullOrEmpty(tenantId), "Tenant ID must not be null or empty.");
     return "/tenants/" + tenantId;
   }
 
   private static String getOidcUrlSuffix(String providerId) {
-    checkArgument(!Strings.isNullOrEmpty(providerId), "provider ID must not be null or empty");
+    checkArgument(!Strings.isNullOrEmpty(providerId), "Provider ID must not be null or empty.");
     return "/oauthIdpConfigs/" + providerId;
   }
 


Test patch:
diff --git a/src/test/java/com/google/firebase/auth/FirebaseAuthIT.java b/src/test/java/com/google/firebase/auth/FirebaseAuthIT.java
index bbc61dd..151eed8 100644
--- a/src/test/java/com/google/firebase/auth/FirebaseAuthIT.java
+++ b/src/test/java/com/google/firebase/auth/FirebaseAuthIT.java
@@ -46,6 +46,7 @@ import com.google.common.util.concurrent.MoreExecutors;
 import com.google.firebase.FirebaseApp;
 import com.google.firebase.FirebaseOptions;
 import com.google.firebase.ImplFirebaseTrampolines;
+import com.google.firebase.auth.ProviderConfigTestUtils.TemporaryProviderConfig;
 import com.google.firebase.auth.hash.Scrypt;
 import com.google.firebase.internal.Nullable;
 import com.google.firebase.testing.IntegrationTestUtils;
@@ -84,8 +85,9 @@ public class FirebaseAuthIT {
   private static final FirebaseAuth auth = FirebaseAuth.getInstance(
       IntegrationTestUtils.ensureDefaultApp());
 
-  @Rule
-  public final TemporaryUser temporaryUser = new TemporaryUser();
+  @Rule public final TemporaryUser temporaryUser = new TemporaryUser();
+  @Rule public final TemporaryProviderConfig temporaryProviderConfig =
+      new TemporaryProviderConfig(auth);
 
   @Test
   public void testGetNonExistingUser() throws Exception {
@@ -563,124 +565,113 @@ public class FirebaseAuthIT {
   public void testOidcProviderConfigLifecycle() throws Exception {
     // Create config provider
     String providerId = "oidc.provider-id";
-    OidcProviderConfig.CreateRequest createRequest =
+    OidcProviderConfig config = temporaryProviderConfig.createOidcProviderConfig(
         new OidcProviderConfig.CreateRequest()
             .setProviderId(providerId)
             .setDisplayName("DisplayName")
             .setEnabled(true)
             .setClientId("ClientId")
-            .setIssuer("https://oidc.com/issuer");
-    OidcProviderConfig config = auth.createOidcProviderConfigAsync(createRequest).get();
+            .setIssuer("https://oidc.com/issuer"));
     assertEquals(providerId, config.getProviderId());
     assertEquals("DisplayName", config.getDisplayName());
     assertTrue(config.isEnabled());
     assertEquals("ClientId", config.getClientId());
     assertEquals("https://oidc.com/issuer", config.getIssuer());
 
-    try {
-      // Get config provider
-      config = auth.getOidcProviderConfigAsync(providerId).get();
-      assertEquals(providerId, config.getProviderId());
-      assertEquals("DisplayName", config.getDisplayName());
-      assertTrue(config.isEnabled());
-      assertEquals("ClientId", config.getClientId());
-      assertEquals("https://oidc.com/issuer", config.getIssuer());
+    // Get config provider
+    config = auth.getOidcProviderConfigAsync(providerId).get();
+    assertEquals(providerId, config.getProviderId());
+    assertEquals("DisplayName", config.getDisplayName());
+    assertTrue(config.isEnabled());
+    assertEquals("ClientId", config.getClientId());
+    assertEquals("https://oidc.com/issuer", config.getIssuer());
 
-      // Update config provider
-      OidcProviderConfig.UpdateRequest updateRequest =
-          new OidcProviderConfig.UpdateRequest(providerId)
-              .setDisplayName("NewDisplayName")
-              .setEnabled(false)
-              .setClientId("NewClientId")
-              .setIssuer("https://oidc.com/new-issuer");
-      config = auth.updateOidcProviderConfigAsync(updateRequest).get();
-      assertEquals(providerId, config.getProviderId());
-      assertEquals("NewDisplayName", config.getDisplayName());
-      assertFalse(config.isEnabled());
-      assertEquals("NewClientId", config.getClientId());
-      assertEquals("https://oidc.com/new-issuer", config.getIssuer());
-    } finally {
-      // Delete config provider
-      auth.deleteProviderConfigAsync(providerId).get();
-    }
+    // Update config provider
+    OidcProviderConfig.UpdateRequest updateRequest =
+        new OidcProviderConfig.UpdateRequest(providerId)
+            .setDisplayName("NewDisplayName")
+            .setEnabled(false)
+            .setClientId("NewClientId")
+            .setIssuer("https://oidc.com/new-issuer");
+    config = auth.updateOidcProviderConfigAsync(updateRequest).get();
+    assertEquals(providerId, config.getProviderId());
+    assertEquals("NewDisplayName", config.getDisplayName());
+    assertFalse(config.isEnabled());
+    assertEquals("NewClientId", config.getClientId());
+    assertEquals("https://oidc.com/new-issuer", config.getIssuer());
 
-    assertOidcProviderConfigDoesNotExist(auth, providerId);
+    // Delete config provider
+    auth.deleteOidcProviderConfigAsync(providerId).get();
+    ProviderConfigTestUtils.assertOidcProviderConfigDoesNotExist(auth, providerId);
   }
 
   @Test
   public void testListOidcProviderConfigs() throws Exception {
     final List<String> providerIds = new ArrayList<>();
 
-    try {
-      // Create provider configs
-      for (int i = 0; i < 3; i++) {
-        String providerId = "oidc.provider-id" + i;
-        providerIds.add(providerId);
-        OidcProviderConfig.CreateRequest createRequest = new OidcProviderConfig.CreateRequest()
+    // Create provider configs
+    for (int i = 0; i < 3; i++) {
+      String providerId = "oidc.provider-id" + i;
+      providerIds.add(providerId);
+      OidcProviderConfig config = temporaryProviderConfig.createOidcProviderConfig(
+          new OidcProviderConfig.CreateRequest()
             .setProviderId(providerId)
             .setClientId("CLIENT_ID")
-            .setIssuer("https://oidc.com/issuer");
-        auth.createOidcProviderConfig(createRequest);
-      }
-
-      // Test list by batches
-      final AtomicInteger collected = new AtomicInteger(0);
-      ListProviderConfigsPage<OidcProviderConfig> page =
-          auth.listOidcProviderConfigsAsync(null).get();
-      while (page != null) {
-        for (OidcProviderConfig providerConfig : page.getValues()) {
-          if (checkProviderConfig(providerIds, providerConfig)) {
-            collected.incrementAndGet();
-          }
-        }
-        page = page.getNextPage();
-      }
-      assertEquals(providerIds.size(), collected.get());
+            .setIssuer("https://oidc.com/issuer"));
+    }
 
-      // Test iterate all
-      collected.set(0);
-      page = auth.listOidcProviderConfigsAsync(null).get();
-      for (OidcProviderConfig providerConfig : page.iterateAll()) {
+    // Test list by batches
+    final AtomicInteger collected = new AtomicInteger(0);
+    ListProviderConfigsPage<OidcProviderConfig> page =
+        auth.listOidcProviderConfigsAsync(null).get();
+    while (page != null) {
+      for (OidcProviderConfig providerConfig : page.getValues()) {
         if (checkProviderConfig(providerIds, providerConfig)) {
           collected.incrementAndGet();
         }
       }
-      assertEquals(providerIds.size(), collected.get());
-
-      // Test iterate async
-      collected.set(0);
-      final Semaphore semaphore = new Semaphore(0);
-      final AtomicReference<Throwable> error = new AtomicReference<>();
-      ApiFuture<ListProviderConfigsPage<OidcProviderConfig>> pageFuture =
-          auth.listOidcProviderConfigsAsync(null);
-      ApiFutures.addCallback(
-          pageFuture,
-          new ApiFutureCallback<ListProviderConfigsPage<OidcProviderConfig>>() {
-            @Override
-            public void onFailure(Throwable t) {
-              error.set(t);
-              semaphore.release();
-            }
+      page = page.getNextPage();
+    }
+    assertEquals(providerIds.size(), collected.get());
 
-            @Override
-            public void onSuccess(ListProviderConfigsPage<OidcProviderConfig> result) {
-              for (OidcProviderConfig providerConfig : result.iterateAll()) {
-                if (checkProviderConfig(providerIds, providerConfig)) {
-                  collected.incrementAndGet();
-                }
-              }
-              semaphore.release();
-            }
-          }, MoreExecutors.directExecutor());
-      semaphore.acquire();
-      assertEquals(providerIds.size(), collected.get());
-      assertNull(error.get());
-    } finally {
-      // Delete provider configs
-      for (String providerId : providerIds) {
-        auth.deleteProviderConfigAsync(providerId).get();
+    // Test iterate all
+    collected.set(0);
+    page = auth.listOidcProviderConfigsAsync(null).get();
+    for (OidcProviderConfig providerConfig : page.iterateAll()) {
+      if (checkProviderConfig(providerIds, providerConfig)) {
+        collected.incrementAndGet();
       }
     }
+    assertEquals(providerIds.size(), collected.get());
+
+    // Test iterate async
+    collected.set(0);
+    final Semaphore semaphore = new Semaphore(0);
+    final AtomicReference<Throwable> error = new AtomicReference<>();
+    ApiFuture<ListProviderConfigsPage<OidcProviderConfig>> pageFuture =
+        auth.listOidcProviderConfigsAsync(null);
+    ApiFutures.addCallback(
+        pageFuture,
+        new ApiFutureCallback<ListProviderConfigsPage<OidcProviderConfig>>() {
+          @Override
+          public void onFailure(Throwable t) {
+            error.set(t);
+            semaphore.release();
+          }
+
+          @Override
+          public void onSuccess(ListProviderConfigsPage<OidcProviderConfig> result) {
+            for (OidcProviderConfig providerConfig : result.iterateAll()) {
+              if (checkProviderConfig(providerIds, providerConfig)) {
+                collected.incrementAndGet();
+              }
+            }
+            semaphore.release();
+          }
+        }, MoreExecutors.directExecutor());
+    semaphore.acquire();
+    assertEquals(providerIds.size(), collected.get());
+    assertNull(error.get());
   }
 
   private Map<String, String> parseLinkParameters(String link) throws Exception {
@@ -823,23 +814,11 @@ public class FirebaseAuthIT {
   }
 
 
-  private static void assertOidcProviderConfigDoesNotExist(
-      AbstractFirebaseAuth firebaseAuth, String providerId) throws Exception {
-    try {
-      firebaseAuth.getOidcProviderConfigAsync(providerId).get();
-      fail("No error thrown for getting a deleted provider config");
-    } catch (ExecutionException e) {
-      assertTrue(e.getCause() instanceof FirebaseAuthException);
-      assertEquals(FirebaseUserManager.CONFIGURATION_NOT_FOUND_ERROR,
-          ((FirebaseAuthException) e.getCause()).getErrorCode());
-    }
-  }
-
   private static void assertUserDoesNotExist(AbstractFirebaseAuth firebaseAuth, String uid)
       throws Exception {
     try {
       firebaseAuth.getUserAsync(uid).get();
-      fail("No error thrown for getting a user which was expected to be absent");
+      fail("No error thrown for getting a user which was expected to be absent.");
     } catch (ExecutionException e) {
       assertTrue(e.getCause() instanceof FirebaseAuthException);
       assertEquals(FirebaseUserManager.USER_NOT_FOUND_ERROR,
diff --git a/src/test/java/com/google/firebase/auth/FirebaseUserManagerTest.java b/src/test/java/com/google/firebase/auth/FirebaseUserManagerTest.java
index 31738df..08d3c3d 100644
--- a/src/test/java/com/google/firebase/auth/FirebaseUserManagerTest.java
+++ b/src/test/java/com/google/firebase/auth/FirebaseUserManagerTest.java
@@ -1709,7 +1709,7 @@ public class FirebaseUserManagerTest {
   public void testDeleteProviderConfig() throws Exception {
     TestResponseInterceptor interceptor = initializeAppForUserManagement("{}");
 
-    FirebaseAuth.getInstance().deleteProviderConfig("PROVIDER_ID");
+    FirebaseAuth.getInstance().deleteOidcProviderConfig("PROVIDER_ID");
 
     checkRequestHeaders(interceptor);
     checkUrl(interceptor, "DELETE", PROJECT_BASE_URL + "/oauthIdpConfigs/PROVIDER_ID");
@@ -1721,7 +1721,7 @@ public class FirebaseUserManagerTest {
         initializeAppForUserManagementWithStatusCode(404,
             "{\"error\": {\"message\": \"CONFIGURATION_NOT_FOUND\"}}");
     try {
-      FirebaseAuth.getInstance().deleteProviderConfig("UNKNOWN");
+      FirebaseAuth.getInstance().deleteOidcProviderConfig("UNKNOWN");
       fail("No error thrown for invalid response");
     } catch (FirebaseAuthException e) {
       assertEquals(FirebaseUserManager.CONFIGURATION_NOT_FOUND_ERROR, e.getErrorCode());
@@ -1737,7 +1737,7 @@ public class FirebaseUserManagerTest {
     TenantAwareFirebaseAuth tenantAwareAuth =
         FirebaseAuth.getInstance().getTenantManager().getAuthForTenant("TENANT_ID");
 
-    tenantAwareAuth.deleteProviderConfig("PROVIDER_ID");
+    tenantAwareAuth.deleteOidcProviderConfig("PROVIDER_ID");
 
     checkRequestHeaders(interceptor);
     checkUrl(interceptor, "DELETE", TENANTS_BASE_URL + "/TENANT_ID/oauthIdpConfigs/PROVIDER_ID");
diff --git a/src/test/java/com/google/firebase/auth/ProviderConfigTestUtils.java b/src/test/java/com/google/firebase/auth/ProviderConfigTestUtils.java
new file mode 100644
index 0000000..0cc636d
--- /dev/null
+++ b/src/test/java/com/google/firebase/auth/ProviderConfigTestUtils.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2020 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.firebase.auth;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import org.junit.rules.ExternalResource;
+
+class ProviderConfigTestUtils {
+
+  static void assertOidcProviderConfigDoesNotExist(
+      AbstractFirebaseAuth firebaseAuth, String providerId) throws Exception {
+    try {
+      firebaseAuth.getOidcProviderConfigAsync(providerId).get();
+      fail("No error thrown for getting a deleted OIDC provider config.");
+    } catch (ExecutionException e) {
+      assertTrue(e.getCause() instanceof FirebaseAuthException);
+      assertEquals(FirebaseUserManager.CONFIGURATION_NOT_FOUND_ERROR,
+          ((FirebaseAuthException) e.getCause()).getErrorCode());
+    }
+  }
+
+  /**
+   * Creates temporary provider configs for testing, and deletes them at the end of each test case.
+   */
+  static final class TemporaryProviderConfig extends ExternalResource {
+
+    private final AbstractFirebaseAuth auth;
+    private final List<String> oidcIds = new ArrayList<>();
+
+    TemporaryProviderConfig(AbstractFirebaseAuth auth) {
+      this.auth = auth;
+    }
+
+    synchronized OidcProviderConfig createOidcProviderConfig(
+        OidcProviderConfig.CreateRequest request) throws FirebaseAuthException {
+      OidcProviderConfig config = auth.createOidcProviderConfig(request);
+      oidcIds.add(config.getProviderId());
+      return config;
+    }
+
+    synchronized void deleteOidcProviderConfig(String providerId) throws FirebaseAuthException {
+      checkArgument(oidcIds.contains(providerId),
+          "Provider ID is not currently associated with a temporary user.");
+      auth.deleteOidcProviderConfig(providerId);
+      oidcIds.remove(providerId);
+    }
+
+    @Override
+    protected synchronized void after() {
+      for (String id : oidcIds) {
+        try {
+          auth.deleteOidcProviderConfig(id);
+        } catch (Exception ignore) {
+          // Ignore
+        }
+      }
+      oidcIds.clear();
+    }
+  }
+}
+
diff --git a/src/test/java/com/google/firebase/auth/TenantAwareFirebaseAuthIT.java b/src/test/java/com/google/firebase/auth/TenantAwareFirebaseAuthIT.java
index ddfc9fe..e585f87 100644
--- a/src/test/java/com/google/firebase/auth/TenantAwareFirebaseAuthIT.java
+++ b/src/test/java/com/google/firebase/auth/TenantAwareFirebaseAuthIT.java
@@ -38,6 +38,7 @@ import com.google.api.core.ApiFutures;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.util.concurrent.MoreExecutors;
 import com.google.firebase.FirebaseApp;
+import com.google.firebase.auth.ProviderConfigTestUtils.TemporaryProviderConfig;
 import com.google.firebase.internal.Nullable;
 import com.google.firebase.testing.IntegrationTestUtils;
 import java.io.IOException;
@@ -52,7 +53,9 @@ import java.util.concurrent.atomic.AtomicReference;
 
 import org.junit.AfterClass;
 import org.junit.BeforeClass;
+import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.ExternalResource;
 
 public class TenantAwareFirebaseAuthIT {
 
@@ -61,16 +64,17 @@ public class TenantAwareFirebaseAuthIT {
   private static final JsonFactory jsonFactory = Utils.getDefaultJsonFactory();
   private static final HttpTransport transport = Utils.getDefaultTransport();
 
-  private static FirebaseAuth auth;
   private static TenantManager tenantManager;
   private static TenantAwareFirebaseAuth tenantAwareAuth;
   private static String tenantId;
 
+  @Rule public final TemporaryProviderConfig temporaryProviderConfig =
+      new TemporaryProviderConfig(tenantAwareAuth);
+
   @BeforeClass
   public static void setUpClass() throws Exception {
     FirebaseApp masterApp = IntegrationTestUtils.ensureDefaultApp();
-    auth = FirebaseAuth.getInstance(masterApp);
-    tenantManager = auth.getTenantManager();
+    tenantManager = FirebaseAuth.getInstance(masterApp).getTenantManager();
     Tenant.CreateRequest tenantCreateRequest =
         new Tenant.CreateRequest().setDisplayName("DisplayName");
     tenantId = tenantManager.createTenant(tenantCreateRequest).getTenantId();
@@ -85,6 +89,7 @@ public class TenantAwareFirebaseAuthIT {
   @Test
   public void testUserLifecycle() throws Exception {
     // Create user
+    // TODO(micahstairs): Use a temporary user here.
     UserRecord userRecord = tenantAwareAuth.createUserAsync(new UserRecord.CreateRequest()).get();
     String uid = userRecord.getUid();
 
@@ -158,6 +163,7 @@ public class TenantAwareFirebaseAuthIT {
 
     try {
       for (int i = 0; i < 3; i++) {
+        // TODO(micahstairs): Use a temporary user here.
         UserRecord.CreateRequest createRequest =
             new UserRecord.CreateRequest().setPassword("password");
         uids.add(tenantAwareAuth.createUserAsync(createRequest).get().getUid());
@@ -229,39 +235,6 @@ public class TenantAwareFirebaseAuthIT {
     }
   }
 
-  @Test
-  public void testGetUserWithMultipleTenantIds() throws Exception {
-    // Create second tenant.
-    Tenant.CreateRequest tenantCreateRequest =
-        new Tenant.CreateRequest().setDisplayName("DisplayName2");
-    String tenantId2 = tenantManager.createTenant(tenantCreateRequest).getTenantId();
-
-    // Create three users (one without a tenant ID, and two with different tenant IDs).
-    UserRecord.CreateRequest createRequest = new UserRecord.CreateRequest();
-    UserRecord nonTenantUserRecord = auth.createUser(createRequest);
-    UserRecord tenantUserRecord1 = tenantAwareAuth.createUser(createRequest);
-    TenantAwareFirebaseAuth tenantAwareAuth2 = auth.getTenantManager().getAuthForTenant(tenantId2);
-    UserRecord tenantUserRecord2 = tenantAwareAuth2.createUser(createRequest);
-
-    // Make sure only non-tenant users can be fetched using the standard client.
-    assertNotNull(auth.getUser(nonTenantUserRecord.getUid()));
-    assertUserDoesNotExist(auth, tenantUserRecord1.getUid());
-    assertUserDoesNotExist(auth, tenantUserRecord2.getUid());
-
-    // Make sure tenant-aware client cannot fetch users outside that tenant.
-    assertUserDoesNotExist(tenantAwareAuth, nonTenantUserRecord.getUid());
-    assertUserDoesNotExist(tenantAwareAuth, tenantUserRecord2.getUid());
-    assertUserDoesNotExist(tenantAwareAuth2, nonTenantUserRecord.getUid());
-    assertUserDoesNotExist(tenantAwareAuth2, tenantUserRecord1.getUid());
-
-    // Make sure tenant-aware client can fetch users under that tenant.
-    assertNotNull(tenantAwareAuth.getUser(tenantUserRecord1.getUid()));
-    assertNotNull(tenantAwareAuth2.getUser(tenantUserRecord2.getUid()));
-
-    // Delete second tenant.
-    tenantManager.deleteTenant(tenantId2);
-  }
-
   @Test
   public void testCustomToken() throws Exception {
     String customToken = tenantAwareAuth.createCustomTokenAsync("user1").get();
@@ -277,7 +250,7 @@ public class TenantAwareFirebaseAuthIT {
     String idToken = signInWithCustomToken(customToken, tenantId);
 
     try {
-      auth.getTenantManager().getAuthForTenant("OTHER").verifyIdTokenAsync(idToken).get();
+      tenantManager.getAuthForTenant("OTHER").verifyIdTokenAsync(idToken).get();
       fail("No error thrown for verifying a token with the wrong tenant-aware client");
     } catch (ExecutionException e) {
       assertTrue(e.getCause() instanceof FirebaseAuthException);
@@ -290,81 +263,75 @@ public class TenantAwareFirebaseAuthIT {
   public void testOidcProviderConfigLifecycle() throws Exception {
     // Create config provider
     String providerId = "oidc.provider-id";
-    OidcProviderConfig.CreateRequest createRequest =
-        new OidcProviderConfig.CreateRequest()
-            .setProviderId(providerId)
-            .setDisplayName("DisplayName")
-            .setEnabled(true)
-            .setClientId("ClientId")
-            .setIssuer("https://oidc.com/issuer");
-    OidcProviderConfig config = tenantAwareAuth.createOidcProviderConfigAsync(createRequest).get();
+    OidcProviderConfig config =
+        temporaryProviderConfig.createOidcProviderConfig(
+          new OidcProviderConfig.CreateRequest()
+              .setProviderId(providerId)
+              .setDisplayName("DisplayName")
+              .setEnabled(true)
+              .setClientId("ClientId")
+              .setIssuer("https://oidc.com/issuer"));
     assertEquals(providerId, config.getProviderId());
     assertEquals("DisplayName", config.getDisplayName());
     assertEquals("ClientId", config.getClientId());
     assertEquals("https://oidc.com/issuer", config.getIssuer());
 
-    try {
-      // Get config provider
-      config = tenantAwareAuth.getOidcProviderConfigAsync(providerId).get();
-      assertEquals(providerId, config.getProviderId());
-      assertEquals("DisplayName", config.getDisplayName());
-      assertEquals("ClientId", config.getClientId());
-      assertEquals("https://oidc.com/issuer", config.getIssuer());
-
-      // Update config provider
-      OidcProviderConfig.UpdateRequest updateRequest =
-          new OidcProviderConfig.UpdateRequest(providerId)
-              .setDisplayName("NewDisplayName")
-              .setEnabled(false)
-              .setClientId("NewClientId")
-              .setIssuer("https://oidc.com/new-issuer");
-      config = tenantAwareAuth.updateOidcProviderConfigAsync(updateRequest).get();
-      assertEquals(providerId, config.getProviderId());
-      assertEquals("NewDisplayName", config.getDisplayName());
-      assertFalse(config.isEnabled());
-      assertEquals("NewClientId", config.getClientId());
-      assertEquals("https://oidc.com/new-issuer", config.getIssuer());
-    } finally {
-      // Delete config provider
-      tenantAwareAuth.deleteProviderConfigAsync(providerId).get();
-      assertOidcProviderConfigDoesNotExist(tenantAwareAuth, providerId);
-    }
+    // Get config provider
+    config = tenantAwareAuth.getOidcProviderConfigAsync(providerId).get();
+    assertEquals(providerId, config.getProviderId());
+    assertEquals("DisplayName", config.getDisplayName());
+    assertEquals("ClientId", config.getClientId());
+    assertEquals("https://oidc.com/issuer", config.getIssuer());
+
+    // Update config provider
+    OidcProviderConfig.UpdateRequest updateRequest =
+        new OidcProviderConfig.UpdateRequest(providerId)
+            .setDisplayName("NewDisplayName")
+            .setEnabled(false)
+            .setClientId("NewClientId")
+            .setIssuer("https://oidc.com/new-issuer");
+    config = tenantAwareAuth.updateOidcProviderConfigAsync(updateRequest).get();
+    assertEquals(providerId, config.getProviderId());
+    assertEquals("NewDisplayName", config.getDisplayName());
+    assertFalse(config.isEnabled());
+    assertEquals("NewClientId", config.getClientId());
+    assertEquals("https://oidc.com/new-issuer", config.getIssuer());
+
+    // Delete config provider
+    temporaryProviderConfig.deleteOidcProviderConfig(providerId);
+    ProviderConfigTestUtils.assertOidcProviderConfigDoesNotExist(tenantAwareAuth, providerId);
   }
 
   @Test
   public void testListOidcProviderConfigs() throws Exception {
     final List<String> providerIds = new ArrayList<>();
-    try {
-      // Create provider configs
-      for (int i = 0; i < 3; i++) {
-        String providerId = "oidc.provider-id" + i;
-        providerIds.add(providerId);
-        OidcProviderConfig.CreateRequest createRequest = new OidcProviderConfig.CreateRequest()
+
+    // Create provider configs
+    for (int i = 0; i < 3; i++) {
+      String providerId = "oidc.provider-id" + i;
+      providerIds.add(providerId);
+      temporaryProviderConfig.createOidcProviderConfig(
+          new OidcProviderConfig.CreateRequest()
             .setProviderId(providerId)
             .setClientId("CLIENT_ID")
-            .setIssuer("https://oidc.com/issuer");
-        tenantAwareAuth.createOidcProviderConfig(createRequest);
-      }
-
-      // List provider configs
-      final AtomicInteger collected = new AtomicInteger(0);
-      ListProviderConfigsPage<OidcProviderConfig> page =
-          tenantAwareAuth.listOidcProviderConfigsAsync(null).get();
-      for (OidcProviderConfig providerConfig : page.iterateAll()) {
-        if (providerIds.contains(providerConfig.getProviderId())) {
-          collected.incrementAndGet();
-          assertEquals("CLIENT_ID", providerConfig.getClientId());
-          assertEquals("https://oidc.com/issuer", providerConfig.getIssuer());
-        }
-      }
-      assertEquals(providerIds.size(), collected.get());
+            .setIssuer("https://oidc.com/issuer"));
+    }
 
-    } finally {
-      // Delete provider configs
-      for (String providerId : providerIds) {
-        tenantAwareAuth.deleteProviderConfigAsync(providerId).get();
+    // List provider configs
+    // NOTE: We do not need to test all of the different ways we can iterate over the provider
+    // configs, since this testing is already performed in FirebaseAuthIT with the tenant-agnostic
+    // tests.
+    final AtomicInteger collected = new AtomicInteger(0);
+    ListProviderConfigsPage<OidcProviderConfig> page =
+        tenantAwareAuth.listOidcProviderConfigsAsync(null).get();
+    for (OidcProviderConfig providerConfig : page.iterateAll()) {
+      if (providerIds.contains(providerConfig.getProviderId())) {
+        collected.incrementAndGet();
+        assertEquals("CLIENT_ID", providerConfig.getClientId());
+        assertEquals("https://oidc.com/issuer", providerConfig.getIssuer());
       }
     }
+    assertEquals(providerIds.size(), collected.get());
   }
 
   private String randomPhoneNumber() {
@@ -415,23 +382,11 @@ public class TenantAwareFirebaseAuthIT {
     }
   }
 
-  private static void assertOidcProviderConfigDoesNotExist(
-      AbstractFirebaseAuth firebaseAuth, String providerId) throws Exception {
-    try {
-      firebaseAuth.getOidcProviderConfigAsync(providerId).get();
-      fail("No error thrown for getting a deleted provider config");
-    } catch (ExecutionException e) {
-      assertTrue(e.getCause() instanceof FirebaseAuthException);
-      assertEquals(FirebaseUserManager.CONFIGURATION_NOT_FOUND_ERROR,
-          ((FirebaseAuthException) e.getCause()).getErrorCode());
-    }
-  }
-
   private static void assertUserDoesNotExist(AbstractFirebaseAuth firebaseAuth, String uid)
       throws Exception {
     try {
       firebaseAuth.getUserAsync(uid).get();
-      fail("No error thrown for getting a user which was expected to be absent");
+      fail("No error thrown for getting a user which was expected to be absent.");
     } catch (ExecutionException e) {
       assertTrue(e.getCause() instanceof FirebaseAuthException);
       assertEquals(FirebaseUserManager.USER_NOT_FOUND_ERROR,