Skip to content

feat: [JWT-3] feature flags, JWT config, and the Identity Verification gate - #1707

Open
nan-li wants to merge 2 commits into
5.6-mainfrom
nan/jwt-pr3-iv-gate
Open

feat: [JWT-3] feature flags, JWT config, and the Identity Verification gate#1707
nan-li wants to merge 2 commits into
5.6-mainfrom
nan/jwt-pr3-iv-gate

Conversation

@nan-li

@nan-li nan-li commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Description

One Line Summary

Add the Identity Verification foundation: feature flags, three-state JWT requirement config, the IV gate service, and remote-params hydration with retry — nothing consumes the gate yet.

Details

Motivation

Identity Verification needs a known answer to “does this app require a token?” before user-scoped work can be sent. That answer is three-state (unknown / on / off), comes from remote params (jwt_required), and must survive a failed first-launch params download without stranding the session’s queued work forever.

Scope

  • OSFeatureManager, OSUserJwtConfig, OSIdentityVerificationService
  • OneSignal.m params hydration from jwt_required, retry/backoff, cache clears for the new keys
  • Replaces unread require_user_id_auth with jwt_required (the requiresUserAuth property itself is removed in a later PR)
  • Unit tests for the three new types
  • No request-pipeline or public JWT listener API yet

Targets 5.6-main (already includes the project-file normalize and read-your-write consistency fixes).

Testing

Unit testing

  • OSFeatureManagerTests
  • OSUserJwtConfigTests
  • OSIdentityVerificationServiceTests

Manual testing

Built in sequence as part of the local JWT stack against an iOS Simulator.

Affected code checklist

  • Notifications
    • Display
    • Open
    • Push Processing
    • Confirm Deliveries
  • Outcomes
  • Sessions
  • In-App Messaging
  • REST API requests
  • Public API changes

Checklist

Overview

  • I have filled out all REQUIRED sections above
  • PR does one thing
  • Any Public API changes are explained in the PR details and conform to existing APIs

Testing

  • I have included test coverage for these changes, or explained why they are not needed
  • All automated tests pass, or I explained why that is not possible
  • I have personally tested this on my device, or explained why that is not possible

Final pass

  • Code is as readable as possible.
  • I have reviewed this PR myself, ensuring it meets each checklist item

Made with Cursor

@nan-li
nan-li force-pushed the nan/jwt-pr3-iv-gate branch from 5411cbc to d0b8588 Compare August 12, 2026 01:26
…gate

The foundation the rest of Identity Verification is built on. Nothing
consumes the gate yet, so this changes no behavior beyond the params
handling below.

OSUserJwtConfig holds whether the app requires a token as a three-state
answer — unknown until remote params say — because "not yet known" and
"not required" have to be told apart before anything can be sent.
OSFeatureManager carries the flags remote params enable, and
OSIdentityVerificationService is the single place that answers whether
Identity Verification is on.

Remote params now hydrate that requirement from jwt_required. A response
that omits the key means Identity Verification is off for the app, while
an empty response answers nothing and leaves the cached requirement
standing.

Because the operation repo will hold every queued operation until the
requirement is known, a params request that failed was going to cost a
first-launch app every tag, session count and event for the rest of the
session: nothing retried it. It now retries with a backoff over five
attempts and, failing that, tries again on the next session.

Replaces the unread require_user_id_auth key with jwt_required. The
requiresUserAuth property it wrote is removed in the next PR.

Co-authored-by: Cursor <cursoragent@cursor.com>
@nan-li
nan-li force-pushed the nan/jwt-pr3-iv-gate branch from d0b8588 to 1b80146 Compare August 12, 2026 01:30

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Multi-model adversarial review (interrogate)

Intent

Add the Identity Verification foundation without wiring consumers: OSFeatureManager, three-state OSUserJwtConfig (jwt_required), and OSIdentityVerificationService; hydrate from remote params; clear new cache keys on app-id change; retry failed params downloads so first-launch sessions are not stranded with an unknown requirement.

Reviewers

  • A: claude-fable-5-thinking-xhigh — 10 findings
  • B: gpt-5.6-sol-xhigh — 6 findings
  • C: cursor-grok-4.5-high-fast — 5 findings
  • D: claude-opus-5-thinking-high — 14 findings

