Skip to content

Commit d169e0e

Browse files
committed
Fix.
1 parent a5c0943 commit d169e0e

File tree

1 file changed

+44
-3
lines changed

1 file changed

+44
-3
lines changed

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

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -420,10 +420,34 @@ private void onLogin(
420420
}
421421

422422
private void completeUserLogin() {
423+
completeUserLogin(_email, _userId, _authToken);
424+
}
425+
426+
/**
427+
* Completes user login with validated credentials.
428+
* This method ensures sensitive operations (syncInApp, syncMessages, registerForPush) only execute
429+
* with server-validated user data, preventing user-controlled bypass attacks.
430+
*
431+
* @param email Server-validated email (can be null)
432+
* @param userId Server-validated userId (can be null)
433+
* @param authToken Server-validated authToken (must not be null for sensitive operations)
434+
*/
435+
private void completeUserLogin(@Nullable String email, @Nullable String userId, @Nullable String authToken) {
423436
if (!isInitialized()) {
424437
return;
425438
}
426439

440+
// Only proceed with sensitive operations if we have server-validated authToken
441+
// This prevents user-controlled bypass where unvalidated userId/email from keychain
442+
// could be used to access another user's data
443+
if (authToken == null) {
444+
IterableLogger.d(TAG, "Skipping sensitive operations - no validated authToken present");
445+
if (_setUserSuccessCallbackHandler != null) {
446+
_setUserSuccessCallbackHandler.onSuccess(new JSONObject());
447+
}
448+
return;
449+
}
450+
427451
if (config.autoPushRegistration) {
428452
registerForPush();
429453
} else if (_setUserSuccessCallbackHandler != null) {
@@ -517,6 +541,23 @@ private void storeAuthData() {
517541
}
518542
}
519543

544+
/**
545+
* Atomically stores auth data and completes login with validated credentials.
546+
* This ensures completeUserLogin uses the exact same data that was stored, preventing
547+
* user-controlled bypass attacks where keychain data could be modified between operations.
548+
*/
549+
private void storeAuthDataAndCompleteLogin() {
550+
// Capture current auth data before storing
551+
final String capturedEmail = _email;
552+
final String capturedUserId = _userId;
553+
final String capturedAuthToken = _authToken;
554+
555+
storeAuthData();
556+
557+
// Pass captured validated data to completeUserLogin
558+
completeUserLogin(capturedEmail, capturedUserId, capturedAuthToken);
559+
}
560+
520561
private void retrieveEmailAndUserId() {
521562
if (_applicationContext == null) {
522563
return;
@@ -595,10 +636,10 @@ void setAuthToken(String authToken, boolean bypassAuth) {
595636
if (isInitialized()) {
596637
if ((authToken != null && !authToken.equalsIgnoreCase(_authToken)) || (_authToken != null && !_authToken.equalsIgnoreCase(authToken))) {
597638
_authToken = authToken;
598-
storeAuthData();
599-
completeUserLogin();
639+
storeAuthDataAndCompleteLogin();
600640
} else if (bypassAuth) {
601-
completeUserLogin();
641+
// Pass current auth data - completeUserLogin will validate authToken before sensitive ops
642+
completeUserLogin(_email, _userId, _authToken);
602643
}
603644
}
604645
}

0 commit comments

Comments
 (0)