sortedNameList = new ArrayList<>(allAppNames);
- Collections.sort(sortedNameList);
- return sortedNameList;
+
+ Collections.sort(allAppNames);
+ return ImmutableList.copyOf(allAppNames);
}
/** Normalizes the app name. */
@@ -281,8 +262,8 @@ public String getName() {
return name;
}
- /**
- * Returns the specified {@link FirebaseOptions}.
+ /**
+ * Returns the specified {@link FirebaseOptions}.
*/
@NonNull
public FirebaseOptions getOptions() {
@@ -297,6 +278,8 @@ public FirebaseOptions getOptions() {
*/
@Nullable
String getProjectId() {
+ checkNotDeleted();
+
// Try to get project ID from user-specified options.
String projectId = options.getProjectId();
@@ -310,17 +293,17 @@ String getProjectId() {
// Try to get project ID from the environment.
if (Strings.isNullOrEmpty(projectId)) {
- projectId = System.getenv("GCLOUD_PROJECT");
+ projectId = FirebaseProcessEnvironment.getenv("GOOGLE_CLOUD_PROJECT");
+ }
+ if (Strings.isNullOrEmpty(projectId)) {
+ projectId = FirebaseProcessEnvironment.getenv("GCLOUD_PROJECT");
}
return projectId;
}
@Override
public boolean equals(Object o) {
- if (!(o instanceof FirebaseApp)) {
- return false;
- }
- return name.equals(((FirebaseApp) o).getName());
+ return o instanceof FirebaseApp && name.equals(((FirebaseApp) o).getName());
}
@Override
@@ -334,8 +317,10 @@ public String toString() {
}
/**
- * Deletes the {@link FirebaseApp} and all its data. All calls to this {@link FirebaseApp}
- * instance will throw once it has been called.
+ * Deletes this {@link FirebaseApp} object, and releases any local state and managed resources
+ * associated with it. All calls to this {@link FirebaseApp} instance will throw once this method
+ * has been called. This also releases any managed resources allocated by other services
+ * attached to this object instance (e.g. {@code FirebaseAuth}).
*
* A no-op if delete was called before.
*/
@@ -363,11 +348,6 @@ public void delete() {
synchronized (appsLock) {
instances.remove(name);
}
-
- FirebaseAppStore appStore = FirebaseAppStore.getInstance();
- if (appStore != null) {
- appStore.removeApp(name);
- }
}
private void checkNotDeleted() {
@@ -383,8 +363,8 @@ private ScheduledExecutorService ensureScheduledExecutorService() {
synchronized (lock) {
checkNotDeleted();
if (scheduledExecutor == null) {
- scheduledExecutor = new RevivingScheduledExecutor(threadManager.getThreadFactory(),
- "firebase-scheduled-worker", GaeThreadFactory.isAvailable());
+ scheduledExecutor = new FirebaseScheduledExecutor(getThreadFactory(),
+ "firebase-scheduled-worker");
}
}
}
@@ -395,10 +375,13 @@ ThreadFactory getThreadFactory() {
return threadManager.getThreadFactory();
}
- // TODO: Return an ApiFuture once Task API is fully removed.
- Task submit(Callable command) {
+ ScheduledExecutorService getScheduledExecutorService() {
+ return ensureScheduledExecutorService();
+ }
+
+ ApiFuture submit(Callable command) {
checkNotNull(command);
- return Tasks.call(executors.getListeningExecutor(), command);
+ return new ListenableFuture2ApiFuture<>(executors.getListeningExecutor().submit(command));
}
ScheduledFuture schedule(Callable command, long delayMillis) {
@@ -411,6 +394,17 @@ ScheduledFuture schedule(Callable command, long delayMillis) {
}
}
+ ScheduledFuture> schedule(Runnable runnable, long delayMillis) {
+ checkNotNull(runnable);
+ try {
+ return ensureScheduledExecutorService()
+ .schedule(runnable, delayMillis, TimeUnit.MILLISECONDS);
+ } catch (Exception e) {
+ // This may fail if the underlying ThreadFactory does not support long-lived threads.
+ throw new UnsupportedOperationException("Scheduled tasks not supported", e);
+ }
+ }
+
void startTokenRefresher() {
synchronized (lock) {
checkNotDeleted();
@@ -462,7 +456,7 @@ static class TokenRefresher implements CredentialsChangedListener {
}
@Override
- public final synchronized void onChanged(OAuth2Credentials credentials) throws IOException {
+ public final synchronized void onChanged(OAuth2Credentials credentials) {
if (state.get() != State.STARTED) {
return;
}
@@ -569,33 +563,24 @@ enum State {
}
}
- private static FirebaseOptions getOptionsFromEnvironment() {
- String defaultConfig = System.getenv(FIREBASE_CONFIG_ENV_VAR);
- try {
- if (Strings.isNullOrEmpty(defaultConfig)) {
- return new FirebaseOptions.Builder()
- .setCredentials(GoogleCredentials.getApplicationDefault())
+ private static FirebaseOptions getOptionsFromEnvironment() throws IOException {
+ String defaultConfig = FirebaseProcessEnvironment.getenv(FIREBASE_CONFIG_ENV_VAR);
+ if (Strings.isNullOrEmpty(defaultConfig)) {
+ return FirebaseOptions.builder()
+ .setCredentials(APPLICATION_DEFAULT_CREDENTIALS)
.build();
- }
- JsonFactory jsonFactory = Utils.getDefaultJsonFactory();
- FirebaseOptions.Builder builder = new FirebaseOptions.Builder();
- JsonParser parser;
- if (defaultConfig.startsWith("{")) {
- parser = jsonFactory.createJsonParser(defaultConfig);
- } else {
- FileReader reader;
- reader = new FileReader(defaultConfig);
- parser = jsonFactory.createJsonParser(reader);
- }
- parser.parseAndClose(builder);
- builder.setCredentials(GoogleCredentials.getApplicationDefault());
-
- return builder.build();
-
- } catch (FileNotFoundException e) {
- throw new IllegalStateException(e);
- } catch (IOException e) {
- throw new IllegalStateException(e);
}
+ JsonFactory jsonFactory = ApiClientUtils.getDefaultJsonFactory();
+ FirebaseOptions.Builder builder = FirebaseOptions.builder();
+ JsonParser parser;
+ if (defaultConfig.startsWith("{")) {
+ parser = jsonFactory.createJsonParser(defaultConfig);
+ } else {
+ FileReader reader = new FileReader(defaultConfig);
+ parser = jsonFactory.createJsonParser(reader);
+ }
+ parser.parseAndClose(builder);
+ builder.setCredentials(APPLICATION_DEFAULT_CREDENTIALS);
+ return builder.build();
}
}
diff --git a/src/main/java/com/google/firebase/FirebaseAppLifecycleListener.java b/src/main/java/com/google/firebase/FirebaseAppLifecycleListener.java
index 60118c249..6493edb60 100644
--- a/src/main/java/com/google/firebase/FirebaseAppLifecycleListener.java
+++ b/src/main/java/com/google/firebase/FirebaseAppLifecycleListener.java
@@ -19,7 +19,7 @@
/**
* A listener which gets notified when {@link com.google.firebase.FirebaseApp} gets deleted.
*/
-// TODO: consider making it public in a future release.
+@Deprecated
interface FirebaseAppLifecycleListener {
/**
diff --git a/src/main/java/com/google/firebase/FirebaseException.java b/src/main/java/com/google/firebase/FirebaseException.java
index f78b3fb98..a5bb80424 100644
--- a/src/main/java/com/google/firebase/FirebaseException.java
+++ b/src/main/java/com/google/firebase/FirebaseException.java
@@ -17,24 +17,55 @@
package com.google.firebase;
import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
import com.google.common.base.Strings;
import com.google.firebase.internal.NonNull;
+import com.google.firebase.internal.Nullable;
-/** Base class for all Firebase exceptions. */
+/**
+ * Base class for all Firebase exceptions.
+ */
public class FirebaseException extends Exception {
- // TODO(b/27677218): Exceptions should have non-empty messages.
- @Deprecated
- protected FirebaseException() {}
+ private final ErrorCode errorCode;
+ private final IncomingHttpResponse httpResponse;
+
+ public FirebaseException(
+ @NonNull ErrorCode errorCode,
+ @NonNull String message,
+ @Nullable Throwable cause,
+ @Nullable IncomingHttpResponse httpResponse) {
+ super(message, cause);
+ checkArgument(!Strings.isNullOrEmpty(message), "Message must not be null or empty");
+ this.errorCode = checkNotNull(errorCode, "ErrorCode must not be null");
+ this.httpResponse = httpResponse;
+ }
+
+ public FirebaseException(
+ @NonNull ErrorCode errorCode,
+ @NonNull String message,
+ @Nullable Throwable cause) {
+ this(errorCode, message, cause, null);
+ }
- public FirebaseException(@NonNull String detailMessage) {
- super(detailMessage);
- checkArgument(!Strings.isNullOrEmpty(detailMessage), "Detail message must not be empty");
+ /**
+ * Returns the platform-wide error code associated with this exception.
+ *
+ * @return A Firebase error code.
+ */
+ public final ErrorCode getErrorCode() {
+ return errorCode;
}
- public FirebaseException(@NonNull String detailMessage, Throwable cause) {
- super(detailMessage, cause);
- checkArgument(!Strings.isNullOrEmpty(detailMessage), "Detail message must not be empty");
+ /**
+ * Returns the HTTP response that resulted in this exception. If the exception was not caused by
+ * an HTTP error response, returns null.
+ *
+ * @return An HTTP response or null.
+ */
+ @Nullable
+ public final IncomingHttpResponse getHttpResponse() {
+ return httpResponse;
}
}
diff --git a/src/main/java/com/google/firebase/FirebaseOptions.java b/src/main/java/com/google/firebase/FirebaseOptions.java
index fc4df2658..03f1b34a4 100644
--- a/src/main/java/com/google/firebase/FirebaseOptions.java
+++ b/src/main/java/com/google/firebase/FirebaseOptions.java
@@ -19,55 +19,103 @@
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
-import com.google.api.client.googleapis.util.Utils;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.util.Key;
import com.google.auth.oauth2.GoogleCredentials;
+import com.google.cloud.firestore.FirestoreOptions;
import com.google.common.base.Strings;
-import com.google.firebase.auth.FirebaseCredential;
-import com.google.firebase.auth.FirebaseCredentials;
-import com.google.firebase.auth.internal.BaseCredential;
-import com.google.firebase.auth.internal.FirebaseCredentialsAdapter;
+import com.google.common.base.Supplier;
+import com.google.common.base.Suppliers;
+import com.google.common.collect.ImmutableList;
+import com.google.firebase.internal.ApiClientUtils;
+import com.google.firebase.internal.ApplicationDefaultCredentialsProvider;
import com.google.firebase.internal.FirebaseThreadManagers;
import com.google.firebase.internal.NonNull;
import com.google.firebase.internal.Nullable;
+import java.io.IOException;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
/** Configurable Firebase options. */
public final class FirebaseOptions {
- // TODO: deprecate and remove it once we can fetch these from Remote Config.
+ private static final List FIREBASE_SCOPES =
+ ImmutableList.of(
+ // Enables access to Firebase Realtime Database.
+ "https://www.googleapis.com/auth/firebase.database",
+
+ // Enables access to the email address associated with a project.
+ "https://www.googleapis.com/auth/userinfo.email",
+
+ // Enables access to Google Identity Toolkit (for user management APIs).
+ "https://www.googleapis.com/auth/identitytoolkit",
+
+ // Enables access to Google Cloud Storage.
+ "https://www.googleapis.com/auth/devstorage.full_control",
+
+ // Enables access to Google Cloud Firestore
+ "https://www.googleapis.com/auth/cloud-platform",
+ "https://www.googleapis.com/auth/datastore");
+
+ static final Supplier APPLICATION_DEFAULT_CREDENTIALS =
+ new Supplier() {
+ @Override
+ public GoogleCredentials get() {
+ try {
+ return ApplicationDefaultCredentialsProvider.getApplicationDefault()
+ .createScoped(FIREBASE_SCOPES);
+ } catch (IOException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+ };
private final String databaseUrl;
private final String storageBucket;
- private final GoogleCredentials credentials;
+ private final Supplier credentialsSupplier;
private final Map databaseAuthVariableOverride;
private final String projectId;
+ private final String serviceAccountId;
private final HttpTransport httpTransport;
+ private final int connectTimeout;
+ private final int readTimeout;
+ private final int writeTimeout;
private final JsonFactory jsonFactory;
private final ThreadManager threadManager;
+ private final FirestoreOptions firestoreOptions;
- private FirebaseOptions(@NonNull FirebaseOptions.Builder builder) {
- this.credentials = checkNotNull(builder.credentials,
- "FirebaseOptions must be initialized with setCredentials().")
- .createScoped(BaseCredential.FIREBASE_SCOPES);
+ private FirebaseOptions(@NonNull final FirebaseOptions.Builder builder) {
this.databaseUrl = builder.databaseUrl;
+ this.credentialsSupplier = checkNotNull(
+ builder.credentialsSupplier, "FirebaseOptions must be initialized with setCredentials().");
this.databaseAuthVariableOverride = builder.databaseAuthVariableOverride;
this.projectId = builder.projectId;
if (!Strings.isNullOrEmpty(builder.storageBucket)) {
checkArgument(!builder.storageBucket.startsWith("gs://"),
"StorageBucket must not include 'gs://' prefix.");
}
+ if (!Strings.isNullOrEmpty(builder.serviceAccountId)) {
+ this.serviceAccountId = builder.serviceAccountId;
+ } else {
+ this.serviceAccountId = null;
+ }
this.storageBucket = builder.storageBucket;
- this.httpTransport = checkNotNull(builder.httpTransport,
- "FirebaseOptions must be initialized with a non-null HttpTransport.");
- this.jsonFactory = checkNotNull(builder.jsonFactory,
- "FirebaseOptions must be initialized with a non-null JsonFactory.");
- this.threadManager = checkNotNull(builder.threadManager,
- "FirebaseOptions must be initialized with a non-null ThreadManager.");
+ this.httpTransport = builder.httpTransport != null ? builder.httpTransport
+ : ApiClientUtils.getDefaultTransport();
+ this.jsonFactory = builder.jsonFactory != null ? builder.jsonFactory
+ : ApiClientUtils.getDefaultJsonFactory();
+ this.threadManager = builder.threadManager != null ? builder.threadManager
+ : FirebaseThreadManagers.DEFAULT_THREAD_MANAGER;
+ checkArgument(builder.connectTimeout >= 0);
+ this.connectTimeout = builder.connectTimeout;
+ checkArgument(builder.readTimeout >= 0);
+ this.readTimeout = builder.readTimeout;
+ checkArgument(builder.writeTimeout >= 0);
+ this.writeTimeout = builder.writeTimeout;
+ this.firestoreOptions = builder.firestoreOptions;
}
/**
@@ -89,7 +137,7 @@ public String getStorageBucket() {
}
GoogleCredentials getCredentials() {
- return credentials;
+ return credentialsSupplier.get();
}
/**
@@ -111,6 +159,16 @@ public String getProjectId() {
return projectId;
}
+ /**
+ * Returns the client email address of the service account.
+ *
+ * @return The client email of the service account set via
+ * {@link Builder#setServiceAccountId(String)}
+ */
+ public String getServiceAccountId() {
+ return serviceAccountId;
+ }
+
/**
* Returns the HttpTransport used to call remote HTTP endpoints. This transport is
* used by all services of the SDK, except for FirebaseDatabase.
@@ -132,33 +190,95 @@ public JsonFactory getJsonFactory() {
return jsonFactory;
}
+ /**
+ * Returns the connect timeout in milliseconds, which is applied to outgoing REST calls
+ * made by the SDK.
+ *
+ * @return Connect timeout in milliseconds. 0 indicates an infinite timeout.
+ */
+ public int getConnectTimeout() {
+ return connectTimeout;
+ }
+
+ /**
+ * Returns the read timeout applied to outgoing REST calls in milliseconds.
+ *
+ * @return Read timeout in milliseconds. 0 indicates an infinite timeout.
+ */
+ public int getReadTimeout() {
+ return readTimeout;
+ }
+
+ /**
+ * Returns the write timeout applied to outgoing REST calls in milliseconds.
+ *
+ * @return Write timeout in milliseconds. 0 indicates an infinite timeout.
+ */
+ public int getWriteTimeout() {
+ return writeTimeout;
+ }
+
@NonNull
ThreadManager getThreadManager() {
return threadManager;
}
+ FirestoreOptions getFirestoreOptions() {
+ return firestoreOptions;
+ }
+
+ /**
+ * Creates an empty builder.
+ *
+ * @return A new builder instance.
+ */
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Creates a new {@code Builder} from the options object.
+ *
+ * The new builder is not backed by this object's values; that is, changes made to the new
+ * builder don't change the values of the origin object.
+ */
+ public Builder toBuilder() {
+ return new Builder(this);
+ }
+
/**
- * Builder for constructing {@link FirebaseOptions}.
+ * Builder for constructing {@link FirebaseOptions}.
*/
public static final class Builder {
@Key("databaseAuthVariableOverride")
private Map databaseAuthVariableOverride = new HashMap<>();
-
+
@Key("databaseUrl")
private String databaseUrl;
@Key("projectId")
private String projectId;
-
+
@Key("storageBucket")
private String storageBucket;
-
- private GoogleCredentials credentials;
- private HttpTransport httpTransport = Utils.getDefaultTransport();
- private JsonFactory jsonFactory = Utils.getDefaultJsonFactory();
- private ThreadManager threadManager = FirebaseThreadManagers.DEFAULT_THREAD_MANAGER;
- /** Constructs an empty builder. */
+ @Key("serviceAccountId")
+ private String serviceAccountId;
+ private Supplier credentialsSupplier;
+ private FirestoreOptions firestoreOptions;
+ private HttpTransport httpTransport;
+ private JsonFactory jsonFactory;
+ private ThreadManager threadManager;
+ private int connectTimeout;
+ private int readTimeout;
+ private int writeTimeout;
+
+ /**
+ * Constructs an empty builder.
+ *
+ * @deprecated Use {@link FirebaseOptions#builder()} instead.
+ */
+ @Deprecated
public Builder() {}
/**
@@ -166,16 +286,23 @@ public Builder() {}
*
* The new builder is not backed by this object's values, that is changes made to the new
* builder don't change the values of the origin object.
+ *
+ * @deprecated Use {@link FirebaseOptions#toBuilder()} instead.
*/
+ @Deprecated
public Builder(FirebaseOptions options) {
databaseUrl = options.databaseUrl;
storageBucket = options.storageBucket;
- credentials = options.credentials;
+ credentialsSupplier = options.credentialsSupplier;
databaseAuthVariableOverride = options.databaseAuthVariableOverride;
projectId = options.projectId;
httpTransport = options.httpTransport;
jsonFactory = options.jsonFactory;
threadManager = options.threadManager;
+ connectTimeout = options.connectTimeout;
+ readTimeout = options.readTimeout;
+ writeTimeout = options.writeTimeout;
+ firestoreOptions = options.firestoreOptions;
}
/**
@@ -215,36 +342,32 @@ public Builder setStorageBucket(String storageBucket) {
}
/**
- * Sets the GoogleCredentials to use to authenticate the SDK.
+ * Sets the GoogleCredentials to use to authenticate the SDK. This parameter
+ * must be specified when creating a new instance of {@link FirebaseOptions}.
*
*
See
* Initialize the SDK for code samples and detailed documentation.
*
* @param credentials A
- * {@code GoogleCredentials}
+ * {@code GoogleCredentials}
* instance used to authenticate the SDK.
* @return This Builder instance is returned so subsequent calls can be chained.
*/
public Builder setCredentials(GoogleCredentials credentials) {
- this.credentials = checkNotNull(credentials);
+ this.credentialsSupplier = Suppliers
+ .ofInstance(checkNotNull(credentials).createScoped(FIREBASE_SCOPES));
return this;
}
/**
- * Sets the FirebaseCredential to use to authenticate the SDK.
+ * Sets the Supplier of GoogleCredentials to use to authenticate the
+ * SDK. This is NOT intended for public use outside the SDK.
*
- * @param credential A FirebaseCredential used to authenticate the SDK. See {@link
- * FirebaseCredentials} for default implementations.
+ * @param credentialsSupplier Supplier instance that wraps GoogleCredentials.
* @return This Builder instance is returned so subsequent calls can be chained.
- * @deprecated Use {@link FirebaseOptions.Builder#setCredentials(GoogleCredentials)}.
*/
- public Builder setCredential(@NonNull FirebaseCredential credential) {
- checkNotNull(credential);
- if (credential instanceof BaseCredential) {
- this.credentials = ((BaseCredential) credential).getGoogleCredentials();
- } else {
- this.credentials = new FirebaseCredentialsAdapter(credential);
- }
+ Builder setCredentials(Supplier credentialsSupplier) {
+ this.credentialsSupplier = checkNotNull(credentialsSupplier);
return this;
}
@@ -284,6 +407,24 @@ public Builder setProjectId(@NonNull String projectId) {
return this;
}
+ /**
+ * Sets the client email address of the service account that should be associated with an app.
+ *
+ * This is used to
+ * create custom auth tokens when service account credentials are not available. The client
+ * email address of a service account can be found in the {@code client_email} field of the
+ * service account JSON.
+ *
+ * @param serviceAccountId A service account email address string.
+ * @return This Builder instance is returned so subsequent calls can be chained.
+ */
+ public Builder setServiceAccountId(@NonNull String serviceAccountId) {
+ checkArgument(!Strings.isNullOrEmpty(serviceAccountId),
+ "Service account ID must not be null or empty");
+ this.serviceAccountId = serviceAccountId;
+ return this;
+ }
+
/**
* Sets the HttpTransport used to make remote HTTP calls. A reasonable default
* is used if not explicitly set. The transport specified by calling this method is
@@ -293,7 +434,8 @@ public Builder setProjectId(@NonNull String projectId) {
* @return This Builder instance is returned so subsequent calls can be chained.
*/
public Builder setHttpTransport(HttpTransport httpTransport) {
- this.httpTransport = httpTransport;
+ this.httpTransport = checkNotNull(httpTransport,
+ "FirebaseOptions must be initialized with a non-null HttpTransport.");
return this;
}
@@ -305,7 +447,8 @@ public Builder setHttpTransport(HttpTransport httpTransport) {
* @return This Builder instance is returned so subsequent calls can be chained.
*/
public Builder setJsonFactory(JsonFactory jsonFactory) {
- this.jsonFactory = jsonFactory;
+ this.jsonFactory = checkNotNull(jsonFactory,
+ "FirebaseOptions must be initialized with a non-null JsonFactory.");
return this;
}
@@ -317,7 +460,64 @@ public Builder setJsonFactory(JsonFactory jsonFactory) {
* @return This Builder instance is returned so subsequent calls can be chained.
*/
public Builder setThreadManager(ThreadManager threadManager) {
- this.threadManager = threadManager;
+ this.threadManager = checkNotNull(threadManager,
+ "FirebaseOptions must be initialized with a non-null ThreadManager.");
+ return this;
+ }
+
+ /**
+ * Sets the FirestoreOptions used to initialize Firestore in the
+ * {@link com.google.firebase.cloud.FirestoreClient} API. This can be used to customize
+ * low-level transport (GRPC) parameters, and timestamp handling behavior.
+ *
+ *
If credentials or a project ID is set in FirestoreOptions, they will get
+ * overwritten by the corresponding parameters in FirebaseOptions.
+ *
+ * @param firestoreOptions A FirestoreOptions instance.
+ * @return This Builder instance is returned so subsequent calls can be chained.
+ */
+ public Builder setFirestoreOptions(FirestoreOptions firestoreOptions) {
+ this.firestoreOptions = firestoreOptions;
+ return this;
+ }
+
+ /**
+ * Sets the connect timeout for outgoing HTTP (REST) connections made by the SDK. This is used
+ * when opening a communication link to a remote HTTP endpoint. This setting does not
+ * affect the {@link com.google.firebase.database.FirebaseDatabase} and
+ * {@link com.google.firebase.cloud.FirestoreClient} APIs.
+ *
+ * @param connectTimeout Connect timeout in milliseconds. Must not be negative.
+ * @return This Builder instance is returned so subsequent calls can be chained.
+ */
+ public Builder setConnectTimeout(int connectTimeout) {
+ this.connectTimeout = connectTimeout;
+ return this;
+ }
+
+ /**
+ * Sets the read timeout for outgoing HTTP (REST) calls made by the SDK. This does not affect
+ * the {@link com.google.firebase.database.FirebaseDatabase} and
+ * {@link com.google.firebase.cloud.FirestoreClient} APIs.
+ *
+ * @param readTimeout Read timeout in milliseconds. Must not be negative.
+ * @return This Builder instance is returned so subsequent calls can be chained.
+ */
+ public Builder setReadTimeout(int readTimeout) {
+ this.readTimeout = readTimeout;
+ return this;
+ }
+
+ /**
+ * Sets the write timeout for outgoing HTTP (REST) calls made by the SDK. This does not affect
+ * the {@link com.google.firebase.database.FirebaseDatabase} and
+ * {@link com.google.firebase.cloud.FirestoreClient} APIs.
+ *
+ * @param writeTimeout Write timeout in milliseconds. Must not be negative.
+ * @return This Builder instance is returned so subsequent calls can be chained.
+ */
+ public Builder setWriteTimeout(int writeTimeout) {
+ this.writeTimeout = writeTimeout;
return this;
}
diff --git a/src/main/java/com/google/firebase/ImplFirebaseTrampolines.java b/src/main/java/com/google/firebase/ImplFirebaseTrampolines.java
index 7ffac6d11..0d3008b18 100644
--- a/src/main/java/com/google/firebase/ImplFirebaseTrampolines.java
+++ b/src/main/java/com/google/firebase/ImplFirebaseTrampolines.java
@@ -16,12 +16,19 @@
package com.google.firebase;
+import com.google.api.core.ApiAsyncFunction;
+import com.google.api.core.ApiFunction;
+import com.google.api.core.ApiFuture;
+import com.google.api.core.ApiFutures;
import com.google.auth.oauth2.GoogleCredentials;
+import com.google.cloud.firestore.FirestoreOptions;
+import com.google.firebase.auth.internal.Utils;
+import com.google.firebase.internal.EmulatorCredentials;
import com.google.firebase.internal.FirebaseService;
import com.google.firebase.internal.NonNull;
-import com.google.firebase.tasks.Task;
import java.util.concurrent.Callable;
+import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ThreadFactory;
/**
@@ -36,6 +43,9 @@ public final class ImplFirebaseTrampolines {
private ImplFirebaseTrampolines() {}
public static GoogleCredentials getCredentials(@NonNull FirebaseApp app) {
+ if (Utils.isEmulatorMode()) {
+ return new EmulatorCredentials();
+ }
return app.getOptions().getCredentials();
}
@@ -43,16 +53,12 @@ public static String getProjectId(@NonNull FirebaseApp app) {
return app.getProjectId();
}
- public static boolean isDefaultApp(@NonNull FirebaseApp app) {
- return app.isDefaultApp();
+ public static FirestoreOptions getFirestoreOptions(@NonNull FirebaseApp app) {
+ return app.getOptions().getFirestoreOptions();
}
- public static String getPersistenceKey(@NonNull FirebaseApp app) {
- return app.getPersistenceKey();
- }
-
- public static String getPersistenceKey(String name, FirebaseOptions options) {
- return FirebaseApp.getPersistenceKey(name, options);
+ public static boolean isDefaultApp(@NonNull FirebaseApp app) {
+ return app.isDefaultApp();
}
public static T getService(
@@ -70,7 +76,25 @@ public static ThreadFactory getThreadFactory(@NonNull FirebaseApp app) {
return app.getThreadFactory();
}
- public static Task submitCallable(@NonNull FirebaseApp app, @NonNull Callable command) {
+ public static ApiFuture transform(
+ ApiFuture extends V> input,
+ final ApiFunction super V, ? extends X> function,
+ @NonNull FirebaseApp app) {
+ return ApiFutures.transform(input, function, app.getScheduledExecutorService());
+ }
+
+ public static ApiFuture transformAsync(
+ ApiFuture input, final ApiAsyncFunction function, @NonNull FirebaseApp app) {
+ return ApiFutures.transformAsync(input, function, app.getScheduledExecutorService());
+ }
+
+ public static ScheduledFuture> schedule(
+ @NonNull FirebaseApp app, @NonNull Runnable runnable, long delayMillis) {
+ return app.schedule(runnable, delayMillis);
+ }
+
+ public static ApiFuture submitCallable(
+ @NonNull FirebaseApp app, @NonNull Callable command) {
return app.submit(command);
}
diff --git a/src/main/java/com/google/firebase/IncomingHttpResponse.java b/src/main/java/com/google/firebase/IncomingHttpResponse.java
new file mode 100644
index 000000000..cfeac5e70
--- /dev/null
+++ b/src/main/java/com/google/firebase/IncomingHttpResponse.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2020 Google Inc.
+ *
+ * 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;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.google.api.client.http.HttpRequest;
+import com.google.api.client.http.HttpResponse;
+import com.google.api.client.http.HttpResponseException;
+import com.google.common.collect.ImmutableMap;
+import com.google.firebase.database.annotations.Nullable;
+import java.util.Map;
+
+/**
+ * Contains information that describes an HTTP response received by the SDK.
+ */
+public final class IncomingHttpResponse {
+
+ private final int statusCode;
+ private final String content;
+ private final Map headers;
+ private final OutgoingHttpRequest request;
+
+ /**
+ * Creates an {@code IncomingHttpResponse} from a successful response and the content read
+ * from it. The caller is expected to read the content from the response, and handle any errors
+ * that may occur while reading.
+ *
+ * @param response A successful response.
+ * @param content Content read from the response.
+ */
+ public IncomingHttpResponse(HttpResponse response, @Nullable String content) {
+ checkNotNull(response, "response must not be null");
+ this.statusCode = response.getStatusCode();
+ this.content = content;
+ this.headers = ImmutableMap.copyOf(response.getHeaders());
+ this.request = new OutgoingHttpRequest(response.getRequest());
+ }
+
+ /**
+ * Creates an {@code IncomingHttpResponse} from an HTTP error response.
+ *
+ * @param e The exception representing the HTTP error response.
+ * @param request The request that resulted in the error.
+ */
+ public IncomingHttpResponse(HttpResponseException e, HttpRequest request) {
+ this(e, new OutgoingHttpRequest(request));
+ }
+
+ /**
+ * Creates an {@code IncomingHttpResponse} from an HTTP error response.
+ *
+ * @param e The exception representing the HTTP error response.
+ * @param request The request that resulted in the error.
+ */
+ public IncomingHttpResponse(HttpResponseException e, OutgoingHttpRequest request) {
+ checkNotNull(e, "exception must not be null");
+ this.statusCode = e.getStatusCode();
+ this.content = e.getContent();
+ this.headers = ImmutableMap.copyOf(e.getHeaders());
+ this.request = checkNotNull(request, "request must not be null");
+ }
+
+ /**
+ * Returns the status code of the response.
+ *
+ * @return An HTTP status code (e.g. 500).
+ */
+ public int getStatusCode() {
+ return this.statusCode;
+ }
+
+ /**
+ * Returns the content of the response as a string.
+ *
+ * @return HTTP content or null.
+ */
+ @Nullable
+ public String getContent() {
+ return this.content;
+ }
+
+ /**
+ * Returns the headers set on the response.
+ *
+ * @return An immutable map of headers (possibly empty).
+ */
+ public Map getHeaders() {
+ return this.headers;
+ }
+
+ /**
+ * Returns the request that resulted in this response.
+ *
+ * @return An HTTP request.
+ */
+ public OutgoingHttpRequest getRequest() {
+ return request;
+ }
+}
diff --git a/src/main/java/com/google/firebase/OutgoingHttpRequest.java b/src/main/java/com/google/firebase/OutgoingHttpRequest.java
new file mode 100644
index 000000000..44af4bff0
--- /dev/null
+++ b/src/main/java/com/google/firebase/OutgoingHttpRequest.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2020 Google Inc.
+ *
+ * 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;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.google.api.client.http.HttpContent;
+import com.google.api.client.http.HttpRequest;
+import com.google.common.base.Strings;
+import com.google.common.collect.ImmutableMap;
+import com.google.firebase.internal.Nullable;
+import java.util.Map;
+
+/**
+ * Contains the information that describe an HTTP request made by the SDK.
+ */
+public final class OutgoingHttpRequest {
+
+ private final String method;
+ private final String url;
+ private final HttpContent content;
+ private final Map headers;
+
+ /**
+ * Creates an {@code OutgoingHttpRequest} from the HTTP method and URL.
+ *
+ * @param method HTTP method name.
+ * @param url Target HTTP URL of the request.
+ */
+ public OutgoingHttpRequest(String method, String url) {
+ checkArgument(!Strings.isNullOrEmpty(method), "method must not be null or empty");
+ checkArgument(!Strings.isNullOrEmpty(url), "url must not be empty");
+ this.method = method;
+ this.url = url;
+ this.content = null;
+ this.headers = ImmutableMap.of();
+ }
+
+ OutgoingHttpRequest(HttpRequest request) {
+ checkNotNull(request, "request must not be null");
+ this.method = request.getRequestMethod();
+ this.url = request.getUrl().toString();
+ this.content = request.getContent();
+ this.headers = ImmutableMap.copyOf(request.getHeaders());
+ }
+
+ /**
+ * Returns the HTTP method of the request.
+ *
+ * @return An HTTP method string (e.g. GET).
+ */
+ public String getMethod() {
+ return method;
+ }
+
+ /**
+ * Returns the URL of the request.
+ *
+ * @return An absolute HTTP URL.
+ */
+ public String getUrl() {
+ return url;
+ }
+
+ /**
+ * Returns any content that was sent with the request.
+ *
+ * @return HTTP content or null.
+ */
+ @Nullable
+ public HttpContent getContent() {
+ return content;
+ }
+
+ /**
+ * Returns the headers set on the request.
+ *
+ * @return An immutable map of headers (possibly empty).
+ */
+ public Map getHeaders() {
+ return headers;
+ }
+}
diff --git a/src/main/java/com/google/firebase/ThreadManager.java b/src/main/java/com/google/firebase/ThreadManager.java
index 4f3c5f29d..8903101f6 100644
--- a/src/main/java/com/google/firebase/ThreadManager.java
+++ b/src/main/java/com/google/firebase/ThreadManager.java
@@ -57,7 +57,7 @@ final void releaseFirebaseExecutors(
* {@link #getThreadFactory()} method.
*
* @param app A {@link FirebaseApp} instance.
- * @return A non-null {@link ExecutorService} instance.
+ * @return A non-null ExecutorService instance.
*/
@NonNull
protected abstract ExecutorService getExecutor(@NonNull FirebaseApp app);
diff --git a/src/main/java/com/google/firebase/auth/AbstractFirebaseAuth.java b/src/main/java/com/google/firebase/auth/AbstractFirebaseAuth.java
new file mode 100644
index 000000000..44fe9b7d2
--- /dev/null
+++ b/src/main/java/com/google/firebase/auth/AbstractFirebaseAuth.java
@@ -0,0 +1,1847 @@
+/*
+ * 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 com.google.common.base.Preconditions.checkNotNull;
+import static com.google.firebase.auth.internal.Utils.isEmulatorMode;
+
+import com.google.api.client.json.JsonFactory;
+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.common.base.Suppliers;
+import com.google.firebase.FirebaseApp;
+import com.google.firebase.auth.FirebaseUserManager.EmailLinkType;
+import com.google.firebase.auth.FirebaseUserManager.UserImportRequest;
+import com.google.firebase.auth.ListProviderConfigsPage.DefaultOidcProviderConfigSource;
+import com.google.firebase.auth.ListProviderConfigsPage.DefaultSamlProviderConfigSource;
+import com.google.firebase.auth.ListUsersPage.DefaultUserSource;
+import com.google.firebase.auth.internal.FirebaseTokenFactory;
+import com.google.firebase.internal.CallableOperation;
+import com.google.firebase.internal.NonNull;
+import com.google.firebase.internal.Nullable;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * This is the abstract class for server-side Firebase Authentication actions.
+ */
+public abstract class AbstractFirebaseAuth {
+
+ private final Object lock = new Object();
+ private final AtomicBoolean destroyed = new AtomicBoolean(false);
+
+ private final FirebaseApp firebaseApp;
+ private final Supplier tokenFactory;
+ private final Supplier extends FirebaseTokenVerifier> idTokenVerifier;
+ private final Supplier extends FirebaseTokenVerifier> cookieVerifier;
+ private final Supplier extends FirebaseUserManager> userManager;
+ private final JsonFactory jsonFactory;
+
+ protected AbstractFirebaseAuth(Builder> builder) {
+ this.firebaseApp = checkNotNull(builder.firebaseApp);
+ this.tokenFactory = threadSafeMemoize(builder.tokenFactory);
+ this.idTokenVerifier = threadSafeMemoize(builder.idTokenVerifier);
+ this.cookieVerifier = threadSafeMemoize(builder.cookieVerifier);
+ this.userManager = threadSafeMemoize(builder.userManager);
+ this.jsonFactory = firebaseApp.getOptions().getJsonFactory();
+ }
+
+ /**
+ * Creates a Firebase custom token for the given UID. This token can then be sent back to a client
+ * application to be used with the signInWithCustomToken
+ * authentication API.
+ *
+ * {@link FirebaseApp} must have been initialized with service account credentials to use call
+ * this method.
+ *
+ * @param uid The UID to store in the token. This identifies the user to other Firebase services
+ * (Realtime Database, Firebase Auth, etc.). Should be less than 128 characters.
+ * @return A Firebase custom token string.
+ * @throws IllegalArgumentException If the specified uid is null or empty, or if the app has not
+ * been initialized with service account credentials.
+ * @throws FirebaseAuthException If an error occurs while generating the custom token.
+ */
+ public String createCustomToken(@NonNull String uid) throws FirebaseAuthException {
+ return createCustomToken(uid, null);
+ }
+
+ /**
+ * Creates a Firebase custom token for the given UID, containing the specified additional claims.
+ * This token can then be sent back to a client application to be used with the signInWithCustomToken
+ * authentication API.
+ *
+ *
This method attempts to generate a token using:
+ *
+ *
+ * - the private key of {@link FirebaseApp}'s service account credentials, if provided at
+ * initialization.
+ *
- the IAM
+ * service if a service account email was specified via {@link
+ * com.google.firebase.FirebaseOptions.Builder#setServiceAccountId(String)}.
+ *
- the App
+ * Identity service if the code is deployed in the Google App Engine standard
+ * environment.
+ *
- the local
+ * Metadata server if the code is deployed in a different GCP-managed environment like
+ * Google Compute Engine.
+ *
+ *
+ * This method throws an exception when all the above fail.
+ *
+ * @param uid The UID to store in the token. This identifies the user to other Firebase services
+ * (Realtime Database, Firebase Auth, etc.). Should be less than 128 characters.
+ * @param developerClaims Additional claims to be stored in the token (and made available to
+ * security rules in Database, Storage, etc.). These must be able to be serialized to JSON
+ * (e.g. contain only Maps, Arrays, Strings, Booleans, Numbers, etc.)
+ * @return A Firebase custom token string.
+ * @throws IllegalArgumentException If the specified uid is null or empty.
+ * @throws IllegalStateException If the SDK fails to discover a viable approach for signing
+ * tokens.
+ * @throws FirebaseAuthException If an error occurs while generating the custom token.
+ */
+ public String createCustomToken(
+ @NonNull String uid, @Nullable Map developerClaims)
+ throws FirebaseAuthException {
+ return createCustomTokenOp(uid, developerClaims).call();
+ }
+
+ /**
+ * Similar to {@link #createCustomToken(String)} but performs the operation asynchronously.
+ *
+ * @param uid The UID to store in the token. This identifies the user to other Firebase services
+ * (Realtime Database, Firebase Auth, etc.). Should be less than 128 characters.
+ * @return An {@code ApiFuture} which will complete successfully with the created Firebase custom
+ * token, or unsuccessfully with the failure Exception.
+ * @throws IllegalArgumentException If the specified uid is null or empty, or if the app has not
+ * been initialized with service account credentials.
+ */
+ public ApiFuture createCustomTokenAsync(@NonNull String uid) {
+ return createCustomTokenAsync(uid, null);
+ }
+
+ /**
+ * Similar to {@link #createCustomToken(String, Map)} but performs the operation asynchronously.
+ *
+ * @param uid The UID to store in the token. This identifies the user to other Firebase services
+ * (Realtime Database, Storage, etc.). Should be less than 128 characters.
+ * @param developerClaims Additional claims to be stored in the token (and made available to
+ * security rules in Database, Storage, etc.). These must be able to be serialized to JSON
+ * (e.g. contain only Maps, Arrays, Strings, Booleans, Numbers, etc.)
+ * @return An {@code ApiFuture} which will complete successfully with the created Firebase custom
+ * token, or unsuccessfully with the failure Exception.
+ * @throws IllegalArgumentException If the specified uid is null or empty, or if the app has not
+ * been initialized with service account credentials.
+ */
+ public ApiFuture createCustomTokenAsync(
+ @NonNull String uid, @Nullable Map developerClaims) {
+ return createCustomTokenOp(uid, developerClaims).callAsync(firebaseApp);
+ }
+
+ private CallableOperation createCustomTokenOp(
+ final String uid, final Map developerClaims) {
+ checkArgument(!Strings.isNullOrEmpty(uid), "uid must not be null or empty");
+ final FirebaseTokenFactory tokenFactory = this.tokenFactory.get();
+ return new CallableOperation() {
+ @Override
+ public String execute() throws FirebaseAuthException {
+ return tokenFactory.createSignedCustomAuthTokenForUser(uid, developerClaims);
+ }
+ };
+ }
+
+ /**
+ * Creates a new Firebase session cookie from the given ID token and options. The returned JWT can
+ * be set as a server-side session cookie with a custom cookie policy.
+ *
+ * @param idToken The Firebase ID token to exchange for a session cookie.
+ * @param options Additional options required to create the cookie.
+ * @return A Firebase session cookie string.
+ * @throws IllegalArgumentException If the ID token is null or empty, or if options is null.
+ * @throws FirebaseAuthException If an error occurs while generating the session cookie.
+ */
+ public String createSessionCookie(@NonNull String idToken, @NonNull SessionCookieOptions options)
+ throws FirebaseAuthException {
+ return createSessionCookieOp(idToken, options).call();
+ }
+
+ /**
+ * Similar to {@link #createSessionCookie(String, SessionCookieOptions)} but performs the
+ * operation asynchronously.
+ *
+ * @param idToken The Firebase ID token to exchange for a session cookie.
+ * @param options Additional options required to create the cookie.
+ * @return An {@code ApiFuture} which will complete successfully with a session cookie string. If
+ * an error occurs while generating the cookie or if the specified ID token is invalid, the
+ * future throws a {@link FirebaseAuthException}.
+ * @throws IllegalArgumentException If the ID token is null or empty, or if options is null.
+ */
+ public ApiFuture createSessionCookieAsync(
+ @NonNull String idToken, @NonNull SessionCookieOptions options) {
+ return createSessionCookieOp(idToken, options).callAsync(firebaseApp);
+ }
+
+ private CallableOperation createSessionCookieOp(
+ final String idToken, final SessionCookieOptions options) {
+ checkArgument(!Strings.isNullOrEmpty(idToken), "idToken must not be null or empty");
+ checkNotNull(options, "options must not be null");
+ final FirebaseUserManager userManager = getUserManager();
+ return new CallableOperation() {
+ @Override
+ protected String execute() throws FirebaseAuthException {
+ return userManager.createSessionCookie(idToken, options);
+ }
+ };
+ }
+
+ /**
+ * Parses and verifies a Firebase ID Token.
+ *
+ * A Firebase application can identify itself to a trusted backend server by sending its
+ * Firebase ID Token (accessible via the {@code getToken} API in the Firebase Authentication
+ * client) with its requests. The backend server can then use the {@code verifyIdToken()} method
+ * to verify that the token is valid. This method ensures that the token is correctly signed, has
+ * not expired, and it was issued to the Firebase project associated with this {@link
+ * FirebaseAuth} instance.
+ *
+ *
This method does not check whether a token has been revoked. Use {@link
+ * #verifyIdToken(String, boolean)} to perform an additional revocation check.
+ *
+ * @param idToken A Firebase ID token string to parse and verify.
+ * @return A {@link FirebaseToken} representing the verified and decoded token.
+ * @throws IllegalArgumentException If the token is null, empty, or if the {@link FirebaseApp}
+ * instance does not have a project ID associated with it.
+ * @throws FirebaseAuthException If an error occurs while parsing or validating the token.
+ */
+ public FirebaseToken verifyIdToken(@NonNull String idToken) throws FirebaseAuthException {
+ return verifyIdToken(idToken, false);
+ }
+
+ /**
+ * Parses and verifies a Firebase ID Token.
+ *
+ *
A Firebase application can identify itself to a trusted backend server by sending its
+ * Firebase ID Token (accessible via the {@code getToken} API in the Firebase Authentication
+ * client) with its requests. The backend server can then use the {@code verifyIdToken()} method
+ * to verify that the token is valid. This method ensures that the token is correctly signed, has
+ * not expired, and it was issued to the Firebase project associated with this {@link
+ * FirebaseAuth} instance.
+ *
+ *
If {@code checkRevoked} is set to true, this method performs an additional check to see if
+ * the ID token has been revoked since it was issues. This requires making an additional remote
+ * API call.
+ *
+ * @param idToken A Firebase ID token string to parse and verify.
+ * @param checkRevoked A boolean denoting whether to check if the tokens were revoked or if
+ * the user is disabled.
+ * @return A {@link FirebaseToken} representing the verified and decoded token.
+ * @throws IllegalArgumentException If the token is null, empty, or if the {@link FirebaseApp}
+ * instance does not have a project ID associated with it.
+ * @throws FirebaseAuthException If an error occurs while parsing or validating the token, or if
+ * the user is disabled.
+ */
+ public FirebaseToken verifyIdToken(@NonNull String idToken, boolean checkRevoked)
+ throws FirebaseAuthException {
+ return verifyIdTokenOp(idToken, checkRevoked).call();
+ }
+
+ /**
+ * Similar to {@link #verifyIdToken(String)} but performs the operation asynchronously.
+ *
+ * @param idToken A Firebase ID Token to verify and parse.
+ * @return An {@code ApiFuture} which will complete successfully with the parsed token, or
+ * unsuccessfully with a {@link FirebaseAuthException}.
+ * @throws IllegalArgumentException If the token is null, empty, or if the {@link FirebaseApp}
+ * instance does not have a project ID associated with it.
+ */
+ public ApiFuture verifyIdTokenAsync(@NonNull String idToken) {
+ return verifyIdTokenAsync(idToken, false);
+ }
+
+ /**
+ * Similar to {@link #verifyIdToken(String, boolean)} but performs the operation asynchronously.
+ *
+ * @param idToken A Firebase ID Token to verify and parse.
+ * @param checkRevoked A boolean denoting whether to check if the tokens were revoked.
+ * @return An {@code ApiFuture} which will complete successfully with the parsed token, or
+ * unsuccessfully with a {@link FirebaseAuthException}.
+ * @throws IllegalArgumentException If the token is null, empty, or if the {@link FirebaseApp}
+ * instance does not have a project ID associated with it.
+ */
+ public ApiFuture
+ verifyIdTokenAsync(@NonNull String idToken, boolean checkRevoked) {
+ return verifyIdTokenOp(idToken, checkRevoked).callAsync(firebaseApp);
+ }
+
+ private CallableOperation verifyIdTokenOp(
+ final String idToken, final boolean checkRevoked) {
+ checkArgument(!Strings.isNullOrEmpty(idToken), "ID token must not be null or empty");
+ final FirebaseTokenVerifier verifier = getIdTokenVerifier(checkRevoked);
+ return new CallableOperation() {
+ @Override
+ protected FirebaseToken execute() throws FirebaseAuthException {
+ return verifier.verifyToken(idToken);
+ }
+ };
+ }
+
+ @VisibleForTesting
+ FirebaseTokenVerifier getIdTokenVerifier(boolean checkRevoked) {
+ FirebaseTokenVerifier verifier = idTokenVerifier.get();
+ if (checkRevoked || isEmulatorMode()) {
+ FirebaseUserManager userManager = getUserManager();
+ verifier = RevocationCheckDecorator.decorateIdTokenVerifier(verifier, userManager);
+ }
+ return verifier;
+ }
+
+ /**
+ * Parses and verifies a Firebase session cookie.
+ *
+ * If verified successfully, returns a parsed version of the cookie from which the UID and the
+ * other claims can be read. If the cookie is invalid, throws a {@link FirebaseAuthException}.
+ *
+ *
This method does not check whether the cookie has been revoked. See {@link
+ * #verifySessionCookie(String, boolean)}.
+ *
+ * @param cookie A Firebase session cookie string to verify and parse.
+ * @return A {@link FirebaseToken} representing the verified and decoded cookie.
+ */
+ public FirebaseToken verifySessionCookie(String cookie) throws FirebaseAuthException {
+ return verifySessionCookie(cookie, false);
+ }
+
+ /**
+ * Parses and verifies a Firebase session cookie.
+ *
+ *
If {@code checkRevoked} is true, additionally verifies that the cookie has not been revoked.
+ *
+ *
If verified successfully, returns a parsed version of the cookie from which the UID and the
+ * other claims can be read. If the cookie is invalid or has been revoked while {@code
+ * checkRevoked} is true, throws a {@link FirebaseAuthException}.
+ *
+ * @param cookie A Firebase session cookie string to verify and parse.
+ * @param checkRevoked A boolean indicating whether to check if the cookie was explicitly revoked
+ * or if the user is disabled.
+ * @return A {@link FirebaseToken} representing the verified and decoded cookie.
+ * @throws FirebaseAuthException If an error occurs while parsing or validating the token, or if
+ * the user is disabled.
+ */
+ public FirebaseToken verifySessionCookie(String cookie, boolean checkRevoked)
+ throws FirebaseAuthException {
+ return verifySessionCookieOp(cookie, checkRevoked).call();
+ }
+
+ /**
+ * Similar to {@link #verifySessionCookie(String)} but performs the operation asynchronously.
+ *
+ * @param cookie A Firebase session cookie string to verify and parse.
+ * @return An {@code ApiFuture} which will complete successfully with the parsed cookie, or
+ * unsuccessfully with the failure Exception.
+ */
+ public ApiFuture verifySessionCookieAsync(String cookie) {
+ return verifySessionCookieAsync(cookie, false);
+ }
+
+ /**
+ * Similar to {@link #verifySessionCookie(String, boolean)} but performs the operation
+ * asynchronously.
+ *
+ * @param cookie A Firebase session cookie string to verify and parse.
+ * @param checkRevoked A boolean indicating whether to check if the cookie was explicitly revoked.
+ * @return An {@code ApiFuture} which will complete successfully with the parsed cookie, or
+ * unsuccessfully with the failure Exception.
+ */
+ public ApiFuture verifySessionCookieAsync(String cookie, boolean checkRevoked) {
+ return verifySessionCookieOp(cookie, checkRevoked).callAsync(firebaseApp);
+ }
+
+ private CallableOperation verifySessionCookieOp(
+ final String cookie, final boolean checkRevoked) {
+ checkArgument(!Strings.isNullOrEmpty(cookie), "Session cookie must not be null or empty");
+ final FirebaseTokenVerifier sessionCookieVerifier = getSessionCookieVerifier(checkRevoked);
+ return new CallableOperation() {
+ @Override
+ public FirebaseToken execute() throws FirebaseAuthException {
+ return sessionCookieVerifier.verifyToken(cookie);
+ }
+ };
+ }
+
+ @VisibleForTesting
+ FirebaseTokenVerifier getSessionCookieVerifier(boolean checkRevoked) {
+ FirebaseTokenVerifier verifier = cookieVerifier.get();
+ if (checkRevoked || isEmulatorMode()) {
+ FirebaseUserManager userManager = getUserManager();
+ verifier = RevocationCheckDecorator.decorateSessionCookieVerifier(verifier, userManager);
+ }
+ return verifier;
+ }
+
+ /**
+ * Revokes all refresh tokens for the specified user.
+ *
+ * Updates the user's tokensValidAfterTimestamp to the current UTC time expressed in
+ * milliseconds since the epoch and truncated to 1 second accuracy. It is important that the
+ * server on which this is called has its clock set correctly and synchronized.
+ *
+ *
While this will revoke all sessions for a specified user and disable any new ID tokens for
+ * existing sessions from getting minted, existing ID tokens may remain active until their natural
+ * expiration (one hour). To verify that ID tokens are revoked, use {@link
+ * #verifyIdTokenAsync(String, boolean)}.
+ *
+ * @param uid The user id for which tokens are revoked.
+ * @throws IllegalArgumentException If the user ID is null or empty.
+ * @throws FirebaseAuthException If an error occurs while revoking tokens.
+ */
+ public void revokeRefreshTokens(@NonNull String uid) throws FirebaseAuthException {
+ revokeRefreshTokensOp(uid).call();
+ }
+
+ /**
+ * Similar to {@link #revokeRefreshTokens(String)} but performs the operation asynchronously.
+ *
+ * @param uid The user id for which tokens are revoked.
+ * @return An {@code ApiFuture} which will complete successfully or fail with a {@link
+ * FirebaseAuthException} in the event of an error.
+ * @throws IllegalArgumentException If the user ID is null or empty.
+ */
+ public ApiFuture revokeRefreshTokensAsync(@NonNull String uid) {
+ return revokeRefreshTokensOp(uid).callAsync(firebaseApp);
+ }
+
+ private CallableOperation revokeRefreshTokensOp(final String uid) {
+ checkArgument(!Strings.isNullOrEmpty(uid), "uid must not be null or empty");
+ final FirebaseUserManager userManager = getUserManager();
+ return new CallableOperation() {
+ @Override
+ protected Void execute() throws FirebaseAuthException {
+ int currentTimeSeconds = (int) (System.currentTimeMillis() / 1000);
+ UserRecord.UpdateRequest request =
+ new UserRecord.UpdateRequest(uid).setValidSince(currentTimeSeconds);
+ userManager.updateUser(request, jsonFactory);
+ return null;
+ }
+ };
+ }
+
+ /**
+ * Gets the user data corresponding to the specified user ID.
+ *
+ * @param uid A user ID string.
+ * @return A {@link UserRecord} instance.
+ * @throws IllegalArgumentException If the user ID string is null or empty.
+ * @throws FirebaseAuthException If an error occurs while retrieving user data.
+ */
+ public UserRecord getUser(@NonNull String uid) throws FirebaseAuthException {
+ return getUserOp(uid).call();
+ }
+
+ /**
+ * Similar to {@link #getUser(String)} but performs the operation asynchronously.
+ *
+ * @param uid A user ID string.
+ * @return An {@code ApiFuture} which will complete successfully with a {@link UserRecord}
+ * instance. If an error occurs while retrieving user data or if the specified user ID does
+ * not exist, the future throws a {@link FirebaseAuthException}.
+ * @throws IllegalArgumentException If the user ID string is null or empty.
+ */
+ public ApiFuture getUserAsync(@NonNull String uid) {
+ return getUserOp(uid).callAsync(firebaseApp);
+ }
+
+ private CallableOperation getUserOp(final String uid) {
+ checkArgument(!Strings.isNullOrEmpty(uid), "uid must not be null or empty");
+ final FirebaseUserManager userManager = getUserManager();
+ return new CallableOperation() {
+ @Override
+ protected UserRecord execute() throws FirebaseAuthException {
+ return userManager.getUserById(uid);
+ }
+ };
+ }
+
+ /**
+ * Gets the user data corresponding to the specified user email.
+ *
+ * @param email A user email address string.
+ * @return A {@link UserRecord} instance.
+ * @throws IllegalArgumentException If the email is null or empty.
+ * @throws FirebaseAuthException If an error occurs while retrieving user data.
+ */
+ public UserRecord getUserByEmail(@NonNull String email) throws FirebaseAuthException {
+ return getUserByEmailOp(email).call();
+ }
+
+ /**
+ * Similar to {@link #getUserByEmail(String)} but performs the operation asynchronously.
+ *
+ * @param email A user email address string.
+ * @return An {@code ApiFuture} which will complete successfully with a {@link UserRecord}
+ * instance. If an error occurs while retrieving user data or if the email address does not
+ * correspond to a user, the future throws a {@link FirebaseAuthException}.
+ * @throws IllegalArgumentException If the email is null or empty.
+ */
+ public ApiFuture getUserByEmailAsync(@NonNull String email) {
+ return getUserByEmailOp(email).callAsync(firebaseApp);
+ }
+
+ private CallableOperation getUserByEmailOp(
+ final String email) {
+ checkArgument(!Strings.isNullOrEmpty(email), "email must not be null or empty");
+ final FirebaseUserManager userManager = getUserManager();
+ return new CallableOperation() {
+ @Override
+ protected UserRecord execute() throws FirebaseAuthException {
+ return userManager.getUserByEmail(email);
+ }
+ };
+ }
+
+ /**
+ * Gets the user data corresponding to the specified user phone number.
+ *
+ * @param phoneNumber A user phone number string.
+ * @return A a {@link UserRecord} instance.
+ * @throws IllegalArgumentException If the phone number is null or empty.
+ * @throws FirebaseAuthException If an error occurs while retrieving user data.
+ */
+ public UserRecord getUserByPhoneNumber(@NonNull String phoneNumber) throws FirebaseAuthException {
+ return getUserByPhoneNumberOp(phoneNumber).call();
+ }
+
+ /**
+ * Gets the user data corresponding to the specified user phone number.
+ *
+ * @param phoneNumber A user phone number string.
+ * @return An {@code ApiFuture} which will complete successfully with a {@link UserRecord}
+ * instance. If an error occurs while retrieving user data or if the phone number does not
+ * correspond to a user, the future throws a {@link FirebaseAuthException}.
+ * @throws IllegalArgumentException If the phone number is null or empty.
+ */
+ public ApiFuture getUserByPhoneNumberAsync(@NonNull String phoneNumber) {
+ return getUserByPhoneNumberOp(phoneNumber).callAsync(firebaseApp);
+ }
+
+ private CallableOperation getUserByPhoneNumberOp(
+ final String phoneNumber) {
+ checkArgument(!Strings.isNullOrEmpty(phoneNumber), "phone number must not be null or empty");
+ final FirebaseUserManager userManager = getUserManager();
+ return new CallableOperation() {
+ @Override
+ protected UserRecord execute() throws FirebaseAuthException {
+ return userManager.getUserByPhoneNumber(phoneNumber);
+ }
+ };
+ }
+
+ /**
+ * Gets the user data for the user corresponding to a given provider ID.
+ *
+ * @param providerId Identifier for the given federated provider: for example,
+ * "google.com" for the Google provider.
+ * @param uid The user identifier with the given provider.
+ * @return A {@link UserRecord} instance.
+ * @throws IllegalArgumentException If the uid is null or empty, or if
+ * the providerId is null, empty, or does not belong to a federated provider.
+ * @throws FirebaseAuthException If an error occurs while retrieving user data.
+ */
+ public UserRecord getUserByProviderUid(
+ @NonNull String providerId, @NonNull String uid) throws FirebaseAuthException {
+ return getUserByProviderUidOp(providerId, uid).call();
+ }
+
+ /**
+ * Gets the user data for the user corresponding to a given provider ID.
+ *
+ * @param providerId Identifer for the given federated provider: for example,
+ * "google.com" for the Google provider.
+ * @param uid The user identifier with the given provider.
+ * @return An {@code ApiFuture} which will complete successfully with a {@link UserRecord}
+ * instance. If an error occurs while retrieving user data or if the provider ID and uid
+ * do not correspond to a user, the future throws a {@link FirebaseAuthException}.
+ * @throws IllegalArgumentException If the uid is null or empty, or if
+ * the provider ID is null, empty, or does not belong to a federated provider.
+ */
+ public ApiFuture getUserByProviderUidAsync(
+ @NonNull String providerId, @NonNull String uid) {
+ return getUserByProviderUidOp(providerId, uid).callAsync(firebaseApp);
+ }
+
+ private CallableOperation getUserByProviderUidOp(
+ final String providerId, final String uid) {
+ checkArgument(!Strings.isNullOrEmpty(providerId), "providerId must not be null or empty");
+ checkArgument(!Strings.isNullOrEmpty(uid), "uid must not be null or empty");
+
+ // Although we don't really advertise it, we want to also handle
+ // non-federated idps with this call. So if we detect one of them, we'll
+ // reroute this request appropriately.
+ if ("phone".equals(providerId)) {
+ return this.getUserByPhoneNumberOp(uid);
+ }
+ if ("email".equals(providerId)) {
+ return this.getUserByEmailOp(uid);
+ }
+
+ checkArgument(!providerId.equals("password")
+ && !providerId.equals("anonymous"), "providerId must belong to a federated provider");
+ final FirebaseUserManager userManager = getUserManager();
+ return new CallableOperation() {
+ @Override
+ protected UserRecord execute() throws FirebaseAuthException {
+ return userManager.getUserByProviderUid(providerId, uid);
+ }
+ };
+ }
+
+ /**
+ * Gets a page of users starting from the specified {@code pageToken}. Page size is limited to
+ * 1000 users.
+ *
+ * @param pageToken A non-empty page token string, or null to retrieve the first page of users.
+ * @return A {@link ListUsersPage} instance.
+ * @throws IllegalArgumentException If the specified page token is empty.
+ * @throws FirebaseAuthException If an error occurs while retrieving user data.
+ */
+ public ListUsersPage listUsers(@Nullable String pageToken) throws FirebaseAuthException {
+ return listUsers(pageToken, FirebaseUserManager.MAX_LIST_USERS_RESULTS);
+ }
+
+ /**
+ * Gets a page of users starting from the specified {@code pageToken}.
+ *
+ * @param pageToken A non-empty page token string, or null to retrieve the first page of users.
+ * @param maxResults Maximum number of users to include in the returned page. This may not exceed
+ * 1000.
+ * @return A {@link ListUsersPage} instance.
+ * @throws IllegalArgumentException If the specified page token is empty, or max results value is
+ * invalid.
+ * @throws FirebaseAuthException If an error occurs while retrieving user data.
+ */
+ public ListUsersPage listUsers(@Nullable String pageToken, int maxResults)
+ throws FirebaseAuthException {
+ return listUsersOp(pageToken, maxResults).call();
+ }
+
+ /**
+ * Similar to {@link #listUsers(String)} but performs the operation asynchronously.
+ *
+ * @param pageToken A non-empty page token string, or null to retrieve the first page of users.
+ * @return An {@code ApiFuture} which will complete successfully with a {@link ListUsersPage}
+ * instance. If an error occurs while retrieving user data, the future throws an exception.
+ * @throws IllegalArgumentException If the specified page token is empty.
+ */
+ public ApiFuture listUsersAsync(@Nullable String pageToken) {
+ return listUsersAsync(pageToken, FirebaseUserManager.MAX_LIST_USERS_RESULTS);
+ }
+
+ /**
+ * Similar to {@link #listUsers(String, int)} but performs the operation asynchronously.
+ *
+ * @param pageToken A non-empty page token string, or null to retrieve the first page of users.
+ * @param maxResults Maximum number of users to include in the returned page. This may not exceed
+ * 1000.
+ * @return An {@code ApiFuture} which will complete successfully with a {@link ListUsersPage}
+ * instance. If an error occurs while retrieving user data, the future throws an exception.
+ * @throws IllegalArgumentException If the specified page token is empty, or max results value is
+ * invalid.
+ */
+ public ApiFuture listUsersAsync(@Nullable String pageToken, int maxResults) {
+ return listUsersOp(pageToken, maxResults).callAsync(firebaseApp);
+ }
+
+ private CallableOperation listUsersOp(
+ @Nullable final String pageToken, final int maxResults) {
+ final FirebaseUserManager userManager = getUserManager();
+ final DefaultUserSource source = new DefaultUserSource(userManager, jsonFactory);
+ final ListUsersPage.Factory factory = new ListUsersPage.Factory(source, maxResults, pageToken);
+ return new CallableOperation() {
+ @Override
+ protected ListUsersPage execute() throws FirebaseAuthException {
+ return factory.create();
+ }
+ };
+ }
+
+ /**
+ * Creates a new user account with the attributes contained in the specified {@link
+ * UserRecord.CreateRequest}.
+ *
+ * @param request A non-null {@link UserRecord.CreateRequest} instance.
+ * @return A {@link UserRecord} instance corresponding to the newly created account.
+ * @throws NullPointerException if the provided request is null.
+ * @throws FirebaseAuthException if an error occurs while creating the user account.
+ */
+ public UserRecord createUser(@NonNull UserRecord.CreateRequest request)
+ throws FirebaseAuthException {
+ return createUserOp(request).call();
+ }
+
+ /**
+ * Similar to {@link #createUser} but performs the operation asynchronously.
+ *
+ * @param request A non-null {@link UserRecord.CreateRequest} instance.
+ * @return An {@code ApiFuture} which will complete successfully with a {@link UserRecord}
+ * instance corresponding to the newly created account. If an error occurs while creating the
+ * user account, the future throws a {@link FirebaseAuthException}.
+ * @throws NullPointerException if the provided request is null.
+ */
+ public ApiFuture createUserAsync(@NonNull UserRecord.CreateRequest request) {
+ return createUserOp(request).callAsync(firebaseApp);
+ }
+
+ private CallableOperation createUserOp(
+ final UserRecord.CreateRequest request) {
+ checkNotNull(request, "create request must not be null");
+ final FirebaseUserManager userManager = getUserManager();
+ return new CallableOperation() {
+ @Override
+ protected UserRecord execute() throws FirebaseAuthException {
+ String uid = userManager.createUser(request);
+ return userManager.getUserById(uid);
+ }
+ };
+ }
+
+ /**
+ * Updates an existing user account with the attributes contained in the specified {@link
+ * UserRecord.UpdateRequest}.
+ *
+ * @param request A non-null {@link UserRecord.UpdateRequest} instance.
+ * @return A {@link UserRecord} instance corresponding to the updated user account.
+ * @throws NullPointerException if the provided update request is null.
+ * @throws FirebaseAuthException if an error occurs while updating the user account.
+ */
+ public UserRecord updateUser(@NonNull UserRecord.UpdateRequest request)
+ throws FirebaseAuthException {
+ return updateUserOp(request).call();
+ }
+
+ /**
+ * Similar to {@link #updateUser} but performs the operation asynchronously.
+ *
+ * @param request A non-null {@link UserRecord.UpdateRequest} instance.
+ * @return An {@code ApiFuture} which will complete successfully with a {@link UserRecord}
+ * instance corresponding to the updated user account. If an error occurs while updating the
+ * user account, the future throws a {@link FirebaseAuthException}.
+ */
+ public ApiFuture updateUserAsync(@NonNull UserRecord.UpdateRequest request) {
+ return updateUserOp(request).callAsync(firebaseApp);
+ }
+
+ private CallableOperation updateUserOp(
+ final UserRecord.UpdateRequest request) {
+ checkNotNull(request, "update request must not be null");
+ final FirebaseUserManager userManager = getUserManager();
+ return new CallableOperation() {
+ @Override
+ protected UserRecord execute() throws FirebaseAuthException {
+ userManager.updateUser(request, jsonFactory);
+ return userManager.getUserById(request.getUid());
+ }
+ };
+ }
+
+ /**
+ * Sets the specified custom claims on an existing user account. A null claims value removes any
+ * claims currently set on the user account. The claims should serialize into a valid JSON string.
+ * The serialized claims must not be larger than 1000 characters.
+ *
+ * @param uid A user ID string.
+ * @param claims A map of custom claims or null.
+ * @throws FirebaseAuthException If an error occurs while updating custom claims.
+ * @throws IllegalArgumentException If the user ID string is null or empty, or the claims payload
+ * is invalid or too large.
+ */
+ public void setCustomUserClaims(@NonNull String uid, @Nullable Map claims)
+ throws FirebaseAuthException {
+ setCustomUserClaimsOp(uid, claims).call();
+ }
+
+ /**
+ * @deprecated Use {@link #setCustomUserClaims(String, Map)} instead.
+ */
+ public void setCustomClaims(@NonNull String uid, @Nullable Map claims)
+ throws FirebaseAuthException {
+ setCustomUserClaims(uid, claims);
+ }
+
+ /**
+ * Similar to {@link #setCustomUserClaims(String, Map)} but performs the operation asynchronously.
+ *
+ * @param uid A user ID string.
+ * @param claims A map of custom claims or null.
+ * @return An {@code ApiFuture} which will complete successfully when the user account has been
+ * updated. If an error occurs while deleting the user account, the future throws a {@link
+ * FirebaseAuthException}.
+ * @throws IllegalArgumentException If the user ID string is null or empty.
+ */
+ public ApiFuture setCustomUserClaimsAsync(
+ @NonNull String uid, @Nullable Map claims) {
+ return setCustomUserClaimsOp(uid, claims).callAsync(firebaseApp);
+ }
+
+ private CallableOperation setCustomUserClaimsOp(
+ final String uid, final Map claims) {
+ checkArgument(!Strings.isNullOrEmpty(uid), "uid must not be null or empty");
+ final FirebaseUserManager userManager = getUserManager();
+ return new CallableOperation() {
+ @Override
+ protected Void execute() throws FirebaseAuthException {
+ final UserRecord.UpdateRequest request =
+ new UserRecord.UpdateRequest(uid).setCustomClaims(claims);
+ userManager.updateUser(request, jsonFactory);
+ return null;
+ }
+ };
+ }
+
+ /**
+ * Deletes the user identified by the specified user ID.
+ *
+ * @param uid A user ID string.
+ * @throws IllegalArgumentException If the user ID string is null or empty.
+ * @throws FirebaseAuthException If an error occurs while deleting the user.
+ */
+ public void deleteUser(@NonNull String uid) throws FirebaseAuthException {
+ deleteUserOp(uid).call();
+ }
+
+ /**
+ * Similar to {@link #deleteUser(String)} but performs the operation asynchronously.
+ *
+ * @param uid A user ID string.
+ * @return An {@code ApiFuture} which will complete successfully when the specified user account
+ * has been deleted. If an error occurs while deleting the user account, the future throws a
+ * {@link FirebaseAuthException}.
+ * @throws IllegalArgumentException If the user ID string is null or empty.
+ */
+ public ApiFuture deleteUserAsync(String uid) {
+ return deleteUserOp(uid).callAsync(firebaseApp);
+ }
+
+ private CallableOperation deleteUserOp(final String uid) {
+ checkArgument(!Strings.isNullOrEmpty(uid), "uid must not be null or empty");
+ final FirebaseUserManager userManager = getUserManager();
+ return new CallableOperation() {
+ @Override
+ protected Void execute() throws FirebaseAuthException {
+ userManager.deleteUser(uid);
+ return null;
+ }
+ };
+ }
+
+ /**
+ * Imports the provided list of users into Firebase Auth. At most 1000 users can be imported at a
+ * time. This operation is optimized for bulk imports and will ignore checks on identifier
+ * uniqueness which could result in duplications.
+ *
+ * {@link UserImportOptions} is required to import users with passwords. See {@link
+ * #importUsers(List, UserImportOptions)}.
+ *
+ * @param users A non-empty list of users to be imported. Length must not exceed 1000.
+ * @return A {@link UserImportResult} instance.
+ * @throws IllegalArgumentException If the users list is null, empty or has more than 1000
+ * elements. Or if at least one user specifies a password.
+ * @throws FirebaseAuthException If an error occurs while importing users.
+ */
+ public UserImportResult importUsers(List users) throws FirebaseAuthException {
+ return importUsers(users, null);
+ }
+
+ /**
+ * Imports the provided list of users into Firebase Auth. At most 1000 users can be imported at a
+ * time. This operation is optimized for bulk imports and will ignore checks on identifier
+ * uniqueness which could result in duplications.
+ *
+ * @param users A non-empty list of users to be imported. Length must not exceed 1000.
+ * @param options a {@link UserImportOptions} instance or null. Required when importing users with
+ * passwords.
+ * @return A {@link UserImportResult} instance.
+ * @throws IllegalArgumentException If the users list is null, empty or has more than 1000
+ * elements. Or if at least one user specifies a password, and options is null.
+ * @throws FirebaseAuthException If an error occurs while importing users.
+ */
+ public UserImportResult importUsers(
+ List users, @Nullable UserImportOptions options)
+ throws FirebaseAuthException {
+ return importUsersOp(users, options).call();
+ }
+
+ /**
+ * Similar to {@link #importUsers(List)} but performs the operation asynchronously.
+ *
+ * @param users A non-empty list of users to be imported. Length must not exceed 1000.
+ * @return An {@code ApiFuture} which will complete successfully when the user accounts are
+ * imported. If an error occurs while importing the users, the future throws a {@link
+ * FirebaseAuthException}.
+ * @throws IllegalArgumentException If the users list is null, empty or has more than 1000
+ * elements. Or if at least one user specifies a password.
+ */
+ public ApiFuture importUsersAsync(List users) {
+ return importUsersAsync(users, null);
+ }
+
+ /**
+ * Similar to {@link #importUsers(List, UserImportOptions)} but performs the operation
+ * asynchronously.
+ *
+ * @param users A non-empty list of users to be imported. Length must not exceed 1000.
+ * @param options a {@link UserImportOptions} instance or null. Required when importing users with
+ * passwords.
+ * @return An {@code ApiFuture} which will complete successfully when the user accounts are
+ * imported. If an error occurs while importing the users, the future throws a {@link
+ * FirebaseAuthException}.
+ * @throws IllegalArgumentException If the users list is null, empty or has more than 1000
+ * elements. Or if at least one user specifies a password, and options is null.
+ */
+ public ApiFuture importUsersAsync(
+ List users, @Nullable UserImportOptions options) {
+ return importUsersOp(users, options).callAsync(firebaseApp);
+ }
+
+ private CallableOperation importUsersOp(
+ final List users, final UserImportOptions options) {
+ final UserImportRequest request = new UserImportRequest(users, options, jsonFactory);
+ final FirebaseUserManager userManager = getUserManager();
+ return new CallableOperation() {
+ @Override
+ protected UserImportResult execute() throws FirebaseAuthException {
+ return userManager.importUsers(request);
+ }
+ };
+ }
+
+ /**
+ * Gets the user data corresponding to the specified identifiers.
+ *
+ * There are no ordering guarantees; in particular, the nth entry in the users result list is
+ * not guaranteed to correspond to the nth entry in the input parameters list.
+ *
+ *
A maximum of 100 identifiers may be specified. If more than 100 identifiers are
+ * supplied, this method throws an {@code IllegalArgumentException}.
+ *
+ * @param identifiers The identifiers used to indicate which user records should be returned. Must
+ * have 100 or fewer entries.
+ * @return The corresponding user records.
+ * @throws IllegalArgumentException If any of the identifiers are invalid or if more than 100
+ * identifiers are specified.
+ * @throws NullPointerException If the identifiers parameter is null.
+ * @throws FirebaseAuthException If an error occurs while retrieving user data.
+ */
+ public GetUsersResult getUsers(@NonNull Collection identifiers)
+ throws FirebaseAuthException {
+ return getUsersOp(identifiers).call();
+ }
+
+ /**
+ * Gets the user data corresponding to the specified identifiers.
+ *
+ * There are no ordering guarantees; in particular, the nth entry in the users result list is
+ * not guaranteed to correspond to the nth entry in the input parameters list.
+ *
+ *
A maximum of 100 identifiers may be specified. If more than 100 identifiers are
+ * supplied, this method throws an {@code IllegalArgumentException}.
+ *
+ * @param identifiers The identifiers used to indicate which user records should be returned.
+ * Must have 100 or fewer entries.
+ * @return An {@code ApiFuture} that resolves to the corresponding user records.
+ * @throws IllegalArgumentException If any of the identifiers are invalid or if more than 100
+ * identifiers are specified.
+ * @throws NullPointerException If the identifiers parameter is null.
+ */
+ public ApiFuture getUsersAsync(@NonNull Collection identifiers) {
+ return getUsersOp(identifiers).callAsync(firebaseApp);
+ }
+
+ private CallableOperation getUsersOp(
+ @NonNull final Collection identifiers) {
+ checkNotNull(identifiers, "identifiers must not be null");
+ checkArgument(identifiers.size() <= FirebaseUserManager.MAX_GET_ACCOUNTS_BATCH_SIZE,
+ "identifiers parameter must have <= " + FirebaseUserManager.MAX_GET_ACCOUNTS_BATCH_SIZE
+ + " entries.");
+
+ final FirebaseUserManager userManager = getUserManager();
+ return new CallableOperation() {
+ @Override
+ protected GetUsersResult execute() throws FirebaseAuthException {
+ Set users = userManager.getAccountInfo(identifiers);
+ Set notFound = new HashSet<>();
+ for (UserIdentifier id : identifiers) {
+ if (!isUserFound(id, users)) {
+ notFound.add(id);
+ }
+ }
+ return new GetUsersResult(users, notFound);
+ }
+ };
+ }
+
+ private boolean isUserFound(UserIdentifier id, Collection userRecords) {
+ for (UserRecord userRecord : userRecords) {
+ if (id.matches(userRecord)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Deletes the users specified by the given identifiers.
+ *
+ * Deleting a non-existing user does not generate an error (the method is idempotent).
+ * Non-existing users are considered to be successfully deleted and are therefore included in the
+ * DeleteUsersResult.getSuccessCount() value.
+ *
+ *
A maximum of 1000 identifiers may be supplied. If more than 1000 identifiers are
+ * supplied, this method throws an {@code IllegalArgumentException}.
+ *
+ *
This API has a rate limit of 1 QPS. Exceeding the limit may result in a quota exceeded
+ * error. If you want to delete more than 1000 users, we suggest adding a delay to ensure you
+ * don't exceed this limit.
+ *
+ * @param uids The uids of the users to be deleted. Must have <= 1000 entries.
+ * @return The total number of successful/failed deletions, as well as the array of errors that
+ * correspond to the failed deletions.
+ * @throws IllegalArgumentException If any of the identifiers are invalid or if more than 1000
+ * identifiers are specified.
+ * @throws FirebaseAuthException If an error occurs while deleting users.
+ */
+ public DeleteUsersResult deleteUsers(List uids) throws FirebaseAuthException {
+ return deleteUsersOp(uids).call();
+ }
+
+ /**
+ * Similar to {@link #deleteUsers(List)} but performs the operation asynchronously.
+ *
+ * @param uids The uids of the users to be deleted. Must have <= 1000 entries.
+ * @return An {@code ApiFuture} that resolves to the total number of successful/failed
+ * deletions, as well as the array of errors that correspond to the failed deletions. If an
+ * error occurs while deleting the user account, the future throws a
+ * {@link FirebaseAuthException}.
+ * @throws IllegalArgumentException If any of the identifiers are invalid or if more than 1000
+ * identifiers are specified.
+ */
+ public ApiFuture deleteUsersAsync(List uids) {
+ return deleteUsersOp(uids).callAsync(firebaseApp);
+ }
+
+ private CallableOperation deleteUsersOp(
+ final List uids) {
+ checkNotNull(uids, "uids must not be null");
+ for (String uid : uids) {
+ UserRecord.checkUid(uid);
+ }
+ checkArgument(uids.size() <= FirebaseUserManager.MAX_DELETE_ACCOUNTS_BATCH_SIZE,
+ "uids parameter must have <= " + FirebaseUserManager.MAX_DELETE_ACCOUNTS_BATCH_SIZE
+ + " entries.");
+ final FirebaseUserManager userManager = getUserManager();
+ return new CallableOperation() {
+ @Override
+ protected DeleteUsersResult execute() throws FirebaseAuthException {
+ return userManager.deleteUsers(uids);
+ }
+ };
+ }
+
+ /**
+ * Generates the out-of-band email action link for password reset flows for the specified email
+ * address.
+ *
+ * @param email The email of the user whose password is to be reset.
+ * @return A password reset link.
+ * @throws IllegalArgumentException If the email address is null or empty.
+ * @throws FirebaseAuthException If an error occurs while generating the link.
+ */
+ public String generatePasswordResetLink(@NonNull String email) throws FirebaseAuthException {
+ return generatePasswordResetLink(email, null);
+ }
+
+ /**
+ * Generates the out-of-band email action link for password reset flows for the specified email
+ * address.
+ *
+ * @param email The email of the user whose password is to be reset.
+ * @param settings The action code settings object which defines whether the link is to be handled
+ * by a mobile app and the additional state information to be passed in the deep link.
+ * @return A password reset link.
+ * @throws IllegalArgumentException If the email address is null or empty.
+ * @throws FirebaseAuthException If an error occurs while generating the link.
+ */
+ public String generatePasswordResetLink(
+ @NonNull String email, @Nullable ActionCodeSettings settings) throws FirebaseAuthException {
+ return generateEmailActionLinkOp(EmailLinkType.PASSWORD_RESET, email, settings).call();
+ }
+
+ /**
+ * Similar to {@link #generatePasswordResetLink(String)} but performs the operation
+ * asynchronously.
+ *
+ * @param email The email of the user whose password is to be reset.
+ * @return An {@code ApiFuture} which will complete successfully with the generated email action
+ * link. If an error occurs while generating the link, the future throws a {@link
+ * FirebaseAuthException}.
+ * @throws IllegalArgumentException If the email address is null or empty.
+ */
+ public ApiFuture generatePasswordResetLinkAsync(@NonNull String email) {
+ return generatePasswordResetLinkAsync(email, null);
+ }
+
+ /**
+ * Similar to {@link #generatePasswordResetLink(String, ActionCodeSettings)} but performs the
+ * operation asynchronously.
+ *
+ * @param email The email of the user whose password is to be reset.
+ * @param settings The action code settings object which defines whether the link is to be handled
+ * by a mobile app and the additional state information to be passed in the deep link.
+ * @return An {@code ApiFuture} which will complete successfully with the generated email action
+ * link. If an error occurs while generating the link, the future throws a {@link
+ * FirebaseAuthException}.
+ * @throws IllegalArgumentException If the email address is null or empty.
+ */
+ public ApiFuture generatePasswordResetLinkAsync(
+ @NonNull String email, @Nullable ActionCodeSettings settings) {
+ return generateEmailActionLinkOp(EmailLinkType.PASSWORD_RESET, email, settings)
+ .callAsync(firebaseApp);
+ }
+
+ /**
+ * Generates the out-of-band email action link for email verification flows for the specified
+ * email address.
+ *
+ * @param email The email of the user to be verified.
+ * @return An email verification link.
+ * @throws IllegalArgumentException If the email address is null or empty.
+ * @throws FirebaseAuthException If an error occurs while generating the link.
+ */
+ public String generateEmailVerificationLink(@NonNull String email) throws FirebaseAuthException {
+ return generateEmailVerificationLink(email, null);
+ }
+
+ /**
+ * Generates the out-of-band email action link for email verification flows for the specified
+ * email address, using the action code settings provided.
+ *
+ * @param email The email of the user to be verified.
+ * @return An email verification link.
+ * @throws IllegalArgumentException If the email address is null or empty.
+ * @throws FirebaseAuthException If an error occurs while generating the link.
+ */
+ public String generateEmailVerificationLink(
+ @NonNull String email, @Nullable ActionCodeSettings settings) throws FirebaseAuthException {
+ return generateEmailActionLinkOp(EmailLinkType.VERIFY_EMAIL, email, settings).call();
+ }
+
+ /**
+ * Similar to {@link #generateEmailVerificationLink(String)} but performs the operation
+ * asynchronously.
+ *
+ * @param email The email of the user to be verified.
+ * @return An {@code ApiFuture} which will complete successfully with the generated email action
+ * link. If an error occurs while generating the link, the future throws a {@link
+ * FirebaseAuthException}.
+ * @throws IllegalArgumentException If the email address is null or empty.
+ */
+ public ApiFuture generateEmailVerificationLinkAsync(@NonNull String email) {
+ return generateEmailVerificationLinkAsync(email, null);
+ }
+
+ /**
+ * Similar to {@link #generateEmailVerificationLink(String, ActionCodeSettings)} but performs the
+ * operation asynchronously.
+ *
+ * @param email The email of the user to be verified.
+ * @param settings The action code settings object which defines whether the link is to be handled
+ * by a mobile app and the additional state information to be passed in the deep link.
+ * @return An {@code ApiFuture} which will complete successfully with the generated email action
+ * link. If an error occurs while generating the link, the future throws a {@link
+ * FirebaseAuthException}.
+ * @throws IllegalArgumentException If the email address is null or empty.
+ */
+ public ApiFuture generateEmailVerificationLinkAsync(
+ @NonNull String email, @Nullable ActionCodeSettings settings) {
+ return generateEmailActionLinkOp(EmailLinkType.VERIFY_EMAIL, email, settings)
+ .callAsync(firebaseApp);
+ }
+
+ /**
+ * Generates the out-of-band email action link for email link sign-in flows, using the action code
+ * settings provided.
+ *
+ * @param email The email of the user signing in.
+ * @param settings The action code settings object which defines whether the link is to be handled
+ * by a mobile app and the additional state information to be passed in the deep link.
+ * @return An email verification link.
+ * @throws IllegalArgumentException If the email address is null or empty.
+ * @throws FirebaseAuthException If an error occurs while generating the link.
+ */
+ public String generateSignInWithEmailLink(
+ @NonNull String email, @NonNull ActionCodeSettings settings) throws FirebaseAuthException {
+ return generateEmailActionLinkOp(EmailLinkType.EMAIL_SIGNIN, email, settings).call();
+ }
+
+ /**
+ * Similar to {@link #generateSignInWithEmailLink(String, ActionCodeSettings)} but performs the
+ * operation asynchronously.
+ *
+ * @param email The email of the user signing in.
+ * @param settings The action code settings object which defines whether the link is to be handled
+ * by a mobile app and the additional state information to be passed in the deep link.
+ * @return An {@code ApiFuture} which will complete successfully with the generated email action
+ * link. If an error occurs while generating the link, the future throws a {@link
+ * FirebaseAuthException}.
+ * @throws IllegalArgumentException If the email address is null or empty.
+ * @throws NullPointerException If the settings is null.
+ */
+ public ApiFuture generateSignInWithEmailLinkAsync(
+ String email, @NonNull ActionCodeSettings settings) {
+ return generateEmailActionLinkOp(EmailLinkType.EMAIL_SIGNIN, email, settings)
+ .callAsync(firebaseApp);
+ }
+
+ private CallableOperation generateEmailActionLinkOp(
+ final EmailLinkType type, final String email, final ActionCodeSettings settings) {
+ checkArgument(!Strings.isNullOrEmpty(email), "email must not be null or empty");
+ if (type == EmailLinkType.EMAIL_SIGNIN) {
+ checkNotNull(settings, "ActionCodeSettings must not be null when generating sign-in links");
+ }
+ final FirebaseUserManager userManager = getUserManager();
+ return new CallableOperation() {
+ @Override
+ protected String execute() throws FirebaseAuthException {
+ return userManager.getEmailActionLink(type, email, settings);
+ }
+ };
+ }
+
+ /**
+ * Creates a new OpenID Connect auth provider config with the attributes contained in the
+ * specified {@link OidcProviderConfig.CreateRequest}.
+ *
+ * @param request A non-null {@link OidcProviderConfig.CreateRequest} instance.
+ * @return An {@link OidcProviderConfig} instance corresponding to the newly created provider
+ * config.
+ * @throws NullPointerException if the provided request is null.
+ * @throws IllegalArgumentException If the provider ID string is null or empty, or is not
+ * prefixed with 'oidc.'.
+ * @throws FirebaseAuthException if an error occurs while creating the provider config.
+ */
+ public OidcProviderConfig createOidcProviderConfig(
+ @NonNull OidcProviderConfig.CreateRequest request) throws FirebaseAuthException {
+ return createOidcProviderConfigOp(request).call();
+ }
+
+ /**
+ * Similar to {@link #createOidcProviderConfig} but performs the operation asynchronously.
+ *
+ * @param request A non-null {@link OidcProviderConfig.CreateRequest} instance.
+ * @return An {@code ApiFuture} which will complete successfully with a {@link OidcProviderConfig}
+ * instance corresponding to the newly created provider config. If an error occurs while
+ * creating the provider config, the future throws a {@link FirebaseAuthException}.
+ * @throws NullPointerException if the provided request is null.
+ * @throws IllegalArgumentException If the provider ID string is null or empty, or is not
+ * prefixed with 'oidc.'.
+ */
+ public ApiFuture createOidcProviderConfigAsync(
+ @NonNull OidcProviderConfig.CreateRequest request) {
+ return createOidcProviderConfigOp(request).callAsync(firebaseApp);
+ }
+
+ private CallableOperation
+ createOidcProviderConfigOp(final OidcProviderConfig.CreateRequest request) {
+ checkNotNull(request, "Create request must not be null.");
+ OidcProviderConfig.checkOidcProviderId(request.getProviderId());
+ final FirebaseUserManager userManager = getUserManager();
+ return new CallableOperation() {
+ @Override
+ protected OidcProviderConfig execute() throws FirebaseAuthException {
+ return userManager.createOidcProviderConfig(request);
+ }
+ };
+ }
+
+ /**
+ * Updates an existing OpenID Connect auth provider config with the attributes contained in the
+ * specified {@link OidcProviderConfig.UpdateRequest}.
+ *
+ * @param request A non-null {@link OidcProviderConfig.UpdateRequest} instance.
+ * @return A {@link OidcProviderConfig} instance corresponding to the updated provider config.
+ * @throws NullPointerException if the provided update request is null.
+ * @throws IllegalArgumentException If the provided update request is invalid.
+ * @throws FirebaseAuthException if an error occurs while updating the provider config.
+ */
+ public OidcProviderConfig updateOidcProviderConfig(
+ @NonNull OidcProviderConfig.UpdateRequest request) throws FirebaseAuthException {
+ return updateOidcProviderConfigOp(request).call();
+ }
+
+ /**
+ * Similar to {@link #updateOidcProviderConfig} but performs the operation asynchronously.
+ *
+ * @param request A non-null {@link OidcProviderConfig.UpdateRequest} instance.
+ * @return An {@code ApiFuture} which will complete successfully with a {@link OidcProviderConfig}
+ * instance corresponding to the updated provider config. If an error occurs while updating
+ * the provider config, the future throws a {@link FirebaseAuthException}.
+ * @throws NullPointerException if the provided update request is null.
+ * @throws IllegalArgumentException If the provided update request is invalid.
+ */
+ public ApiFuture updateOidcProviderConfigAsync(
+ @NonNull OidcProviderConfig.UpdateRequest request) {
+ return updateOidcProviderConfigOp(request).callAsync(firebaseApp);
+ }
+
+ private CallableOperation updateOidcProviderConfigOp(
+ final OidcProviderConfig.UpdateRequest request) {
+ checkNotNull(request, "Update request must not be null.");
+ checkArgument(!request.getProperties().isEmpty(),
+ "Update request must have at least one property set.");
+ final FirebaseUserManager userManager = getUserManager();
+ return new CallableOperation() {
+ @Override
+ protected OidcProviderConfig execute() throws FirebaseAuthException {
+ return userManager.updateOidcProviderConfig(request);
+ }
+ };
+ }
+
+ /**
+ * Gets the OpenID Connect auth provider corresponding to the specified provider ID.
+ *
+ * @param providerId A provider ID string.
+ * @return An {@link OidcProviderConfig} instance.
+ * @throws IllegalArgumentException If the provider ID string is null or empty, or is not prefixed
+ * with 'oidc'.
+ * @throws FirebaseAuthException If an error occurs while retrieving the provider config.
+ */
+ public OidcProviderConfig getOidcProviderConfig(@NonNull String providerId)
+ throws FirebaseAuthException {
+ return getOidcProviderConfigOp(providerId).call();
+ }
+
+ /**
+ * Similar to {@link #getOidcProviderConfig(String)} but performs the operation asynchronously.
+ * Page size is limited to 100 provider configs.
+ *
+ * @param providerId A provider ID string.
+ * @return An {@code ApiFuture} which will complete successfully with an
+ * {@link OidcProviderConfig} instance. If an error occurs while retrieving the provider
+ * config or if the specified provider ID does not exist, the future throws a
+ * {@link FirebaseAuthException}.
+ * @throws IllegalArgumentException If the provider ID string is null or empty, or is not
+ * prefixed with 'oidc.'.
+ */
+ public ApiFuture getOidcProviderConfigAsync(@NonNull String providerId) {
+ return getOidcProviderConfigOp(providerId).callAsync(firebaseApp);
+ }
+
+ private CallableOperation
+ getOidcProviderConfigOp(final String providerId) {
+ OidcProviderConfig.checkOidcProviderId(providerId);
+ final FirebaseUserManager userManager = getUserManager();
+ return new CallableOperation() {
+ @Override
+ protected OidcProviderConfig execute() throws FirebaseAuthException {
+ return userManager.getOidcProviderConfig(providerId);
+ }
+ };
+ }
+
+ /**
+ * Gets a page of OpenID Connect auth provider configs starting from the specified
+ * {@code pageToken}. Page size is limited to 100 provider configs.
+ *
+ * @param pageToken A non-empty page token string, or null to retrieve the first page of provider
+ * configs.
+ * @return A {@link ListProviderConfigsPage} instance.
+ * @throws IllegalArgumentException If the specified page token is empty
+ * @throws FirebaseAuthException If an error occurs while retrieving provider config data.
+ */
+ public ListProviderConfigsPage listOidcProviderConfigs(
+ @Nullable String pageToken) throws FirebaseAuthException {
+ int maxResults = FirebaseUserManager.MAX_LIST_PROVIDER_CONFIGS_RESULTS;
+ return listOidcProviderConfigsOp(pageToken, maxResults).call();
+ }
+
+ /**
+ * Gets a page of OpenID Connect auth provider configs starting from the specified
+ * {@code pageToken}.
+ *
+ * @param pageToken A non-empty page token string, or null to retrieve the first page of provider
+ * configs.
+ * @param maxResults Maximum number of provider configs to include in the returned page. This may
+ * not exceed 100.
+ * @return A {@link ListProviderConfigsPage} instance.
+ * @throws IllegalArgumentException If the specified page token is empty, or max results value is
+ * invalid.
+ * @throws FirebaseAuthException If an error occurs while retrieving provider config data.
+ */
+ public ListProviderConfigsPage listOidcProviderConfigs(
+ @Nullable String pageToken, int maxResults) throws FirebaseAuthException {
+ return listOidcProviderConfigsOp(pageToken, maxResults).call();
+ }
+
+ /**
+ * Similar to {@link #listOidcProviderConfigs(String)} but performs the operation asynchronously.
+ * Page size is limited to 100 provider configs.
+ *
+ * @param pageToken A non-empty page token string, or null to retrieve the first page of provider
+ * configs.
+ * @return An {@code ApiFuture} which will complete successfully with a
+ * {@link ListProviderConfigsPage} instance. If an error occurs while retrieving provider
+ * config data, the future throws an exception.
+ * @throws IllegalArgumentException If the specified page token is empty.
+ */
+ public ApiFuture> listOidcProviderConfigsAsync(
+ @Nullable String pageToken) {
+ int maxResults = FirebaseUserManager.MAX_LIST_PROVIDER_CONFIGS_RESULTS;
+ return listOidcProviderConfigsAsync(pageToken, maxResults);
+ }
+
+ /**
+ * Similar to {@link #listOidcProviderConfigs(String, int)} but performs the operation
+ * asynchronously.
+ *
+ * @param pageToken A non-empty page token string, or null to retrieve the first page of provider
+ * configs.
+ * @param maxResults Maximum number of provider configs to include in the returned page. This may
+ * not exceed 100.
+ * @return An {@code ApiFuture} which will complete successfully with a
+ * {@link ListProviderConfigsPage} instance. If an error occurs while retrieving provider
+ * config data, the future throws an exception.
+ * @throws IllegalArgumentException If the specified page token is empty, or max results value is
+ * invalid.
+ */
+ public ApiFuture> listOidcProviderConfigsAsync(
+ @Nullable String pageToken,
+ int maxResults) {
+ return listOidcProviderConfigsOp(pageToken, maxResults).callAsync(firebaseApp);
+ }
+
+ private CallableOperation, FirebaseAuthException>
+ listOidcProviderConfigsOp(@Nullable final String pageToken, final int maxResults) {
+ final FirebaseUserManager userManager = getUserManager();
+ final DefaultOidcProviderConfigSource source = new DefaultOidcProviderConfigSource(userManager);
+ final ListProviderConfigsPage.Factory factory =
+ new ListProviderConfigsPage.Factory(source, maxResults, pageToken);
+ return
+ new CallableOperation, FirebaseAuthException>() {
+ @Override
+ protected ListProviderConfigsPage execute()
+ throws FirebaseAuthException {
+ return factory.create();
+ }
+ };
+ }
+
+ /**
+ * Deletes the OpenID Connect 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, or is not prefixed
+ * with 'oidc'.
+ * @throws FirebaseAuthException If an error occurs while deleting the provider config.
+ */
+ public void deleteOidcProviderConfig(@NonNull String providerId) throws FirebaseAuthException {
+ deleteOidcProviderConfigOp(providerId).call();
+ }
+
+ /**
+ * 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
+ * config has been deleted. If an error occurs while deleting the provider config, the future
+ * throws a {@link FirebaseAuthException}.
+ * @throws IllegalArgumentException If the provider ID string is null or empty, or is not prefixed
+ * with "oidc.".
+ */
+ public ApiFuture deleteOidcProviderConfigAsync(String providerId) {
+ return deleteOidcProviderConfigOp(providerId).callAsync(firebaseApp);
+ }
+
+ private CallableOperation deleteOidcProviderConfigOp(
+ final String providerId) {
+ OidcProviderConfig.checkOidcProviderId(providerId);
+ final FirebaseUserManager userManager = getUserManager();
+ return new CallableOperation() {
+ @Override
+ protected Void execute() throws FirebaseAuthException {
+ userManager.deleteOidcProviderConfig(providerId);
+ return null;
+ }
+ };
+ }
+
+ /**
+ * Creates a new SAML Auth provider config with the attributes contained in the specified
+ * {@link SamlProviderConfig.CreateRequest}.
+ *
+ * @param request A non-null {@link SamlProviderConfig.CreateRequest} instance.
+ * @return An {@link SamlProviderConfig} instance corresponding to the newly created provider
+ * config.
+ * @throws NullPointerException if the provided request is null.
+ * @throws IllegalArgumentException If the provider ID string is null or empty, or is not prefixed
+ * with 'saml'.
+ * @throws FirebaseAuthException if an error occurs while creating the provider config.
+ */
+ public SamlProviderConfig createSamlProviderConfig(
+ @NonNull SamlProviderConfig.CreateRequest request) throws FirebaseAuthException {
+ return createSamlProviderConfigOp(request).call();
+ }
+
+ /**
+ * Similar to {@link #createSamlProviderConfig} but performs the operation asynchronously.
+ *
+ * @param request A non-null {@link SamlProviderConfig.CreateRequest} instance.
+ * @return An {@code ApiFuture} which will complete successfully with a {@link SamlProviderConfig}
+ * instance corresponding to the newly created provider config. If an error occurs while
+ * creating the provider config, the future throws a {@link FirebaseAuthException}.
+ * @throws NullPointerException if the provided request is null.
+ * @throws IllegalArgumentException If the provider ID string is null or empty, or is not prefixed
+ * with 'saml'.
+ */
+ public ApiFuture createSamlProviderConfigAsync(
+ @NonNull SamlProviderConfig.CreateRequest request) {
+ return createSamlProviderConfigOp(request).callAsync(firebaseApp);
+ }
+
+ private CallableOperation
+ createSamlProviderConfigOp(final SamlProviderConfig.CreateRequest request) {
+ checkNotNull(request, "Create request must not be null.");
+ SamlProviderConfig.checkSamlProviderId(request.getProviderId());
+ final FirebaseUserManager userManager = getUserManager();
+ return new CallableOperation() {
+ @Override
+ protected SamlProviderConfig execute() throws FirebaseAuthException {
+ return userManager.createSamlProviderConfig(request);
+ }
+ };
+ }
+
+ /**
+ * Updates an existing SAML Auth provider config with the attributes contained in the specified
+ * {@link SamlProviderConfig.UpdateRequest}.
+ *
+ * @param request A non-null {@link SamlProviderConfig.UpdateRequest} instance.
+ * @return A {@link SamlProviderConfig} instance corresponding to the updated provider config.
+ * @throws NullPointerException if the provided update request is null.
+ * @throws IllegalArgumentException If the provided update request is invalid.
+ * @throws FirebaseAuthException if an error occurs while updating the provider config.
+ */
+ public SamlProviderConfig updateSamlProviderConfig(
+ @NonNull SamlProviderConfig.UpdateRequest request) throws FirebaseAuthException {
+ return updateSamlProviderConfigOp(request).call();
+ }
+
+ /**
+ * Similar to {@link #updateSamlProviderConfig} but performs the operation asynchronously.
+ *
+ * @param request A non-null {@link SamlProviderConfig.UpdateRequest} instance.
+ * @return An {@code ApiFuture} which will complete successfully with a {@link SamlProviderConfig}
+ * instance corresponding to the updated provider config. If an error occurs while updating
+ * the provider config, the future throws a {@link FirebaseAuthException}.
+ * @throws NullPointerException if the provided update request is null.
+ * @throws IllegalArgumentException If the provided update request is invalid.
+ */
+ public ApiFuture updateSamlProviderConfigAsync(
+ @NonNull SamlProviderConfig.UpdateRequest request) {
+ return updateSamlProviderConfigOp(request).callAsync(firebaseApp);
+ }
+
+ private CallableOperation updateSamlProviderConfigOp(
+ final SamlProviderConfig.UpdateRequest request) {
+ checkNotNull(request, "Update request must not be null.");
+ checkArgument(!request.getProperties().isEmpty(),
+ "Update request must have at least one property set.");
+ final FirebaseUserManager userManager = getUserManager();
+ return new CallableOperation() {
+ @Override
+ protected SamlProviderConfig execute() throws FirebaseAuthException {
+ return userManager.updateSamlProviderConfig(request);
+ }
+ };
+ }
+
+ /**
+ * Gets the SAML Auth provider config corresponding to the specified provider ID.
+ *
+ * @param providerId A provider ID string.
+ * @return An {@link SamlProviderConfig} instance.
+ * @throws IllegalArgumentException If the provider ID string is null or empty, or is not prefixed
+ * with 'saml'.
+ * @throws FirebaseAuthException If an error occurs while retrieving the provider config.
+ */
+ public SamlProviderConfig getSamlProviderConfig(@NonNull String providerId)
+ throws FirebaseAuthException {
+ return getSamlProviderConfigOp(providerId).call();
+ }
+
+ /**
+ * Similar to {@link #getSamlProviderConfig(String)} but performs the operation asynchronously.
+ * Page size is limited to 100 provider configs.
+ *
+ * @param providerId A provider ID string.
+ * @return An {@code ApiFuture} which will complete successfully with an
+ * {@link SamlProviderConfig} instance. If an error occurs while retrieving the provider
+ * config or if the specified provider ID does not exist, the future throws a
+ * {@link FirebaseAuthException}.
+ * @throws IllegalArgumentException If the provider ID string is null or empty, or is not prefixed
+ * with 'saml'.
+ */
+ public ApiFuture getSamlProviderConfigAsync(@NonNull String providerId) {
+ return getSamlProviderConfigOp(providerId).callAsync(firebaseApp);
+ }
+
+ private CallableOperation
+ getSamlProviderConfigOp(final String providerId) {
+ SamlProviderConfig.checkSamlProviderId(providerId);
+ final FirebaseUserManager userManager = getUserManager();
+ return new CallableOperation() {
+ @Override
+ protected SamlProviderConfig execute() throws FirebaseAuthException {
+ return userManager.getSamlProviderConfig(providerId);
+ }
+ };
+ }
+
+ /**
+ * Gets a page of SAML Auth provider configs starting from the specified {@code pageToken}. Page
+ * size is limited to 100 provider configs.
+ *
+ * @param pageToken A non-empty page token string, or null to retrieve the first page of provider
+ * configs.
+ * @return A {@link ListProviderConfigsPage} instance.
+ * @throws IllegalArgumentException If the specified page token is empty.
+ * @throws FirebaseAuthException If an error occurs while retrieving provider config data.
+ */
+ public ListProviderConfigsPage listSamlProviderConfigs(
+ @Nullable String pageToken) throws FirebaseAuthException {
+ return listSamlProviderConfigs(
+ pageToken,
+ FirebaseUserManager.MAX_LIST_PROVIDER_CONFIGS_RESULTS);
+ }
+
+ /**
+ * Gets a page of SAML Auth provider configs starting from the specified {@code pageToken}.
+ *
+ * @param pageToken A non-empty page token string, or null to retrieve the first page of provider
+ * configs.
+ * @param maxResults Maximum number of provider configs to include in the returned page. This may
+ * not exceed 100.
+ * @return A {@link ListProviderConfigsPage} instance.
+ * @throws IllegalArgumentException If the specified page token is empty, or max results value is
+ * invalid.
+ * @throws FirebaseAuthException If an error occurs while retrieving provider config data.
+ */
+ public ListProviderConfigsPage listSamlProviderConfigs(
+ @Nullable String pageToken, int maxResults) throws FirebaseAuthException {
+ return listSamlProviderConfigsOp(pageToken, maxResults).call();
+ }
+
+ /**
+ * Similar to {@link #listSamlProviderConfigs(String)} but performs the operation asynchronously.
+ * Page size is limited to 100 provider configs.
+ *
+ * @param pageToken A non-empty page token string, or null to retrieve the first page of provider
+ * configs.
+ * @return An {@code ApiFuture} which will complete successfully with a
+ * {@link ListProviderConfigsPage} instance. If an error occurs while retrieving provider
+ * config data, the future throws an exception.
+ * @throws IllegalArgumentException If the specified page token is empty.
+ */
+ public ApiFuture> listSamlProviderConfigsAsync(
+ @Nullable String pageToken) {
+ int maxResults = FirebaseUserManager.MAX_LIST_PROVIDER_CONFIGS_RESULTS;
+ return listSamlProviderConfigsAsync(pageToken, maxResults);
+ }
+
+ /**
+ * Similar to {@link #listSamlProviderConfigs(String, int)} but performs the operation
+ * asynchronously.
+ *
+ * @param pageToken A non-empty page token string, or null to retrieve the first page of provider
+ * configs.
+ * @param maxResults Maximum number of provider configs to include in the returned page. This may
+ * not exceed 100.
+ * @return An {@code ApiFuture} which will complete successfully with a
+ * {@link ListProviderConfigsPage} instance. If an error occurs while retrieving provider
+ * config data, the future throws an exception.
+ * @throws IllegalArgumentException If the specified page token is empty, or max results value is
+ * invalid.
+ */
+ public ApiFuture> listSamlProviderConfigsAsync(
+ @Nullable String pageToken,
+ int maxResults) {
+ return listSamlProviderConfigsOp(pageToken, maxResults).callAsync(firebaseApp);
+ }
+
+ private CallableOperation, FirebaseAuthException>
+ listSamlProviderConfigsOp(@Nullable final String pageToken, final int maxResults) {
+ final FirebaseUserManager userManager = getUserManager();
+ final DefaultSamlProviderConfigSource source = new DefaultSamlProviderConfigSource(userManager);
+ final ListProviderConfigsPage.Factory factory =
+ new ListProviderConfigsPage.Factory(source, maxResults, pageToken);
+ return
+ new CallableOperation, FirebaseAuthException>() {
+ @Override
+ protected ListProviderConfigsPage execute()
+ throws FirebaseAuthException {
+ return factory.create();
+ }
+ };
+ }
+
+ /**
+ * Deletes the SAML 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, or is not prefixed
+ * with "saml.".
+ * @throws FirebaseAuthException If an error occurs while deleting the provider config.
+ */
+ public void deleteSamlProviderConfig(@NonNull String providerId) throws FirebaseAuthException {
+ deleteSamlProviderConfigOp(providerId).call();
+ }
+
+ /**
+ * Similar to {@link #deleteSamlProviderConfig} but performs the operation asynchronously.
+ *
+ * @param providerId A provider ID string.
+ * @return An {@code ApiFuture} which will complete successfully when the specified provider
+ * config has been deleted. If an error occurs while deleting the provider config, the future
+ * throws a {@link FirebaseAuthException}.
+ * @throws IllegalArgumentException If the provider ID string is null or empty, or is not prefixed
+ * with "saml.".
+ */
+ public ApiFuture deleteSamlProviderConfigAsync(String providerId) {
+ return deleteSamlProviderConfigOp(providerId).callAsync(firebaseApp);
+ }
+
+ private CallableOperation deleteSamlProviderConfigOp(
+ final String providerId) {
+ SamlProviderConfig.checkSamlProviderId(providerId);
+ final FirebaseUserManager userManager = getUserManager();
+ return new CallableOperation() {
+ @Override
+ protected Void execute() throws FirebaseAuthException {
+ userManager.deleteSamlProviderConfig(providerId);
+ return null;
+ }
+ };
+ }
+
+ FirebaseUserManager getUserManager() {
+ return this.userManager.get();
+ }
+
+ Supplier threadSafeMemoize(final Supplier supplier) {
+ return Suppliers.memoize(
+ new Supplier() {
+ @Override
+ public T get() {
+ checkNotNull(supplier);
+ synchronized (lock) {
+ return supplier.get();
+ }
+ }
+ });
+ }
+
+ protected abstract static class Builder> {
+
+ private FirebaseApp firebaseApp;
+ private Supplier tokenFactory;
+ private Supplier extends FirebaseTokenVerifier> idTokenVerifier;
+ private Supplier extends FirebaseTokenVerifier> cookieVerifier;
+ private Supplier extends FirebaseUserManager> userManager;
+
+ protected abstract T getThis();
+
+ public FirebaseApp getFirebaseApp() {
+ return firebaseApp;
+ }
+
+ public T setFirebaseApp(FirebaseApp firebaseApp) {
+ this.firebaseApp = firebaseApp;
+ return getThis();
+ }
+
+ public T setIdTokenVerifier(Supplier extends FirebaseTokenVerifier> idTokenVerifier) {
+ this.idTokenVerifier = idTokenVerifier;
+ return getThis();
+ }
+
+ public T setCookieVerifier(Supplier extends FirebaseTokenVerifier> cookieVerifier) {
+ this.cookieVerifier = cookieVerifier;
+ return getThis();
+ }
+
+ T setUserManager(Supplier userManager) {
+ this.userManager = userManager;
+ return getThis();
+ }
+
+ T setTokenFactory(Supplier tokenFactory) {
+ this.tokenFactory = tokenFactory;
+ return getThis();
+ }
+ }
+
+ protected static > T populateBuilderFromApp(
+ Builder builder, final FirebaseApp app, @Nullable final String tenantId) {
+ return builder.setFirebaseApp(app)
+ .setTokenFactory(
+ new Supplier() {
+ @Override
+ public FirebaseTokenFactory get() {
+ return FirebaseTokenUtils.createTokenFactory(app, Clock.SYSTEM, tenantId);
+ }
+ })
+ .setIdTokenVerifier(
+ new Supplier() {
+ @Override
+ public FirebaseTokenVerifier get() {
+ return FirebaseTokenUtils.createIdTokenVerifier(app, Clock.SYSTEM, tenantId);
+ }
+ })
+ .setCookieVerifier(
+ new Supplier() {
+ @Override
+ public FirebaseTokenVerifier get() {
+ return FirebaseTokenUtils.createSessionCookieVerifier(app, Clock.SYSTEM, tenantId);
+ }
+ })
+ .setUserManager(
+ new Supplier() {
+ @Override
+ public FirebaseUserManager get() {
+ return FirebaseUserManager.createUserManager(app, tenantId);
+ }
+ });
+ }
+}
diff --git a/src/main/java/com/google/firebase/auth/ActionCodeSettings.java b/src/main/java/com/google/firebase/auth/ActionCodeSettings.java
new file mode 100644
index 000000000..8ffccd542
--- /dev/null
+++ b/src/main/java/com/google/firebase/auth/ActionCodeSettings.java
@@ -0,0 +1,221 @@
+/*
+ * Copyright 2018 Google Inc.
+ *
+ * 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 com.google.common.base.Strings;
+import com.google.common.collect.ImmutableMap;
+
+import com.google.firebase.internal.NonNull;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Map;
+
+/**
+ * Defines the required continue/state URL with optional Android and iOS settings. Used when
+ * invoking the email action link generation APIs in {@link FirebaseAuth}.
+ */
+public final class ActionCodeSettings {
+
+ private final Map properties;
+
+ private ActionCodeSettings(Builder builder) {
+ checkArgument(!Strings.isNullOrEmpty(builder.url), "URL must not be null or empty");
+ try {
+ new URL(builder.url);
+ } catch (MalformedURLException e) {
+ throw new IllegalArgumentException("Malformed URL string", e);
+ }
+ if (builder.androidInstallApp || !Strings.isNullOrEmpty(builder.androidMinimumVersion)) {
+ checkArgument(!Strings.isNullOrEmpty(builder.androidPackageName),
+ "Android package name is required when specifying other Android settings");
+ }
+ ImmutableMap.Builder properties = ImmutableMap.builder()
+ .put("continueUrl", builder.url)
+ .put("canHandleCodeInApp", builder.handleCodeInApp);
+ if (!Strings.isNullOrEmpty(builder.dynamicLinkDomain)) {
+ properties.put("dynamicLinkDomain", builder.dynamicLinkDomain);
+ }
+ if (!Strings.isNullOrEmpty(builder.linkDomain)) {
+ properties.put("linkDomain", builder.linkDomain);
+ }
+ if (!Strings.isNullOrEmpty(builder.iosBundleId)) {
+ properties.put("iOSBundleId", builder.iosBundleId);
+ }
+ if (!Strings.isNullOrEmpty(builder.androidPackageName)) {
+ properties.put("androidPackageName", builder.androidPackageName);
+ if (!Strings.isNullOrEmpty(builder.androidMinimumVersion)) {
+ properties.put("androidMinimumVersion", builder.androidMinimumVersion);
+ }
+ if (builder.androidInstallApp) {
+ properties.put("androidInstallApp", builder.androidInstallApp);
+ }
+ }
+ this.properties = properties.build();
+ }
+
+ Map getProperties() {
+ return this.properties;
+ }
+
+ /**
+ * Creates a new {@link ActionCodeSettings.Builder}.
+ *
+ * @return A {@link ActionCodeSettings.Builder} instance.
+ */
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ public static final class Builder {
+
+ private String url;
+ private boolean handleCodeInApp;
+ private String dynamicLinkDomain;
+ private String linkDomain;
+ private String iosBundleId;
+ private String androidPackageName;
+ private String androidMinimumVersion;
+ private boolean androidInstallApp;
+
+ private Builder() { }
+
+ /**
+ * Sets the link continue/state URL.
+ *
+ * This parameter has different meanings in different contexts:
+ *
+ *
+ * - When the link is handled in the web action widgets, this is the deep link in the
+ * {@code continueUrl} query parameter.
+ * - When the link is handled in the app directly, this is the {@code continueUrl} query
+ * parameter in the deep link of the Dynamic Link.
+ *
+ *
+ * This parameter must be specified when creating a new {@link ActionCodeSettings} instance.
+ *
+ * @param url Continue/state URL string.
+ * @return This builder.
+ */
+ public Builder setUrl(@NonNull String url) {
+ this.url = url;
+ return this;
+ }
+
+ /**
+ * Specifies whether to open the link via a mobile app or a browser. The default is false.
+ * When set to true, the action code link is sent as a Universal Link or an Android App Link
+ * and is opened by the app if installed. In the false case, the code is sent to the web widget
+ * first and then redirects to the app if installed.
+ *
+ * @param handleCodeInApp true to open the link in the app, and false otherwise.
+ * @return This builder.
+ */
+ public Builder setHandleCodeInApp(boolean handleCodeInApp) {
+ this.handleCodeInApp = handleCodeInApp;
+ return this;
+ }
+
+ /**
+ * Sets the dynamic link domain to use for the current link if it is to be opened using
+ * Firebase Dynamic Links, as multiple dynamic link domains can be configured per project. This
+ * setting provides the ability to explicitly choose one. If none is provided, the oldest
+ * domain is used by default.
+ *
+ * @param dynamicLinkDomain Firebase Dynamic Link domain string.
+ * @return This builder.
+ * @deprecated Use {@link #setLinkDomain(String)} instead.
+ */
+ @Deprecated
+ public Builder setDynamicLinkDomain(String dynamicLinkDomain) {
+ this.dynamicLinkDomain = dynamicLinkDomain;
+ return this;
+ }
+
+ /**
+ * Sets the link domain to use for the current link if it is to be opened using
+ * {@code handleCodeInApp}, as multiple link domains can be configured per project. This
+ * setting provides the ability to explicitly choose one. If none is provided, the default
+ * Firebase Hosting domain will be used.
+ *
+ * @param linkDomain Link domain string.
+ * @return This builder.
+ */
+ public Builder setLinkDomain(String linkDomain) {
+ this.linkDomain = linkDomain;
+ return this;
+ }
+
+ /**
+ * Sets the bundle ID of the iOS app where the link should be handled if the
+ * application is already installed on the device.
+ *
+ * @param iosBundleId The iOS bundle ID string.
+ */
+ public Builder setIosBundleId(String iosBundleId) {
+ this.iosBundleId = iosBundleId;
+ return this;
+ }
+
+ /**
+ * Sets the Android package name of the app where the link should be handled if the
+ * Android app is installed. Must be specified when setting other Android-specific settings.
+ *
+ * @param androidPackageName Package name string. Must be specified, and must not be null
+ * or empty.
+ * @return This builder.
+ */
+ public Builder setAndroidPackageName(String androidPackageName) {
+ this.androidPackageName = androidPackageName;
+ return this;
+ }
+
+ /**
+ * Sets the minimum version for Android app. If the installed app is an older version, the user
+ * is taken to the Play Store to upgrade the app.
+ *
+ * @param androidMinimumVersion Minimum version string.
+ * @return This builder.
+ */
+ public Builder setAndroidMinimumVersion(String androidMinimumVersion) {
+ this.androidMinimumVersion = androidMinimumVersion;
+ return this;
+ }
+
+ /**
+ * Specifies whether to install the Android app if the device supports it and the app is not
+ * already installed.
+ *
+ * @param androidInstallApp true to install the app, and false otherwise.
+ * @return This builder.
+ */
+ public Builder setAndroidInstallApp(boolean androidInstallApp) {
+ this.androidInstallApp = androidInstallApp;
+ return this;
+ }
+
+ /**
+ * Builds a new {@link ActionCodeSettings}.
+ *
+ * @return A non-null {@link ActionCodeSettings}.
+ */
+ public ActionCodeSettings build() {
+ return new ActionCodeSettings(this);
+ }
+ }
+}
diff --git a/src/main/java/com/google/firebase/auth/AuthErrorCode.java b/src/main/java/com/google/firebase/auth/AuthErrorCode.java
new file mode 100644
index 000000000..9f7ecebf1
--- /dev/null
+++ b/src/main/java/com/google/firebase/auth/AuthErrorCode.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright 2020 Google Inc.
+ *
+ * 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;
+
+/**
+ * Error codes that can be raised by the Firebase Auth APIs.
+ */
+public enum AuthErrorCode {
+
+ /**
+ * Failed to retrieve public key certificates required to verify JWTs.
+ */
+ CERTIFICATE_FETCH_FAILED,
+
+ /**
+ * No IdP configuration found for the given identifier.
+ */
+ CONFIGURATION_NOT_FOUND,
+
+ /**
+ * A user already exists with the provided email.
+ */
+ EMAIL_ALREADY_EXISTS,
+
+ /**
+ * No user record found for the given email, typically raised when
+ * generating a password reset link using an email for a user that
+ * is not already registered.
+ */
+ EMAIL_NOT_FOUND,
+
+ /**
+ * The specified ID token is expired.
+ */
+ EXPIRED_ID_TOKEN,
+
+ /**
+ * The specified session cookie is expired.
+ */
+ EXPIRED_SESSION_COOKIE,
+
+ /**
+ * The provided dynamic link domain is not configured or authorized for the current project.
+ */
+ INVALID_DYNAMIC_LINK_DOMAIN,
+
+ /**
+ * The provided hosting link domain is not configured or authorized for the current project.
+ */
+ INVALID_HOSTING_LINK_DOMAIN,
+
+ /**
+ * The specified ID token is invalid.
+ */
+ INVALID_ID_TOKEN,
+
+ /**
+ * The specified session cookie is invalid.
+ */
+ INVALID_SESSION_COOKIE,
+
+ /**
+ * A user already exists with the provided phone number.
+ */
+ PHONE_NUMBER_ALREADY_EXISTS,
+
+ /**
+ * The specified ID token has been revoked.
+ */
+ REVOKED_ID_TOKEN,
+
+ /**
+ * The specified session cookie has been revoked.
+ */
+ 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.
+ */
+ UID_ALREADY_EXISTS,
+
+ /**
+ * The domain of the continue URL is not whitelisted. Whitelist the domain in the Firebase
+ * console.
+ */
+ UNAUTHORIZED_CONTINUE_URL,
+
+ /**
+ * No user record found for the given identifier.
+ */
+ USER_NOT_FOUND,
+
+ /**
+ * The user record is disabled.
+ */
+ USER_DISABLED,
+}
diff --git a/src/main/java/com/google/firebase/auth/DeleteUsersResult.java b/src/main/java/com/google/firebase/auth/DeleteUsersResult.java
new file mode 100644
index 000000000..e8ca7dba5
--- /dev/null
+++ b/src/main/java/com/google/firebase/auth/DeleteUsersResult.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2020 Google Inc.
+ *
+ * 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 com.google.common.collect.ImmutableList;
+import com.google.firebase.auth.internal.BatchDeleteResponse;
+import com.google.firebase.internal.NonNull;
+import java.util.List;
+
+/**
+ * Represents the result of the {@link FirebaseAuth#deleteUsersAsync(List)} API.
+ */
+public final class DeleteUsersResult {
+
+ private final int successCount;
+ private final List errors;
+
+ DeleteUsersResult(int users, BatchDeleteResponse response) {
+ ImmutableList.Builder errorsBuilder = ImmutableList.builder();
+ List responseErrors = response.getErrors();
+ if (responseErrors != null) {
+ checkArgument(users >= responseErrors.size());
+ for (BatchDeleteResponse.ErrorInfo error : responseErrors) {
+ errorsBuilder.add(new ErrorInfo(error.getIndex(), error.getMessage()));
+ }
+ }
+ errors = errorsBuilder.build();
+ successCount = users - errors.size();
+ }
+
+ /**
+ * Returns the number of users that were deleted successfully (possibly zero). Users that did not
+ * exist prior to calling {@link FirebaseAuth#deleteUsersAsync(List)} are considered to be
+ * successfully deleted.
+ */
+ public int getSuccessCount() {
+ return successCount;
+ }
+
+ /**
+ * Returns the number of users that failed to be deleted (possibly zero).
+ */
+ public int getFailureCount() {
+ return errors.size();
+ }
+
+ /**
+ * A list of {@link ErrorInfo} instances describing the errors that were encountered during
+ * the deletion. Length of this list is equal to the return value of
+ * {@link #getFailureCount()}.
+ *
+ * @return A non-null list (possibly empty).
+ */
+ @NonNull
+ public List getErrors() {
+ return errors;
+ }
+}
diff --git a/src/main/java/com/google/firebase/auth/EmailIdentifier.java b/src/main/java/com/google/firebase/auth/EmailIdentifier.java
new file mode 100644
index 000000000..8e729c220
--- /dev/null
+++ b/src/main/java/com/google/firebase/auth/EmailIdentifier.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2020 Google Inc.
+ *
+ * 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 com.google.firebase.auth.internal.GetAccountInfoRequest;
+import com.google.firebase.internal.NonNull;
+
+/**
+ * Used for looking up an account by email.
+ *
+ * @see {FirebaseAuth#getUsers}
+ */
+public final class EmailIdentifier extends UserIdentifier {
+ private final String email;
+
+ public EmailIdentifier(@NonNull String email) {
+ UserRecord.checkEmail(email);
+ this.email = email;
+ }
+
+ @Override
+ public String toString() {
+ return "EmailIdentifier(" + email + ")";
+ }
+
+ @Override
+ void populate(@NonNull GetAccountInfoRequest payload) {
+ payload.addEmail(email);
+ }
+
+ @Override
+ boolean matches(@NonNull UserRecord userRecord) {
+ return email.equals(userRecord.getEmail());
+ }
+}
diff --git a/src/main/java/com/google/firebase/auth/ErrorInfo.java b/src/main/java/com/google/firebase/auth/ErrorInfo.java
new file mode 100644
index 000000000..7f7a5e346
--- /dev/null
+++ b/src/main/java/com/google/firebase/auth/ErrorInfo.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2018 Google Inc.
+ *
+ * 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 java.util.List;
+
+/**
+ * Represents an error encountered while importing an {@link ImportUserRecord}.
+ */
+public final class ErrorInfo {
+
+ private final int index;
+ private final String reason;
+
+ ErrorInfo(int index, String reason) {
+ this.index = index;
+ this.reason = reason;
+ }
+
+ /**
+ * The index of the failed user in the list passed to the
+ * {@link FirebaseAuth#importUsersAsync(List, UserImportOptions)} method.
+ *
+ * @return an integer index.
+ */
+ public int getIndex() {
+ return index;
+ }
+
+ /**
+ * A string describing the error.
+ *
+ * @return A string error message.
+ */
+ public String getReason() {
+ return reason;
+ }
+}
diff --git a/src/main/java/com/google/firebase/auth/ExportedUserRecord.java b/src/main/java/com/google/firebase/auth/ExportedUserRecord.java
index e367f55ae..9fdfc9c5e 100644
--- a/src/main/java/com/google/firebase/auth/ExportedUserRecord.java
+++ b/src/main/java/com/google/firebase/auth/ExportedUserRecord.java
@@ -17,6 +17,7 @@
package com.google.firebase.auth;
import com.google.api.client.json.JsonFactory;
+import com.google.common.io.BaseEncoding;
import com.google.firebase.auth.internal.DownloadAccountResponse.User;
import com.google.firebase.internal.Nullable;
@@ -28,10 +29,17 @@ public class ExportedUserRecord extends UserRecord {
private final String passwordHash;
private final String passwordSalt;
+ private static final String REDACTED_BASE64 = BaseEncoding.base64Url().encode(
+ "REDACTED".getBytes());
ExportedUserRecord(User response, JsonFactory jsonFactory) {
super(response, jsonFactory);
- this.passwordHash = response.getPasswordHash();
+ String passwordHash = response.getPasswordHash();
+ if (passwordHash != null && !passwordHash.equals(REDACTED_BASE64)) {
+ this.passwordHash = passwordHash;
+ } else {
+ this.passwordHash = null;
+ }
this.passwordSalt = response.getPasswordSalt();
}
diff --git a/src/main/java/com/google/firebase/auth/FirebaseAuth.java b/src/main/java/com/google/firebase/auth/FirebaseAuth.java
index bbb9feb2b..27e79960d 100644
--- a/src/main/java/com/google/firebase/auth/FirebaseAuth.java
+++ b/src/main/java/com/google/firebase/auth/FirebaseAuth.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 Google Inc.
+ * Copyright 2017 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,33 +16,11 @@
package com.google.firebase.auth;
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkNotNull;
-import static com.google.common.base.Preconditions.checkState;
-
-import com.google.api.client.googleapis.auth.oauth2.GooglePublicKeysManager;
-import com.google.api.client.json.JsonFactory;
-import com.google.api.client.util.Clock;
-import com.google.api.core.ApiFuture;
-import com.google.auth.oauth2.GoogleCredentials;
-import com.google.auth.oauth2.ServiceAccountCredentials;
-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.ListUsersPage.DefaultUserSource;
-import com.google.firebase.auth.ListUsersPage.PageFactory;
-import com.google.firebase.auth.UserRecord.CreateRequest;
-import com.google.firebase.auth.UserRecord.UpdateRequest;
-import com.google.firebase.auth.internal.FirebaseTokenFactory;
-import com.google.firebase.auth.internal.FirebaseTokenVerifier;
+import com.google.firebase.auth.multitenancy.TenantManager;
import com.google.firebase.internal.FirebaseService;
-import com.google.firebase.internal.Nullable;
-import com.google.firebase.internal.TaskToApiFuture;
-import com.google.firebase.tasks.Task;
-import java.util.Map;
-import java.util.concurrent.Callable;
-import java.util.concurrent.atomic.AtomicBoolean;
/**
* This class is the entry point for all server-side Firebase Authentication actions.
@@ -52,43 +30,19 @@
* custom tokens for use by client-side code, verifying Firebase ID Tokens received from clients, or
* creating new FirebaseApp instances that are scoped to a particular authentication UID.
*/
-public class FirebaseAuth {
+public final class FirebaseAuth extends AbstractFirebaseAuth {
- private final GooglePublicKeysManager googlePublicKeysManager;
- private final Clock clock;
+ private static final String SERVICE_ID = FirebaseAuth.class.getName();
- private final FirebaseApp firebaseApp;
- private final GoogleCredentials credentials;
- private final String projectId;
- private final JsonFactory jsonFactory;
- private final FirebaseUserManager userManager;
- private final AtomicBoolean destroyed;
- private final Object lock;
+ private final Supplier tenantManager;
- private FirebaseAuth(FirebaseApp firebaseApp) {
- this(firebaseApp,
- FirebaseTokenVerifier.buildGooglePublicKeysManager(
- firebaseApp.getOptions().getHttpTransport()),
- Clock.SYSTEM);
+ private FirebaseAuth(final Builder builder) {
+ super(builder);
+ tenantManager = threadSafeMemoize(builder.tenantManager);
}
- /**
- * Constructor for injecting a GooglePublicKeysManager, which is used to verify tokens are
- * correctly signed. This should only be used for testing to override the default key manager.
- */
- @VisibleForTesting
- FirebaseAuth(
- FirebaseApp firebaseApp, GooglePublicKeysManager googlePublicKeysManager, Clock clock) {
- this.firebaseApp = checkNotNull(firebaseApp);
- this.googlePublicKeysManager = checkNotNull(googlePublicKeysManager);
- this.clock = checkNotNull(clock);
- this.credentials = ImplFirebaseTrampolines.getCredentials(firebaseApp);
- this.projectId = ImplFirebaseTrampolines.getProjectId(firebaseApp);
- this.jsonFactory = firebaseApp.getOptions().getJsonFactory();
- this.userManager = new FirebaseUserManager(jsonFactory,
- firebaseApp.getOptions().getHttpTransport(), this.credentials);
- this.destroyed = new AtomicBoolean(false);
- this.lock = new Object();
+ public TenantManager getTenantManager() {
+ return tenantManager.get();
}
/**
@@ -107,540 +61,54 @@ public static FirebaseAuth getInstance() {
* @return A FirebaseAuth instance.
*/
public static synchronized FirebaseAuth getInstance(FirebaseApp app) {
- FirebaseAuthService service = ImplFirebaseTrampolines.getService(app, SERVICE_ID,
- FirebaseAuthService.class);
+ FirebaseAuthService service =
+ ImplFirebaseTrampolines.getService(app, SERVICE_ID, FirebaseAuthService.class);
if (service == null) {
service = ImplFirebaseTrampolines.addService(app, new FirebaseAuthService(app));
}
return service.getInstance();
}
- /**
- * Similar to {@link #createCustomTokenAsync(String)}, but returns a {@link Task}.
- *
- * @param uid The UID to store in the token. This identifies the user to other Firebase services
- * (Firebase Database, Firebase Auth, etc.)
- * @return A {@link Task} which will complete successfully with the created Firebase Custom Token,
- * or unsuccessfully with the failure Exception.
- * @deprecated Use {@link #createCustomTokenAsync(String)}
- */
- public Task createCustomToken(String uid) {
- return createCustomToken(uid, null);
- }
-
- /**
- * Similar to {@link #createCustomTokenAsync(String, Map)}, but returns a {@link Task}.
- *
- * @param uid The UID to store in the token. This identifies the user to other Firebase services
- * (Realtime Database, Storage, etc.). Should be less than 128 characters.
- * @param developerClaims Additional claims to be stored in the token (and made available to
- * security rules in Database, Storage, etc.). These must be able to be serialized to JSON
- * (e.g. contain only Maps, Arrays, Strings, Booleans, Numbers, etc.)
- * @return A {@link Task} which will complete successfully with the created Firebase Custom Token,
- * or unsuccessfully with the failure Exception.
- * @deprecated Use {@link #createCustomTokenAsync(String, Map)}
- */
- public Task createCustomToken(
- final String uid, final Map developerClaims) {
- checkNotDestroyed();
- checkState(credentials instanceof ServiceAccountCredentials,
- "Must initialize FirebaseApp with a service account credential to call "
- + "createCustomToken()");
-
- final ServiceAccountCredentials serviceAccount = (ServiceAccountCredentials) credentials;
- return call(new Callable() {
- @Override
- public String call() throws Exception {
- FirebaseTokenFactory tokenFactory = FirebaseTokenFactory.getInstance();
- return tokenFactory.createSignedCustomAuthTokenForUser(
- uid,
- developerClaims,
- serviceAccount.getClientEmail(),
- serviceAccount.getPrivateKey());
- }
- });
- }
-
- /**
- * Creates a Firebase Custom Token associated with the given UID. This token can then be provided
- * back to a client application for use with the
- * signInWithCustomToken
- * authentication API.
- *
- * @param uid The UID to store in the token. This identifies the user to other Firebase services
- * (Firebase Realtime Database, Firebase Auth, etc.)
- * @return An {@code ApiFuture} which will complete successfully with the created Firebase Custom
- * Token, or unsuccessfully with the failure Exception.
- */
- public ApiFuture createCustomTokenAsync(String uid) {
- return new TaskToApiFuture<>(createCustomToken(uid));
- }
-
- /**
- * Creates a Firebase Custom Token associated with the given UID and additionally containing the
- * specified developerClaims. This token can then be provided back to a client application for use
- * with the signInWithCustomToken authentication API.
- *
- * @param uid The UID to store in the token. This identifies the user to other Firebase services
- * (Realtime Database, Storage, etc.). Should be less than 128 characters.
- * @param developerClaims Additional claims to be stored in the token (and made available to
- * security rules in Database, Storage, etc.). These must be able to be serialized to JSON
- * (e.g. contain only Maps, Arrays, Strings, Booleans, Numbers, etc.)
- * @return An {@code ApiFuture} which will complete successfully with the created Firebase Custom
- * Token, or unsuccessfully with the failure Exception.
- */
- public ApiFuture createCustomTokenAsync(
- final String uid, final Map developerClaims) {
- return new TaskToApiFuture<>(createCustomToken(uid, developerClaims));
- }
-
- /**
- * Similar to {@link #verifyIdTokenAsync(String)}, but returns a {@link Task}.
- *
- * @param token A Firebase ID Token to verify and parse.
- * @return A {@link Task} which will complete successfully with the parsed token, or
- * unsuccessfully with the failure Exception.
- * @deprecated Use {@link #verifyIdTokenAsync(String)}
- */
- public Task verifyIdToken(final String token) {
- return verifyIdToken(token, false);
- }
-
- private Task verifyIdToken(final String token, final boolean checkRevoked) {
- checkNotDestroyed();
- checkState(!Strings.isNullOrEmpty(projectId),
- "Must initialize FirebaseApp with a project ID to call verifyIdToken()");
- return call(new Callable() {
- @Override
- public FirebaseToken call() throws Exception {
-
- FirebaseTokenVerifier firebaseTokenVerifier =
- new FirebaseTokenVerifier.Builder()
- .setProjectId(projectId)
- .setPublicKeysManager(googlePublicKeysManager)
- .setClock(clock)
- .build();
-
- FirebaseToken firebaseToken = FirebaseToken.parse(jsonFactory, token);
-
- // This will throw a FirebaseAuthException with details on how the token is invalid.
- firebaseTokenVerifier.verifyTokenAndSignature(firebaseToken.getToken());
-
- if (checkRevoked) {
- String uid = firebaseToken.getUid();
- UserRecord user = userManager.getUserById(uid);
- long issuedAt = (long) firebaseToken.getClaims().get("iat");
- if (user.getTokensValidAfterTimestamp() > issuedAt * 1000) {
- throw new FirebaseAuthException(FirebaseUserManager.ID_TOKEN_REVOKED_ERROR,
- "Firebase auth token revoked");
- }
- }
- return firebaseToken;
- }
- });
- }
-
- private Task revokeRefreshTokens(String uid) {
- checkNotDestroyed();
- int currentTimeSeconds = (int) (System.currentTimeMillis() / 1000);
- final UpdateRequest request = new UpdateRequest(uid).setValidSince(currentTimeSeconds);
- return call(new Callable() {
- @Override
- public Void call() throws Exception {
- userManager.updateUser(request, jsonFactory);
- return null;
- }
- });
- }
-
- /**
- * Revokes all refresh tokens for the specified user.
- *
- * Updates the user's tokensValidAfterTimestamp to the current UTC time expressed in
- * milliseconds since the epoch and truncated to 1 second accuracy. It is important that the
- * server on which this is called has its clock set correctly and synchronized.
- *
- *
While this will revoke all sessions for a specified user and disable any new ID tokens for
- * existing sessions from getting minted, existing ID tokens may remain active until their
- * natural expiration (one hour).
- * To verify that ID tokens are revoked, use {@link #verifyIdTokenAsync(String, boolean)}.
- *
- * @param uid The user id for which tokens are revoked.
- * @return An {@code ApiFuture} which will complete successfully or if updating the user fails,
- * unsuccessfully with the failure Exception.
- */
- public ApiFuture revokeRefreshTokensAsync(String uid) {
- return new TaskToApiFuture<>(revokeRefreshTokens(uid));
- }
-
- /**
- * Parses and verifies a Firebase ID Token.
- *
- * A Firebase application can identify itself to a trusted backend server by sending its
- * Firebase ID Token (accessible via the getToken API in the Firebase Authentication client) with
- * its request.
- *
- *
The backend server can then use the verifyIdToken() method to verify the token is valid,
- * meaning: the token is properly signed, has not expired, and it was issued for the project
- * associated with this FirebaseAuth instance (which by default is extracted from your service
- * account)
- *
- *
If the token is valid, the returned Future will complete successfully and provide a
- * parsed version of the token from which the UID and other claims in the token can be inspected.
- * If the token is invalid, the future throws an exception indicating the failure.
- *
- *
This does not check whether a token has been revoked.
- * See {@link #verifyIdTokenAsync(String, boolean)} below.
- *
- * @param token A Firebase ID Token to verify and parse.
- * @return An {@code ApiFuture} which will complete successfully with the parsed token, or
- * unsuccessfully with the failure Exception.
- */
- public ApiFuture verifyIdTokenAsync(final String token) {
- return verifyIdTokenAsync(token, false);
- }
-
- /**
- * Parses and verifies a Firebase ID Token and if requested, checks whether it was revoked.
- *
- * A Firebase application can identify itself to a trusted backend server by sending its
- * Firebase ID Token (accessible via the getToken API in the Firebase Authentication client) with
- * its request.
- *
- *
The backend server can then use the verifyIdToken() method to verify the token is valid,
- * meaning: the token is properly signed, has not expired, and it was issued for the project
- * associated with this FirebaseAuth instance (which by default is extracted from your service
- * account)
- *
- *
If {@code checkRevoked} is true, additionally checks if the token has been revoked.
- *
- *
If the token is valid, and not revoked, the returned Future will complete successfully and
- * provide a parsed version of the token from which the UID and other claims in the token can be
- * inspected.
- * If the token is invalid or has been revoked, the future throws an exception indicating the
- * failure.
- *
- * @param token A Firebase ID Token to verify and parse.
- * @param checkRevoked A boolean denoting whether to check if the tokens were revoked.
- * @return An {@code ApiFuture} which will complete successfully with the parsed token, or
- * unsuccessfully with the failure Exception.
- */
- public ApiFuture verifyIdTokenAsync(final String token,
- final boolean checkRevoked) {
- return new TaskToApiFuture<>(verifyIdToken(token, checkRevoked));
- }
-
- /**
- * Similar to {@link #getUserAsync(String)}, but returns a {@link Task}.
- *
- * @param uid A user ID string.
- * @return A {@link Task} which will complete successfully with a {@link UserRecord} instance.
- * If an error occurs while retrieving user data or if the specified user ID does not exist,
- * the task fails with a {@link FirebaseAuthException}.
- * @throws IllegalArgumentException If the user ID string is null or empty.
- * @deprecated Use {@link #getUserAsync(String)}
- */
- public Task getUser(final String uid) {
- checkNotDestroyed();
- checkArgument(!Strings.isNullOrEmpty(uid), "uid must not be null or empty");
- return call(new Callable() {
- @Override
- public UserRecord call() throws Exception {
- return userManager.getUserById(uid);
- }
- });
- }
-
- /**
- * Gets the user data corresponding to the specified user ID.
- *
- * @param uid A user ID string.
- * @return An {@code ApiFuture} which will complete successfully with a {@link UserRecord}
- * instance. If an error occurs while retrieving user data or if the specified user ID does
- * not exist, the future throws a {@link FirebaseAuthException}.
- * @throws IllegalArgumentException If the user ID string is null or empty.
- */
- public ApiFuture getUserAsync(final String uid) {
- return new TaskToApiFuture<>(getUser(uid));
- }
-
- /**
- * Similar to {@link #getUserByEmailAsync(String)}, but returns a {@link Task}.
- *
- * @param email A user email address string.
- * @return A {@link Task} which will complete successfully with a {@link UserRecord} instance.
- * If an error occurs while retrieving user data or if the email address does not correspond
- * to a user, the task fails with a {@link FirebaseAuthException}.
- * @throws IllegalArgumentException If the email is null or empty.
- * @deprecated Use {@link #getUserByEmailAsync(String)}
- */
- public Task getUserByEmail(final String email) {
- checkNotDestroyed();
- checkArgument(!Strings.isNullOrEmpty(email), "email must not be null or empty");
- return call(new Callable() {
- @Override
- public UserRecord call() throws Exception {
- return userManager.getUserByEmail(email);
- }
- });
- }
-
- /**
- * Gets the user data corresponding to the specified user email.
- *
- * @param email A user email address string.
- * @return An {@code ApiFuture} which will complete successfully with a {@link UserRecord}
- * instance. If an error occurs while retrieving user data or if the email address does not
- * correspond to a user, the future throws a {@link FirebaseAuthException}.
- * @throws IllegalArgumentException If the email is null or empty.
- */
- public ApiFuture getUserByEmailAsync(final String email) {
- return new TaskToApiFuture<>(getUserByEmail(email));
- }
-
- /**
- * Similar to {@link #getUserByPhoneNumberAsync(String)}, but returns a {@link Task}.
- *
- * @param phoneNumber A user phone number string.
- * @return A {@link Task} which will complete successfully with a {@link UserRecord} instance.
- * If an error occurs while retrieving user data or if the phone number does not
- * correspond to a user, the task fails with a {@link FirebaseAuthException}.
- * @throws IllegalArgumentException If the phone number is null or empty.
- * @deprecated Use {@link #getUserByPhoneNumberAsync(String)}
- */
- public Task getUserByPhoneNumber(final String phoneNumber) {
- checkNotDestroyed();
- checkArgument(!Strings.isNullOrEmpty(phoneNumber), "phone number must not be null or empty");
- return call(new Callable() {
- @Override
- public UserRecord call() throws Exception {
- return userManager.getUserByPhoneNumber(phoneNumber);
- }
- });
- }
-
- /**
- * Gets the user data corresponding to the specified user phone number.
- *
- * @param phoneNumber A user phone number string.
- * @return An {@code ApiFuture} which will complete successfully with a {@link UserRecord}
- * instance. If an error occurs while retrieving user data or if the phone number does not
- * correspond to a user, the future throws a {@link FirebaseAuthException}.
- * @throws IllegalArgumentException If the phone number is null or empty.
- */
- public ApiFuture getUserByPhoneNumberAsync(final String phoneNumber) {
- return new TaskToApiFuture<>(getUserByPhoneNumber(phoneNumber));
- }
-
- private Task listUsers(@Nullable String pageToken, int maxResults) {
- checkNotDestroyed();
- final PageFactory factory = new PageFactory(
- new DefaultUserSource(userManager, jsonFactory), maxResults, pageToken);
- return call(new Callable() {
- @Override
- public ListUsersPage call() throws Exception {
- return factory.create();
- }
- });
- }
-
- /**
- * Gets a page of users starting from the specified {@code pageToken}. Page size will be
- * limited to 1000 users.
- *
- * @param pageToken A non-empty page token string, or null to retrieve the first page of users.
- * @return An {@code ApiFuture} which will complete successfully with a {@link ListUsersPage}
- * instance. If an error occurs while retrieving user data, the future throws an exception.
- * @throws IllegalArgumentException If the specified page token is empty.
- */
- public ApiFuture listUsersAsync(@Nullable String pageToken) {
- return listUsersAsync(pageToken, FirebaseUserManager.MAX_LIST_USERS_RESULTS);
- }
-
- /**
- * Gets a page of users starting from the specified {@code pageToken}.
- *
- * @param pageToken A non-empty page token string, or null to retrieve the first page of users.
- * @param maxResults Maximum number of users to include in the returned page. This may not
- * exceed 1000.
- * @return An {@code ApiFuture} which will complete successfully with a {@link ListUsersPage}
- * instance. If an error occurs while retrieving user data, the future throws an exception.
- * @throws IllegalArgumentException If the specified page token is empty, or max results value
- * is invalid.
- */
- public ApiFuture listUsersAsync(@Nullable String pageToken, int maxResults) {
- return new TaskToApiFuture<>(listUsers(pageToken, maxResults));
- }
-
- /**
- * Similar to {@link #createUserAsync(CreateRequest)}, but returns a {@link Task}.
- *
- * @param request A non-null {@link CreateRequest} instance.
- * @return A {@link Task} which will complete successfully with a {@link UserRecord} instance
- * corresponding to the newly created account. If an error occurs while creating the user
- * account, the task fails with a {@link FirebaseAuthException}.
- * @throws NullPointerException if the provided request is null.
- * @deprecated Use {@link #createUserAsync(CreateRequest)}
- */
- public Task createUser(final CreateRequest request) {
- checkNotDestroyed();
- checkNotNull(request, "create request must not be null");
- return call(new Callable() {
- @Override
- public UserRecord call() throws Exception {
- String uid = userManager.createUser(request);
- return userManager.getUserById(uid);
- }
- });
- }
-
- /**
- * Creates a new user account with the attributes contained in the specified
- * {@link CreateRequest}.
- *
- * @param request A non-null {@link CreateRequest} instance.
- * @return An {@code ApiFuture} which will complete successfully with a {@link UserRecord}
- * instance corresponding to the newly created account. If an error occurs while creating the
- * user account, the future throws a {@link FirebaseAuthException}.
- * @throws NullPointerException if the provided request is null.
- */
- public ApiFuture createUserAsync(final CreateRequest request) {
- return new TaskToApiFuture<>(createUser(request));
+ private static FirebaseAuth fromApp(final FirebaseApp app) {
+ return populateBuilderFromApp(builder(), app, null)
+ .setTenantManager(new Supplier() {
+ @Override
+ public TenantManager get() {
+ return new TenantManager(app);
+ }
+ })
+ .build();
}
- /**
- * Similar to {@link #updateUserAsync(UpdateRequest)}, but returns a {@link Task}.
- *
- * @param request A non-null {@link UpdateRequest} instance.
- * @return A {@link Task} which will complete successfully with a {@link UserRecord} instance
- * corresponding to the updated user account. If an error occurs while updating the user
- * account, the task fails with a {@link FirebaseAuthException}.
- * @throws NullPointerException if the provided update request is null.
- * @deprecated Use {@link #updateUserAsync(UpdateRequest)}
- */
- public Task