Act On

  1. Stale appId in params retry (A/B/C/D) — scheduleDownloadIOSParamsRetryWithAppId: captures appId and only guards _downloadedParameters / _didCallDownloadParameters. handleAppIdChange: resets those flags, so a pending retry for app A can run after a switch to B and rehydrate IV + other remote params from the wrong app.
  2. App-id change clears disk but not in-memory singletons (A/B/C/D) — OSUD_USE_IDENTITY_VERIFICATION / OSUD_SDK_FEATURE_FLAGS are removed, but OSUserJwtConfig.shared / OSFeatureManager.shared keep the previous app’s values. refreshIfUnknown() cannot repair a non-unknown in-memory value. Add a production reset() and call it from handleAppIdChange:.
  3. No tests for the OneSignal.m retry/hydrate path (A/B/C/D) — Swift unit tests cover the leaf types; the new retry state machine (four entry points, three statics, dispatch_after) is untested. That is where the highest-severity bugs live.

Consider

  1. Nested retry + indiscriminate 4xx (A/B/C/D) — OneSignalClient already retries 0/5xx up to 5 attempts; the outer loop multiplies that and also retries permanent 4xx / missing-app-id failures.
  2. Empty-body 200 strands unknown (A/B/D) — client can call successBlock(nil); hydration is skipped but _downloadedParameters = true still blocks further retries/session refetch.
  3. Hydration handler double-delivery / stale last value (A/B/C/D) — register-then-read outside the lock races URLSession-queue hydrate. No production consumers yet, but this is the deferred-work signal.
  4. Orphan requiresUserAuth (A/B/C/D) — writer removed; @objc public property remains permanently false. PR text says removal is a later PR — prefer deleting in this wave if the stack allows.
  5. refreshIfUnknown / refreshIfEmpty unwired (D, noted by others) — prewarm helpers have no production caller; recoverIfDeferred already exists in OneSignal.m.

Noted

  • Unused JWT notification/token defines (OS_ON_JWT_CONFIG_*, etc.) before posters exist
  • Feature-flag hydration deferred (scoped; newCodePathsRun == ivBehaviorActive until then)
  • Doc comments claim OneSignalUserManagerImpl references that do not exist yet
  • Hold lock across UserDefaults synchronize; case-canonicalize on isEnabled lookup

Dismissed

  • Collapsing feature manager + jwt config + gate into one type in this PR — stack intentionally lands the foundation before consumers; defer structural judo to the wiring PR unless reset/retry fixes force a reshape.
  • “No consumers yet so races don’t matter” — foundation contract will be trusted by the next PRs; fix the retry/app-id issues now.

Agreement Map

All four models independently flagged (1) stale app-id retry, (2) disk-only app-id clear, and (3) missing OneSignal.m tests. Nested client/outer retry and observer race were near-unanimous. Lone-model pressure was mostly on collapsing types and resilient-storage mirroring — useful context, lower weight for this PR’s stated scope.

Lead verdict: Do not merge until Act On #1#2 are fixed; add at least a focused test for the retry/app-id interaction before wiring consumers.

Open in Web View Automation 

Sent by Cursor Automation: Untitled

Comment thread iOS_SDK/OneSignalSDK/Source/OneSignal.m
Comment thread iOS_SDK/OneSignalSDK/Source/OneSignal.m
Comment thread iOS_SDK/OneSignalSDK/Source/OneSignal.m Outdated
Comment thread iOS_SDK/OneSignalSDK/Source/OneSignal.m
@nan-li nan-li changed the title feat: feature flags, JWT config, and the Identity Verification gate feat: [JWT-1] feature flags, JWT config, and the Identity Verification gate Aug 12, 2026
@nan-li nan-li changed the title feat: [JWT-1] feature flags, JWT config, and the Identity Verification gate feat: [JWT-3] feature flags, JWT config, and the Identity Verification gate Aug 12, 2026
Outer iOS-params retries now follow OSResponseStatusRetryable so
classified 4xx are not re-armed for the session. Hydration handlers
snapshot requirement under the same lock as registration.

Co-authored-by: Cursor <cursoragent@cursor.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant