() {
+ @Override
+ public FirebaseUserManager get() {
+ return FirebaseUserManager
+ .builder()
+ .setFirebaseApp(app)
+ .setTenantId(tenantId)
+ .build();
+ }
+ });
+ }
+
+ /**
+ * 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 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.
+ * @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, 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) {
+ checkNotDestroyed();
+ 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) {
+ 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);
+ 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) {
+ 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 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) {
+ checkNotDestroyed();
+ 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) {
+ 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
+ * 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) {
+ 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 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) {
+ 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);
+ }
+ };
+ }
+
+ /**
+ * 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 {@link 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 {@link 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) {
+ checkNotDestroyed();
+ 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 {@link 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.
+ * @throw 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}.
+ * @throw 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) {
+ checkNotDestroyed();
+ 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) {
+ 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);
+ }
+ };
+ }
+
+ /**
+ * 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) {
+ checkNotDestroyed();
+ 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) {
+ checkNotDestroyed();
+ 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) {
+ checkNotDestroyed();
+ 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) {
+ checkNotDestroyed();
+ 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) {
+ checkNotDestroyed();
+ 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) {
+ checkNotDestroyed();
+ 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) {
+ checkNotDestroyed();
+ 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) {
+ checkNotDestroyed();
+ 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) {
+ checkNotDestroyed();
+ 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) {
+ checkNotDestroyed();
+ SamlProviderConfig.checkSamlProviderId(providerId);
+ final FirebaseUserManager userManager = getUserManager();
+ return new CallableOperation() {
+ @Override
+ protected Void execute() throws FirebaseAuthException {
+ userManager.deleteSamlProviderConfig(providerId);
+ return null;
+ }
+ };
+ }
+
+ FirebaseApp getFirebaseApp() {
+ return this.firebaseApp;
+ }
+
+ FirebaseTokenVerifier getCookieVerifier() {
+ return this.cookieVerifier.get();
+ }
+
+ FirebaseUserManager getUserManager() {
+ return this.userManager.get();
+ }
+
+ protected Supplier threadSafeMemoize(final Supplier supplier) {
+ return Suppliers.memoize(
+ new Supplier() {
+ @Override
+ public T get() {
+ checkNotNull(supplier);
+ synchronized (lock) {
+ checkNotDestroyed();
+ return supplier.get();
+ }
+ }
+ });
+ }
+
+ void checkNotDestroyed() {
+ synchronized (lock) {
+ checkState(
+ !destroyed.get(),
+ "FirebaseAuth instance is no longer alive. This happens when "
+ + "the parent FirebaseApp instance has been deleted.");
+ }
+ }
+
+ final void destroy() {
+ synchronized (lock) {
+ doDestroy();
+ destroyed.set(true);
+ }
+ }
+
+ /** Performs any additional required clean up. */
+ protected abstract void doDestroy();
+
+ static Builder builder() {
+ return new Builder();
+ }
+
+ static class Builder {
+ protected FirebaseApp firebaseApp;
+ private Supplier tokenFactory;
+ private Supplier extends FirebaseTokenVerifier> idTokenVerifier;
+ private Supplier extends FirebaseTokenVerifier> cookieVerifier;
+ private Supplier userManager;
+
+ private Builder() {}
+
+ Builder setFirebaseApp(FirebaseApp firebaseApp) {
+ this.firebaseApp = firebaseApp;
+ return this;
+ }
+
+ Builder setTokenFactory(Supplier tokenFactory) {
+ this.tokenFactory = tokenFactory;
+ return this;
+ }
+
+ Builder setIdTokenVerifier(Supplier extends FirebaseTokenVerifier> idTokenVerifier) {
+ this.idTokenVerifier = idTokenVerifier;
+ return this;
+ }
+
+ Builder setCookieVerifier(Supplier extends FirebaseTokenVerifier> cookieVerifier) {
+ this.cookieVerifier = cookieVerifier;
+ return this;
+ }
+
+ Builder setUserManager(Supplier userManager) {
+ this.userManager = userManager;
+ return this;
+ }
+ }
+}
diff --git a/src/main/java/com/google/firebase/auth/FirebaseAuth.java b/src/main/java/com/google/firebase/auth/FirebaseAuth.java
index 923778af4..f44bd2343 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.
@@ -18,36 +18,19 @@
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.auth.multitenancy.TenantManager;
import com.google.firebase.internal.CallableOperation;
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.Collection;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.atomic.AtomicBoolean;
/**
* This class is the entry point for all server-side Firebase Authentication actions.
@@ -57,29 +40,24 @@
* 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;
+ FirebaseAuth(final Builder builder) {
+ super(builder);
+ tenantManager = threadSafeMemoize(new Supplier() {
+ @Override
+ public TenantManager get() {
+ return new TenantManager(builder.firebaseApp);
+ }
+ });
+ }
- 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();
}
/**
@@ -98,8 +76,8 @@ 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));
}
@@ -107,8 +85,8 @@ public static synchronized FirebaseAuth getInstance(FirebaseApp app) {
}
/**
- * 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.
+ * 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.
@@ -116,8 +94,8 @@ public static synchronized FirebaseAuth getInstance(FirebaseApp app) {
* @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 {
+ public String createSessionCookie(@NonNull String idToken, @NonNull SessionCookieOptions options)
+ throws FirebaseAuthException {
return createSessionCookieOp(idToken, options).call();
}
@@ -127,14 +105,14 @@ public String createSessionCookie(
*
* @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}.
+ * @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);
+ return createSessionCookieOp(idToken, options).callAsync(getFirebaseApp());
}
private CallableOperation createSessionCookieOp(
@@ -157,8 +135,8 @@ protected String execute() throws FirebaseAuthException {
* 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)}.
+ *
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.
@@ -170,20 +148,18 @@ public FirebaseToken verifySessionCookie(String cookie) throws FirebaseAuthExcep
/**
* Parses and verifies a Firebase session cookie.
*
- *
If {@code checkRevoked} is true, additionally verifies that the cookie has not been
- * revoked.
+ *
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}.
+ * 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.
+ * @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 {
+ public FirebaseToken verifySessionCookie(String cookie, boolean checkRevoked)
+ throws FirebaseAuthException {
return verifySessionCookieOp(cookie, checkRevoked).call();
}
@@ -203,13 +179,12 @@ public ApiFuture verifySessionCookieAsync(String cookie) {
* 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.
+ * @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);
+ return verifySessionCookieOp(cookie, checkRevoked).callAsync(getFirebaseApp());
}
private CallableOperation verifySessionCookieOp(
@@ -227,7 +202,7 @@ public FirebaseToken execute() throws FirebaseAuthException {
@VisibleForTesting
FirebaseTokenVerifier getSessionCookieVerifier(boolean checkRevoked) {
- FirebaseTokenVerifier verifier = cookieVerifier.get();
+ FirebaseTokenVerifier verifier = getCookieVerifier();
if (checkRevoked) {
FirebaseUserManager userManager = getUserManager();
verifier = RevocationCheckDecorator.decorateSessionCookieVerifier(verifier, userManager);
@@ -235,1109 +210,41 @@ FirebaseTokenVerifier getSessionCookieVerifier(boolean checkRevoked) {
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 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 {@link 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 {@link 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) {
- checkNotDestroyed();
- 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;
- }
-
- /**
- * 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) {
- 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;
- }
- };
- }
-
- /**
- * 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 {@link 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.
- * @throw 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}.
- * @throw 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) {
- checkNotDestroyed();
- 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);
- }
- };
- }
-
- /**
- * Imports the provided list of users into Firebase Auth. You can import a maximum of 1000 users
- * at a time. This operation is optimized for bulk imports and does not check 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);
- }
- }
+ @Override
+ protected void doDestroy() { }
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() {
- @Override
- public FirebaseTokenVerifier get() {
- return FirebaseTokenUtils.createSessionCookieVerifier(app, Clock.SYSTEM);
- }
- })
- .setUserManager(new Supplier() {
- @Override
- public FirebaseUserManager get() {
- return new FirebaseUserManager(app);
- }
- })
- .build();
- }
-
- @VisibleForTesting
- static Builder builder() {
- return new Builder();
- }
-
- static class Builder {
- private FirebaseApp firebaseApp;
- private Supplier tokenFactory;
- private Supplier extends FirebaseTokenVerifier> idTokenVerifier;
- private Supplier extends FirebaseTokenVerifier> cookieVerifier;
- private Supplier userManager;
-
- private Builder() { }
-
- Builder setFirebaseApp(FirebaseApp firebaseApp) {
- this.firebaseApp = firebaseApp;
- return this;
- }
-
- Builder setTokenFactory(Supplier tokenFactory) {
- this.tokenFactory = tokenFactory;
- return this;
- }
-
- Builder setIdTokenVerifier(Supplier extends FirebaseTokenVerifier> idTokenVerifier) {
- this.idTokenVerifier = idTokenVerifier;
- return this;
- }
-
- Builder setCookieVerifier(Supplier extends FirebaseTokenVerifier> cookieVerifier) {
- this.cookieVerifier = cookieVerifier;
- return this;
- }
-
- Builder setUserManager(Supplier userManager) {
- this.userManager = userManager;
- return this;
- }
-
- FirebaseAuth build() {
- return new FirebaseAuth(this);
- }
+ return new FirebaseAuth(
+ AbstractFirebaseAuth.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() {
+ @Override
+ public FirebaseTokenVerifier get() {
+ return FirebaseTokenUtils.createSessionCookieVerifier(app, Clock.SYSTEM);
+ }
+ })
+ .setUserManager(
+ new Supplier() {
+ @Override
+ public FirebaseUserManager get() {
+ return FirebaseUserManager.builder().setFirebaseApp(app).build();
+ }
+ }));
}
private static class FirebaseAuthService extends FirebaseService {
diff --git a/src/main/java/com/google/firebase/auth/FirebaseToken.java b/src/main/java/com/google/firebase/auth/FirebaseToken.java
index 3d7b0b254..835fa41c9 100644
--- a/src/main/java/com/google/firebase/auth/FirebaseToken.java
+++ b/src/main/java/com/google/firebase/auth/FirebaseToken.java
@@ -42,6 +42,15 @@ public String getUid() {
return (String) claims.get("sub");
}
+ /** Returns the tenant ID for the this token. */
+ public String getTenantId() {
+ Map firebase = (Map) claims.get("firebase");
+ if (firebase == null) {
+ return null;
+ }
+ return (String) firebase.get("tenant");
+ }
+
/** Returns the Issuer for the this token. */
public String getIssuer() {
return (String) claims.get("iss");
@@ -57,14 +66,14 @@ public String getPicture() {
return (String) claims.get("picture");
}
- /**
+ /**
* Returns the e-mail address for this user, or {@code null} if it's unavailable.
*/
public String getEmail() {
return (String) claims.get("email");
}
- /**
+ /**
* Indicates if the email address returned by {@link #getEmail()} has been verified as good.
*/
public boolean isEmailVerified() {
diff --git a/src/main/java/com/google/firebase/auth/FirebaseTokenUtils.java b/src/main/java/com/google/firebase/auth/FirebaseTokenUtils.java
index dbb562872..e0105e9aa 100644
--- a/src/main/java/com/google/firebase/auth/FirebaseTokenUtils.java
+++ b/src/main/java/com/google/firebase/auth/FirebaseTokenUtils.java
@@ -30,6 +30,7 @@
import com.google.firebase.ImplFirebaseTrampolines;
import com.google.firebase.auth.internal.CryptoSigners;
import com.google.firebase.auth.internal.FirebaseTokenFactory;
+import com.google.firebase.internal.Nullable;
import java.io.IOException;
@@ -52,11 +53,17 @@ final class FirebaseTokenUtils {
private FirebaseTokenUtils() { }
static FirebaseTokenFactory createTokenFactory(FirebaseApp firebaseApp, Clock clock) {
+ return createTokenFactory(firebaseApp, clock, null);
+ }
+
+ static FirebaseTokenFactory createTokenFactory(
+ FirebaseApp firebaseApp, Clock clock, @Nullable String tenantId) {
try {
return new FirebaseTokenFactory(
firebaseApp.getOptions().getJsonFactory(),
clock,
- CryptoSigners.getCryptoSigner(firebaseApp));
+ CryptoSigners.getCryptoSigner(firebaseApp),
+ tenantId);
} catch (IOException e) {
throw new IllegalStateException(
"Failed to initialize FirebaseTokenFactory. Make sure to initialize the SDK "
@@ -68,6 +75,11 @@ static FirebaseTokenFactory createTokenFactory(FirebaseApp firebaseApp, Clock cl
}
static FirebaseTokenVerifierImpl createIdTokenVerifier(FirebaseApp app, Clock clock) {
+ return createIdTokenVerifier(app, clock, null);
+ }
+
+ static FirebaseTokenVerifierImpl createIdTokenVerifier(
+ FirebaseApp app, Clock clock, @Nullable String tenantId) {
String projectId = ImplFirebaseTrampolines.getProjectId(app);
checkState(!Strings.isNullOrEmpty(projectId),
"Must initialize FirebaseApp with a project ID to call verifyIdToken()");
@@ -82,6 +94,7 @@ static FirebaseTokenVerifierImpl createIdTokenVerifier(FirebaseApp app, Clock cl
.setJsonFactory(app.getOptions().getJsonFactory())
.setPublicKeysManager(publicKeysManager)
.setIdTokenVerifier(idTokenVerifier)
+ .setTenantId(tenantId)
.build();
}
diff --git a/src/main/java/com/google/firebase/auth/FirebaseTokenVerifierImpl.java b/src/main/java/com/google/firebase/auth/FirebaseTokenVerifierImpl.java
index c164173a6..e1a5a9a19 100644
--- a/src/main/java/com/google/firebase/auth/FirebaseTokenVerifierImpl.java
+++ b/src/main/java/com/google/firebase/auth/FirebaseTokenVerifierImpl.java
@@ -28,6 +28,7 @@
import com.google.api.client.util.ArrayMap;
import com.google.common.base.Joiner;
import com.google.common.base.Strings;
+import com.google.firebase.internal.Nullable;
import java.io.IOException;
import java.math.BigDecimal;
import java.security.GeneralSecurityException;
@@ -45,6 +46,7 @@ final class FirebaseTokenVerifierImpl implements FirebaseTokenVerifier {
"https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit";
private static final String ERROR_INVALID_CREDENTIAL = "ERROR_INVALID_CREDENTIAL";
private static final String ERROR_RUNTIME_EXCEPTION = "ERROR_RUNTIME_EXCEPTION";
+ static final String TENANT_ID_MISMATCH_ERROR = "tenant-id-mismatch";
private final JsonFactory jsonFactory;
private final GooglePublicKeysManager publicKeysManager;
@@ -53,6 +55,7 @@ final class FirebaseTokenVerifierImpl implements FirebaseTokenVerifier {
private final String shortName;
private final String articledShortName;
private final String docUrl;
+ private final String tenantId;
private FirebaseTokenVerifierImpl(Builder builder) {
this.jsonFactory = checkNotNull(builder.jsonFactory);
@@ -65,6 +68,7 @@ private FirebaseTokenVerifierImpl(Builder builder) {
this.shortName = builder.shortName;
this.articledShortName = prefixWithIndefiniteArticle(this.shortName);
this.docUrl = builder.docUrl;
+ this.tenantId = Strings.nullToEmpty(builder.tenantId);
}
/**
@@ -90,7 +94,9 @@ public FirebaseToken verifyToken(String token) throws FirebaseAuthException {
IdToken idToken = parse(token);
checkContents(idToken);
checkSignature(idToken);
- return new FirebaseToken(idToken.getPayload());
+ FirebaseToken firebaseToken = new FirebaseToken(idToken.getPayload());
+ checkTenantId(firebaseToken);
+ return firebaseToken;
}
GooglePublicKeysManager getPublicKeysManager() {
@@ -278,6 +284,18 @@ private boolean containsLegacyUidField(IdToken.Payload payload) {
return false;
}
+ private void checkTenantId(final FirebaseToken firebaseToken) throws FirebaseAuthException {
+ String tokenTenantId = Strings.nullToEmpty(firebaseToken.getTenantId());
+ if (!this.tenantId.equals(tokenTenantId)) {
+ throw new FirebaseAuthException(
+ TENANT_ID_MISMATCH_ERROR,
+ String.format(
+ "The tenant ID ('%s') of the token did not match the expected value ('%s')",
+ tokenTenantId,
+ tenantId));
+ }
+ }
+
static Builder builder() {
return new Builder();
}
@@ -290,6 +308,7 @@ static final class Builder {
private String shortName;
private IdTokenVerifier idTokenVerifier;
private String docUrl;
+ private String tenantId;
private Builder() { }
@@ -323,6 +342,11 @@ Builder setDocUrl(String docUrl) {
return this;
}
+ Builder setTenantId(@Nullable String tenantId) {
+ this.tenantId = tenantId;
+ return this;
+ }
+
FirebaseTokenVerifierImpl build() {
return new FirebaseTokenVerifierImpl(this);
}
diff --git a/src/main/java/com/google/firebase/auth/FirebaseUserManager.java b/src/main/java/com/google/firebase/auth/FirebaseUserManager.java
index ab8759c4f..b73882277 100644
--- a/src/main/java/com/google/firebase/auth/FirebaseUserManager.java
+++ b/src/main/java/com/google/firebase/auth/FirebaseUserManager.java
@@ -20,37 +20,30 @@
import static com.google.common.base.Preconditions.checkNotNull;
import com.google.api.client.http.GenericUrl;
-import com.google.api.client.http.HttpContent;
-import com.google.api.client.http.HttpRequest;
import com.google.api.client.http.HttpRequestFactory;
-import com.google.api.client.http.HttpResponse;
-import com.google.api.client.http.HttpResponseException;
import com.google.api.client.http.HttpResponseInterceptor;
-import com.google.api.client.http.json.JsonHttpContent;
import com.google.api.client.json.GenericJson;
import com.google.api.client.json.JsonFactory;
-import com.google.api.client.json.JsonObjectParser;
import com.google.api.client.util.Key;
import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Joiner;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.firebase.FirebaseApp;
import com.google.firebase.ImplFirebaseTrampolines;
-import com.google.firebase.auth.UserRecord.CreateRequest;
-import com.google.firebase.auth.UserRecord.UpdateRequest;
+import com.google.firebase.auth.internal.AuthHttpClient;
import com.google.firebase.auth.internal.BatchDeleteResponse;
import com.google.firebase.auth.internal.DownloadAccountResponse;
import com.google.firebase.auth.internal.GetAccountInfoRequest;
import com.google.firebase.auth.internal.GetAccountInfoResponse;
-import com.google.firebase.auth.internal.HttpErrorResponse;
+import com.google.firebase.auth.internal.ListOidcProviderConfigsResponse;
+import com.google.firebase.auth.internal.ListSamlProviderConfigsResponse;
import com.google.firebase.auth.internal.UploadAccountResponse;
import com.google.firebase.internal.ApiClientUtils;
import com.google.firebase.internal.NonNull;
import com.google.firebase.internal.Nullable;
-import com.google.firebase.internal.SdkUtils;
-import java.io.IOException;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
@@ -66,30 +59,7 @@
*/
class FirebaseUserManager {
- static final String USER_NOT_FOUND_ERROR = "user-not-found";
- static final String INTERNAL_ERROR = "internal-error";
-
- // Map of server-side error codes to SDK error codes.
- // SDK error codes defined at: https://firebase.google.com/docs/auth/admin/errors
- private static final Map ERROR_CODES = ImmutableMap.builder()
- .put("CLAIMS_TOO_LARGE", "claims-too-large")
- .put("CONFIGURATION_NOT_FOUND", "project-not-found")
- .put("INSUFFICIENT_PERMISSION", "insufficient-permission")
- .put("DUPLICATE_EMAIL", "email-already-exists")
- .put("DUPLICATE_LOCAL_ID", "uid-already-exists")
- .put("EMAIL_EXISTS", "email-already-exists")
- .put("INVALID_CLAIMS", "invalid-claims")
- .put("INVALID_EMAIL", "invalid-email")
- .put("INVALID_PAGE_SELECTION", "invalid-page-token")
- .put("INVALID_PHONE_NUMBER", "invalid-phone-number")
- .put("PHONE_NUMBER_EXISTS", "phone-number-already-exists")
- .put("PROJECT_NOT_FOUND", "project-not-found")
- .put("USER_NOT_FOUND", USER_NOT_FOUND_ERROR)
- .put("WEAK_PASSWORD", "invalid-password")
- .put("UNAUTHORIZED_DOMAIN", "unauthorized-continue-uri")
- .put("INVALID_DYNAMIC_LINK_DOMAIN", "invalid-dynamic-link-domain")
- .build();
-
+ static final int MAX_LIST_PROVIDER_CONFIGS_RESULTS = 100;
static final int MAX_GET_ACCOUNTS_BATCH_SIZE = 100;
static final int MAX_DELETE_ACCOUNTS_BATCH_SIZE = 1000;
static final int MAX_LIST_USERS_RESULTS = 1000;
@@ -100,45 +70,41 @@ class FirebaseUserManager {
"iss", "jti", "nbf", "nonce", "sub", "firebase");
private static final String ID_TOOLKIT_URL =
- "https://identitytoolkit.googleapis.com/v1/projects/%s";
- private static final String CLIENT_VERSION_HEADER = "X-Client-Version";
+ "https://identitytoolkit.googleapis.com/%s/projects/%s";
- private final String baseUrl;
+ private final String userMgtBaseUrl;
+ private final String idpConfigMgtBaseUrl;
private final JsonFactory jsonFactory;
- private final HttpRequestFactory requestFactory;
- private final String clientVersion = "Java/Admin/" + SdkUtils.getVersion();
-
- private HttpResponseInterceptor interceptor;
-
- /**
- * Creates a new FirebaseUserManager instance.
- *
- * @param app A non-null {@link FirebaseApp}.
- */
- FirebaseUserManager(@NonNull FirebaseApp app) {
- this(app, null);
- }
+ private final AuthHttpClient httpClient;
- FirebaseUserManager(@NonNull FirebaseApp app, @Nullable HttpRequestFactory requestFactory) {
- checkNotNull(app, "FirebaseApp must not be null");
+ private FirebaseUserManager(Builder builder) {
+ FirebaseApp app = checkNotNull(builder.app, "FirebaseApp must not be null");
String projectId = ImplFirebaseTrampolines.getProjectId(app);
checkArgument(!Strings.isNullOrEmpty(projectId),
"Project ID is required to access the auth service. Use a service account credential or "
+ "set the project ID explicitly via FirebaseOptions. Alternatively you can also "
+ "set the project ID via the GOOGLE_CLOUD_PROJECT environment variable.");
- this.baseUrl = String.format(ID_TOOLKIT_URL, projectId);
- this.jsonFactory = app.getOptions().getJsonFactory();
-
- if (requestFactory == null) {
- requestFactory = ApiClientUtils.newAuthorizedRequestFactory(app);
+ final String idToolkitUrlV1 = String.format(ID_TOOLKIT_URL, "v1", projectId);
+ final String idToolkitUrlV2 = String.format(ID_TOOLKIT_URL, "v2", projectId);
+ final String tenantId = builder.tenantId;
+ if (tenantId == null) {
+ this.userMgtBaseUrl = idToolkitUrlV1;
+ this.idpConfigMgtBaseUrl = idToolkitUrlV2;
+ } else {
+ checkArgument(!tenantId.isEmpty(), "Tenant ID must not be empty.");
+ this.userMgtBaseUrl = idToolkitUrlV1 + "/tenants/" + tenantId;
+ this.idpConfigMgtBaseUrl = idToolkitUrlV2 + "/tenants/" + tenantId;
}
- this.requestFactory = requestFactory;
+ this.jsonFactory = app.getOptions().getJsonFactory();
+ HttpRequestFactory requestFactory = builder.requestFactory == null
+ ? ApiClientUtils.newAuthorizedRequestFactory(app) : builder.requestFactory;
+ this.httpClient = new AuthHttpClient(jsonFactory, requestFactory);
}
@VisibleForTesting
void setInterceptor(HttpResponseInterceptor interceptor) {
- this.interceptor = interceptor;
+ httpClient.setInterceptor(interceptor);
}
UserRecord getUserById(String uid) throws FirebaseAuthException {
@@ -147,7 +113,8 @@ UserRecord getUserById(String uid) throws FirebaseAuthException {
GetAccountInfoResponse response = post(
"/accounts:lookup", payload, GetAccountInfoResponse.class);
if (response == null || response.getUsers() == null || response.getUsers().isEmpty()) {
- throw new FirebaseAuthException(USER_NOT_FOUND_ERROR,
+ throw new FirebaseAuthException(
+ AuthHttpClient.USER_NOT_FOUND_ERROR,
"No user record found for the provided user ID: " + uid);
}
return new UserRecord(response.getUsers().get(0), jsonFactory);
@@ -159,7 +126,8 @@ UserRecord getUserByEmail(String email) throws FirebaseAuthException {
GetAccountInfoResponse response = post(
"/accounts:lookup", payload, GetAccountInfoResponse.class);
if (response == null || response.getUsers() == null || response.getUsers().isEmpty()) {
- throw new FirebaseAuthException(USER_NOT_FOUND_ERROR,
+ throw new FirebaseAuthException(
+ AuthHttpClient.USER_NOT_FOUND_ERROR,
"No user record found for the provided email: " + email);
}
return new UserRecord(response.getUsers().get(0), jsonFactory);
@@ -171,7 +139,8 @@ UserRecord getUserByPhoneNumber(String phoneNumber) throws FirebaseAuthException
GetAccountInfoResponse response = post(
"/accounts:lookup", payload, GetAccountInfoResponse.class);
if (response == null || response.getUsers() == null || response.getUsers().isEmpty()) {
- throw new FirebaseAuthException(USER_NOT_FOUND_ERROR,
+ throw new FirebaseAuthException(
+ AuthHttpClient.USER_NOT_FOUND_ERROR,
"No user record found for the provided phone number: " + phoneNumber);
}
return new UserRecord(response.getUsers().get(0), jsonFactory);
@@ -180,7 +149,7 @@ UserRecord getUserByPhoneNumber(String phoneNumber) throws FirebaseAuthException
Set getAccountInfo(@NonNull Collection identifiers)
throws FirebaseAuthException {
if (identifiers.isEmpty()) {
- return new HashSet();
+ return new HashSet<>();
}
GetAccountInfoRequest payload = new GetAccountInfoRequest();
@@ -192,7 +161,8 @@ Set getAccountInfo(@NonNull Collection