Orchestration SDKs

Integrating FIDO auth journeys in iOS

PingOne Advanced Identity Cloud PingAM iOS

The FIDO module offers a streamlined API for handling FIDO interactions, including registering and authenticating with passkeys.

It abstracts away the complexities of the underlying FIDO protocols, allowing you to quickly add strong authentication to your applications.

For an example of the journeys you can build with this module, see the sample passkey journey.

Before you begin

To enable FIDO authentication in your app you must complete the following tasks:

Adding FIDO entitlements to an iOS app

  1. In Xcode, in the Project Navigator, double-click your application to open the Project pane.

  2. On the Signing & Capabilities tab, click Capability, type Associated Domains, and then double click the result to add the capability.

  3. In Domains, click the Add () button, and enter the following entries:

    1. webcredentials:, followed by the hostname where you uploaded the apple-app-site-association file earlier.

      For example, webcredentials:my-rp-domain.com

      The result will resemble the following:

      ios fido capabilities davinci
      Figure 1. Configuring Associated Domains entitlements in iOS.

Step 1. Installing modules

To install the FIDO module for iOS, use Swift Package Manager (SPM) or Cocoapods to add the dependency to your project.

  • SPM (Swift Package Manager)

  • CocoaPods

You can install packages by using SPM (Swift Package Manager) on the iOS project.

  1. In Xcode, in the Project Navigator, right-click your project, and then click Add Package Dependencies…​.

  2. In the Search or Enter Package URL field, enter the URL of the repo containing the Orchestration SDK for iOS, https://github.com/ForgeRock/ping-ios-sdk.git.

  3. In Add to Project, select the name of your project, and then click Add Package.

    Xcode shows a dialog containing the libraries available for iOS.

  4. Select the PingFido library, and in the Add to Target column select the name of your project.

  5. Repeat the previous step for any other packages you want to use in your project.

  6. Click Add Package.

    Xcode displays the chosen packages and any prerequisites they might have in the Package Dependencies pane of the Project Navigator.

  1. If you don’t already have CocoaPods, install the latest version.

  2. If you don’t already have a Podfile, in a terminal window, run the following command to create a new Podfile:

    pod init
  3. Add the following lines to your Podfile:

    pod 'PingFido'
  4. Run the following command to install pods:

    pod install

Step 2. Registering FIDO authenticators

To register a FIDO authenticator, use the register() function. The function returns either success or failure.

The Orchestration SDK provides the FidoRegistrationCallback class for handling the FIDO registration requests:

Step 1. Handling FIDO callbacks in a Advanced Identity Cloud or PingAM journey
ForEach(Array(continueNode.callbacks.enumerated()), id: \.offset) { index, callback in
  switch callback {
    case let fidoRegistrationCallback as FidoRegistrationCallback:
      FidoRegistrationCallbackView(callback: fidoRegistrationCallback, onNext: onNext)
  }
}
Step 2. Calling register in FidoRegistrationCallbackView
Task {
    // Get the window
    guard let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
          let window = windowScene.windows.first else {
        print("Could not find active window scene.")
        return // Exit if no window found
    }

    // Call the async function and await its Result
    let result = await callback.register(window: window)

    // Handle the Result
    switch result {
    case .success(let attestationValue):
        // Optional: Use attestationValue if needed
        print("FIDO Registration successful: \(attestationValue)")
        // Call onNext
        onNext()
    case .failure(let error):
        // Handle errors
        print("FIDO Registration failed: \(error.localizedDescription)")
        // Call onNext
        onNext()
    }
}

Step 3. Authenticating using a FIDO authenticator

To authenticate using a registered FIDO authenticator, use the authenticate() function. The function returns either success or failure.

The Orchestration SDK provides the Fido2AuthenticationCallback class for handling the FIDO authentication requests:

Step 1. Handling FIDO callbacks in a Advanced Identity Cloud or PingAM journey
ForEach(Array(continueNode.callbacks.enumerated()), id: \.offset) { index, callback in
  switch callback {
    case let fidoAuthenticationCallback as FidoAuthenticationCallback:
      FidoAuthenticationCallbackView(callback: fidoAuthenticationCallback, onNext: onNext)
  }
}
Step 2. Calling authenticate in FidoAuthenticationCallbackView
Task {
    // Get the window
    guard let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
          let window = windowScene.windows.first else {
        print("Could not find active window scene.")
        return // Exit if no window found
    }

    // Call the async function and await its Result
    let result = await callback.authenticate(window: window)

    // Handle the Result
    switch result {
    case .success(let attestationValue):
        // Optional: Use attestationValue if needed
        print("FIDO Authentication successful: \(attestationValue)")
        // Call onNext
        onNext()
    case .failure(let error):
        // Handle errors
        print("FIDO Authentication failed: \(error.localizedDescription)")
        // Call onNext
        onNext()
    }
}

Optionally, pass preferImmediatelyAvailableCredentials: true to authenticate() to fail immediately if no local passkey is already available on the device:

let result = await callback.authenticate(window: window, preferImmediatelyAvailableCredentials: true)

Without the switch, the OS displays a dialog offering remote passkey options, rather than failing immediately:

An iOS Sign In dialog for a passkey, with a Use Passkey button and a Sign In with Other Device button
Figure 2. The iOS-supplied passkey dialog, showing the "Sign In with Other Device" button

The switch does not prevent the iOS-supplied dialog if local passkeys do exist, and the dialog always offers remote passkeys as an option.

By default, preferImmediatelyAvailableCredentials is false.

Step 4. Enabling passkey autofill (WebAuthn conditional UI)

Optionally, enable passkey autofill. Passkey autofill, also known as WebAuthn conditional UI, lets users sign in with a saved passkey directly from the autofill suggestions shown when they focus the username field, skipping the extra step of pressing a dedicated "Sign in with a passkey" button.

Conditional UI is server-driven: after you configure the journey to enable it, the server includes mediation metadata with the FidoAuthenticationCallback payload. When the metadata requests conditional mediation, call the authenticateWithAutoFill(window:) method instead of authenticate(window:).

Passkey autofill requires iOS 17 or later.

To add passkey autofill support to your app:

  1. Check whether the server requested conditional mediation using the isConditionalMediationRequested property, and start the autofill-assisted ceremony:

    Starting an autofill-assisted passkey ceremony
    if fidoAuthenticationCallback.isConditionalMediationRequested {
      let result = await fidoAuthenticationCallback.authenticateWithAutoFill(window: window)
      switch result {
      case .success(let attestationValue):
        // Authentication successful, proceed to the next step in the journey
        onNext()
      case .failure(let error):
        // Handle errors, for example fall back to the modal authenticate method
        onNext()
      }
    }
  2. Set the username field’s content type to .username when conditional mediation was requested, so that passkey suggestions are offered when the user focuses it:

    Setting the username content type for passkey suggestions
    TextField("Username", text: $username)
      .textContentType(fidoAuthenticationCallback.isConditionalMediationRequested ? .username : nil)

    Passkey suggestions do not appear in the iOS simulator. Test autofill-assisted sign-in on a physical device.

The autofill-assisted ceremony runs silently in the background and displays no dialog. Keep the modal authenticate(window:) method available as a fallback, for example as a Sign in with a passkey button, so users without a registered passkey can still sign in.

Starting any new ceremony, such as a modal authenticate(window:) or register(window:) call, cancels a pending autofill-assisted request. The hosting SwiftUI view disappearing also cancels the request through structured concurrency. Call the cancel() method explicitly in teardown code that doesn’t rely on structured-concurrency cancellation.