All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.firebase.auth.FirebaseAuth Maven / Gradle / Ivy

/*
 * Copyright 2017 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.firebase.auth;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;

import com.google.api.client.json.JsonFactory;
import com.google.api.client.util.Clock;
import com.google.api.core.ApiFuture;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Strings;
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import com.google.firebase.FirebaseApp;
import com.google.firebase.ImplFirebaseTrampolines;
import com.google.firebase.auth.FirebaseUserManager.EmailLinkType;
import com.google.firebase.auth.FirebaseUserManager.UserImportRequest;
import com.google.firebase.auth.ListUsersPage.DefaultUserSource;
import com.google.firebase.auth.ListUsersPage.PageFactory;
import com.google.firebase.auth.UserRecord.CreateRequest;
import com.google.firebase.auth.UserRecord.UpdateRequest;
import com.google.firebase.auth.internal.FirebaseTokenFactory;
import com.google.firebase.internal.CallableOperation;
import com.google.firebase.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.
 *
 * 

You can get an instance of FirebaseAuth via {@link FirebaseAuth#getInstance(FirebaseApp)} and * then use it to perform a variety of authentication-related operations, including generating * 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 { 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 FirebaseApp firebaseApp; private final Supplier tokenFactory; private final Supplier idTokenVerifier; private final Supplier cookieVerifier; private final Supplier userManager; private final JsonFactory jsonFactory; 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(); } /** * Gets the FirebaseAuth instance for the default {@link FirebaseApp}. * * @return The FirebaseAuth instance for the default {@link FirebaseApp}. */ public static FirebaseAuth getInstance() { return FirebaseAuth.getInstance(FirebaseApp.getInstance()); } /** * Gets an instance of FirebaseAuth for a specific {@link FirebaseApp}. * * @param app The {@link FirebaseApp} to get a FirebaseAuth instance for. * @return A FirebaseAuth instance. */ public static synchronized FirebaseAuth getInstance(FirebaseApp app) { FirebaseAuthService service = ImplFirebaseTrampolines.getService(app, SERVICE_ID, FirebaseAuthService.class); if (service == null) { service = ImplFirebaseTrampolines.addService(app, new FirebaseAuthService(app)); } return service.getInstance(); } /** * Creates a new Firebase session cookie from the given ID token and options. The returned JWT * can be set as a server-side session cookie with a custom cookie policy. * * @param idToken The Firebase ID token to exchange for a session cookie. * @param options Additional options required to create the cookie. * @return A Firebase session cookie string. * @throws IllegalArgumentException If the ID token is null or empty, or if options is null. * @throws FirebaseAuthException If an error occurs while generating the session cookie. */ public String createSessionCookie( @NonNull String idToken, @NonNull SessionCookieOptions options) throws FirebaseAuthException { return createSessionCookieOp(idToken, options).call(); } /** * Similar to {@link #createSessionCookie(String, SessionCookieOptions)} but performs the * operation asynchronously. * * @param idToken The Firebase ID token to exchange for a session cookie. * @param options Additional options required to create the cookie. * @return An {@code ApiFuture} which will complete successfully with a session cookie string. * If an error occurs while generating the cookie or if the specified ID token is invalid, * the future throws a {@link FirebaseAuthException}. * @throws IllegalArgumentException If the ID token is null or empty, or if options is null. */ public ApiFuture createSessionCookieAsync( @NonNull String idToken, @NonNull SessionCookieOptions options) { return createSessionCookieOp(idToken, options).callAsync(firebaseApp); } private CallableOperation createSessionCookieOp( final String idToken, final SessionCookieOptions options) { checkNotDestroyed(); checkArgument(!Strings.isNullOrEmpty(idToken), "idToken must not be null or empty"); checkNotNull(options, "options must not be null"); final FirebaseUserManager userManager = getUserManager(); return new CallableOperation() { @Override protected String execute() throws FirebaseAuthException { return userManager.createSessionCookie(idToken, options); } }; } /** * Parses and verifies a Firebase session cookie. * *

If verified successfully, returns a parsed version of the cookie from which the UID and the * other claims can be read. If the cookie is invalid, throws a {@link FirebaseAuthException}. * *

This method does not check whether the cookie has been revoked. See * {@link #verifySessionCookie(String, boolean)}. * * @param cookie A Firebase session cookie string to verify and parse. * @return A {@link FirebaseToken} representing the verified and decoded cookie. */ public FirebaseToken verifySessionCookie(String cookie) throws FirebaseAuthException { return verifySessionCookie(cookie, false); } /** * Parses and verifies a Firebase session cookie. * *

If {@code checkRevoked} is true, additionally verifies that the cookie has not been * revoked. * *

If verified successfully, returns a parsed version of the cookie from which the UID and the * other claims can be read. If the cookie is invalid or has been revoked while * {@code checkRevoked} is true, throws a {@link FirebaseAuthException}. * * @param cookie A Firebase session cookie string to verify and parse. * @param checkRevoked A boolean indicating whether to check if the cookie was explicitly * revoked. * @return A {@link FirebaseToken} representing the verified and decoded cookie. */ public FirebaseToken verifySessionCookie( String cookie, boolean checkRevoked) throws FirebaseAuthException { return verifySessionCookieOp(cookie, checkRevoked).call(); } /** * Similar to {@link #verifySessionCookie(String)} but performs the operation asynchronously. * * @param cookie A Firebase session cookie string to verify and parse. * @return An {@code ApiFuture} which will complete successfully with the parsed cookie, or * unsuccessfully with the failure Exception. */ public ApiFuture verifySessionCookieAsync(String cookie) { return verifySessionCookieAsync(cookie, false); } /** * Similar to {@link #verifySessionCookie(String, boolean)} but performs the operation * asynchronously. * * @param cookie A Firebase session cookie string to verify and parse. * @param checkRevoked A boolean indicating whether to check if the cookie was explicitly * revoked. * @return An {@code ApiFuture} which will complete successfully with the parsed cookie, or * unsuccessfully with the failure Exception. */ public ApiFuture verifySessionCookieAsync(String cookie, boolean checkRevoked) { return verifySessionCookieOp(cookie, checkRevoked).callAsync(firebaseApp); } private CallableOperation verifySessionCookieOp( final String cookie, final boolean checkRevoked) { checkNotDestroyed(); checkArgument(!Strings.isNullOrEmpty(cookie), "Session cookie must not be null or empty"); final FirebaseTokenVerifier sessionCookieVerifier = getSessionCookieVerifier(checkRevoked); return new CallableOperation() { @Override public FirebaseToken execute() throws FirebaseAuthException { return sessionCookieVerifier.verifyToken(cookie); } }; } @VisibleForTesting FirebaseTokenVerifier getSessionCookieVerifier(boolean checkRevoked) { FirebaseTokenVerifier verifier = cookieVerifier.get(); if (checkRevoked) { FirebaseUserManager userManager = getUserManager(); verifier = RevocationCheckDecorator.decorateSessionCookieVerifier(verifier, userManager); } return verifier; } /** * Creates a Firebase custom token for the given UID. This token can then be sent back to a client * application to be used with the * signInWithCustomToken * authentication API. * *

{@link FirebaseApp} must have been initialized with service account credentials to use * call this method. * * @param uid The UID to store in the token. This identifies the user to other Firebase services * (Realtime Database, Firebase Auth, etc.). Should be less than 128 characters. * @return A Firebase custom token string. * @throws IllegalArgumentException If the specified uid is null or empty, or if the app has not * been initialized with service account credentials. * @throws FirebaseAuthException If an error occurs while generating the custom token. */ public String createCustomToken(@NonNull String uid) throws FirebaseAuthException { return createCustomToken(uid, null); } /** * Creates a Firebase custom token for the given UID, containing the specified additional * claims. This token can then be sent back to a client application to be used with the * signInWithCustomToken * authentication API. * *

This method attempts to generate a token using: *

    *
  1. the private key of {@link FirebaseApp}'s service account credentials, if provided at * initialization. *
  2. the IAM service * if a service account email was specified via * {@link com.google.firebase.FirebaseOptions.Builder#setServiceAccountId(String)}. *
  3. the App Identity * service if the code is deployed in the Google App Engine standard environment. *
  4. 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); } } 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 idTokenVerifier; private Supplier 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 idTokenVerifier) { this.idTokenVerifier = idTokenVerifier; return this; } Builder setCookieVerifier(Supplier cookieVerifier) { this.cookieVerifier = cookieVerifier; return this; } Builder setUserManager(Supplier userManager) { this.userManager = userManager; return this; } FirebaseAuth build() { return new FirebaseAuth(this); } } private static class FirebaseAuthService extends FirebaseService { FirebaseAuthService(FirebaseApp app) { super(SERVICE_ID, FirebaseAuth.fromApp(app)); } @Override public void destroy() { instance.destroy(); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy