providerIds) {
null,
null,
true,
- true);
+ true,
+ enableAnonymousUpgrade);
}
+
}
diff --git a/auth/src/test/java/com/firebase/ui/auth/viewmodel/LinkingSocialProviderResponseHandlerTest.java b/auth/src/test/java/com/firebase/ui/auth/viewmodel/LinkingSocialProviderResponseHandlerTest.java
new file mode 100644
index 000000000..068f9c38a
--- /dev/null
+++ b/auth/src/test/java/com/firebase/ui/auth/viewmodel/LinkingSocialProviderResponseHandlerTest.java
@@ -0,0 +1,249 @@
+package com.firebase.ui.auth.viewmodel;
+
+import android.arch.lifecycle.Observer;
+
+import com.firebase.ui.auth.FirebaseAuthAnonymousUpgradeException;
+import com.firebase.ui.auth.IdpResponse;
+import com.firebase.ui.auth.data.model.FlowParameters;
+import com.firebase.ui.auth.data.model.Resource;
+import com.firebase.ui.auth.data.model.User;
+import com.firebase.ui.auth.testhelpers.AutoCompleteTask;
+import com.firebase.ui.auth.testhelpers.AutoContinueTask;
+import com.firebase.ui.auth.testhelpers.FakeAuthResult;
+import com.firebase.ui.auth.testhelpers.ResourceMatchers;
+import com.firebase.ui.auth.testhelpers.TestConstants;
+import com.firebase.ui.auth.testhelpers.TestHelper;
+import com.firebase.ui.auth.util.data.AuthOperationManager;
+import com.firebase.ui.auth.util.data.ProviderUtils;
+import com.firebase.ui.auth.viewmodel.idp.LinkingSocialProviderResponseHandler;
+import com.google.firebase.auth.AuthCredential;
+import com.google.firebase.auth.FacebookAuthProvider;
+import com.google.firebase.auth.FirebaseAuth;
+import com.google.firebase.auth.FirebaseUser;
+import com.google.firebase.auth.GoogleAuthCredential;
+import com.google.firebase.auth.GoogleAuthProvider;
+import com.google.firebase.auth.PhoneAuthCredential;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.InOrder;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+import java.util.Collections;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.argThat;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+/**
+ * Unit tests for {@link LinkingSocialProviderResponseHandler}.
+ *
+ * This handler is used by WelcomeBackIdpPrompt. This prompt handles the case where a user attempts
+ * to log in with an IDP, but there is a user that has a different IDP with the same email.
+ *
+ * In this case, the handler will link the provider to the existing firebase user. When the user
+ * is anonymous, a triple linking case occurs.
+ */
+@RunWith(RobolectricTestRunner.class)
+public class LinkingSocialProviderResponseHandlerTest {
+
+ @Mock FirebaseAuth mMockAuth;
+ @Mock FirebaseAuth mScratchMockAuth;
+
+ @Mock FirebaseUser mMockUser;
+ @Mock PhoneAuthCredential mCredential;
+ @Mock Observer> mResponseObserver;
+
+ private LinkingSocialProviderResponseHandler mHandler;
+
+
+ @Before
+ public void setUp() {
+ TestHelper.initialize();
+ MockitoAnnotations.initMocks(this);
+
+ mHandler = new LinkingSocialProviderResponseHandler(RuntimeEnvironment.application);
+ FlowParameters testParams = TestHelper.getFlowParameters(Collections.singletonList(
+ GoogleAuthProvider.PROVIDER_ID));
+ mHandler.initializeForTesting(testParams, mMockAuth, null, null);
+ }
+
+ @Test
+ public void testSignIn_withSameIdp_expectSuccess() {
+ mHandler.getOperation().observeForever(mResponseObserver);
+
+ // Fake social response from Google
+ IdpResponse response = new IdpResponse.Builder(new User.Builder(
+ GoogleAuthProvider.PROVIDER_ID, TestConstants.EMAIL).build())
+ .setToken(TestConstants.TOKEN)
+ .build();
+
+ when(mMockAuth.signInWithCredential(any(AuthCredential.class)))
+ .thenReturn(AutoCompleteTask.forSuccess(FakeAuthResult.INSTANCE));
+
+ mHandler.startSignIn(response);
+
+ verify(mMockAuth).signInWithCredential(any(GoogleAuthCredential.class));
+
+ InOrder inOrder = inOrder(mResponseObserver);
+ inOrder.verify(mResponseObserver)
+ .onChanged(argThat(ResourceMatchers.isLoading()));
+ inOrder.verify(mResponseObserver)
+ .onChanged(argThat(ResourceMatchers.isSuccess()));
+ }
+
+
+ @Test
+ public void testSignIn_anonymousUpgradeEnabledWithSameIdp_expectMergeFailure() {
+ mHandler.getOperation().observeForever(mResponseObserver);
+ setupAnonymousUpgrade();
+
+ // Fake social response from Google
+ IdpResponse response = new IdpResponse.Builder(new User.Builder(
+ GoogleAuthProvider.PROVIDER_ID, TestConstants.EMAIL).build())
+ .setToken(TestConstants.TOKEN)
+ .build();
+
+ mHandler.startSignIn(response);
+
+ // Since we are signing in with the same IDP and anonymous upgrade is enabled, a merge
+ // failure should occur without any RPC calls
+
+ AuthCredential credential = GoogleAuthProvider.getCredential(TestConstants.TOKEN, null);
+
+ InOrder inOrder = inOrder(mResponseObserver);
+ inOrder.verify(mResponseObserver)
+ .onChanged(argThat(ResourceMatchers.isLoading()));
+
+ ArgumentCaptor> resolveCaptor =
+ ArgumentCaptor.forClass(Resource.class);
+ inOrder.verify(mResponseObserver).onChanged(resolveCaptor.capture());
+
+ FirebaseAuthAnonymousUpgradeException e =
+ (FirebaseAuthAnonymousUpgradeException) resolveCaptor.getValue().getException();
+
+ GoogleAuthCredential responseCredential =
+ (GoogleAuthCredential) e.getResponse().getCredentialForLinking();
+
+ assertThat(responseCredential.getProvider()).isEqualTo(credential.getProvider());
+ assertThat(responseCredential.getSignInMethod()).isEqualTo(credential.getSignInMethod());
+
+ }
+
+ @Test
+ public void testSignIn_withDifferentIdp_expectSuccess() {
+ mHandler.getOperation().observeForever(mResponseObserver);
+
+ // We're going to fake a sign in with facebook, where the email belongs
+ // to an existing account with a Google provider.
+
+ // Fake social response from Google
+ IdpResponse response = new IdpResponse.Builder(new User.Builder(
+ GoogleAuthProvider.PROVIDER_ID, TestConstants.EMAIL).build())
+ .setToken(TestConstants.TOKEN)
+ .build();
+
+ // Set facebook credential
+ AuthCredential facebookAuthCredential =
+ FacebookAuthProvider.getCredential(TestConstants.TOKEN);
+ mHandler.setRequestedSignInCredentialForEmail(facebookAuthCredential, TestConstants.EMAIL);
+
+
+ // mock sign in with Google credential to always work
+ when(mMockAuth.signInWithCredential(any(GoogleAuthCredential.class)))
+ .thenReturn(AutoCompleteTask.forSuccess(FakeAuthResult.INSTANCE));
+
+ // Mock linking with Facebook to always work
+ when(FakeAuthResult.INSTANCE.getUser().linkWithCredential(facebookAuthCredential))
+ .thenReturn(new AutoContinueTask<>(FakeAuthResult.INSTANCE,
+ FakeAuthResult.INSTANCE,
+ true,
+ null));
+
+ mHandler.startSignIn(response);
+
+ verify(mMockAuth).signInWithCredential(any(GoogleAuthCredential.class));
+ verify(FakeAuthResult.INSTANCE.getUser()).linkWithCredential(facebookAuthCredential);
+
+ InOrder inOrder = inOrder(mResponseObserver);
+ inOrder.verify(mResponseObserver)
+ .onChanged(argThat(ResourceMatchers.isLoading()));
+ inOrder.verify(mResponseObserver)
+ .onChanged(argThat(ResourceMatchers.isSuccess()));
+ }
+
+ @Test
+ public void testSignIn_anonymousUpgradeEnabledWithDifferentIdp_expectMergeFailure() {
+ mHandler.getOperation().observeForever(mResponseObserver);
+ setupAnonymousUpgrade();
+
+ // We're going to fake a sign in with facebook, where the email belongs
+ // to an existing account with a Google provider.
+ // We need to link Facebook to this account, and then a merge failure should occur
+ // so that the developer can handle it.
+ // Before we can link, they need to sign in with Google to prove they own the account.
+
+ // Fake social response from Google
+ IdpResponse response = new IdpResponse.Builder(new User.Builder(
+ GoogleAuthProvider.PROVIDER_ID, TestConstants.EMAIL).build())
+ .setToken(TestConstants.TOKEN)
+ .build();
+
+ // Set facebook credential
+ AuthCredential facebookAuthCredential =
+ FacebookAuthProvider.getCredential(TestConstants.TOKEN);
+ mHandler.setRequestedSignInCredentialForEmail(facebookAuthCredential, TestConstants.EMAIL);
+
+ when(mScratchMockAuth.signInWithCredential(any(GoogleAuthCredential.class)))
+ .thenReturn(AutoCompleteTask.forSuccess(FakeAuthResult.INSTANCE));
+
+ // Mock linking with Facebook to always work
+ when(FakeAuthResult.INSTANCE.getUser().linkWithCredential(facebookAuthCredential))
+ .thenReturn(new AutoContinueTask<>(FakeAuthResult.INSTANCE,
+ FakeAuthResult.INSTANCE,
+ true,
+ null));
+
+ mHandler.startSignIn(response);
+
+ verify(mScratchMockAuth).signInWithCredential(any(GoogleAuthCredential.class));
+ verify(FakeAuthResult.INSTANCE.getUser()).linkWithCredential(facebookAuthCredential);
+
+ InOrder inOrder = inOrder(mResponseObserver);
+ inOrder.verify(mResponseObserver)
+ .onChanged(argThat(ResourceMatchers.isLoading()));
+
+ ArgumentCaptor> resolveCaptor =
+ ArgumentCaptor.forClass(Resource.class);
+ inOrder.verify(mResponseObserver).onChanged(resolveCaptor.capture());
+
+ // Merge failure should occur after successful linking
+ FirebaseAuthAnonymousUpgradeException e =
+ (FirebaseAuthAnonymousUpgradeException) resolveCaptor.getValue().getException();
+
+ AuthCredential credential = ProviderUtils.getAuthCredential(response);
+ GoogleAuthCredential responseCredential =
+ (GoogleAuthCredential) e.getResponse().getCredentialForLinking();
+
+ assertThat(responseCredential.getProvider()).isEqualTo(credential.getProvider());
+ assertThat(responseCredential.getSignInMethod()).isEqualTo(credential.getSignInMethod());
+
+ }
+
+ private void setupAnonymousUpgrade() {
+ FlowParameters testParams = TestHelper.getFlowParameters(Collections.singletonList(
+ GoogleAuthProvider.PROVIDER_ID), true);
+ mHandler.initializeForTesting(testParams, mMockAuth, null, null);
+ when(mMockAuth.getCurrentUser()).thenReturn(mMockUser);
+ when(mMockUser.isAnonymous()).thenReturn(true);
+ AuthOperationManager.getInstance().mScratchAuth = mScratchMockAuth;
+ }
+}
diff --git a/auth/src/test/java/com/firebase/ui/auth/viewmodel/PhoneProviderResponseHandlerTest.java b/auth/src/test/java/com/firebase/ui/auth/viewmodel/PhoneProviderResponseHandlerTest.java
new file mode 100644
index 000000000..dd55f1937
--- /dev/null
+++ b/auth/src/test/java/com/firebase/ui/auth/viewmodel/PhoneProviderResponseHandlerTest.java
@@ -0,0 +1,140 @@
+package com.firebase.ui.auth.viewmodel;
+
+
+import android.arch.lifecycle.Observer;
+
+import com.firebase.ui.auth.FirebaseAuthAnonymousUpgradeException;
+import com.firebase.ui.auth.IdpResponse;
+import com.firebase.ui.auth.data.model.FlowParameters;
+import com.firebase.ui.auth.data.model.Resource;
+import com.firebase.ui.auth.data.model.User;
+import com.firebase.ui.auth.testhelpers.AutoCompleteTask;
+import com.firebase.ui.auth.testhelpers.FakeAuthResult;
+import com.firebase.ui.auth.testhelpers.ResourceMatchers;
+import com.firebase.ui.auth.testhelpers.TestConstants;
+import com.firebase.ui.auth.testhelpers.TestHelper;
+import com.firebase.ui.auth.viewmodel.phone.PhoneProviderResponseHandler;
+import com.google.firebase.auth.AuthResult;
+import com.google.firebase.auth.FirebaseAuth;
+import com.google.firebase.auth.FirebaseAuthUserCollisionException;
+import com.google.firebase.auth.FirebaseUser;
+import com.google.firebase.auth.GoogleAuthCredential;
+import com.google.firebase.auth.PhoneAuthCredential;
+import com.google.firebase.auth.PhoneAuthProvider;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.InOrder;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+import java.util.Collections;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.ArgumentMatchers.argThat;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+/**
+ * Unit tests for {@link PhoneProviderResponseHandler}.
+ */
+@RunWith(RobolectricTestRunner.class)
+public class PhoneProviderResponseHandlerTest {
+
+ @Mock FirebaseAuth mMockAuth;
+ @Mock FirebaseUser mMockUser;
+ @Mock PhoneAuthCredential mCredential;
+ @Mock Observer> mResponseObserver;
+
+ private PhoneProviderResponseHandler mHandler;
+
+ @Before
+ public void setUp() {
+ TestHelper.initialize();
+ MockitoAnnotations.initMocks(this);
+
+ mHandler = new PhoneProviderResponseHandler(RuntimeEnvironment.application);
+ FlowParameters testParams = TestHelper.getFlowParameters(Collections.singletonList(
+ PhoneAuthProvider.PROVIDER_ID));
+ mHandler.initializeForTesting(testParams, mMockAuth, null, null);
+ }
+
+ @Test
+ public void testSignIn_withValidCredentialAndNewUser_expectSuccess() {
+ mHandler.getOperation().observeForever(mResponseObserver);
+
+ when(mMockAuth.signInWithCredential(mCredential))
+ .thenReturn(AutoCompleteTask.forSuccess(FakeAuthResult.INSTANCE));
+
+ IdpResponse response = new IdpResponse.Builder(new User.Builder(
+ PhoneAuthProvider.PROVIDER_ID, TestConstants.EMAIL).build())
+ .build();
+
+ mHandler.startSignIn(mCredential, response);
+ verify(mMockAuth).signInWithCredential(mCredential);
+ verify(mResponseObserver).onChanged(argThat(ResourceMatchers.isSuccess()));
+ }
+
+ @Test
+ public void testSignIn_autoUpgradeAnonymousEnabledWithNewUser_expectSuccess() {
+ mHandler.getOperation().observeForever(mResponseObserver);
+ setupAnonymousUpgrade();
+
+ when(mMockAuth.getCurrentUser().linkWithCredential(mCredential))
+ .thenReturn(AutoCompleteTask.forSuccess(FakeAuthResult.INSTANCE));
+
+ IdpResponse response = new IdpResponse.Builder(new User.Builder(
+ PhoneAuthProvider.PROVIDER_ID, TestConstants.EMAIL).build())
+ .build();
+
+ mHandler.startSignIn(mCredential, response);
+
+ verify(mMockAuth.getCurrentUser()).linkWithCredential(mCredential);
+ verify(mResponseObserver).onChanged(argThat(ResourceMatchers.isSuccess()));
+ }
+
+
+ @Test
+ public void testSignIn_autoUpgradeAnonymousEnabledWithExistingUser_expectMergeFailure() {
+ mHandler.getOperation().observeForever(mResponseObserver);
+ setupAnonymousUpgrade();
+
+ when(mMockAuth.getCurrentUser().linkWithCredential(mCredential))
+ .thenReturn(AutoCompleteTask.forFailure(
+ new FirebaseAuthUserCollisionException("foo", "bar", mCredential)));
+
+ IdpResponse response = new IdpResponse.Builder(new User.Builder(
+ PhoneAuthProvider.PROVIDER_ID, TestConstants.EMAIL).build())
+ .build();
+
+ mHandler.startSignIn(mCredential, response);
+
+ verify(mMockAuth.getCurrentUser()).linkWithCredential(mCredential);
+
+ InOrder inOrder = inOrder(mResponseObserver);
+ inOrder.verify(mResponseObserver)
+ .onChanged(argThat(ResourceMatchers.isLoading()));
+
+ ArgumentCaptor> resolveCaptor =
+ ArgumentCaptor.forClass(Resource.class);
+ inOrder.verify(mResponseObserver).onChanged(resolveCaptor.capture());
+
+ FirebaseAuthAnonymousUpgradeException e =
+ (FirebaseAuthAnonymousUpgradeException) resolveCaptor.getValue().getException();
+
+ assertThat(e.getResponse().getCredentialForLinking()).isNotNull();
+ }
+
+ private void setupAnonymousUpgrade() {
+ FlowParameters testParams = TestHelper.getFlowParameters(Collections.singletonList(
+ PhoneAuthProvider.PROVIDER_ID), true);
+ mHandler.initializeForTesting(testParams, mMockAuth, null, null);
+ when(mMockAuth.getCurrentUser()).thenReturn(mMockUser);
+ when(mMockUser.isAnonymous()).thenReturn(true);
+ }
+}
diff --git a/auth/src/test/java/com/firebase/ui/auth/viewmodel/SocialProviderResponseHandlerTest.java b/auth/src/test/java/com/firebase/ui/auth/viewmodel/SocialProviderResponseHandlerTest.java
index e5f1348b6..36bf76bee 100644
--- a/auth/src/test/java/com/firebase/ui/auth/viewmodel/SocialProviderResponseHandlerTest.java
+++ b/auth/src/test/java/com/firebase/ui/auth/viewmodel/SocialProviderResponseHandlerTest.java
@@ -4,6 +4,7 @@
import android.arch.lifecycle.Observer;
import com.firebase.ui.auth.AuthUI;
+import com.firebase.ui.auth.FirebaseAuthAnonymousUpgradeException;
import com.firebase.ui.auth.IdpResponse;
import com.firebase.ui.auth.data.model.FlowParameters;
import com.firebase.ui.auth.data.model.IntentRequiredException;
@@ -15,6 +16,8 @@
import com.firebase.ui.auth.testhelpers.ResourceMatchers;
import com.firebase.ui.auth.testhelpers.TestConstants;
import com.firebase.ui.auth.testhelpers.TestHelper;
+import com.firebase.ui.auth.ui.email.WelcomeBackPasswordPrompt;
+import com.firebase.ui.auth.ui.idp.WelcomeBackIdpPrompt;
import com.firebase.ui.auth.viewmodel.idp.SocialProviderResponseHandler;
import com.firebase.ui.auth.viewmodel.smartlock.SmartLockHandler;
import com.google.firebase.auth.AuthCredential;
@@ -23,9 +26,9 @@
import com.google.firebase.auth.FacebookAuthProvider;
import com.google.firebase.auth.FirebaseAuth;
import com.google.firebase.auth.FirebaseAuthUserCollisionException;
+import com.google.firebase.auth.FirebaseUser;
import com.google.firebase.auth.GoogleAuthProvider;
import com.google.firebase.auth.SignInMethodQueryResult;
-import com.google.firebase.auth.UserProfileChangeRequest;
import org.junit.Before;
import org.junit.Test;
@@ -37,8 +40,10 @@
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
+import java.util.Arrays;
import java.util.Collections;
+import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Mockito.inOrder;
@@ -51,6 +56,7 @@
@RunWith(RobolectricTestRunner.class)
public class SocialProviderResponseHandlerTest {
@Mock FirebaseAuth mMockAuth;
+ @Mock FirebaseUser mUser;
@Mock Observer> mResultObserver;
private SocialProviderResponseHandler mHandler;
@@ -73,8 +79,6 @@ public void testSignInIdp_success() {
when(mMockAuth.signInWithCredential(any(AuthCredential.class)))
.thenReturn(AutoCompleteTask.forSuccess(FakeAuthResult.INSTANCE));
- when(FakeAuthResult.INSTANCE.getUser().updateProfile(any(UserProfileChangeRequest.class)))
- .thenReturn(AutoCompleteTask.forSuccess(null));
IdpResponse response = new IdpResponse.Builder(new User.Builder(
GoogleAuthProvider.PROVIDER_ID, TestConstants.EMAIL).build())
@@ -83,8 +87,9 @@ public void testSignInIdp_success() {
mHandler.startSignIn(response);
- InOrder inOrder = inOrder(mResultObserver);
+ verify(mMockAuth).signInWithCredential(any(AuthCredential.class));
+ InOrder inOrder = inOrder(mResultObserver);
inOrder.verify(mResultObserver)
.onChanged(argThat(ResourceMatchers.isLoading()));
inOrder.verify(mResultObserver)
@@ -132,8 +137,10 @@ public void testSignInIdp_resolution() {
mHandler.startSignIn(response);
- InOrder inOrder = inOrder(mResultObserver);
+ verify(mMockAuth).signInWithCredential(any(AuthCredential.class));
+ verify(mMockAuth).fetchSignInMethodsForEmail(any(String.class));
+ InOrder inOrder = inOrder(mResultObserver);
inOrder.verify(mResultObserver)
.onChanged(argThat(ResourceMatchers.isLoading()));
@@ -150,4 +157,166 @@ public void testSignInIdp_resolution() {
inOrder.verify(mResultObserver)
.onChanged(argThat(ResourceMatchers.isSuccess()));
}
+
+
+ @Test
+ public void testSignInIdp_anonymousUserUpgradeEnabledAndNewUser_expectSuccess() {
+ mHandler.getOperation().observeForever(mResultObserver);
+ setupAnonymousUpgrade();
+
+ when(mMockAuth.getCurrentUser().linkWithCredential(any(AuthCredential.class)))
+ .thenReturn(AutoCompleteTask.forSuccess(FakeAuthResult.INSTANCE));
+
+ IdpResponse response = new IdpResponse.Builder(new User.Builder(
+ GoogleAuthProvider.PROVIDER_ID, TestConstants.EMAIL).build())
+ .setToken(TestConstants.TOKEN)
+ .build();
+
+ mHandler.startSignIn(response);
+
+ verify(mMockAuth.getCurrentUser()).linkWithCredential(any(AuthCredential.class));
+
+ InOrder inOrder = inOrder(mResultObserver);
+ inOrder.verify(mResultObserver)
+ .onChanged(argThat(ResourceMatchers.isLoading()));
+ inOrder.verify(mResultObserver)
+ .onChanged(argThat(ResourceMatchers.isSuccess()));
+ }
+
+ @Test
+ public void testSignInIdp_anonymousUserUpgradeEnabledAndExistingUserWithSameIdp_expectMergeFailure() {
+ mHandler.getOperation().observeForever(mResultObserver);
+ setupAnonymousUpgrade();
+
+ when(mMockAuth.getCurrentUser().linkWithCredential(any(AuthCredential.class)))
+ .thenReturn(AutoCompleteTask.forFailure(
+ new FirebaseAuthUserCollisionException("foo", "bar")));
+
+ // Case 1: Anon user signing in with a Google credential that belongs to an existing user.
+ when(mMockAuth.fetchSignInMethodsForEmail(any(String.class)))
+ .thenReturn(AutoCompleteTask.forSuccess(
+ new FakeSignInMethodQueryResult(Arrays.asList(
+ GoogleAuthProvider.GOOGLE_SIGN_IN_METHOD,
+ FacebookAuthProvider.FACEBOOK_SIGN_IN_METHOD))));
+
+
+ IdpResponse response = new IdpResponse.Builder(new User.Builder(
+ GoogleAuthProvider.PROVIDER_ID, TestConstants.EMAIL).build())
+ .setToken(TestConstants.TOKEN)
+ .build();
+
+ mHandler.startSignIn(response);
+
+ verify(mMockAuth.getCurrentUser()).linkWithCredential(any(AuthCredential.class));
+
+ InOrder inOrder = inOrder(mResultObserver);
+ inOrder.verify(mResultObserver)
+ .onChanged(argThat(ResourceMatchers.isLoading()));
+
+ ArgumentCaptor> resolveCaptor =
+ ArgumentCaptor.forClass(Resource.class);
+ inOrder.verify(mResultObserver).onChanged(resolveCaptor.capture());
+
+ FirebaseAuthAnonymousUpgradeException e =
+ (FirebaseAuthAnonymousUpgradeException) resolveCaptor.getValue().getException();
+
+ assertThat(e.getResponse().getCredentialForLinking()).isNotNull();
+ }
+
+ @Test
+ public void testSignInIdp_anonymousUserUpgradeEnabledAndExistingIdpUserWithDifferentIdp_expectMergeFailure() {
+ mHandler.getOperation().observeForever(mResultObserver);
+ setupAnonymousUpgrade();
+
+ when(mMockAuth.getCurrentUser().linkWithCredential(any(AuthCredential.class)))
+ .thenReturn(AutoCompleteTask.forFailure(
+ new FirebaseAuthUserCollisionException("foo", "bar")));
+
+ // Case 2 & 3: trying to link with an account that has 1 idp, which is different from the
+ // one that we're trying to log in with
+ when(mMockAuth.fetchSignInMethodsForEmail(any(String.class)))
+ .thenReturn(AutoCompleteTask.forSuccess(
+ new FakeSignInMethodQueryResult(Collections.singletonList(
+ FacebookAuthProvider.FACEBOOK_SIGN_IN_METHOD))));
+
+ IdpResponse response = new IdpResponse.Builder(new User.Builder(
+ GoogleAuthProvider.PROVIDER_ID, TestConstants.EMAIL).build())
+ .setToken(TestConstants.TOKEN)
+ .build();
+
+ mHandler.startSignIn(response);
+
+ verify(mMockAuth.getCurrentUser()).linkWithCredential(any(AuthCredential.class));
+
+ InOrder inOrder = inOrder(mResultObserver);
+ inOrder.verify(mResultObserver)
+ .onChanged(argThat(ResourceMatchers.isLoading()));
+
+ ArgumentCaptor> resolveCaptor =
+ ArgumentCaptor.forClass(Resource.class);
+ inOrder.verify(mResultObserver).onChanged(resolveCaptor.capture());
+
+ // Make sure that we are trying to start the WelcomeBackIdpPrompt activity
+ IntentRequiredException e =
+ ((IntentRequiredException) resolveCaptor.getValue().getException());
+ assertThat(e.getIntent().getComponent().getClassName())
+ .isEqualTo(WelcomeBackIdpPrompt.class.toString().split(" ")[1]);
+
+ assertThat(IdpResponse.fromResultIntent(e.getIntent())).isEqualTo(response);
+
+ }
+
+ @Test
+ public void testSignInIdp_anonymousUserUpgradeEnabledAndExistingPasswordUserWithDifferentIdp_expectMergeFailure() {
+ mHandler.getOperation().observeForever(mResultObserver);
+ setupAnonymousUpgrade();
+
+ when(mMockAuth.getCurrentUser().linkWithCredential(any(AuthCredential.class)))
+ .thenReturn(AutoCompleteTask.forFailure(
+ new FirebaseAuthUserCollisionException("foo", "bar")));
+
+ // Case 2 & 3: trying to link with an account that has 1 password provider and logging in
+ // with an idp that has the same email
+ when(mMockAuth.fetchSignInMethodsForEmail(any(String.class)))
+ .thenReturn(AutoCompleteTask.forSuccess(
+ new FakeSignInMethodQueryResult(Collections.singletonList(
+ EmailAuthProvider.EMAIL_PASSWORD_SIGN_IN_METHOD))));
+
+ IdpResponse response = new IdpResponse.Builder(new User.Builder(
+ FacebookAuthProvider.PROVIDER_ID, TestConstants.EMAIL).build())
+ .setToken(TestConstants.TOKEN)
+ .setSecret(TestConstants.SECRET)
+ .build();
+
+ mHandler.startSignIn(response);
+
+ verify(mMockAuth.getCurrentUser()).linkWithCredential(any(AuthCredential.class));
+
+ InOrder inOrder = inOrder(mResultObserver);
+ inOrder.verify(mResultObserver)
+ .onChanged(argThat(ResourceMatchers.isLoading()));
+
+ ArgumentCaptor> resolveCaptor =
+ ArgumentCaptor.forClass(Resource.class);
+ inOrder.verify(mResultObserver).onChanged(resolveCaptor.capture());
+
+ // Make sure that we are trying to start the WelcomeBackIdpPrompt activity
+ IntentRequiredException e =
+ ((IntentRequiredException) resolveCaptor.getValue().getException());
+ assertThat(e.getIntent().getComponent().getClassName())
+ .isEqualTo(WelcomeBackPasswordPrompt.class.toString().split(" ")[1]);
+
+ assertThat(IdpResponse.fromResultIntent(e.getIntent())).isEqualTo(response);
+ }
+
+ private void setupAnonymousUpgrade() {
+ // enableAnonymousUpgrade must be set to true
+ FlowParameters testParams = TestHelper.getFlowParameters(Collections.singletonList(
+ EmailAuthProvider.PROVIDER_ID), /* enableAnonymousUpgrade */ true);
+ mHandler.initializeForTesting(testParams, mMockAuth, null, null);
+
+ when(mUser.isAnonymous()).thenReturn(true);
+ when(mMockAuth.getCurrentUser()).thenReturn(mUser);
+ }
+
}
diff --git a/auth/src/test/java/com/firebase/ui/auth/viewmodel/WelcomeBackPasswordHandlerTest.java b/auth/src/test/java/com/firebase/ui/auth/viewmodel/WelcomeBackPasswordHandlerTest.java
index 40f2b8a3c..d584babd4 100644
--- a/auth/src/test/java/com/firebase/ui/auth/viewmodel/WelcomeBackPasswordHandlerTest.java
+++ b/auth/src/test/java/com/firebase/ui/auth/viewmodel/WelcomeBackPasswordHandlerTest.java
@@ -2,6 +2,7 @@
import android.arch.lifecycle.Observer;
+import com.firebase.ui.auth.FirebaseAuthAnonymousUpgradeException;
import com.firebase.ui.auth.IdpResponse;
import com.firebase.ui.auth.data.model.FlowParameters;
import com.firebase.ui.auth.data.model.Resource;
@@ -12,18 +13,23 @@
import com.firebase.ui.auth.testhelpers.ResourceMatchers;
import com.firebase.ui.auth.testhelpers.TestConstants;
import com.firebase.ui.auth.testhelpers.TestHelper;
+import com.firebase.ui.auth.util.data.AuthOperationManager;
import com.firebase.ui.auth.viewmodel.email.WelcomeBackPasswordHandler;
import com.google.android.gms.auth.api.credentials.Credential;
import com.google.android.gms.auth.api.credentials.CredentialsClient;
import com.google.firebase.auth.AuthCredential;
import com.google.firebase.auth.AuthResult;
+import com.google.firebase.auth.EmailAuthCredential;
import com.google.firebase.auth.EmailAuthProvider;
import com.google.firebase.auth.FacebookAuthProvider;
import com.google.firebase.auth.FirebaseAuth;
+import com.google.firebase.auth.FirebaseUser;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
@@ -31,8 +37,10 @@
import java.util.Collections;
+import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.argThat;
+import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -43,8 +51,9 @@
public class WelcomeBackPasswordHandlerTest {
@Mock FirebaseAuth mMockAuth;
+ @Mock FirebaseAuth mScratchMockAuth;
+ @Mock FirebaseUser mUser;
@Mock CredentialsClient mMockCredentials;
-
@Mock Observer> mResponseObserver;
private WelcomeBackPasswordHandler mHandler;
@@ -129,4 +138,117 @@ public void testSignIn_propagatesFailure() {
// Verify that we get a failure event
verify(mResponseObserver).onChanged(argThat(ResourceMatchers.isFailure()));
}
+
+ @Test
+ public void testSignIn_anonymousUserUpgradeEnabledWithEmailFlow_expectValidateCredentialAndMergeFailure() {
+ setupAnonymousUpgrade();
+ mHandler.getOperation().observeForever(mResponseObserver);
+
+ // Fake user with password provider
+ User user = new User.Builder(EmailAuthProvider.PROVIDER_ID, TestConstants.EMAIL)
+ .build();
+ IdpResponse response = new IdpResponse.Builder(user)
+ .build();
+
+ // Need to control FirebaseAuth's return values
+ AuthOperationManager authOperationManager = AuthOperationManager.getInstance();
+ authOperationManager.mScratchAuth = mScratchMockAuth;
+
+ when(mScratchMockAuth.signInWithCredential(any(AuthCredential.class)))
+ .thenReturn(AutoCompleteTask.forSuccess(FakeAuthResult.INSTANCE));
+
+ // Kick off the sign in flow
+ mHandler.startSignIn(TestConstants.EMAIL, TestConstants.PASSWORD, response, null);
+
+ // Verify that signInWithCredential was called
+ ArgumentCaptor credentialCaptor
+ = ArgumentCaptor.forClass(EmailAuthCredential.class);
+ verify(mScratchMockAuth).signInWithCredential(credentialCaptor.capture());
+
+ EmailAuthCredential capturedCredential = credentialCaptor.getValue();
+ assertThat(capturedCredential.getEmail()).isEqualTo(TestConstants.EMAIL);
+ assertThat(capturedCredential.getPassword()).isEqualTo(TestConstants.PASSWORD);
+
+ verifyMergeFailure();
+ }
+
+ @Test
+ public void testSignIn_anonymousUserUpgradeEnabledWithSocialFlow_expectSafeLinksIdpCredentialAndMergeFailure() {
+ setupAnonymousUpgrade();
+
+ mHandler.getOperation().observeForever(mResponseObserver);
+
+ // Fake social response from Facebook
+ User user = new User.Builder(FacebookAuthProvider.PROVIDER_ID, TestConstants.EMAIL)
+ .build();
+
+ IdpResponse response = new IdpResponse.Builder(user)
+ .setToken(TestConstants.TOKEN)
+ .setSecret(TestConstants.SECRET)
+ .build();
+
+ AuthCredential credential = FacebookAuthProvider.getCredential(TestConstants.TOKEN);
+
+ // Need to control FirebaseAuth's return values
+ AuthOperationManager authOperationManager = AuthOperationManager.getInstance();
+ authOperationManager.mScratchAuth = mScratchMockAuth;
+
+ when(mScratchMockAuth.signInWithCredential(any(AuthCredential.class)))
+ .thenReturn(AutoCompleteTask.forSuccess(FakeAuthResult.INSTANCE));
+
+ // Mock linking with Facebook to always work
+ when(FakeAuthResult.INSTANCE.getUser().linkWithCredential(credential))
+ .thenReturn(new AutoContinueTask<>(FakeAuthResult.INSTANCE,
+ FakeAuthResult.INSTANCE,
+ true,
+ null));
+
+ // Kick off the sign in flow
+ mHandler.startSignIn(TestConstants.EMAIL, TestConstants.PASSWORD, response, credential);
+
+ // Verify that signInWithCredential was called
+ ArgumentCaptor credentialCaptor
+ = ArgumentCaptor.forClass(EmailAuthCredential.class);
+ verify(mScratchMockAuth).signInWithCredential(credentialCaptor.capture());
+
+ EmailAuthCredential capturedCredential = credentialCaptor.getValue();
+ assertThat(capturedCredential.getEmail()).isEqualTo(TestConstants.EMAIL);
+ assertThat(capturedCredential.getPassword()).isEqualTo(TestConstants.PASSWORD);
+
+ // Verify that account linking is attempted
+ verify(FakeAuthResult.INSTANCE.getUser()).linkWithCredential(credential);
+
+ verifyMergeFailure();
+ }
+
+ private void setupAnonymousUpgrade() {
+ // enableAnonymousUpgrade must be set to true
+ FlowParameters testParams = TestHelper.getFlowParameters(Collections.singletonList(
+ EmailAuthProvider.PROVIDER_ID), /* enableAnonymousUpgrade */ true);
+ mHandler.initializeForTesting(testParams, mMockAuth, mMockCredentials, null);
+
+ // Mock isAnonymous() to return true so canUpgradeAnonymous will return true
+ when(mUser.isAnonymous()).thenReturn(true);
+ when(mMockAuth.getCurrentUser()).thenReturn(mUser);
+ }
+
+ private void verifyMergeFailure() {
+ InOrder inOrder = inOrder(mResponseObserver);
+
+ inOrder.verify(mResponseObserver)
+ .onChanged(argThat(ResourceMatchers.isLoading()));
+
+ ArgumentCaptor> resolveCaptor =
+ ArgumentCaptor.forClass(Resource.class);
+ inOrder.verify(mResponseObserver).onChanged(resolveCaptor.capture());
+
+ FirebaseAuthAnonymousUpgradeException e =
+ ((FirebaseAuthAnonymousUpgradeException) resolveCaptor.getValue().getException());
+
+ EmailAuthCredential responseCredential =
+ (EmailAuthCredential) e.getResponse().getCredentialForLinking();
+
+ assertThat(responseCredential.getEmail()).isEqualTo(TestConstants.EMAIL);
+ assertThat(responseCredential.getPassword()).isEqualTo(TestConstants.PASSWORD);
+ }
}
diff --git a/buildSrc/src/main/kotlin/Config.kt b/buildSrc/src/main/kotlin/Config.kt
index 4d6f8b7ce..e076d1206 100644
--- a/buildSrc/src/main/kotlin/Config.kt
+++ b/buildSrc/src/main/kotlin/Config.kt
@@ -11,7 +11,7 @@ object Config {
}
object Plugins {
- const val android = "com.android.tools.build:gradle:3.2.0-alpha17"
+ const val android = "com.android.tools.build:gradle:3.2.0-beta01"
const val kotlin = "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
const val google = "com.google.gms:google-services:4.0.1"
@@ -91,6 +91,7 @@ object Config {
object Test {
const val junit = "junit:junit:4.12"
+ const val truth = "com.google.truth:truth:0.40"
const val mockito = "org.mockito:mockito-android:2.18.3"
const val robolectric = "org.robolectric:robolectric:3.8"