Skip to content

feat(rn-fido): Add support for FidoRegistrationCollector and FidoAuthenticationCollector(SDKS-5302) - #70

Open
tsdamas wants to merge 5 commits into
mainfrom
SDKS-5302
Open

feat(rn-fido): Add support for FidoRegistrationCollector and FidoAuthenticationCollector(SDKS-5302)#70
tsdamas wants to merge 5 commits into
mainfrom
SDKS-5302

Conversation

@tsdamas

@tsdamas tsdamas commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds DaVinci passkey support to the React Native FIDO bridge. The FIDO2 DaVinci collector type is now serialized with its full payload on Android and iOS, and FidoClient gains registerForDaVinci / authenticateForDaVinci ceremonies that run the native WebAuthn flow and submit natively through the collector when the DaVinci flow advances.

Based on PR #67 (SDKS-5164); consumes the integration collector registry introduced there.

Changes

  • Expose DaVinci FIDO2 collectors through the public React Native API:
    • fidoCollectorType ('FIDO2') and FidoRegistrationCollector / FidoAuthenticationCollector, discriminated by action: 'REGISTER' | 'AUTHENTICATE'.
    • publicKeyCredentialCreationOptions / publicKeyCredentialRequestOptions payloads and the Android-only trigger field.
  • Add FidoClient.registerForDaVinci() and FidoClient.authenticateForDaVinci(). They resolve the collector from a DaVinci instance, run the native ceremony, and return the informational attestation/assertion payload. Submission happens natively via collector.payload() during daVinci.next({ collectors: [] }); the FIDO collector key must not be passed in next() input.
  • Add optional index to the DaVinci ceremony options for selecting among multiple FIDO2 collectors with the same action.
  • Activate the FIDO integration at client creation: createFidoClient registers FIDO2 with the integration collector registry in @ping-identity/rn-types and eagerly registers the native DaVinci collector serializer, so nodes mapped before the first ceremony still carry action and the WebAuthn options.
  • Update the Android bridge:
    • One-shot, synchronized DaVinci serializer registration emitting the action-discriminated FIDO2 payload (key, type, action, label, required, trigger, pre-encoded WebAuthn options).
    • registerForDaVinci / authenticateForDaVinci ceremonies with foreground-activity and collector-resolution preconditions.
    • New stable error code FIDO_COLLECTOR_NOT_FOUND.
    • New methods exposed via both TurboModule and classic module.
  • Update the iOS bridge:
    • Thread-safe one-shot serializer registration gate (NSLock-guarded, @unchecked Sendable).
    • @MainActor ceremony methods with window-availability preconditions.
    • Logger forwarding for standalone FIDO operations via the client's native logger id.
    • New methods exposed via both TurboModule and classic module.
  • Warn in both DaVinci node mappers (Android and iOS) when an integration collector type has no registered serializer and falls back to the generic {key, type} payload. This is the failure mode diagnosed in SDKS-5302, where top-level fields like action were silently dropped.
  • Add a test-only resetDaVinciCollectorSerializersForTesting seam to iOS CoreRuntime for hermetic serializer tests.
  • Wire FIDO passkey ceremonies into PingSampleApp: a DaVinciFidoField renderer with cancel detection, fidoCollectorType in handledCollectorTypes, and associated-domain passkey configuration (Android Digital Asset Links asset_statements, iOS webcredentials entitlements, ASSOCIATED_DOMAIN env).
  • Add and expand test coverage:
    • Android Robolectric serializer payload and registration tests, plus RNPingFidoTest ceremony coverage.
    • iOS XCTest serializer payload, registration idempotence, and window-unavailable cases.
    • JS unit tests for both DaVinci methods, default options forwarding, and FidoError code preservation.
    • PingTestRunner integration tests for DaVinci ceremony orchestration (registerForDaVinci then next({ collectors: [] }) submits without the FIDO key) and native-spec contract updates.
  • Document the DaVinci integration in the rn-fido README (registration flow, handledCollectorTypes, index semantics) and add the FIDO2 integration collector row and guidance to the rn-davinci README.

Platform notes

  • iOS omits trigger because the iOS SDK does not expose it; revisit after upgrading to native SDK 2.2 (tracked by a TODO in fido.types.ts).
  • WebAuthn options arrive pre-encoded: Android emits unpadded base64url and iOS emits standard base64, so consumers decoding these values must accept both flavours.

Verification

  • Formatting completed
  • Lint executed
  • Packages built
  • Type checking completed
  • JavaScript unit tests executed
  • Android native tests executed
  • iOS native tests executed
  • PingTestRunner integration tests executed
  • Sample-app smoke test completed

@coderabbitai

coderabbitai Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Advanced

Run ID: c1fcd731-495a-4576-abb9-ab5e64671ca2


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.

@tsdamas
tsdamas requested review from pingidentity-gaurav and rodrigoareis and removed request for pingidentity-gaurav September 2, 2026 22:14
@codecov

codecov Bot commented Sep 2, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 77.22420% with 128 lines in your changes missing coverage. Please review.
✅ Project coverage is 71.61%. Comparing base (2fa80cc) to head (85aa409).

