fix(jwt): Multi-user fixes for identity verification - WIP for manual testing#2586
Open
nan-li wants to merge 12 commits intoidentity_verification_betafrom
Open
fix(jwt): Multi-user fixes for identity verification - WIP for manual testing#2586nan-li wants to merge 12 commits intoidentity_verification_betafrom
nan-li wants to merge 12 commits intoidentity_verification_betafrom
Conversation
Add operationJwt, operationExternalId, and requiresJwt to Operation for multi-user JWT management. Override requiresJwt=false on UpdateSubscriptionOperation since its backend endpoint has no JWT param. Made-with: Cursor
- Stamp operationJwt/operationExternalId on operations at enqueue time - Rewrite getNextOps: gate on isInitializedWithRemote, discard anonymous ops when IV is on, skip ops with null JWT (instead of blocking all) - FAIL_UNAUTHORIZED: null JWT per-op, add unauthorizedRetries counter with max 3, fire jwtInvalidatedCallback with correct externalId - Stamp JWT on follow-up operations from executors - Add updateJwtForExternalId to update JWT on queued ops and wake queue - Subscribe to ConfigModelStore to wake queue when remote params arrive - Add listener management for IUserJwtInvalidatedListener on IOperationRepo Made-with: Cursor
- updateUserJwt now calls operationRepo.updateJwtForExternalId to update JWT on all queued operations for the given externalId - Delegate JWT invalidated listener add/remove to OperationRepo instead of UserManager Made-with: Cursor
…delStore Replace _identityModelStore.model.jwtToken with operation.operationJwt in all 5 executors so each operation uses the JWT stamped at enqueue time rather than the current user's JWT. Made-with: Cursor
JWT invalidation is now handled by OperationRepo with the correct per-operation externalId. Remove jwtInvalidatedCallback EventProducer, jwtTokenInvalidated tracking, and JWT_TOKEN handling from onModelUpdated. UserManager listener methods are now no-ops since OneSignalImp delegates directly to OperationRepo. Made-with: Cursor
Update existing JWT tests to match new per-operation behavior. Add tests: - getNextOps skips null-JWT ops, allows requiresJwt=false through - getNextOps discards anonymous ops when IV is on - getNextOps returns null when isInitializedWithRemote is false - getNextOps passes all ops when IV is off - FAIL_UNAUTHORIZED nulls JWT and fires listener with correct externalId - FAIL_UNAUTHORIZED drops ops after max retries - updateJwtForExternalId updates JWT and resets retry count - updateJwtForExternalId only affects matching user - Enqueue stamps JWT/externalId from IdentityModelStore - Follow-up operations inherit JWT/externalId from starting op Made-with: Cursor
Executors were calling _identityModelStore.getIdentityAlias() to build backend API URL paths, which returns the *current* user's alias. When executing operations for a previous user, this caused the wrong user's identity to be used in the URL (e.g., nan00's refresh-user fetching nan01's data), resulting in 401 errors. Replace with operation-derived alias: use operationExternalId when JWT is present, otherwise fall back to onesignalId from the operation. Made-with: Cursor
Test that each executor uses the operation's own JWT/externalId to derive the identity alias for API calls, rather than reading from the current user's IdentityModelStore. Covers both JWT-present (external_id path) and no-JWT (onesignal_id path) scenarios for all 4 affected executors: Identity, RefreshUser, Subscription, UpdateUser. Made-with: Cursor
…WT presence The identity alias derivation incorrectly used operationJwt != null to decide between external_id and onesignal_id for the API URL path. This is wrong because even if an operation has a JWT, if identity verification is disabled on the server, the backend expects onesignal_id. Now uses _configModelStore.model.useIdentityVerification as the condition, which reflects the actual server configuration. Injected ConfigModelStore into IdentityOperationExecutor and UpdateUserOperationExecutor (the two that didn't have it). Updated all unit tests accordingly. Made-with: Cursor
33e503a to
518f3e5
Compare
518f3e5 to
ca1dc4d
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
One Line Summary
Fix JWT identity verification to support multi-user operation queues, per-operation JWT tracking, and correct per-user API routing.
Details
Motivation
When identity verification (JWT) is enabled, multiple bugs cause an infinite 401 retry loop:
FAIL_UNAUTHORIZEDre-enqueues operations indefinitely with no retry limitUserJwtInvalidatedListenerfires with the current user's externalId instead of the failing operation's userScope
operationJwtandoperationExternalId, stamped at enqueue time. This makes operations self-contained for multi-user support.getNextOps()skips operations with null JWT (waiting for refresh) rather than blocking the entire queue. Other users' operations continue executing.unauthorizedRetriescounter (max 3), firesUserJwtInvalidatedListenerwith the correctoperationExternalId.Not changed: logout() behavior, login() enqueue logic, operation grouping semantics, LoginUserOperationExecutor create-user flow.
Testing
Unit testing
Manual testing
Affected code checklist
Checklist
Overview
Testing
Final pass