Skip to content

Commit db3f49f

Browse files
On same email login also complete the login flow
1 parent 6bc8f7b commit db3f49f

File tree

3 files changed

+58
-0
lines changed

3 files changed

+58
-0
lines changed

iterableapi/src/main/java/com/iterable/iterableapi/IterableApi.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1076,6 +1076,9 @@ public void setEmail(@Nullable String email, @Nullable String authToken, @Nullab
10761076

10771077
if (_email != null && _email.equals(email)) {
10781078
checkAndUpdateAuthToken(authToken);
1079+
_setUserSuccessCallbackHandler = successHandler;
1080+
_setUserFailureCallbackHandler = failureHandler;
1081+
onLogin(authToken, email, true, merge, replay, false, failureHandler);
10791082
return;
10801083
}
10811084

@@ -1146,6 +1149,9 @@ public void setUserId(@Nullable String userId, @Nullable String authToken, @Null
11461149

11471150
if (_userId != null && _userId.equals(userId)) {
11481151
checkAndUpdateAuthToken(authToken);
1152+
_setUserSuccessCallbackHandler = successHandler;
1153+
_setUserFailureCallbackHandler = failureHandler;
1154+
onLogin(authToken, userId, false, merge, replay, isUnknown, failureHandler);
11491155
return;
11501156
}
11511157

iterableapi/src/test/java/com/iterable/iterableapi/IterableApiAuthSecurityTests.java

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,53 @@ public void testCompleteUserLogin_WithJWTAuth_WithToken_ExecutesSensitiveOps() t
141141
verify(mockEmbeddedManager).syncMessages();
142142
}
143143

144+
/**
145+
* Regression test: calling setEmail with the same email that's already set (e.g. after app
146+
* restart where email is restored from keychain) should still trigger the full login flow
147+
* (request auth token, syncInApp, syncMessages).
148+
*
149+
* Previously, the same-email path called checkAndUpdateAuthToken(null) which did nothing,
150+
* so no login side effects occurred.
151+
*/
152+
@Test
153+
public void testSetEmail_SameEmail_StillTriggersLogin() throws Exception {
154+
initIterableWithAuth();
155+
156+
dispatcher.enqueueResponse("/users/update", new MockResponse().setResponseCode(200).setBody("{}"));
157+
doReturn(validJWT).when(authHandler).onAuthTokenRequested();
158+
159+
IterableApi api = spy(IterableApi.getInstance());
160+
IterableApi.sharedInstance = api;
161+
162+
IterableInAppManager mockInAppManager = mock(IterableInAppManager.class);
163+
IterableEmbeddedManager mockEmbeddedManager = mock(IterableEmbeddedManager.class);
164+
when(api.getInAppManager()).thenReturn(mockInAppManager);
165+
when(api.getEmbeddedManager()).thenReturn(mockEmbeddedManager);
166+
167+
// First login — triggers full flow
168+
api.setEmail("user@example.com");
169+
server.takeRequest(1, TimeUnit.SECONDS);
170+
shadowOf(getMainLooper()).idle();
171+
172+
verify(mockInAppManager).syncInApp();
173+
verify(mockEmbeddedManager).syncMessages();
174+
175+
// Clear invocations so we can verify the second call independently
176+
org.mockito.Mockito.clearInvocations(mockInAppManager, mockEmbeddedManager);
177+
178+
// Enqueue another response for the second login's /users/update call
179+
dispatcher.enqueueResponse("/users/update", new MockResponse().setResponseCode(200).setBody("{}"));
180+
181+
// Second login with SAME email — simulates app restart where email is in keychain
182+
api.setEmail("user@example.com");
183+
server.takeRequest(1, TimeUnit.SECONDS);
184+
shadowOf(getMainLooper()).idle();
185+
186+
// This SHOULD still trigger login side effects
187+
verify(mockInAppManager).syncInApp();
188+
verify(mockEmbeddedManager).syncMessages();
189+
}
190+
144191
/**
145192
* Test that completeUserLogin executes sensitive operations when JWT auth is NOT enabled,
146193
* even without an authToken.

iterableapi/src/test/java/com/iterable/iterableapi/IterableApiAuthTests.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package com.iterable.iterableapi;
22

3+
import android.content.Context;
4+
35
import com.iterable.iterableapi.unit.PathBasedQueueDispatcher;
46

57
import org.junit.After;
@@ -46,6 +48,7 @@ public class IterableApiAuthTests extends BaseTest {
4648

4749
@Before
4850
public void setUp() {
51+
getContext().getSharedPreferences(IterableConstants.SHARED_PREFS_FILE, Context.MODE_PRIVATE).edit().clear().apply();
4952

5053
server = new MockWebServer();
5154
dispatcher = new PathBasedQueueDispatcher();
@@ -521,6 +524,8 @@ public void testTokenRefreshDoesNotTriggerPushRegistration() throws Exception {
521524

522525
try {
523526
// Initialize with auth and auto push registration enabled
527+
// Clear keychain data from setUp so retrieveEmailAndUserId starts fresh
528+
getContext().getSharedPreferences(IterableConstants.SHARED_PREFS_FILE, Context.MODE_PRIVATE).edit().clear().apply();
524529
IterableApi.sharedInstance = new IterableApi();
525530
authHandler = mock(IterableAuthHandler.class);
526531
IterableApi.initialize(getContext(), "apiKey",

0 commit comments

Comments
 (0)