@@ -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