Skip to content

Conversation

@seshanthS
Copy link
Collaborator

@seshanthS seshanthS commented Nov 12, 2025

Summary by CodeRabbit

  • Bug Fixes

    • Key deletion now filters to process only valid Turnkey public keys, preventing non-Turnkey values from being acted on.
  • New Features

    • iOS in-app browser updated with modern authentication session support, improved lifecycle/scene handling, enhanced presentation options, and robust open/close/auth flows.
  • Chores

    • Reinstall workflow updated to use a safer node cleanup command.
    • Repository ignore rules adjusted to include patch files.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 12, 2025

Walkthrough

Two dependency patches: one filters Turnkey publicKeys to only process 66- or 130‑char hex keys before deletion; the other replaces the iOS RNInAppBrowser implementation to add auth flows, Safari presentation, scene-aware startup, and full session lifecycle management. package.json script tweak updates reinstall cleanup.

Changes

Cohort / File(s) Summary
Turnkey Public Key Filtering
patches/@turnkey+core+1.7.0.patch
Adds filteredPublicKeys (hex strings of length 66 or 130), short-circuits when empty, and iterates over filteredPublicKeys in the key-deletion flow in core.js/core.mjs variants.
iOS In-App Browser Module Rewrite
patches/react-native-inappbrowser-reborn+3.7.0.patch
Replaces RNInAppBrowser.m with a feature-complete module: exports openAuth, open, isAvailable, close, closeAuth; implements ASWebAuthenticationSession/SFAuthenticationSession flows, scene-aware startup for iOS13+, presentation/transition mapping, single-instance enforcement, resolver/rejecter storage, lifecycle helpers, and detailed error handling.
Root package.json script tweak
package.json
Updates reinstall-app script to run yarn clean:node inside app instead of removing app/node_modules with rm -rf during reinstall workflow.
.cursorignore update
.cursorignore
Removes exclusion of patches/ directory from ignore rules so patch files are no longer ignored.

Sequence Diagram(s)

sequenceDiagram
    participant JS as JavaScript
    participant ObjC as RNInAppBrowser (Obj-C)
    participant Scene as UIScene (iOS13+)
    participant Auth as AuthSession (AS/SFAuth)
    participant Safari as SFSafariVC

    JS->>ObjC: openAuth(url, redirectURL, options)
    ObjC->>ObjC: initializeWebBrowserWithResolver()
    alt iOS13+ and no active scene
        ObjC->>Scene: waitForActiveSceneAndStartSession() (retry/backoff)
        Scene-->>ObjC: scene becomes active
    end
    ObjC->>Auth: startAuthSession(url, callback)
    Auth->>Safari: present web auth UI
    Safari-->>Auth: user completes or cancels
    Auth-->>ObjC: completion (success/cancel/error)
    ObjC->>ObjC: flowDidFinish() (cleanup)
    ObjC-->>JS: resolve({type:"success"}) or resolve({type:"cancel"/"dismiss"})
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Focus-review items (medium+ priority):
    • RNInAppBrowser: race conditions and single-instance enforcement (ensure no concurrent resolver/rejecter leaks or stale state).
    • Auth session lifecycle: correct mapping of ASWebAuthenticationSession / SFAuthenticationSession callbacks to JS, redirect URL validation, and secure handling of callback URLs.
    • Turnkey key filter: confirm hex-length heuristics (66 / 130) match all valid key formats and cannot misclassify document hashes or non-key strings.

Possibly related PRs

Suggested labels

codex

Suggested reviewers

  • aaronmgdr

Poem

🔑 Hex keys sorted, only Turnkey's remain,
🌊 Safari wakes, auth sessions stake their claim.
🔄 Scripts trimmed tidy, patches no longer hide,
🚦 Flows guarded, single-present at the ride. ✨

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Title check ✅ Passed The PR title focuses on enabling Turnkey for iOS, but the substantial changes involve iOS in-app browser modifications and related infrastructure updates. The title partially captures the scope.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/inappbrowser-turnkey

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@seshanthS seshanthS requested a review from transphorm November 12, 2025 10:05
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
patches/react-native-inappbrowser-reborn+3.7.0.patch (2)

191-206: Consider more robust redirect URL parsing.

