sortedNameList = new ArrayList<>(allAppNames);
- Collections.sort(sortedNameList);
- return sortedNameList;
+
+ Collections.sort(allAppNames);
+ return ImmutableList.copyOf(allAppNames);
}
/** Normalizes the app name. */
@@ -293,6 +278,8 @@ public FirebaseOptions getOptions() {
*/
@Nullable
String getProjectId() {
+ checkNotDeleted();
+
// Try to get project ID from user-specified options.
String projectId = options.getProjectId();
@@ -306,10 +293,10 @@ String getProjectId() {
// Try to get project ID from the environment.
if (Strings.isNullOrEmpty(projectId)) {
- projectId = System.getenv("GOOGLE_CLOUD_PROJECT");
+ projectId = FirebaseProcessEnvironment.getenv("GOOGLE_CLOUD_PROJECT");
}
if (Strings.isNullOrEmpty(projectId)) {
- projectId = System.getenv("GCLOUD_PROJECT");
+ projectId = FirebaseProcessEnvironment.getenv("GCLOUD_PROJECT");
}
return projectId;
}
@@ -330,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.
*/
@@ -359,11 +348,6 @@ public void delete() {
synchronized (appsLock) {
instances.remove(name);
}
-
- FirebaseAppStore appStore = FirebaseAppStore.getInstance();
- if (appStore != null) {
- appStore.removeApp(name);
- }
}
private void checkNotDeleted() {
@@ -580,20 +564,19 @@ enum State {
}
private static FirebaseOptions getOptionsFromEnvironment() throws IOException {
- String defaultConfig = System.getenv(FIREBASE_CONFIG_ENV_VAR);
+ String defaultConfig = FirebaseProcessEnvironment.getenv(FIREBASE_CONFIG_ENV_VAR);
if (Strings.isNullOrEmpty(defaultConfig)) {
- return new FirebaseOptions.Builder()
+ return FirebaseOptions.builder()
.setCredentials(APPLICATION_DEFAULT_CREDENTIALS)
.build();
}
- JsonFactory jsonFactory = Utils.getDefaultJsonFactory();
- FirebaseOptions.Builder builder = new FirebaseOptions.Builder();
+ JsonFactory jsonFactory = ApiClientUtils.getDefaultJsonFactory();
+ FirebaseOptions.Builder builder = FirebaseOptions.builder();
JsonParser parser;
if (defaultConfig.startsWith("{")) {
parser = jsonFactory.createJsonParser(defaultConfig);
} else {
- FileReader reader;
- reader = new FileReader(defaultConfig);
+ FileReader reader = new FileReader(defaultConfig);
parser = jsonFactory.createJsonParser(reader);
}
parser.parseAndClose(builder);
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 f0561d5e5..03f1b34a4 100644
--- a/src/main/java/com/google/firebase/FirebaseOptions.java
+++ b/src/main/java/com/google/firebase/FirebaseOptions.java
@@ -19,7 +19,6 @@
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;
@@ -29,6 +28,8 @@
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;
@@ -64,7 +65,8 @@ public final class FirebaseOptions {
@Override
public GoogleCredentials get() {
try {
- return GoogleCredentials.getApplicationDefault().createScoped(FIREBASE_SCOPES);
+ return ApplicationDefaultCredentialsProvider.getApplicationDefault()
+ .createScoped(FIREBASE_SCOPES);
} catch (IOException e) {
throw new IllegalStateException(e);
}
@@ -80,6 +82,7 @@ public GoogleCredentials get() {
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;
@@ -100,16 +103,18 @@ private FirebaseOptions(@NonNull final FirebaseOptions.Builder builder) {
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;
}
@@ -196,8 +201,7 @@ public int getConnectTimeout() {
}
/**
- * Returns the read timeout in milliseconds, which is applied to outgoing REST calls
- * made by the SDK.
+ * Returns the read timeout applied to outgoing REST calls in milliseconds.
*
* @return Read timeout in milliseconds. 0 indicates an infinite timeout.
*/
@@ -205,6 +209,15 @@ 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;
@@ -224,18 +237,28 @@ public static Builder builder() {
}
/**
- * Builder for constructing {@link FirebaseOptions}.
+ * 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}.
*/
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;
@@ -243,13 +266,19 @@ public static final class Builder {
private String serviceAccountId;
private Supplier credentialsSupplier;
private FirestoreOptions firestoreOptions;
- private HttpTransport httpTransport = Utils.getDefaultTransport();
- private JsonFactory jsonFactory = Utils.getDefaultJsonFactory();
- private ThreadManager threadManager = FirebaseThreadManagers.DEFAULT_THREAD_MANAGER;
+ private HttpTransport httpTransport;
+ private JsonFactory jsonFactory;
+ private ThreadManager threadManager;
private int connectTimeout;
private int readTimeout;
+ private int writeTimeout;
- /** Constructs an empty builder. */
+ /**
+ * Constructs an empty builder.
+ *
+ * @deprecated Use {@link FirebaseOptions#builder()} instead.
+ */
+ @Deprecated
public Builder() {}
/**
@@ -257,7 +286,10 @@ 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;
@@ -269,6 +301,7 @@ public Builder(FirebaseOptions options) {
threadManager = options.threadManager;
connectTimeout = options.connectTimeout;
readTimeout = options.readTimeout;
+ writeTimeout = options.writeTimeout;
firestoreOptions = options.firestoreOptions;
}
@@ -401,7 +434,8 @@ public Builder setServiceAccountId(@NonNull String serviceAccountId) {
* @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;
}
@@ -413,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;
}
@@ -425,7 +460,8 @@ 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;
}
@@ -472,6 +508,19 @@ public Builder setReadTimeout(int 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;
+ }
+
/**
* Builds the {@link FirebaseOptions} instance from the previously set options.
*
diff --git a/src/main/java/com/google/firebase/ImplFirebaseTrampolines.java b/src/main/java/com/google/firebase/ImplFirebaseTrampolines.java
index 7a50e0b07..0d3008b18 100644
--- a/src/main/java/com/google/firebase/ImplFirebaseTrampolines.java
+++ b/src/main/java/com/google/firebase/ImplFirebaseTrampolines.java
@@ -22,11 +22,12 @@
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 java.util.concurrent.Callable;
-import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ThreadFactory;
@@ -42,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();
}
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/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
index 0b102b7a3..8ffccd542 100644
--- a/src/main/java/com/google/firebase/auth/ActionCodeSettings.java
+++ b/src/main/java/com/google/firebase/auth/ActionCodeSettings.java
@@ -51,6 +51,9 @@ private ActionCodeSettings(Builder builder) {
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);
}
@@ -84,6 +87,7 @@ 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;
@@ -135,12 +139,28 @@ public Builder setHandleCodeInApp(boolean handleCodeInApp) {
*
* @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.
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/FirebaseAuth.java b/src/main/java/com/google/firebase/auth/FirebaseAuth.java
index f7f6231ad..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,35 +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.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.ImplFirebaseTrampolines;
-import com.google.firebase.auth.FirebaseUserManager.EmailLinkType;
-import com.google.firebase.auth.FirebaseUserManager.UserImportRequest;
-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.internal.CallableOperation;
+import com.google.firebase.auth.multitenancy.TenantManager;
import com.google.firebase.internal.FirebaseService;
-import com.google.firebase.internal.NonNull;
-import com.google.firebase.internal.Nullable;
-
-import java.io.IOException;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.atomic.AtomicBoolean;
/**
* This class is the entry point for all server-side Firebase Authentication actions.
@@ -54,29 +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 static final String SERVICE_ID = FirebaseAuth.class.getName();
- private static final String ERROR_CUSTOM_TOKEN = "ERROR_CUSTOM_TOKEN";
-
- private final Object lock = new Object();
- private final AtomicBoolean destroyed = new AtomicBoolean(false);
+ private final Supplier tenantManager;
- 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;
+ private FirebaseAuth(final Builder builder) {
+ super(builder);
+ tenantManager = threadSafeMemoize(builder.tenantManager);
+ }
- private FirebaseAuth(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();
+ public TenantManager getTenantManager() {
+ return tenantManager.get();
}
/**
@@ -95,1123 +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();
}
- /**
- * 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) {
- checkNotDestroyed();
- 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 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.
- * @return A {@link FirebaseToken} representing the verified and decoded cookie.
- */
- 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) {
- checkNotDestroyed();
- 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) {
- FirebaseUserManager userManager = getUserManager();
- verifier = RevocationCheckDecorator.decorateSessionCookieVerifier(verifier, userManager);
- }
- return verifier;
- }
-
- /**
- * 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) {
- checkNotDestroyed();
- 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 {
- try {
- return tokenFactory.createSignedCustomAuthTokenForUser(uid, developerClaims);
- } catch (IOException e) {
- throw new FirebaseAuthException(ERROR_CUSTOM_TOKEN,
- "Failed to generate a custom token", e);
- }
- }
- };
- }
-
- /**
- * 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 token 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 token) throws FirebaseAuthException {
- return verifyIdToken(token, 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 token A Firebase ID token string to parse and verify.
- * @param checkRevoked A boolean denoting whether to check if the tokens were revoked.
- * @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 token, boolean checkRevoked) throws FirebaseAuthException {
- return verifyIdTokenOp(token, checkRevoked).call();
- }
-
- /**
- * Similar to {@link #verifyIdToken(String)} but performs the operation asynchronously.
- *
- * @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 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 token) {
- return verifyIdTokenAsync(token, false);
- }
-
- /**
- * Similar to {@link #verifyIdToken(String, boolean)} but performs the operation asynchronously.
- *
- * @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 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 token, boolean checkRevoked) {
- return verifyIdTokenOp(token, checkRevoked).callAsync(firebaseApp);
- }
-
- private CallableOperation verifyIdTokenOp(
- final String token, final boolean checkRevoked) {
- checkNotDestroyed();
- checkArgument(!Strings.isNullOrEmpty(token), "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(token);
- }
- };
- }
-
- @VisibleForTesting
- FirebaseTokenVerifier getIdTokenVerifier(boolean checkRevoked) {
- FirebaseTokenVerifier verifier = idTokenVerifier.get();
- if (checkRevoked) {
- FirebaseUserManager userManager = getUserManager();
- verifier = RevocationCheckDecorator.decorateIdTokenVerifier(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) {
- checkNotDestroyed();
- 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);
- UpdateRequest request = new 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) {
- checkNotDestroyed();
- 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) {
- checkNotDestroyed();
- 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) {
- checkNotDestroyed();
- 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 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 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) {
- checkNotDestroyed();
- final FirebaseUserManager userManager = getUserManager();
- final PageFactory factory = new PageFactory(
- new DefaultUserSource(userManager, jsonFactory), 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 CreateRequest}.
- *
- * @param request A non-null {@link 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 CreateRequest request) throws FirebaseAuthException {
- return createUserOp(request).call();
- }
-
- /**
- * Similar to {@link #createUser(CreateRequest)} but performs the operation asynchronously.
- *
- * @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(@NonNull CreateRequest request) {
- return createUserOp(request).callAsync(firebaseApp);
- }
-
- private CallableOperation createUserOp(
- final CreateRequest request) {
- checkNotDestroyed();
- 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 UpdateRequest}.
- *
- * @param request A non-null {@link UpdateRequest} instance.
- * @return A {@link UserRecord} instance corresponding to the updated user account.
- * account, the task fails with a {@link FirebaseAuthException}.
- * @throws NullPointerException if the provided update request is null.
- * @throws FirebaseAuthException if an error occurs while updating the user account.
- */
- public UserRecord updateUser(@NonNull UpdateRequest request) throws FirebaseAuthException {
- return updateUserOp(request).call();
- }
-
- /**
- * Similar to {@link #updateUser(UpdateRequest)} but performs the operation asynchronously.
- *
- * @param request A non-null {@link 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 UpdateRequest request) {
- return updateUserOp(request).callAsync(firebaseApp);
- }
-
- private CallableOperation updateUserOp(
- final UpdateRequest request) {
- checkNotDestroyed();
- 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) {
- checkNotDestroyed();
- 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 UpdateRequest request = new 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) {
- checkNotDestroyed();
- 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) {
- checkNotDestroyed();
- 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);
- }
- };
- }
-
- /**
- * 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);
- }
-
- @VisibleForTesting
- FirebaseUserManager getUserManager() {
- return this.userManager.get();
- }
-
- private CallableOperation generateEmailActionLinkOp(
- final EmailLinkType type, final String email, final ActionCodeSettings settings) {
- checkNotDestroyed();
- 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);
- }
- };
- }
-
- private Supplier threadSafeMemoize(final Supplier supplier) {
- return Suppliers.memoize(new Supplier() {
- @Override
- public T get() {
- checkNotNull(supplier);
- synchronized (lock) {
- checkNotDestroyed();
- return supplier.get();
- }
- }
- });
- }
-
- private void checkNotDestroyed() {
- synchronized (lock) {
- checkState(!destroyed.get(), "FirebaseAuth instance is no longer alive. This happens when "
- + "the parent FirebaseApp instance has been deleted.");
- }
- }
-
- private void destroy() {
- synchronized (lock) {
- destroyed.set(true);
- }
- }
-
private static FirebaseAuth fromApp(final FirebaseApp app) {
- return FirebaseAuth.builder()
- .setFirebaseApp(app)
- .setTokenFactory(new Supplier() {
- @Override
- public FirebaseTokenFactory get() {
- return FirebaseTokenUtils.createTokenFactory(app, Clock.SYSTEM);
- }
- })
- .setIdTokenVerifier(new Supplier() {
- @Override
- public FirebaseTokenVerifier get() {
- return FirebaseTokenUtils.createIdTokenVerifier(app, Clock.SYSTEM);
- }
- })
- .setCookieVerifier(new Supplier