Skip to content

feat(davinci): add SOCIAL_LOGIN_BUTTON IdP collector support (SDKS-5128)#59

Open
pingidentity-gaurav wants to merge 3 commits into
mainfrom
SDKS-5128
Open

feat(davinci): add SOCIAL_LOGIN_BUTTON IdP collector support (SDKS-5128)#59
pingidentity-gaurav wants to merge 3 commits into
mainfrom
SDKS-5128

Conversation

@pingidentity-gaurav

@pingidentity-gaurav pingidentity-gaurav commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Adds IdpCollector (SOCIAL_LOGIN_BUTTON) support to the DaVinci bridge on iOS and Android
  • Renames the collector type from the internal 'IDP' normalization to the server-native 'SOCIAL_LOGIN_BUTTON' string, matching the pattern used by all other collectors
  • Adds rawFieldKey helper for collectors whose id() doesn't match the server key field (extensible for future collectors)
  • Moves setDaVinciCollectorResolver from eager init into configureDaVinci on iOS, matching the Journey pattern
  • Hardens integrationRequiredCollectorTypes to Set<DaVinciCollector['type']> for compile-time safety
  • Adds DaVinciIdpField component and useDaVinciAutoForwarder hook to the sample app for seamless post-IdP-authorize auto-advance
  • Hides missing first/family name fields in UserProfileInfoCard

Test plan

  • Android: social login (Google/Facebook) via DaVinci flow completes without showing intermediate "Continue" button
  • iOS: same flow works on iPhone Ping device
  • Unit tests pass: yarn test --filter=@ping-identity/rn-davinci --filter=@ping-identity/rn-external-idp
  • Typecheck passes: yarn typecheck
  • No regression on Journey or OIDC user profile card

Summary by CodeRabbit

  • New Features

    • Added social sign-in options for DaVinci flows, including branded Google, Facebook, and Apple buttons.
    • Added external identity-provider authorization support across iOS and Android.
    • Added a User Profile button after successful authentication.
    • Automatically advances eligible sign-in steps and restarts flows when returning to the screen.
  • Bug Fixes

    • Improved sign-in error displays with clearer messages and retry actions.
    • Improved handling of missing profile information.

@coderabbitai

coderabbitai Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Adds DaVinci social-login collectors and external IdP authorization across shared types, native bridges, form handling, the sample app, and related tests. It also adds DaVinci instance identity access, collector resolution, retry behavior, profile navigation, and iOS configuration entries.

Changes

DaVinci external IdP flow

Layer / File(s) Summary
Collector contracts and native mapping
packages/davinci/src/*, packages/davinci/android/..., packages/davinci/ios/..., packages/davinci/src/__tests__/*
Adds IdpCollector, classifies SOCIAL_LOGIN_BUTTON as integration-required, serializes it on Android and iOS, and expands mapper/form tests.
DaVinci instance and collector resolution
packages/davinci/src/davinci.ts, packages/davinci/src/useDavinci.tsx, packages/core/ios/CoreRuntime.swift, packages/types/src/index.ts
Adds getId(), exports DaVinci context contracts, and registers resolver APIs for active DaVinci collectors.
External IdP authorization bridge
packages/external-idp/*, PingTestRunner/*
Adds authorizeForDaVinci to TypeScript, Android, and iOS APIs, resolves collectors natively, propagates errors, and tests bridge behavior.
Sample app IdP and profile flow
PingSampleApp/ui/*, PingSampleApp/metro.config.js, PingSampleApp/ios/*, .gitignore
Renders branded social-login buttons, wires authorization and auto-forwarding through the panel, adds retry/profile actions, updates profile display handling, and configures local DaVinci resolution.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant DaVinciIdpField
  participant DaVinciClientPanel
  participant ExternalIdpClient
  participant NativeBridge
  DaVinciIdpField->>DaVinciClientPanel: onIdpAuthorize(collector)
  DaVinciClientPanel->>ExternalIdpClient: authorizeForDaVinci(daVinci, index)
  ExternalIdpClient->>NativeBridge: authorizeForDaVinci(davinciId, options, config)
  NativeBridge-->>ExternalIdpClient: authorization result
  ExternalIdpClient-->>DaVinciClientPanel: resolve or reject
  DaVinciClientPanel->>DaVinciClientPanel: next({ collectors: [] })
Loading

Possibly related PRs

Suggested reviewers: tsdamas, rodrigoareis, george-bafaloukas-forgerock, witrisna

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately captures the main change: adding SOCIAL_LOGIN_BUTTON IdP collector support in DaVinci.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch SDKS-5128

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint install failed. For unrecoverable errors, disable the tool in CodeRabbit configuration.


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.

@codecov

codecov Bot commented Jul 24, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 58.25545% with 134 lines in your changes missing coverage. Please review.
✅ Project coverage is 71.70%. Comparing base (81d22e9) to head (2e92fe8).

Files with missing lines Patch % Lines
...gidentity/rnexternalidp/RNPingExternalIdpCommon.kt 0.00% 66 Missing ⚠️
...ges/external-idp/ios/RNPingExternalIdpCommon.swift 33.80% 47 Missing ⚠️
packages/core/ios/CoreRuntime.swift 42.85% 12 Missing ⚠️
...pingidentity/rndavinci/mapper/DaVinciNodeMapper.kt 87.50% 0 Missing and 2 partials ⚠️
packages/davinci/ios/RNPingDavinciCommon.swift 50.00% 2 Missing ⚠️
...nal-idp/ios/Tests/RNPingExternalIdpImplTests.swift 93.93% 2 Missing ⚠️
packages/davinci/src/davinci.ts 0.00% 1 Missing ⚠️
packages/davinci/src/useDavinci.tsx 0.00% 1 Missing ⚠️
...gidentity/rnexternalidp/RNPingExternalIdpModule.kt 0.00% 1 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##               main      #59      +/-   ##
============================================
+ Coverage     71.41%   71.70%   +0.29%     
- Complexity      181      187       +6     
============================================
  Files           154      161       +7     
  Lines         18052    18890     +838     
  Branches        539      674     +135     
============================================
+ Hits          12891    13546     +655     
- Misses         5111     5271     +160     
- Partials         50       73      +23     
Flag Coverage Δ
android 17.07% <16.86%> (-0.04%) ⬇️
ios 80.34% <72.48%> (-0.12%) ⬇️
javascript 91.31% <77.77%> (-7.28%) ⬇️
Components Coverage Δ
Bridge glue 50.97% <34.21%> (-0.04%) ⬇️
Pure logic 81.43% <95.00%> (+0.26%) ⬆️
Files with missing lines Coverage Δ
...ackages/davinci/ios/Mapper/DaVinciNodeMapper.swift 94.27% <100.00%> (+0.55%) ⬆️
...ges/davinci/ios/Tests/DaVinciNodeMapperTests.swift 99.47% <100.00%> (+0.06%) ⬆️
packages/davinci/src/collectorHelpers.ts 92.80% <100.00%> (ø)
...kages/external-idp/ios/RNPingExternalIdpImpl.swift 100.00% <100.00%> (ø)
...ckages/external-idp/src/NativeRNPingExternalIdp.ts 96.96% <ø> (ø)
packages/external-idp/src/externalIdp.ts 100.00% <100.00%> (ø)
...ckages/external-idp/src/types/externalIdp.types.ts 100.00% <ø> (ø)
packages/types/src/index.ts 96.66% <ø> (ø)
packages/davinci/src/davinci.ts 95.93% <0.00%> (ø)
packages/davinci/src/useDavinci.tsx 78.94% <0.00%> (ø)
... and 7 more

... and 3 files with indirect coverage changes


Continue to review full report in Codecov by Harness.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 81d22e9...2e92fe8. Read the comment docs.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@github-actions

github-actions Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor
PR Preview Action v1.8.1

QR code for preview link

🚀 View preview at
https://ForgeRock.github.io/ping-react-native-sdk/docs-preview/pr-59/

Built to branch gh-pages at 2026-07-24 14:36 UTC.
Preview will be ready when the GitHub Pages deployment is complete.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🧹 Nitpick comments (2)
packages/davinci/src/types/node.types.ts (1)

385-385: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Optional: preserve literal autocomplete on idpType.

'GOOGLE' | 'FACEBOOK' | 'APPLE' | string collapses to string in TypeScript, so editors lose autocomplete for the known providers while still accepting any string. If keeping the union open is the goal, ... | (string & {}) retains both.

♻️ Suggested change
-  idpType: 'GOOGLE' | 'FACEBOOK' | 'APPLE' | string;
+  idpType: 'GOOGLE' | 'FACEBOOK' | 'APPLE' | (string & {});
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/davinci/src/types/node.types.ts` at line 385, Update the idpType
declaration to preserve autocomplete for the known GOOGLE, FACEBOOK, and APPLE
literals while continuing to accept arbitrary strings, using the open-string
union pattern described in the review.
packages/external-idp/android/src/main/java/com/pingidentity/rnexternalidp/RNPingExternalIdpCommon.kt (1)

317-425: 📐 Maintainability & Code Quality | 🔵 Trivial

Consider extracting the shared authorize-boilerplate.

authorizeForDaVinci duplicates most of authorizeForJourney's control flow: the foreground-activity guard, parseCallbackIndex usage, scope.launchBridge wrapping, and the CancellationException / ClassNotFoundException / NoClassDefFoundError catch structure are copy-pasted verbatim, differing only in the resolved collector type and success payload. A small helper (e.g. runIdpAuthorize(promise, errorCode) { ... } wrapping the guard + catch logic, with the resolve/collector-lookup passed as a lambda) would reduce this duplication and keep future error-handling changes in one place.
[medium_effort_and_medium_reward]

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@packages/external-idp/android/src/main/java/com/pingidentity/rnexternalidp/RNPingExternalIdpCommon.kt`
around lines 317 - 425, Extract the duplicated authorization flow from
authorizeForDaVinci and authorizeForJourney into a shared helper, such as
runIdpAuthorize, that owns the foreground-activity check, scope.launchBridge
wrapper, and CancellationException/provider-SDK/general error handling. Pass the
provider-specific collector resolution and authorization operation as lambdas,
while preserving each method’s collector type and success payload behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/davinci/android/build.gradle`:
- Around line 99-100: Make IdpCollector available at runtime for
DaVinciNodeMapper.mapCollectorPayload by changing the external-idp dependency
from compileOnly to the appropriate runtime-inclusive configuration. Preserve
the existing collector mapping behavior so apps using rn-davinci alone cannot
fail with a missing IdpCollector class.

In `@packages/external-idp/ios/RNPingExternalIdpCommon.swift`:
- Around line 302-304: The authorization flow in IdpCollector.authorize must
receive only the callback URL scheme, not the full redirect URI. Update the
callbackURLScheme construction near callConfig.redirectUri to parse and extract
its scheme while preserving nil for an empty redirect URI, then pass that value
to authorize.

In `@PingSampleApp/ios/PingSampleApp/Info.plist`:
- Around line 66-67: Update the GIDServerClientID entry in Info.plist to
reference the backend web/server OAuth client ID configuration, while preserving
GIDClientID as the iOS client ID. Use the existing backend client-ID symbol or
setting rather than GOOGLE_IOS_CLIENT_ID.

In `@PingSampleApp/ui/davinci/hooks/useDaVinciAutoForwarder.ts`:
- Around line 73-94: Prevent idpJustAuthorized from leaking past the immediate
post-authorization ContinueNode: in
PingSampleApp/ui/davinci/hooks/useDaVinciAutoForwarder.ts:73-94, call
onForwarded() when the ContinueNode cannot auto-forward due to manual input,
integration requirements, unsupported state, or !canSubmit, while preserving the
successful-forward path; in
PingSampleApp/ui/davinci/hooks/useDaVinciClientPanelController.ts:122-131, reset
setIdpJustAuthorized(false) in onStart alongside setIdpError(null).

In `@PingSampleApp/ui/davinci/hooks/useDaVinciClientPanelController.ts`:
- Around line 169-203: Update onIdpAuthorize to handle a missing collector match
explicitly instead of passing index 0 to authorizeForDaVinci. When findIndex
returns -1, surface an appropriate error through the existing IdP error handling
and return without authorizing; preserve the current authorization flow for
valid indices.

---

Nitpick comments:
In `@packages/davinci/src/types/node.types.ts`:
- Line 385: Update the idpType declaration to preserve autocomplete for the
known GOOGLE, FACEBOOK, and APPLE literals while continuing to accept arbitrary
strings, using the open-string union pattern described in the review.

In
`@packages/external-idp/android/src/main/java/com/pingidentity/rnexternalidp/RNPingExternalIdpCommon.kt`:
- Around line 317-425: Extract the duplicated authorization flow from
authorizeForDaVinci and authorizeForJourney into a shared helper, such as
runIdpAuthorize, that owns the foreground-activity check, scope.launchBridge
wrapper, and CancellationException/provider-SDK/general error handling. Pass the
provider-specific collector resolution and authorization operation as lambdas,
while preserving each method’s collector type and success payload behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 73a3afb5-4ff0-4d69-9705-98887b6e606e

📥 Commits

Reviewing files that changed from the base of the PR and between 81d22e9 and 2e92fe8.

⛔ Files ignored due to path filters (1)
  • PingSampleApp/ios/Podfile.lock is excluded by !**/*.lock
📒 Files selected for processing (48)
  • .gitignore
  • PingSampleApp/ios/PingSampleApp/Info.plist
  • PingSampleApp/metro.config.js
  • PingSampleApp/ui/DaVinciScreen.tsx
  • PingSampleApp/ui/davinci/components/molecules/DaVinciFieldRenderer.tsx
  • PingSampleApp/ui/davinci/components/molecules/DaVinciIdpField.tsx
  • PingSampleApp/ui/davinci/components/molecules/types.ts
  • PingSampleApp/ui/davinci/components/organisms/DaVinciClientPanel.tsx
  • PingSampleApp/ui/davinci/components/organisms/DaVinciContinueNodePanel.tsx
  • PingSampleApp/ui/davinci/hooks/useDaVinciAutoForwarder.ts
  • PingSampleApp/ui/davinci/hooks/useDaVinciAutoStartEffect.ts
  • PingSampleApp/ui/davinci/hooks/useDaVinciClientPanelController.ts
  • PingSampleApp/ui/userProfile/components/molecules/UserProfileInfoCard.tsx
  • PingTestRunner/__tests__/integration/external-idp.test.ts
  • PingTestRunner/android/settings.gradle
  • packages/core/ios/CoreRuntime.swift
  • packages/davinci/RNPingDavinci.podspec
  • packages/davinci/android/build.gradle
  • packages/davinci/android/src/main/java/com/pingidentity/rndavinci/mapper/DaVinciNodeMapper.kt
  • packages/davinci/android/src/test/java/com/pingidentity/rndavinci/mapper/DaVinciNodeMapperTest.kt
  • packages/davinci/ios/Mapper/DaVinciNodeMapper.swift
  • packages/davinci/ios/RNPingDavinciCommon.swift
  • packages/davinci/ios/Tests/DaVinciNodeMapperTests.swift
  • packages/davinci/src/__tests__/collectorHelpers.test.ts
  • packages/davinci/src/__tests__/useDavinciForm.test.tsx
  • packages/davinci/src/collectorHelpers.ts
  • packages/davinci/src/davinci.ts
  • packages/davinci/src/index.tsx
  • packages/davinci/src/types/client.types.ts
  • packages/davinci/src/types/node.types.ts
  • packages/davinci/src/useDavinci.tsx
  • packages/external-idp/README.md
  • packages/external-idp/android/build.gradle
  • packages/external-idp/android/src/main/java/com/pingidentity/rnexternalidp/RNPingExternalIdpCommon.kt
  • packages/external-idp/android/src/newarch/java/com/pingidentity/rnexternalidp/RNPingExternalIdpModule.kt
  • packages/external-idp/android/src/oldarch/java/com/pingidentity/rnexternalidp/RNPingExternalIdpClassicModule.kt
  • packages/external-idp/android/src/test/java/com/pingidentity/rnexternalidp/RNPingExternalIdpTest.kt
  • packages/external-idp/ios/RNPingExternalIdp.mm
  • packages/external-idp/ios/RNPingExternalIdpClassic.mm
  • packages/external-idp/ios/RNPingExternalIdpCommon.swift
  • packages/external-idp/ios/RNPingExternalIdpImpl.swift
  • packages/external-idp/ios/Tests/RNPingExternalIdpImplTests.swift
  • packages/external-idp/src/NativeRNPingExternalIdp.ts
  • packages/external-idp/src/__tests__/index.test.tsx
  • packages/external-idp/src/externalIdp.ts
  • packages/external-idp/src/types/externalIdp.types.ts
  • packages/external-idp/src/types/index.ts
  • packages/types/src/index.ts

Comment on lines +99 to +100
// Required: IdpCollector is defined in external-idp. The JS authorizeForDaVinci API is optional but this native dep is not.
compileOnly("com.pingidentity.sdks:external-idp:2.0.1")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Confirm the mapper references IdpCollector at runtime (unconditional type check)
rg -nP -C3 '\bis\s+IdpCollector\b|collector\.idpId' \
  packages/davinci/android/src/main/java/com/pingidentity/rndavinci/mapper/DaVinciNodeMapper.kt

# Confirm external-idp is only compileOnly (not implementation/api) in the davinci module
rg -nP 'external-idp' packages/davinci/android/build.gradle

Repository: ForgeRock/ping-react-native-sdk

Length of output: 1554


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Files =="
git ls-files | rg '(^packages/davinci/|build\.gradle|Podspec$|package\.json$)' || true

echo
echo "== DaVinci Android build deps outline =="
awk '
/dependencies \{/,/^\}/ { print NR": "$0 }
' packages/davinci/android/build.gradle | sed 's/.*dependencies \{/dependencies {/; s/^\}\s*$/}/'

echo
echo "== All external-idp references =="
rg -n 'external-idp|external_idp|PingExternalIdP|IdpCollector|daVinci|DaVinci' .

Repository: ForgeRock/ping-react-native-sdk

Length of output: 5601


🏁 Script executed:

#!/bin/bash
set -euo pipefail

wc -l packages/davinci/android/build.gradle
echo
cat -n packages/davinci/android/build.gradle

echo
echo "== external-idp references in DaVinci =="
rg -n 'external-idp|external_idp|PingExternalIdP|IdpCollector|collectors|collector' packages/davinci -S

echo
echo "== DaVinci podspec dependency lines =="
rg -n 'dependency|vendored_libraries|s\.dependency|PingExternalIdP' packages/davinci/RNPingDavinci.podspec -A2 -B2

echo
echo "== external-idp Gradle and package artifacts =='
cat -n packages/external-idp/package.json
cat -n packages/external-idp/android/build.gradle

Repository: ForgeRock/ping-react-native-sdk

Length of output: 50388


🌐 Web query:

Ping Identity react native SDK rn-davinci external-idp 2.0.1 IdpCollector darnci

💡 Result:

In the context of Ping Identity orchestration and DaVinci flows, IdpCollector is a component returned by a DaVinci flow to the client application when the user needs to authenticate using an external Identity Provider (IdP) [1][2]. The references to rn-davinci, 2.0.1, and darnci appear to be specific internal naming conventions or legacy terminology [3]. The Ping Identity documentation has moved toward a unified "Ping Orchestration SDKs 2.0" framework, which integrates support for DaVinci flows and replaces older modular SDK approaches [3]. Key technical points regarding IdpCollector and external IdPs: 1. Handling External IdPs: When a DaVinci flow returns an IdpCollector, the client application must use this collector to initiate the authentication process with the chosen external IdP [1][2]. 2. Implementation: For mobile platforms (like those using React Native), this typically involves calling an authorize method on the IdpCollector [2]. This initiates the redirect or embedded flow required to authenticate the user and return to the application via a continueToken [1]. 3. Architecture: The current recommendation is to use the Ping Orchestration SDKs, which provide unified documentation for JavaScript, Android, and iOS integrations [3]. Older documentation specifically labeled "DaVinci module" has been folded into this new Orchestration SDK documentation [3]. If you are encountering errors or specific behavior related to version 2.0.1 or terms like darnci, it is recommended to ensure your project is utilizing the current Ping Orchestration SDKs, as these are the primary supported libraries for DaVinci flow orchestration [4][3].

Citations:


🌐 Web query:

com.pingidentity.sdks external-idp 2.0.1 maven IdpCollector

💡 Result:

The artifact com.pingidentity.sdks:external-idp is a component of the Ping (ForgeRock) SDK suite used for integrating external identity providers (IdPs) into applications, particularly within DaVinci orchestration flows [1][2]. Regarding your specific query about version 2.0.1 and the IdpCollector class: 1. IdpCollector Usage: The IdpCollector is a node type returned by a DaVinci flow when an authentication process requires an external IdP [3][4][5]. Your application code must be configured to handle this collector node to initiate the authentication process (e.g., by calling authorize) [3][4]. 2. Dependency Information: While documentation references version 2.0.1 in the context of DaVinci Android SDK integration [1], publicly available artifacts on Maven Central for com.pingidentity.sdks:external-idp currently list version 1.2.0 as the latest [6]. If you are working in an environment that requires 2.0.1, ensure you are using the correct repository configuration provided by Ping Identity, as orchestration SDK dependencies are sometimes distributed through specialized channels [1]. 3. Documentation: For detailed implementation guidance, including class references and code samples for handling IdpCollector nodes in Android, iOS, and JavaScript, you should refer to the official Ping Identity Developer documentation for DaVinci SDKs [1][7][3][4][5].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Gradle GradleUtils compileOnly handling =="
rg -n 'compileOnly|implementation|api|transitive' --glob '*.*gradle*' .

echo
echo "== Kotlin type checks runtime semantics probe only if compiler available =="
if command -v kotlinc >/dev/null 2>&1; then
  tmp="$(mktemp -d)"
  cat > "$tmp/Main.kt" <<'KT'
sealed class C
object IdpCollector : C()
class Collector
fun C.isOrDoesWork(c: C): String = when(c) {
    is IdpCollector -> "idp"
    else -> "other"
}
fun collectorWork(c: Collector?): String = when(c) {
    is IdpCollector -> "idp"
    else -> "error"
}
fun main() {
  println(Collector().let { c -> c.isOrDoesWork(c) || collectorWork(c as? Collector) })
}
KT
  kotlinc "$tmp/Main.kt" -include-runtime -d "$tmp/out.jar" 2>&1 | head -80 || true
else
  echo "kotlinc not available"
fi

Repository: ForgeRock/ping-react-native-sdk

Length of output: 13238


Make DaVinci runtime handle IdpCollector safely when external-idp is absent.