The current logic attempts to extract the scheme from a full URL but could fail silently if the URL is malformed. Consider validating the extracted scheme format.

       NSString *escapedRedirectURL = nil;
       if (redirectURL) {
           // Try to parse as a full URL first
           NSURL *redirectUrl = [[NSURL alloc] initWithString:redirectURL];
           if (redirectUrl && redirectUrl.scheme) {
               // It's a full URL, extract the scheme
               escapedRedirectURL = redirectUrl.scheme;
               NSLog(@"[RNInAppBrowser] Extracted callback URL scheme from full URL: %@", escapedRedirectURL);
           } else {
               // It's probably just a scheme already (e.g., "com.warroom.proofofpassport")
               escapedRedirectURL = redirectURL;
               NSLog(@"[RNInAppBrowser] Using redirect URL as scheme directly: %@", escapedRedirectURL);
           }
+          
+          // Validate scheme format
+          if (escapedRedirectURL && ![escapedRedirectURL containsString:@"://"]) {
+              NSLog(@"[RNInAppBrowser] Validated callback URL scheme: %@", escapedRedirectURL);
+          }
       } else {
           NSLog(@"[RNInAppBrowser] WARNING: No redirect URL provided!");
       }

793-811: Replace deprecated keyWindow with proper fallback.

The implementation correctly searches for active scenes, but the fallback to keyWindow is deprecated in iOS 13+.

     // Fallback to keyWindow
     NSLog(@"[RNInAppBrowser] No active scene found, using keyWindow");
-    return [UIApplication sharedApplication].keyWindow;
+    // Use the first window from connected scenes as fallback
+    for (UIScene *scene in [UIApplication sharedApplication].connectedScenes) {
+      if ([scene isKindOfClass:[UIWindowScene class]]) {
+        UIWindowScene *windowScene = (UIWindowScene *)scene;
+        if (windowScene.windows.firstObject) {
+          return windowScene.windows.firstObject;
+        }
+      }
+    }
+    // Last resort: keyWindow (deprecated but handles edge cases)
+    return [UIApplication sharedApplication].keyWindow;
   }
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 58f61cc and e8660b2.

📒 Files selected for processing (2)
  • patches/@turnkey+core+1.7.0.patch (4 hunks)
  • patches/react-native-inappbrowser-reborn+3.7.0.patch (1 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-09-10T14:47:40.945Z
Learnt from: shazarre
Repo: selfxyz/self PR: 1041
File: app/src/providers/passportDataProvider.tsx:297-301
Timestamp: 2025-09-10T14:47:40.945Z
Learning: In app/src/providers/passportDataProvider.tsx: The deleteDocumentDirectlyFromKeychain function is a low-level utility used by the DocumentsAdapter and should not include error handling since callers like deleteDocument() already implement appropriate try/catch with logging for Keychain operations.

Applied to files:

  • patches/@turnkey+core+1.7.0.patch
📚 Learning: 2025-09-22T11:10:57.879Z
Learnt from: CR
Repo: selfxyz/self PR: 0
File: app/AGENTS.md:0-0
Timestamp: 2025-09-22T11:10:57.879Z
Learning: Applies to app/ios/**/*.{m,mm,swift} : Document complex native iOS module changes in the PR

Applied to files:

  • patches/react-native-inappbrowser-reborn+3.7.0.patch
⏰ Context from checks skipped due to timeout of 300000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: type-check
🔇 Additional comments (4)
patches/@Turnkey+core+1.7.0.patch (2)

30-31: Correct implementation of the filter.

The loop now iterates over filteredPublicKeys instead of publicKeys, ensuring only validated Turnkey keys are deleted.


9-22: Approve with caution — verify stored public‑key encoding (128‑char / 0x cases)

Patch correctly limits deletions to likely Turnkey P‑256 keys (66 = compressed, 130 = uncompressed with 0x04). However code/tests also use raw x/y coords (64+64 = 128 hex) and keys may appear with a "0x" prefix. Confirm protocol_store.public_keys / Turnkey API always returns 66 or 130‑char hex (no 128‑char or "0x" variants). If not, either normalize (strip "0x") or extend the filter to accept 128‑char hex before deleting keys.

Patch location: patches/@Turnkey+core+1.7.0.patch -> node_modules/@turnkey/core/dist/clients/core.js(.mjs).

patches/react-native-inappbrowser-reborn+3.7.0.patch (2)

510-575: App may start auth session unexpectedly when backgrounded during scene wait.

The 5-second retry loop uses dispatch_after to poll for an active scene, but lacks any lifecycle listeners to detect when the app enters the background. If the user switches apps during the wait period, the auth session could still start when the app returns to foreground, triggering an unexpected modal or session resumption.

Add a cancel mechanism that detects applicationDidEnterBackground and stops the retry attempts, or move the scene-wait logic into applicationDidBecomeActive to ensure it only runs when appropriate.


167-189: Verify error handling strategy for auth completion handler simplification.

The completion handler treats all errors (user cancellation, network failures, authentication failures) as a single "cancel" type, sacrificing error granularity. While the extensive logging captures details for debugging, the JavaScript layer cannot distinguish between different failure modes.

Confirm whether this simplified "success"/"cancel" response contract is intentional and sufficient for your auth flow requirements. If the caller code needs to differentiate between user cancellations and actual authentication failures, you may want to preserve error type specificity instead of collapsing all errors into one category.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
patches/react-native-inappbrowser-reborn+3.7.0.patch (2)

184-196: Clean up redirect URL parsing logic.

The redirect URL parsing has an empty else block and lacks validation. Consider:

  1. Remove the empty else block
  2. Add validation for the extracted/provided scheme
  3. Log a warning if scheme extraction fails
       NSString *escapedRedirectURL = nil;
       if (redirectURL) {
           // Try to parse as a full URL first
           NSURL *redirectUrl = [[NSURL alloc] initWithString:redirectURL];
           if (redirectUrl && redirectUrl.scheme) {
               // It's a full URL, extract the scheme
               escapedRedirectURL = redirectUrl.scheme;
           } else {
               // It's probably just a scheme already (e.g., "com.warroom.proofofpassport")
               escapedRedirectURL = redirectURL;
           }
-      } else {
       }

494-529: Consider using scene lifecycle notifications instead of polling.

The current polling approach (0.1s intervals, 50 attempts) works but could be more efficient and reliable. Using UISceneDidActivateNotification would eliminate the need for polling and provide immediate response when the scene becomes active.

If the polling timeout expires without finding an active scene, the auth session still starts (line 524), which may lead to the same timing issues this code attempts to solve. Consider either:

  1. Rejecting the promise if no active scene is found after timeout
  2. Using NSNotificationCenter to observe UISceneDidActivateNotification
+ // Add observer in init or when needed
+ [[NSNotificationCenter defaultCenter] addObserverForName:UISceneDidActivateNotification
+                                                     object:nil
+                                                      queue:nil
+                                                 usingBlock:^(NSNotification *note) {
+     [self startAuthSession];
+ }];
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9b3b51e and 2eb3975.

📒 Files selected for processing (2)
  • .cursorignore (0 hunks)
  • patches/react-native-inappbrowser-reborn+3.7.0.patch (1 hunks)
💤 Files with no reviewable changes (1)
  • .cursorignore
🧰 Additional context used
🧠 Learnings (6)
📓 Common learnings
Learnt from: CR
Repo: selfxyz/self PR: 0
File: .cursorrules:0-0
Timestamp: 2025-09-22T11:10:22.019Z
Learning: Test, build, and deploy scripts (`yarn test`, `yarn ios`, `yarn test:e2e:ios`, Fastlane, etc.) must be used for automation.
📚 Learning: 2025-09-22T11:10:57.879Z
Learnt from: CR
Repo: selfxyz/self PR: 0
File: app/AGENTS.md:0-0
Timestamp: 2025-09-22T11:10:57.879Z
Learning: Applies to app/ios/**/*.{m,mm,swift} : Document complex native iOS module changes in the PR

Applied to files:

  • patches/react-native-inappbrowser-reborn+3.7.0.patch
📚 Learning: 2025-09-22T11:10:57.879Z
Learnt from: CR
Repo: selfxyz/self PR: 0
File: app/AGENTS.md:0-0
Timestamp: 2025-09-22T11:10:57.879Z
Learning: Applies to app/**/*.{ios,android,web}.{ts,tsx,js,jsx} : Explain platform-specific code paths in the PR description (files with .ios, .android, or .web extensions)

Applied to files:

  • patches/react-native-inappbrowser-reborn+3.7.0.patch
📚 Learning: 2025-09-22T11:10:22.019Z
Learnt from: CR
Repo: selfxyz/self PR: 0
File: .cursorrules:0-0
Timestamp: 2025-09-22T11:10:22.019Z
Learning: Applies to **/*.{js,ts,tsx,jsx,sol,nr} : ALWAYS redact/mask sensitive fields in logs using consistent patterns (e.g., `***-***-1234` for passport numbers, `J*** D***` for names).

Applied to files:

  • patches/react-native-inappbrowser-reborn+3.7.0.patch
📚 Learning: 2025-08-29T15:31:15.924Z
Learnt from: CR
Repo: selfxyz/self PR: 0
File: packages/mobile-sdk-alpha/AGENTS.md:0-0
Timestamp: 2025-08-29T15:31:15.924Z
Learning: Address CodeRabbitAI feedback and resolve security warnings during review

Applied to files:

  • patches/react-native-inappbrowser-reborn+3.7.0.patch
📚 Learning: 2025-09-22T11:10:22.019Z
Learnt from: CR
Repo: selfxyz/self PR: 0
File: .cursorrules:0-0
Timestamp: 2025-09-22T11:10:22.019Z
Learning: Applies to **/*.{js,ts,tsx,jsx,sol,nr} : NEVER log sensitive data including PII (names, DOB, passport numbers, addresses), credentials, tokens, API keys, private keys, or session identifiers.

Applied to files:

  • patches/react-native-inappbrowser-reborn+3.7.0.patch

Comment on lines +770 to +786
+ - (UIWindow *)presentationAnchorForWebAuthenticationSession:(ASWebAuthenticationSession *)session API_AVAILABLE(ios(13.0))
+ {
+ // Search for an active foreground scene
+ for (UIScene *scene in [UIApplication sharedApplication].connectedScenes) {
+ if (scene.activationState == UISceneActivationStateForegroundActive &&
+ [scene isKindOfClass:[UIWindowScene class]]) {
+ UIWindowScene *windowScene = (UIWindowScene *)scene;
+ UIWindow *window = windowScene.keyWindow ?: windowScene.windows.firstObject;
+ if (window) {
+ return window;
+ }
+ }
+ }
+
+ // Fallback to keyWindow
+ return [UIApplication sharedApplication].keyWindow;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Replace deprecated keyWindow fallback.

The fallback to keyWindow at line 785 uses a deprecated API (iOS 13+). Since this method is only called on iOS 13+, the fallback should use scene-based window access.

   - (UIWindow *)presentationAnchorForWebAuthenticationSession:(ASWebAuthenticationSession *)session API_AVAILABLE(ios(13.0))
   {
     // Search for an active foreground scene
     for (UIScene *scene in [UIApplication sharedApplication].connectedScenes) {
       if (scene.activationState == UISceneActivationStateForegroundActive &&
           [scene isKindOfClass:[UIWindowScene class]]) {
         UIWindowScene *windowScene = (UIWindowScene *)scene;
         UIWindow *window = windowScene.keyWindow ?: windowScene.windows.firstObject;
         if (window) {
           return window;
         }
       }
     }
 
-    // Fallback to keyWindow
-    return [UIApplication sharedApplication].keyWindow;
+    // Fallback: return first available window from any scene
+    for (UIScene *scene in [UIApplication sharedApplication].connectedScenes) {
+      if ([scene isKindOfClass:[UIWindowScene class]]) {
+        UIWindowScene *windowScene = (UIWindowScene *)scene;
+        if (windowScene.windows.firstObject) {
+          return windowScene.windows.firstObject;
+        }
+      }
+    }
+    return nil;
   }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
+ - (UIWindow *)presentationAnchorForWebAuthenticationSession:(ASWebAuthenticationSession *)session API_AVAILABLE(ios(13.0))
+ {
+ // Search for an active foreground scene
+ for (UIScene *scene in [UIApplication sharedApplication].connectedScenes) {
+ if (scene.activationState == UISceneActivationStateForegroundActive &&
+ [scene isKindOfClass:[UIWindowScene class]]) {
+ UIWindowScene *windowScene = (UIWindowScene *)scene;
+ UIWindow *window = windowScene.keyWindow ?: windowScene.windows.firstObject;
+ if (window) {
+ return window;
+ }
+ }
+ }
+
+ // Fallback to keyWindow
+ return [UIApplication sharedApplication].keyWindow;
}
- (UIWindow *)presentationAnchorForWebAuthenticationSession:(ASWebAuthenticationSession *)session API_AVAILABLE(ios(13.0))
{
// Search for an active foreground scene
for (UIScene *scene in [UIApplication sharedApplication].connectedScenes) {
if (scene.activationState == UISceneActivationStateForegroundActive &&
[scene isKindOfClass:[UIWindowScene class]]) {
UIWindowScene *windowScene = (UIWindowScene *)scene;
UIWindow *window = windowScene.keyWindow ?: windowScene.windows.firstObject;
if (window) {
return window;
}
}
}
// Fallback: return first available window from any scene
for (UIScene *scene in [UIApplication sharedApplication].connectedScenes) {
if ([scene isKindOfClass:[UIWindowScene class]]) {
UIWindowScene *windowScene = (UIWindowScene *)scene;
if (windowScene.windows.firstObject) {
return windowScene.windows.firstObject;
}
}
}
return nil;
}
🤖 Prompt for AI Agents
In patches/react-native-inappbrowser-reborn+3.7.0.patch around lines 770 to 786,
the fallback uses the deprecated [UIApplication sharedApplication].keyWindow;
replace that with scene-based window discovery. After the existing loop for a
foregroundActive UIWindowScene, add a second pass that finds any UIWindowScene
in connectedScenes and returns its windows.firstObject (or keyWindow equivalent
from that scene), and only return nil (or handle absence) if no scene window is
found — do not call the deprecated keyWindow API.

@transphorm transphorm marked this pull request as draft November 12, 2025 18:31
@transphorm transphorm changed the title SELF-1204: fix - Inappbrowser not opening for turnkey SELF-1204: Implement Turnkey for iOS Nov 12, 2025
@transphorm transphorm changed the title SELF-1204: Implement Turnkey for iOS SELF-1204: Enable Turnkey for iOS Nov 12, 2025
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.

3 participants