Files with missing lines Patch % Lines
packages/fido/ios/RNPingFidoCommon.swift 60.90% 95 Missing ⚠️
packages/fido/ios/RNPingFidoImpl.swift 0.00% 18 Missing ⚠️
...ackages/davinci/ios/Mapper/DaVinciNodeMapper.swift 0.00% 9 Missing ⚠️
...pingidentity/rndavinci/mapper/DaVinciNodeMapper.kt 0.00% 4 Missing ⚠️
...ackages/fido/ios/Tests/RNPingFidoCommonTests.swift 98.89% 2 Missing ⚠️

❌ Your patch check has failed because the patch coverage (77.22%) is below the target coverage (80.00%). You can increase the patch coverage or adjust the target coverage.
❌ Your project check has failed because the head coverage (71.61%) is below the target coverage (80.00%). You can increase the head coverage or adjust the target coverage.

Additional details and impacted files
@@             Coverage Diff              @@
##               main      #70      +/-   ##
============================================
+ Coverage     70.82%   71.61%   +0.78%     
  Complexity      239      239              
============================================
  Files           208      170      -38     
  Lines         24057    22734    -1323     
  Branches       1175      818     -357     
============================================
- Hits          17039    16280     -759     
+ Misses         6845     6370     -475     
+ Partials        173       84      -89     
Flag Coverage Δ
android 16.04% <0.00%> (-0.02%) ⬇️
ios 80.97% <77.61%> (+0.06%) ⬆️
javascript 88.29% <100.00%> (+17.90%) ⬆️
Components Coverage Δ
Bridge glue 51.54% <61.38%> (+0.83%) ⬆️
Pure logic 80.86% <0.00%> (-0.47%) ⬇️
Files with missing lines Coverage Δ
packages/core/ios/CoreRuntime.swift 82.29% <100.00%> (+11.83%) ⬆️
.../Tests/RNPingFidoSerializerRegistrationTests.swift 100.00% <100.00%> (ø)
packages/fido/src/NativeRNPingFido.ts 95.83% <100.00%> (+5.35%) ⬆️
packages/fido/src/types/fido.types.ts 100.00% <100.00%> (ø)
...ackages/fido/ios/Tests/RNPingFidoCommonTests.swift 97.71% <98.89%> (+2.59%) ⬆️
...pingidentity/rndavinci/mapper/DaVinciNodeMapper.kt 72.76% <0.00%> (-1.21%) ⬇️
...ackages/davinci/ios/Mapper/DaVinciNodeMapper.swift 89.35% <0.00%> (-2.32%) ⬇️
packages/fido/ios/RNPingFidoImpl.swift 60.00% <0.00%> (-40.00%) ⬇️
packages/fido/ios/RNPingFidoCommon.swift 49.57% <60.90%> (+16.23%) ⬆️

... and 39 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 2fa80cc...85aa409. 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 Sep 2, 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-70/

Built to branch gh-pages at 2026-09-10 22:27 UTC.
Preview will be ready when the GitHub Pages deployment is complete.

@rodrigoareis rodrigoareis 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.

Changes looks good to me. Left some minor comments.

* @param optionsKey Bridge payload key for the collector's WebAuthn options.
* @param options Transformed WebAuthn options. Always present on a live
* collector: the native collector sets the field synchronously inside `init()`,
* which throws `IllegalArgumentException` when the field is missing from the

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.

It seems that exception isn't caught between the collector's init() and CoreRuntime's consumers — it propagates up through CollectorFactory.collector() and is only caught by Workflow's top-level catch { }, which turns the whole node into a FailureNode. So a malformed FIDO2 collector from the server fails the entire DaVinci step (not just the FIDO field), while iOS degrades gracefully (empty options, rest of the form still renders) since init(with:) there is non-throwing. Could we add a TODO-PARITY note here (like the ones already added for base64 encoding/trigger) so this asymmetry is tracked, and maybe file an SDK ticket with the Android FIDO team?

This is not something we need to fix in this PR, just want to make sure it's a known/accepted risk rather than a surprise later.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Absolutely.

*/
@Test
fun daVinciSerializerReturnsActionAndCreationOptionsForRegistration() {
RNPingFidoCommon.registerDaVinciSerializer()

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.

Tests calls the real RNPingFidoCommon.registerDaVinciSerializer() but doesn't reset the one-shot registration guard in @Before/@After like RNPingFidoSerializerRegistrationTest does. Could we add RNPingFidoCommon.resetSerializerRegistrationForTesting() to a @Before/@After here too, so these tests don't implicitly depend on running before any other test class that already flips the guard?

handlers.resolve(JsonBridgeMapper.encodeJsonObject(payload))
case .failure(let error):
let nsError = error as NSError
let code = isRecoverableFidoAuthenticationFailure(nsError)

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.

[iOS] User cancel rejects as FIDO_AUTHENTICATE_ERROR instead of FIDO_AUTHENTICATE_CANCELLED (blocking)

The DaVinci collector returns the transformed error from handleError() (FidoError.unsupportedAction("The operation was canceled.")), but isRecoverableFidoAuthenticationFailure only matches raw ASAuthorizationError 1001, so cancel never maps to the cancelled code. (Android is fine: its collector returns the raw exception.)

if let fidoError = error as? FidoError,
   case .unsupportedAction(let message) = fidoError,
   message == FidoConstants.ERROR_NOT_ALLOWED_MESSAGE {
    return true // cancelled
}

A test on this path would be good too.

@pingidentity-gaurav pingidentity-gaurav 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.

LGTM

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.

3 participants