DaVinciNodeMapper.mapCollectorPayload is executed for every collector and references com.pingidentity.idp.davinci.IdpCollector directly; with external-idp marked compileOnly, it is not guaranteed to be on the consumer app’s runtime classpath. If an app uses only rn-davinci without rn-external-idp, collector mapping can fail. Guard IdpCollector at runtime or add the dependency so this required native path does not throw.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/davinci/android/build.gradle` around lines 99 - 100, Make
IdpCollector available at runtime for DaVinciNodeMapper.mapCollectorPayload by
changing the external-idp dependency from compileOnly to the appropriate
runtime-inclusive configuration. Preserve the existing collector mapping
behavior so apps using rn-davinci alone cannot fail with a missing IdpCollector
class.

Comment on lines +302 to +304
let collector = matching[collectorIndex]
let callbackURLScheme = callConfig.redirectUri.isEmpty ? nil : callConfig.redirectUri
let result = await collector.authorize(callbackURLScheme: callbackURLScheme)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n -C 3 'callbackURLScheme|redirectUri|authorizeForDaVinci' \
  packages/external-idp packages/davinci

Repository: ForgeRock/ping-react-native-sdk

Length of output: 50388


🌐 Web query:

For Ping Identity DaVinci iOS SDK version 2.0.1, does IdpCollector.authorize(callbackURLScheme:) require only a URL scheme, or does it accept a complete redirect URI?

💡 Result:

For Ping Identity DaVinci iOS SDK version 2.0.1, the IdpCollector.authorize method does not take a parameter for a redirect URI or scheme [1]. The authentication process is managed by the SDK, which relies on the configuration established in the PingOne platform and your iOS app's Info.plist [2][3]. When integrating external Identity Providers (IdPs) in your iOS application, you must configure a custom URL scheme in your app's Xcode project settings (specifically in the URL Types panel) to handle the callback after authentication [2][3]. The corresponding value (e.g., myapp://example.com) must also be set in the PingOne Form connector's Application Return URL field to ensure the IdP knows where to redirect the user upon completion [3]. The SDK automatically handles the interaction with the external IdP, and you initiate this process simply by calling authorize on the IdpCollector object [1].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Candidate files:"
fd -a 'RNPingExternalIdpCommon.swift|authenticate_with_external_idps|handling_uri_schemes' . | sed 's#^\./##' | head -50

echo
echo "Relevant ios Swift declarations:"
rg -n -C 4 'func authorize|authorize\(callbackURLScheme|callbackURLScheme:' packages/external-idp/ios/RNPingExternalId pCommon.swift packages 2>/dev/null || true

Repository: ForgeRock/ping-react-native-sdk

Length of output: 12733


🌐 Web query:

site:developer.pingidentity.com OrchSDKs DaVinci iOS authorize callbackURLScheme IdpCollector

💡 Result:

In the Ping Identity Orchestration (DaVinci) SDK for iOS, the IdpCollector is a component used to facilitate social sign-on (authentication with external identity providers like Apple, Google, or Facebook) [1][2]. To implement social sign-on with the IdpCollector, you must integrate the PingExternalIdp library alongside your core DaVinci dependency (such as PingDavinci) [3]. The core components of this process involve: 1. Handling URI Schemes: Because the authentication flow redirects users to an external identity provider and back to your app, you must configure a custom URL scheme in Xcode under your project's Info tab [4]. This ensures your app is correctly brought back to the foreground when the authentication process finishes [4]. 2. Using IdpCollector: When a DaVinci flow reaches an external IdP step, it returns an IdpCollector [1][5]. Your application code should detect this collector (often within a ViewModel), and call the authorize method on the IdpCollector instance to initiate the external authentication flow [1]. Example Swift usage: public func startSocialAuthentication async -> Result<Bool, IdpExceptions> { return await idpCollector.authorize } The authorize method is asynchronous and returns a result indicating whether the authentication succeeded or failed [1]. If successful, the flow typically continues; if not, it returns a failure with associated IdpExceptions [1].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Inspect relevant Swift implementation and config parser:"
sed -n '230,315p' packages/external-idp/ios/RNPingExternalIdpCommon.swift
echo
sed -n '240,265p' packages/external-idp/ios/Config/ExternalIdpConfigParser.swift
echo
echo "Check for callbackURLScheme parsing/normalization:"
rg -n 'callbackURLScheme|callbackScheme|parsedUrl|URL(string|path|init)|scheme' packages/external-idp/ios packages/browser/ios tests 2>/dev/null || true

echo
echo "List repo packages that define/dependency PingExternalIdP:"
rg -n 'PingExternalIdP|ExternalIdp|IdpCollector\.authorize|IdpCollector' packages -g '*.podspec' -g '*.pod' -g '*.swift' -g '*.kt' -g '*.java' -g '*.mm' | head -200

Repository: ForgeRock/ping-react-native-sdk

Length of output: 3623


🌐 Web query:

"PingExternalIdP" "IdpCollector" "authorize(callbackURLScheme:"

💡 Result:

In the Ping Identity SDKs (such as the iOS Unified SDK), the IdpCollector class provides an authorize method used to initiate authentication with an external Identity Provider (IdP) [1][2]. The method is defined as follows in Swift: open func authorize(callbackURLScheme: String? = nil) async -> Result<Bool, IdpExceptions> [1] The callbackURLScheme parameter is an optional string used to specify the custom URI scheme for returning to your application after the external authentication process completes [1]. When calling this method, you provide the scheme configured in your PingOne application settings to ensure the browser or external user-agent can successfully redirect the user back to your app [3]. Key details regarding the authorize method: - Asynchronous execution: The method is asynchronous and returns a Result type [1][2]. - Result Handling: It returns a success (indicating the authentication flow was initiated or completed as expected) or a failure, which includes IdpExceptions detailing any root causes for issues [4][2]. - Usage: It is typically invoked when your application handles an IdpCollector node received from the DaVinci orchestration flow [4][2]. - Android variance: In Android SDKs, the authorize method may require a URI parameter, such as a redirect URI string, to facilitate the callback process [5].

Citations:


Use only the callback scheme for DaVinci external IdP authorization.

IdpCollector.authorize(callbackURLScheme:) expects the custom URL scheme, but callConfig.redirectUri is passed as the full redirect URI such as com.myapp://callback. Extract the scheme before this call so the SDK can route the auth callback back to the app.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/external-idp/ios/RNPingExternalIdpCommon.swift` around lines 302 -
304, The authorization flow in IdpCollector.authorize must receive only the
callback URL scheme, not the full redirect URI. Update the callbackURLScheme
construction near callConfig.redirectUri to parse and extract its scheme while
preserving nil for an empty redirect URI, then pass that value to authorize.

Comment on lines +66 to +67
<key>GIDServerClientID</key>
<string>$(GOOGLE_IOS_CLIENT_ID)</string>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n --glob '*.xcconfig' --glob '*.pbxproj' --glob '*.plist' \
  'GOOGLE_(IOS|SERVER)_CLIENT_ID' PingSampleApp packages

Repository: ForgeRock/ping-react-native-sdk

Length of output: 469


🌐 Web query:

Google Sign-In for iOS GIDServerClientID backend OAuth client ID developers.google.com

💡 Result:

The GIDServerClientID key is used in your iOS or macOS application's Info.plist file to configure the Google Sign-In SDK for backend authentication or offline access [1][2][3]. When your application needs to communicate with a backend server—such as to verify the user's ID token or exchange an authorization code for tokens—it must identify your backend project to Google's authentication system [4][1]. The GIDServerClientID serves this purpose [1]. Key details regarding GIDServerClientID: 1. Purpose: It represents your backend server's OAuth client ID, not your iOS/macOS application's client ID [1]. Providing this allows the SDK to include the correct audience information in the ID token or to request an authorization code that your specific backend is authorized to exchange [5][4][6]. 2. Configuration: You set this by adding the GIDServerClientID key to your Info.plist file with your web application's OAuth client ID as the string value [1][3]: GIDServerClientID YOUR_SERVER_CLIENT_ID 3. Obtaining the ID: You must create this client ID in the Google Cloud Console by selecting the Web application type for your project's OAuth client ID [1]. If you do not include this key, you will be unable to securely authenticate users on your backend or perform offline access actions like obtaining a serverAuthCode [5][4][1]. For most integrations, this is an optional key used only if your app requires a backend component [1][3].

Citations:


Use the backend OAuth client ID for GIDServerClientID.

GIDServerClientID must be the web/server OAuth client ID used by the backend, while GIDClientID should remain the iOS client ID.

Proposed fix
 <key>GIDServerClientID</key>
-<string>$(GOOGLE_IOS_CLIENT_ID)</string>
+<string>$(GOOGLE_SERVER_CLIENT_ID)</string>
📝 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
<key>GIDServerClientID</key>
<string>$(GOOGLE_IOS_CLIENT_ID)</string>
<key>GIDServerClientID</key>
<string>$(GOOGLE_SERVER_CLIENT_ID)</string>
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@PingSampleApp/ios/PingSampleApp/Info.plist` around lines 66 - 67, Update the
GIDServerClientID entry in Info.plist to reference the backend web/server OAuth
client ID configuration, while preserving GIDClientID as the iOS client ID. Use
the existing backend client-ID symbol or setting rather than
GOOGLE_IOS_CLIENT_ID.

Comment on lines +73 to +94
useEffect(() => {
if (node?.type !== 'ContinueNode') {
lastNodeKeyRef.current = null;
return;
}
if (!enabled) return;
if (loading) return;
if (!continueNodeKey) return;
if (lastNodeKeyRef.current === continueNodeKey) return;

const { meta, canSubmit, input } = form;
if (meta.hasManual) return;
if (meta.hasIntegrationRequired) return;
if (meta.hasUnsupported) return;
if (!canSubmit) return;

lastNodeKeyRef.current = continueNodeKey;
onForwarded();
void next(input).catch(() => {
// Error state is surfaced by the useDaVinci hook.
});
}, [continueNodeKey, enabled, form, loading, next, node?.type, onForwarded]);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

idpJustAuthorized can get stuck true, later auto-forwarding unrelated nodes (including a fresh flow's first node).

The flag is only ever cleared by onForwarded() inside the auto-forwarder's success path. If the post-authorize ContinueNode requires manual input, the effect returns early without clearing it, and nothing else (including onStart) resets it either — so it can leak into unrelated later nodes or a subsequent start(), contradicting the hook's own "only immediately after IdP authorize" / "never fires on the initial start() node" invariants.

  • PingSampleApp/ui/davinci/hooks/useDaVinciAutoForwarder.ts#L73-L94: clear the window (call onForwarded()) as soon as a ContinueNode check concludes it can't auto-forward (manual/unsupported/integration-required/!canSubmit), not only on a successful forward.
  • PingSampleApp/ui/davinci/hooks/useDaVinciClientPanelController.ts#L122-L131: as a defense-in-depth backstop, also reset setIdpJustAuthorized(false) in onStart alongside setIdpError(null).
📍 Affects 2 files
  • PingSampleApp/ui/davinci/hooks/useDaVinciAutoForwarder.ts#L73-L94 (this comment)
  • PingSampleApp/ui/davinci/hooks/useDaVinciClientPanelController.ts#L122-L131
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@PingSampleApp/ui/davinci/hooks/useDaVinciAutoForwarder.ts` around lines 73 -
94, Prevent idpJustAuthorized from leaking past the immediate post-authorization
ContinueNode: in
PingSampleApp/ui/davinci/hooks/useDaVinciAutoForwarder.ts:73-94, call
onForwarded() when the ContinueNode cannot auto-forward due to manual input,
integration requirements, unsupported state, or !canSubmit, while preserving the
successful-forward path; in
PingSampleApp/ui/davinci/hooks/useDaVinciClientPanelController.ts:122-131, reset
setIdpJustAuthorized(false) in onStart alongside setIdpError(null).

