diff --git a/src/main/java/com/google/firebase/auth/AbstractFirebaseAuth.java b/src/main/java/com/google/firebase/auth/AbstractFirebaseAuth.java index fc9c8df0b..b2e46695e 100644 --- a/src/main/java/com/google/firebase/auth/AbstractFirebaseAuth.java +++ b/src/main/java/com/google/firebase/auth/AbstractFirebaseAuth.java @@ -553,6 +553,64 @@ protected UserRecord execute() throws FirebaseAuthException { }; } + /** + * Gets the user data for the user corresponding to a given provider id. + * + * @param providerId Identifier for the given federated provider, for example, + * "google.com" for the Google provider. + * @param uid The user identifier with the given provider. + * @return A {@link UserRecord} instance. + * @throws IllegalArgumentException If the uid is null or empty, or if + * the providerId is null, empty, or does not belong to a federated provider. + * @throws FirebaseAuthException If an error occurs while retrieving user data. + */ + public UserRecord getUserByProviderUid( + @NonNull String providerId, @NonNull String uid) throws FirebaseAuthException { + return getUserByProviderUidOp(providerId, uid).call(); + } + + /** + * Gets the user data for the user corresponding to a given provider id. + * + * @param providerId Identifer for the given federated provider, for example, + * "google.com" for the Google provider. + * @param uid The user identifier with the given provider. + * @return An {@code ApiFuture} which will complete successfully with a {@link UserRecord} + * instance. If an error occurs while retrieving user data or if the provider ID and uid + * do not correspond to a user, the future throws a {@link FirebaseAuthException}. + * @throws IllegalArgumentException If the uid is null or empty, or if + * the provider ID is null, empty, or does not belong to a federated provider. + */ + public ApiFuture getUserByProviderUidAsync( + @NonNull String providerId, @NonNull String uid) { + return getUserByProviderUidOp(providerId, uid).callAsync(firebaseApp); + } + + private CallableOperation getUserByProviderUidOp( + final String providerId, final String uid) { + checkArgument(!Strings.isNullOrEmpty(providerId), "providerId must not be null or empty"); + checkArgument(!Strings.isNullOrEmpty(uid), "uid must not be null or empty"); + + // Although we don't really advertise it, we want to also handle + // non-federated idps with this call. So if we detect one of them, we'll + // reroute this request appropriately. + if (providerId == "phone") { + return this.getUserByPhoneNumberOp(uid); + } else if (providerId == "email") { + return this.getUserByEmailOp(uid); + } + + checkArgument(!providerId.equals("password") + && !providerId.equals("anonymous"), "providerId must belong to a federated provider"); + final FirebaseUserManager userManager = getUserManager(); + return new CallableOperation() { + @Override + protected UserRecord execute() throws FirebaseAuthException { + return userManager.getUserByProviderUid(providerId, uid); + } + }; + } + /** * Gets a page of users starting from the specified {@code pageToken}. Page size is limited to * 1000 users. diff --git a/src/main/java/com/google/firebase/auth/FirebaseUserManager.java b/src/main/java/com/google/firebase/auth/FirebaseUserManager.java index 554d0179a..fb6ca966d 100644 --- a/src/main/java/com/google/firebase/auth/FirebaseUserManager.java +++ b/src/main/java/com/google/firebase/auth/FirebaseUserManager.java @@ -145,6 +145,15 @@ Set getAccountInfo(@NonNull Collection identifiers) return results; } + UserRecord getUserByProviderUid( + String providerId, String uid) throws FirebaseAuthException { + final Map payload = ImmutableMap.of( + "federatedUserId", ImmutableList.of( + ImmutableMap.builder() + .put("rawId", uid).put("providerId", providerId).build())); + return lookupUserAccount(payload, uid); + } + String createUser(UserRecord.CreateRequest request) throws FirebaseAuthException { GenericJson response = post("/accounts", request.getProperties(), GenericJson.class); return (String) response.get("localId"); diff --git a/src/test/java/com/google/firebase/auth/FirebaseAuthIT.java b/src/test/java/com/google/firebase/auth/FirebaseAuthIT.java index 35fa21d4d..d4349f40e 100644 --- a/src/test/java/com/google/firebase/auth/FirebaseAuthIT.java +++ b/src/test/java/com/google/firebase/auth/FirebaseAuthIT.java @@ -265,6 +265,35 @@ public void testCreateUserWithParams() throws Exception { checkRecreateUser(randomUser.getUid()); } + @Test + public void testLookupUserByPhone() throws Exception { + UserRecord user1 = createTemporaryUser(); + UserRecord user2 = importTemporaryUser(); + + UserRecord lookedUpRecord = auth.getUserByPhoneNumberAsync( + user1.getPhoneNumber()).get(); + assertEquals(user1.getUid(), lookedUpRecord.getUid()); + + lookedUpRecord = auth.getUserByPhoneNumberAsync(user2.getPhoneNumber()).get(); + assertEquals(user2.getUid(), lookedUpRecord.getUid()); + } + + @Test + public void testLookupUserByProviderUid() throws Exception { + UserRecord user = importTemporaryUser(); + + UserRecord lookedUpRecord = auth.getUserByProviderUidAsync( + "google.com", user.getUid() + "_google.com").get(); + assertEquals(user.getUid(), lookedUpRecord.getUid()); + assertEquals(2, lookedUpRecord.getProviderData().length); + List providers = new ArrayList<>(); + for (UserInfo provider : lookedUpRecord.getProviderData()) { + providers.add(provider.getProviderId()); + } + assertTrue(providers.contains("phone")); + assertTrue(providers.contains("google.com")); + } + @Test public void testUserLifecycle() throws Exception { // Create user @@ -929,6 +958,54 @@ public void onSuccess(ListProviderConfigsPage result) { assertNull(error.get()); } + /** + * Create a temporary user. This user will automatically be cleaned up after testing completes. + */ + private UserRecord createTemporaryUser() throws Exception { + RandomUser randomUser = UserTestUtils.generateRandomUserInfo(); + + UserRecord.CreateRequest user = new UserRecord.CreateRequest() + .setUid(randomUser.getUid()) + .setDisplayName("Random User") + .setEmail(randomUser.getEmail()) + .setEmailVerified(true) + .setPhoneNumber(randomUser.getPhoneNumber()) + .setPhotoUrl("https://example.com/photo.png") + .setPassword("password"); + + return temporaryUser.create(user); + } + + /** + * Import a temporary user. This user will automatically be cleaned up after testing completes. + */ + private UserRecord importTemporaryUser() throws Exception { + RandomUser randomUser = UserTestUtils.generateRandomUserInfo(); + + ImportUserRecord.Builder builder = ImportUserRecord.builder() + .setUid(randomUser.getUid()) + .setDisabled(false) + .setEmail(randomUser.getEmail()) + .setEmailVerified(true) + .setPhoneNumber(randomUser.getPhoneNumber()) + .setUserMetadata( + new UserMetadata(/* creationTimestamp= */ 20L, /* lastSignInTimestamp= */ 20L, + /* lastRefreshTimestamp= */ 20L)) + .addUserProvider( + UserProvider.builder() + .setProviderId("google.com") + .setUid(randomUser.getUid() + "_google.com") + .build()); + + ImportUserRecord user = builder.build(); + UserImportResult result = auth.importUsersAsync(ImmutableList.of(user)).get(); + assertEquals(result.getSuccessCount(), 1); + assertEquals(result.getFailureCount(), 0); + temporaryUser.registerUid(randomUser.getUid()); + + return auth.getUserAsync(randomUser.getUid()).get(); + } + private Map parseLinkParameters(String link) throws Exception { Map result = new HashMap<>(); int queryBegin = link.indexOf('?'); diff --git a/src/test/java/com/google/firebase/auth/FirebaseUserManagerTest.java b/src/test/java/com/google/firebase/auth/FirebaseUserManagerTest.java index 772f9ba8d..faeb1180c 100644 --- a/src/test/java/com/google/firebase/auth/FirebaseUserManagerTest.java +++ b/src/test/java/com/google/firebase/auth/FirebaseUserManagerTest.java @@ -349,6 +349,56 @@ public void testInvalidProviderIdentifier() { } } + @Test + public void testGetUserByProviderUidWithInvalidProviderId() throws Exception { + initializeAppForUserManagement(); + try { + FirebaseAuth.getInstance().getUserByProviderUidAsync("", "uid").get(); + fail("No error thrown for invalid request"); + } catch (IllegalArgumentException expected) { + } + } + + @Test + public void testGetUserByProviderUidWithInvalidProviderUid() throws Exception { + initializeAppForUserManagement(); + try { + FirebaseAuth.getInstance().getUserByProviderUidAsync("id", "").get(); + fail("No error thrown for invalid request"); + } catch (IllegalArgumentException expected) { + } + } + + @Test + public void testGetUserByProviderUidWithValidInput() throws Exception { + TestResponseInterceptor interceptor = initializeAppForUserManagement( + TestUtils.loadResource("getUser.json")); + UserRecord userRecord = FirebaseAuth.getInstance() + .getUserByProviderUidAsync("google.com", "google_uid").get(); + checkUserRecord(userRecord); + checkRequestHeaders(interceptor); + } + + @Test + public void testGetUserByProviderUidWithPhone() throws Exception { + TestResponseInterceptor interceptor = initializeAppForUserManagement( + TestUtils.loadResource("getUser.json")); + UserRecord userRecord = FirebaseAuth.getInstance() + .getUserByProviderUidAsync("phone", "+1234567890").get(); + checkUserRecord(userRecord); + checkRequestHeaders(interceptor); + } + + @Test + public void testGetUserByProviderUidWithEmail() throws Exception { + TestResponseInterceptor interceptor = initializeAppForUserManagement( + TestUtils.loadResource("getUser.json")); + UserRecord userRecord = FirebaseAuth.getInstance() + .getUserByProviderUidAsync("email", "testuser@example.com").get(); + checkUserRecord(userRecord); + checkRequestHeaders(interceptor); + } + @Test public void testListUsers() throws Exception { final TestResponseInterceptor interceptor = initializeAppForUserManagement( @@ -2761,7 +2811,7 @@ private static void checkUserRecord(UserRecord userRecord) { assertEquals("http://www.example.com/testuser/photo.png", userRecord.getPhotoUrl()); assertEquals(1234567890, userRecord.getUserMetadata().getCreationTimestamp()); assertEquals(0, userRecord.getUserMetadata().getLastSignInTimestamp()); - assertEquals(2, userRecord.getProviderData().length); + assertEquals(3, userRecord.getProviderData().length); assertFalse(userRecord.isDisabled()); assertTrue(userRecord.isEmailVerified()); assertEquals(1494364393000L, userRecord.getTokensValidAfterTimestamp()); @@ -2781,6 +2831,10 @@ private static void checkUserRecord(UserRecord userRecord) { assertEquals("+1234567890", provider.getPhoneNumber()); assertEquals("phone", provider.getProviderId()); + provider = userRecord.getProviderData()[2]; + assertEquals("google_uid", provider.getUid()); + assertEquals("google.com", provider.getProviderId()); + Map claims = userRecord.getCustomClaims(); assertEquals(2, claims.size()); assertTrue((boolean) claims.get("admin")); diff --git a/src/test/java/com/google/firebase/snippets/FirebaseAuthSnippets.java b/src/test/java/com/google/firebase/snippets/FirebaseAuthSnippets.java index e86496a21..e96170cf9 100644 --- a/src/test/java/com/google/firebase/snippets/FirebaseAuthSnippets.java +++ b/src/test/java/com/google/firebase/snippets/FirebaseAuthSnippets.java @@ -94,6 +94,16 @@ public static void getUserByPhoneNumber( // [END get_user_by_phone] } + public static void getUserByProviderUId( + String providerId, String uid) throws FirebaseAuthException { + // [START get_user_by_provider_uid] + UserRecord userRecord = FirebaseAuth.getInstance().getUserByProviderUid( + providerId, uid); + // See the UserRecord reference doc for the contents of userRecord. + System.out.println("Successfully fetched user data: " + userRecord.getUid()); + // [END get_user_by_provider_uid] + } + public static void createUser() throws FirebaseAuthException { // [START create_user] CreateRequest request = new CreateRequest() diff --git a/src/test/resources/getUser.json b/src/test/resources/getUser.json index 3d57f1082..8d9ff32ef 100644 --- a/src/test/resources/getUser.json +++ b/src/test/resources/getUser.json @@ -17,6 +17,9 @@ "providerId" : "phone", "phoneNumber" : "+1234567890", "rawId" : "+1234567890" + }, { + "providerId" : "google.com", + "rawId" : "google_uid" } ], "photoUrl" : "http://www.example.com/testuser/photo.png", "passwordHash" : "passwordhash", diff --git a/src/test/resources/listUsers.json b/src/test/resources/listUsers.json index 47e169709..c2f99c142 100644 --- a/src/test/resources/listUsers.json +++ b/src/test/resources/listUsers.json @@ -16,6 +16,9 @@ "providerId" : "phone", "phoneNumber" : "+1234567890", "rawId" : "+1234567890" + }, { + "providerId" : "google.com", + "rawId" : "google_uid" } ], "photoUrl" : "http://www.example.com/testuser/photo.png", "passwordHash" : "passwordHash", @@ -43,6 +46,9 @@ "providerId" : "phone", "phoneNumber" : "+1234567890", "rawId" : "+1234567890" + }, { + "providerId" : "google.com", + "rawId" : "google_uid" } ], "photoUrl" : "http://www.example.com/testuser/photo.png", "passwordHash" : "passwordHash",