---
title: Configure client apps for one-time passcodes
description: Explains how to handle `DeviceRegistrationCollector` and `DeviceAuthenticationCollector` in your Android, iOS, and JavaScript apps to implement one-time passcode authentication.
component: orchsdks
page_id: orchsdks:davinci:use-cases/otp/01_handle-one-time-passcodes-in-client-apps
canonical_url: https://developer.pingidentity.com/orchsdks/davinci/use-cases/otp/01_handle-one-time-passcodes-in-client-apps.html
revdate: Fri, 9 Jan 2026 17:23:51 +0000
keywords: ["DaVinci", "OTP", "Client Apps", "Android", "iOS", "JavaScript", "Collectors"]
section_ids:
  android-otp: Configure an Android app to use one-time passcodes
  ios-otp: Configure an iOS app to use one-time passcodes
  javascript-otp: Configure a JavaScript app to use one-time passcodes
---

# Configure client apps for one-time passcodes

[icon: circle-check, set=far]PingOne [icon: android, set=fab]Android [icon: apple, set=fab]iOS [icon: js, set=fab]JavaScript

Select your platform to discover how to configure your client application to use one-time passcodes during an authentication flow.

[icon: android, set=fab, size=3x]

#### [Android](#android-otp)

Configure an Android app to use one-time passcodes

[icon: apple, set=fab, size=3x]

#### [iOS](#ios-otp)

Configure an iOS app to use one-time passcodes

[icon: js, set=fab, size=3x]

#### [JavaScript](#javascript-otp)

Configure a JavaScript app to use one-time passcodes

## Configure an Android app to use one-time passcodes

Your app must handle the `DeviceRegistrationCollector` and `DeviceAuthenticationCollector` collector types that DaVinci sends. These contain details of the available one-time passcode delivery methods.

|   |                                                                                                                 |
| - | --------------------------------------------------------------------------------------------------------------- |
|   | Learn more about setting up an app to receive collectors in the [Try the DaVinci module](../../tutorials.html). |

Loop through the collectors returned by DaVinci, ensuring you handle the one-time passcode collectors:

```kotlin
continueNode.collectors.forEach {
    when (it) {

        // Other collectors here...

        is DeviceRegistrationCollector -> {
            // Compose to display DeviceRegistrationCollector
            DeviceRegistration(it, onNext)
        }
        is DeviceAuthenticationCollector -> {
            // Compose to display DeviceAuthenticationCollector
            DeviceAuthentication(it, onNext)
        }
        // Compose to display PhoneNumberCollector
        is PhoneNumberCollector -> PhoneNumber (it, onNodeUpdated)
    }
}
```

After collecting the data for a node you can proceed to the next node in the flow by calling the `next()` method on your current `node` object.

Learn more about `node.next()` in [Continuing a DaVinci flow](../../usage/android/03-stepping-through-davinci-flows.html#node-next).

Sample collector view files:

* [Sample DeviceRegistration.kt code](https://github.com/ForgeRock/ping-android-sdk/blob/master/samples/app/src/main/java/com/pingidentity/samples/app/davinci/collector/DeviceRegistration.kt)

* [Sample DeviceAuthentication.kt code](https://github.com/ForgeRock/ping-android-sdk/blob/master/samples/app/src/main/java/com/pingidentity/samples/app/davinci/collector/DeviceAuthentication.kt)

* [Sample PhoneNumber.kt code](https://github.com/ForgeRock/ping-android-sdk/blob/master/samples/app/src/main/java/com/pingidentity/samples/app/davinci/collector/PhoneNumber.kt)

## Configure an iOS app to use one-time passcodes

Your app must handle the `DeviceRegistrationCollector` and `DeviceAuthenticationCollector` collector types that DaVinci sends. These contain details of the available one-time passcode delivery methods.

|   |                                                                                                                 |
| - | --------------------------------------------------------------------------------------------------------------- |
|   | Learn more about setting up an app to receive collectors in the [Try the DaVinci module](../../tutorials.html). |

Loop through the collectors returned by DaVinci, ensuring you handle the one-time passcode collectors:

```swift
ForEach(continueNode.collectors , id: \.id) { collector in
    switch collector {

    // Other collectors here...

    case is DeviceRegistrationCollector:
        if let deviceRegistrationCollector = collector as? DeviceRegistrationCollector {
            DeviceRegistrationView(field: deviceRegistrationCollector, onNext: onNext)
        }
    case is DeviceAuthenticationCollector:
        if let deviceAuthenticationCollector = collector as? DeviceAuthenticationCollector {
            DeviceAuthenticationView(field: deviceAuthenticationCollector, onNext: onNext)
        }
    case is PhoneNumberCollector:
        if let phoneNumberCollector = collector as? PhoneNumberCollector {
            PhoneNumberView(field: phoneNumberCollector, onNodeUpdated: onNodeUpdated)
        }
    default:
        EmptyView()
    }
}
```

After collecting the data for a node you can proceed to the next node in the flow by calling the `next()` method on your current `node` object.

Learn more about `node.next()` in [Continuing a DaVinci flow](../../usage/ios/03-stepping-through-davinci-flows.html#node-next).

Sample collector view files:

* [Sample DeviceRegistrationView.swift code](https://github.com/ForgeRock/ping-ios-sdk/blob/master/SampleApps/PingExample/PingExample/Collectors/DeviceRegistrationView.swift)

* [Sample DeviceAuthenticationView.swift code](https://github.com/ForgeRock/ping-ios-sdk/blob/master/SampleApps/PingExample/PingExample/Collectors/DeviceAuthenticationView.swift)

* [Sample PhoneNumberView.swift code](https://github.com/ForgeRock/ping-ios-sdk/blob/master/SampleApps/PingExample/PingExample/Collectors/PhoneNumberView.swift)

## Configure a JavaScript app to use one-time passcodes

Your app must handle the `DeviceRegistrationCollector` and `DeviceAuthenticationCollector` collector types that DaVinci sends. These contain details of the available one-time passcode delivery methods.

You might also need to handle `PhoneNumberCollector` collectors, if the user chooses Voice or SMS as their MFA method, for example.

|   |                                                                                                                 |
| - | --------------------------------------------------------------------------------------------------------------- |
|   | Learn more about setting up an app to receive collectors in the [Try the DaVinci module](../../tutorials.html). |

Loop through the collectors returned by DaVinci, ensuring you handle the one-time passcode collectors:

```typescript
const collectors = davinciClient.getCollectors();

collectors.forEach((collector) => {
  if (
    collector.type === 'DeviceRegistrationCollector' ||
    collector.type === 'DeviceAuthenticationCollector'
  ) {
    deviceComponent(
      collector, // Object of the collector
      davinciClient.update(collector), // Return updater for collector
    );
  } else if (collector.type === 'PhoneNumberCollector') {
    phoneNumberComponent(
      collector, // Object of the collector
      davinciClient.update(collector), // Return updater for collector
    );
  }
});
```

In this example, a `deviceComponent` or `phoneNumberComponent` component handles rendering the relevant user interface. Pass the selected option into the `updater()` method:

Example `deviceComponent` file to render OTP selection

```typescript
export default function deviceComponent(
  collector: DeviceRegistrationCollector | DeviceAuthenticationCollector,
  updater: Updater,
) {
  const groupLabel = collector.output.label || 'Select an option';

  // Bind to options to handle user selection
  function eventHandler(event) {
    const selectedValue = // get value from event's target element

    updater(selectedValue);
  }

  // Iterate over the options and render each
  for (const option of collector.output.options) {
    const elementLabel = option.label;
    const elementValue = option.value;

    // Render each element to DOM
  }
}
```

Example `phoneNumberComponent` file to render OTP selection

```typescript
export default function phoneNumberComponent(
  collector: PhoneNumberCollector,
  updater: Updater,
) {
  const phoneLabel = collector.output.label || 'Phone Number';

  // Get default or existing values
  const countryCodeValue = collector.output.value.countryCode;
  const phoneNumberValue = collector.output.value.phoneNumber;

  // This just uses a mutable object for simplicity
  let phoneObject = {
    countryCode: countryCodeValue,
    phoneNumber: phoneNumberValue,
  };

  // Add change event listener to country code select
  function handleCountryCodeEvent(event) {
    const selectedValue = // get value from event's target element

    // Mutate object then pass to updater
    phoneNumber = { ...phoneObject, countryCode: selectedValue };
    updater(phoneNumber);
  }

  // Add change event listener to phone number input
  function handlePhoneNumberEvent(event) {
    const selectedValue = // get value from event's target element

    // Mutate object then pass to updater
    phoneNumber = { ...phoneObject, phoneNumber: selectedValue };
    updater(phoneNumber);
  }

  // Render both country code select and phone number input
}
```