Comment on lines +169 to +203
const onIdpAuthorize = useCallback(
async (collector: IdpCollector): Promise<void> => {
if (loading) {
return;
}
const davinciClient = davinciContext?.client;
if (!davinciClient) {
console.warn(
'[DaVinci] authorizeForDaVinci: no DaVinci client in context',
);
return;
}
// Determine which index this collector occupies among IDP collectors on the
// current node so the native side can resolve the right IdpCollector.
const idpFields = form.fields.filter(
f => f.type === 'SOCIAL_LOGIN_BUTTON',
);
const index = idpFields.findIndex(f => f.key === collector.key);
try {
// authorizeForDaVinci opens the IdP browser flow and sets the
// resume request internally — token flows through the subsequent next().
await externalIdp.authorizeForDaVinci(davinciClient, {
index: index >= 0 ? index : 0,
});
setIdpJustAuthorized(true);
await next({ collectors: [] });
} catch (err) {
console.warn('[DaVinci] authorizeForDaVinci failed:', err);
const msg = err instanceof Error ? err.message : String(err);
setIdpError(msg);
}
},
[davinciContext?.client, externalIdp, form.fields, loading, next],
);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Fallback to index 0 on unmatched collector could authorize the wrong IdP.

If collector.key isn't found among SOCIAL_LOGIN_BUTTON fields, index >= 0 ? index : 0 silently defaults to the first IdP collector rather than surfacing an error — when there are multiple social-login buttons, this could authorize a different provider than the one the user tapped.

🐛 Suggested fix
       const index = idpFields.findIndex(f => f.key === collector.key);
+      if (index < 0) {
+        console.warn('[DaVinci] authorizeForDaVinci: collector key not found among SOCIAL_LOGIN_BUTTON fields');
+        setIdpError('Unable to resolve the selected sign-in provider.');
+        return;
+      }
       try {
         await externalIdp.authorizeForDaVinci(davinciClient, {
-          index: index >= 0 ? index : 0,
+          index,
         });
📝 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
const onIdpAuthorize = useCallback(
async (collector: IdpCollector): Promise<void> => {
if (loading) {
return;
}
const davinciClient = davinciContext?.client;
if (!davinciClient) {
console.warn(
'[DaVinci] authorizeForDaVinci: no DaVinci client in context',
);
return;
}
// Determine which index this collector occupies among IDP collectors on the
// current node so the native side can resolve the right IdpCollector.
const idpFields = form.fields.filter(
f => f.type === 'SOCIAL_LOGIN_BUTTON',
);
const index = idpFields.findIndex(f => f.key === collector.key);
try {
// authorizeForDaVinci opens the IdP browser flow and sets the
// resume request internally — token flows through the subsequent next().
await externalIdp.authorizeForDaVinci(davinciClient, {
index: index >= 0 ? index : 0,
});
setIdpJustAuthorized(true);
await next({ collectors: [] });
} catch (err) {
console.warn('[DaVinci] authorizeForDaVinci failed:', err);
const msg = err instanceof Error ? err.message : String(err);
setIdpError(msg);
}
},
[davinciContext?.client, externalIdp, form.fields, loading, next],
);
const onIdpAuthorize = useCallback(
async (collector: IdpCollector): Promise<void> => {
if (loading) {
return;
}
const davinciClient = davinciContext?.client;
if (!davinciClient) {
console.warn(
'[DaVinci] authorizeForDaVinci: no DaVinci client in context',
);
return;
}
// Determine which index this collector occupies among IDP collectors on the
// current node so the native side can resolve the right IdpCollector.
const idpFields = form.fields.filter(
f => f.type === 'SOCIAL_LOGIN_BUTTON',
);
const index = idpFields.findIndex(f => f.key === collector.key);
if (index < 0) {
console.warn(
'[DaVinci] authorizeForDaVinci: collector key not found among SOCIAL_LOGIN_BUTTON fields',
);
setIdpError('Unable to resolve the selected sign-in provider.');
return;
}
try {
// authorizeForDaVinci opens the IdP browser flow and sets the
// resume request internally — token flows through the subsequent next().
await externalIdp.authorizeForDaVinci(davinciClient, {
index,
});
setIdpJustAuthorized(true);
await next({ collectors: [] });
} catch (err) {
console.warn('[DaVinci] authorizeForDaVinci failed:', err);
const msg = err instanceof Error ? err.message : String(err);
setIdpError(msg);
}
},
[davinciContext?.client, externalIdp, form.fields, loading, next],
);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@PingSampleApp/ui/davinci/hooks/useDaVinciClientPanelController.ts` around
lines 169 - 203, Update onIdpAuthorize to handle a missing collector match
explicitly instead of passing index 0 to authorizeForDaVinci. When findIndex
returns -1, surface an appropriate error through the existing IdP error handling
and return without authorizing; preserve the current authorization flow for
valid indices.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant