Skip to content

Commit 13c11ed

Browse files
committed
Fix pre-HYDRATE logout and IAM fetch TOCTOU race
- LogoutHelper: remove suppressBackendOperation=true in pre-HYDRATE branch so CreateSubscriptionOperation is enqueued alongside the anonymous LoginUserOperation (fixes IV=OFF path) - InAppMessagesManager: capture JWT once before guard check to eliminate TOCTOU between guard and backend call - nit: LoginUserOperationExecutor: add error log when anonymous LoginUserOperation is dropped with no subscription op
1 parent fb2353f commit 13c11ed

File tree

3 files changed

+8
-8
lines changed

3 files changed

+8
-8
lines changed

OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/LogoutHelper.kt

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,16 +38,14 @@ class LogoutHelper(
3838
),
3939
)
4040
} else {
41-
// IV state unknown (pre-HYDRATE). Take the safe path: disable push
42-
// and suppress backend op (like IV=ON), but also enqueue a LoginUserOperation
43-
// so the anonymous user is created on the backend if IV turns out to be OFF.
44-
// If IV=ON, removeOperationsWithoutExternalId() will purge the anonymous op.
41+
// IV unknown (pre-HYDRATE): disable push, enqueue anonymous user.
42+
// If IV=ON at HYDRATE, removeOperationsWithoutExternalId() purges these.
4543
configModel.pushSubscriptionId?.let { pushSubId ->
4644
subscriptionModelStore.get(pushSubId)
4745
?.let { it.isDisabledInternally = true }
4846
}
4947

50-
userSwitcher.createAndSwitchToNewUser(suppressBackendOperation = true)
48+
userSwitcher.createAndSwitchToNewUser()
5149

5250
operationRepo.enqueue(
5351
LoginUserOperation(

OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/impl/executors/LoginUserOperationExecutor.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ internal class LoginUserOperationExecutor(
7474
// Anonymous Login being processed alone will surely be rejected, so we need to drop the request
7575
val containsSubscriptionOperation = operations.any { it is CreateSubscriptionOperation || it is TransferSubscriptionOperation }
7676
if (!containsSubscriptionOperation && loginUserOp.externalId == null) {
77+
Logging.error("LoginUserOperationExecutor: dropping anonymous LoginUserOperation with no subscription op: $loginUserOp")
7778
return ExecutionResponse(ExecutionResult.FAIL_NORETRY)
7879
}
7980
if (loginUserOp.existingOnesignalId == null || loginUserOp.externalId == null) {

OneSignalSDK/onesignal/in-app-messages/src/main/java/com/onesignal/inAppMessages/internal/InAppMessagesManager.kt

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -302,12 +302,15 @@ internal class InAppMessagesManager(
302302
}
303303

304304
val externalId = _identityModelStore.model.externalId
305+
// Capture JWT once to avoid TOCTOU: the same snapshot is used for the guard
306+
// check and the backend call, so a concurrent invalidation can't slip between them.
307+
val jwt = externalId?.let { _jwtTokenStore.getJwt(it) }
305308
if (_configModelStore.model.useIdentityVerification == true) {
306309
if (externalId == null) {
307310
Logging.debug("InAppMessagesManager.fetchMessages: Skipping IAM fetch for anonymous user while identity verification is enabled.")
308311
return
309312
}
310-
if (_jwtTokenStore.getJwt(externalId) == null) {
313+
if (jwt == null) {
311314
Logging.debug("InAppMessagesManager.fetchMessages: Skipping IAM fetch while JWT is invalidated for user: $externalId")
312315
return
313316
}
@@ -328,8 +331,6 @@ internal class InAppMessagesManager(
328331
externalId,
329332
_identityModelStore.model.onesignalId,
330333
)
331-
val jwt = externalId?.let { _jwtTokenStore.getJwt(it) }
332-
333334
// lambda so that it is updated on each potential retry
334335
val sessionDurationProvider = { _time.currentTimeMillis - _sessionService.startTime }
335336
val newMessages = _backend.listInAppMessages(appId, aliasLabel, aliasValue, subscriptionId, rywData, sessionDurationProvider, jwt)

0 commit comments

Comments
 (0)