Skip to content

Commit fa4c589

Browse files
authored
Merge pull request #931 from Iterable/MOB-11813-JWTrework-for-null-in-bg
[MOB-11813] - JWT in background - Rework
2 parents baa6b87 + 50e002a commit fa4c589

File tree

2 files changed

+51
-27
lines changed

2 files changed

+51
-27
lines changed

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

Lines changed: 49 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package com.iterable.iterableapi;
22

33
import android.util.Base64;
4+
5+
import androidx.annotation.Nullable;
46
import androidx.annotation.VisibleForTesting;
57

68
import org.json.JSONException;
@@ -19,6 +21,7 @@ public class IterableAuthManager implements IterableActivityMonitor.AppStateCall
1921
private final IterableApi api;
2022
private final IterableAuthHandler authHandler;
2123
private final long expiringAuthTokenRefreshPeriod;
24+
private final IterableActivityMonitor activityMonitor;
2225
@VisibleForTesting
2326
Timer timer;
2427
private boolean hasFailedPriorAuth;
@@ -28,7 +31,8 @@ public class IterableAuthManager implements IterableActivityMonitor.AppStateCall
2831
boolean pauseAuthRetry;
2932
int retryCount;
3033
private boolean isLastAuthTokenValid;
31-
private boolean isTimerScheduled;
34+
private volatile boolean isTimerScheduled;
35+
private volatile boolean isInForeground = true; // Assume foreground initially
3236

3337
private final ExecutorService executor = Executors.newSingleThreadExecutor();
3438

@@ -37,7 +41,8 @@ public class IterableAuthManager implements IterableActivityMonitor.AppStateCall
3741
this.authHandler = authHandler;
3842
this.authRetryPolicy = authRetryPolicy;
3943
this.expiringAuthTokenRefreshPeriod = expiringAuthTokenRefreshPeriod;
40-
IterableActivityMonitor.getInstance().addCallback(this);
44+
this.activityMonitor = IterableActivityMonitor.getInstance();
45+
this.activityMonitor.addCallback(this);
4146
}
4247

4348
public synchronized void requestNewAuthToken(boolean hasFailedPriorAuth) {
@@ -52,7 +57,6 @@ public void pauseAuthRetries(boolean pauseRetry) {
5257
void reset() {
5358
clearRefreshTimer();
5459
setIsLastAuthTokenValid(false);
55-
IterableActivityMonitor.getInstance().removeCallback(this);
5660
}
5761

5862
void setIsLastAuthTokenValid(boolean isValid) {
@@ -97,6 +101,14 @@ public void run() {
97101
pendingAuth = false;
98102
return;
99103
}
104+
105+
// Only request new auth token if app is in foreground
106+
if (!isInForeground) {
107+
IterableLogger.w(TAG, "Auth token request skipped - app is in background");
108+
pendingAuth = false;
109+
return;
110+
}
111+
100112
final String authToken = authHandler.onAuthTokenRequested();
101113
pendingAuth = false;
102114
retryCount++;
@@ -143,15 +155,23 @@ private void handleAuthTokenFailure(Throwable throwable) {
143155
scheduleAuthTokenRefresh(getNextRetryInterval(), false, null);
144156
}
145157

146-
public void queueExpirationRefresh(String encodedJWT) {
158+
public void queueExpirationRefresh(@Nullable String encodedJWT) {
147159
clearRefreshTimer();
148160
try {
161+
if (encodedJWT == null) {
162+
IterableLogger.d(TAG, "JWT is null. Scheduling token refresh");
163+
if (!isTimerScheduled) {
164+
scheduleAuthTokenRefresh(getNextRetryInterval(), false, null);
165+
}
166+
return;
167+
}
168+
149169
long expirationTimeSeconds = decodedExpiration(encodedJWT);
150170
long triggerExpirationRefreshTime = expirationTimeSeconds * 1000L - expiringAuthTokenRefreshPeriod - IterableUtil.currentTimeMillis();
151171
if (triggerExpirationRefreshTime > 0) {
152172
scheduleAuthTokenRefresh(triggerExpirationRefreshTime, true, null);
153173
} else {
154-
IterableLogger.w(TAG, "The expiringAuthTokenRefreshPeriod has already passed for the current JWT");
174+
scheduleAuthTokenRefresh(getNextRetryInterval(), true, null);
155175
}
156176
} catch (Exception e) {
157177
IterableLogger.e(TAG, "Error while parsing JWT for the expiration", e);
@@ -246,6 +266,20 @@ private static String getJson(String strEncoded) throws UnsupportedEncodingExcep
246266
return new String(decodedBytes, "UTF-8");
247267
}
248268

269+
/**
270+
* Checks if the current auth token needs to be refreshed and handles any deferred auth requests.
271+
* This method is called when the app comes to foreground to ensure timely token refresh.
272+
*/
273+
private void checkAndHandleAuthRefresh() {
274+
// First, check if current auth token needs refresh based on expiration
275+
if (api.getEmail() != null || api.getUserId() != null) {
276+
String currentAuthToken = api.getAuthToken();
277+
queueExpirationRefresh(currentAuthToken);
278+
} else {
279+
IterableLogger.d(TAG, "Email or userId is not available. Skipping token refresh");
280+
}
281+
}
282+
249283
void clearRefreshTimer() {
250284
if (timer != null) {
251285
timer.cancel();
@@ -255,35 +289,24 @@ void clearRefreshTimer() {
255289
}
256290

257291
@Override
258-
public void onSwitchToBackground() {
292+
public void onSwitchToForeground() {
259293
try {
260-
IterableLogger.v(TAG, "App switched to background. Clearing auth refresh timer.");
261-
clearRefreshTimer();
294+
IterableLogger.d(TAG, "App switched to foreground - enabling auth token requests");
295+
isInForeground = true;
296+
checkAndHandleAuthRefresh();
262297
} catch (Exception e) {
263-
IterableLogger.e(TAG, "Error in onSwitchToBackground", e);
298+
IterableLogger.e(TAG, "Error occurred in handling auth token refresh", e);
264299
}
265300
}
266301

267302
@Override
268-
public void onSwitchToForeground() {
269-
if (pendingAuth) return;
303+
public void onSwitchToBackground() {
270304
try {
271-
IterableLogger.v(TAG, "App switched to foreground. Re-evaluating auth token refresh.");
272-
String authToken = api.getAuthToken();
273-
274-
if (authToken != null) {
275-
queueExpirationRefresh(authToken);
276-
// If queueExpirationRefresh didn't schedule a timer (expired token case), request new token
277-
if (!isTimerScheduled && !pendingAuth) {
278-
IterableLogger.d(TAG, "Token expired, requesting new token on foreground");
279-
requestNewAuthToken(false, null, true);
280-
}
281-
} else if ((api.getEmail() != null || api.getUserId() != null) && !pendingAuth) {
282-
IterableLogger.d(TAG, "App foregrounded, user identified, no auth token present. Requesting new token.");
283-
requestNewAuthToken(false, null, true);
284-
}
305+
IterableLogger.d(TAG, "App switched to background - disabling auth token requests");
306+
isInForeground = false;
307+
clearRefreshTimer();
285308
} catch (Exception e) {
286-
IterableLogger.e(TAG, "Error in onSwitchToForeground", e);
309+
IterableLogger.e(TAG, "Error while switching to background", e);
287310
}
288311
}
289312
}

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
import static org.mockito.ArgumentMatchers.any;
4040
import static org.mockito.ArgumentMatchers.eq;
4141
import static org.mockito.ArgumentMatchers.nullable;
42+
import static org.mockito.Mockito.atLeastOnce;
4243
import static org.mockito.Mockito.clearInvocations;
4344
import static org.mockito.Mockito.doReturn;
4445
import static org.mockito.Mockito.mock;
@@ -336,7 +337,7 @@ public void testAutomaticPushRegistrationOnInitAndForeground() throws Exception
336337
ActivityController<Activity> activityController = Robolectric.buildActivity(Activity.class).create().start().resume();
337338

338339
ArgumentCaptor<IterablePushRegistrationData> capturedPushRegistrationData = ArgumentCaptor.forClass(IterablePushRegistrationData.class);
339-
verify(IterablePushRegistration.instance).executePushRegistrationTask(capturedPushRegistrationData.capture());
340+
verify(IterablePushRegistration.instance, atLeastOnce()).executePushRegistrationTask(capturedPushRegistrationData.capture());
340341
assertEquals(IterablePushRegistrationData.PushRegistrationAction.ENABLE, capturedPushRegistrationData.getValue().pushRegistrationAction);
341342

342343
activityController.pause().stop().destroy();

0 commit comments

Comments
 (0)