Skip to content

Commit 2dbb142

Browse files
committed
Fixes flow so that reregister and fetch messages gets called after receiving a valid JWT when in the invalid state
1 parent 636a5d4 commit 2dbb142

2 files changed

Lines changed: 109 additions & 1 deletion

File tree

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

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
/**
2727
* Created by David Truong dt@iterable.com
2828
*/
29-
public class IterableApi {
29+
public class IterableApi implements IterableAuthManager.AuthTokenReadyListener {
3030
//region SDK (private/internal)
3131
//---------------------------------------------------------------------------------------
3232
static volatile IterableApi sharedInstance = new IterableApi();
@@ -181,6 +181,7 @@ Context getMainActivityContext() {
181181
IterableAuthManager getAuthManager() {
182182
if (authManager == null) {
183183
authManager = new IterableAuthManager(this, config.authHandler, config.retryPolicy, config.expiringAuthTokenRefreshPeriod);
184+
authManager.addAuthTokenReadyListener(this);
184185
}
185186
return authManager;
186187
}
@@ -484,6 +485,19 @@ private void completeUserLogin(@Nullable String email, @Nullable String userId,
484485
getEmbeddedManager().syncMessages();
485486
}
486487

488+
@Override
489+
public void onAuthTokenReady() {
490+
IterableLogger.d(TAG, "Auth token recovered after invalidation. Resyncing messages and push registration.");
491+
if (!isInitialized()) {
492+
return;
493+
}
494+
if (config.autoPushRegistration) {
495+
registerForPush();
496+
}
497+
getInAppManager().syncInApp();
498+
getEmbeddedManager().syncMessages();
499+
}
500+
487501
private final IterableActivityMonitor.AppStateCallback activityMonitorListener = new IterableActivityMonitor.AppStateCallback() {
488502
@Override
489503
public void onSwitchToForeground() {

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

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -567,4 +567,98 @@ public void testTokenRefreshDoesNotTriggerPushRegistration() throws Exception {
567567
}
568568
}
569569

570+
@Test
571+
public void testAuthTokenRecoveryFrom401TriggersPushAndMessageSync() throws Exception {
572+
IterablePushRegistration.IterablePushRegistrationImpl originalPushImpl = IterablePushRegistration.instance;
573+
IterablePushRegistration.instance = mock(IterablePushRegistration.IterablePushRegistrationImpl.class);
574+
575+
try {
576+
// Initialize with auth and auto push registration enabled
577+
getContext().getSharedPreferences(IterableConstants.SHARED_PREFS_FILE, Context.MODE_PRIVATE).edit().clear().apply();
578+
IterableApi.sharedInstance = new IterableApi();
579+
authHandler = mock(IterableAuthHandler.class);
580+
IterableApi.initialize(getContext(), "apiKey",
581+
new IterableConfig.Builder()
582+
.setAuthHandler(authHandler)
583+
.setAutoPushRegistration(true)
584+
.setPushIntegrationName("pushIntegration")
585+
.build());
586+
587+
// Initial login
588+
doReturn(validJWT).when(authHandler).onAuthTokenRequested();
589+
IterableApi.getInstance().setEmail("test@example.com");
590+
Thread.sleep(500);
591+
shadowOf(getMainLooper()).idle();
592+
shadowOf(getMainLooper()).runToEndOfTasks();
593+
594+
// Verify initial push registration happened
595+
verify(IterablePushRegistration.instance).executePushRegistrationTask(any(IterablePushRegistrationData.class));
596+
597+
// Reset mock to clear invocation history
598+
Mockito.reset(IterablePushRegistration.instance);
599+
600+
// Simulate 401 rejection: mark auth token as INVALID
601+
IterableApi.getInstance().getAuthManager().setAuthTokenInvalid();
602+
603+
// Simulate token recovery: transition from INVALID -> VALID
604+
// This should trigger AuthTokenReadyListeners (including IterableApi)
605+
IterableApi.getInstance().getAuthManager().setIsLastAuthTokenValid(true);
606+
607+
// Allow main looper to process any posted callbacks
608+
shadowOf(getMainLooper()).idle();
609+
shadowOf(getMainLooper()).runToEndOfTasks();
610+
611+
// After 401 recovery, push registration SHOULD be triggered
612+
verify(IterablePushRegistration.instance).executePushRegistrationTask(any(IterablePushRegistrationData.class));
613+
} finally {
614+
IterablePushRegistration.instance = originalPushImpl;
615+
}
616+
}
617+
618+
@Test
619+
public void testRoutineTokenRefreshDoesNotTriggerResync() throws Exception {
620+
IterablePushRegistration.IterablePushRegistrationImpl originalPushImpl = IterablePushRegistration.instance;
621+
IterablePushRegistration.instance = mock(IterablePushRegistration.IterablePushRegistrationImpl.class);
622+
623+
try {
624+
// Initialize with auth and auto push registration enabled
625+
getContext().getSharedPreferences(IterableConstants.SHARED_PREFS_FILE, Context.MODE_PRIVATE).edit().clear().apply();
626+
IterableApi.sharedInstance = new IterableApi();
627+
authHandler = mock(IterableAuthHandler.class);
628+
IterableApi.initialize(getContext(), "apiKey",
629+
new IterableConfig.Builder()
630+
.setAuthHandler(authHandler)
631+
.setAutoPushRegistration(true)
632+
.setPushIntegrationName("pushIntegration")
633+
.build());
634+
635+
// Initial login
636+
doReturn(validJWT).when(authHandler).onAuthTokenRequested();
637+
IterableApi.getInstance().setEmail("test@example.com");
638+
Thread.sleep(500);
639+
shadowOf(getMainLooper()).idle();
640+
shadowOf(getMainLooper()).runToEndOfTasks();
641+
642+
// Verify initial push registration happened
643+
verify(IterablePushRegistration.instance).executePushRegistrationTask(any(IterablePushRegistrationData.class));
644+
Mockito.reset(IterablePushRegistration.instance);
645+
646+
// Simulate routine token refresh (no INVALID state involved)
647+
// Auth state stays UNKNOWN -> UNKNOWN, so listener should NOT fire
648+
doReturn(newJWT).when(authHandler).onAuthTokenRequested();
649+
IterableApi.getInstance().getAuthManager().requestNewAuthToken(false, null);
650+
Thread.sleep(500);
651+
shadowOf(getMainLooper()).idle();
652+
shadowOf(getMainLooper()).runToEndOfTasks();
653+
654+
// Verify token was actually refreshed
655+
assertEquals(newJWT, IterableApi.getInstance().getAuthToken());
656+
657+
// Assert that push registration was NOT called on routine refresh
658+
verify(IterablePushRegistration.instance, never()).executePushRegistrationTask(any(IterablePushRegistrationData.class));
659+
} finally {
660+
IterablePushRegistration.instance = originalPushImpl;
661+
}
662+
}
663+
570664
}

0 commit comments

Comments
 (0)