This section covers how you can add FIDO authentication to your mobile app. Authentication Lifecycle shows how to implement FIDO registration, authentication and transaction confirmation including high level APIs for Sign-in, Sign-up and Transaction flows. The remaining sections focus on additional, common features that you can add to enhance your app's authentication functionality.
If your target usage scenario doesn’t require certain features, you can safely ignore them and their accompanying documentation. Note that adding features that won’t be used can unnecessarily increase the size of your app.
The following table outlines individual App SDK features along with the benefits of including them in your app.
Feature | Benefits |
|---|---|
Passkeys | A FIDO credential that can be used for passwordless authentication. A synced passkey can be backed up and restored to a different device. A device-bound passkey is strongly tied to a specific device or authenticator. See Support for Passkeys in the iCloud Keychain. |
Out-of-Band (OOB) authentication | Enables a mobile device to provide FIDO authentication for a web app running on a non-FIDO device, such as a desktop without FIDO2 authenticators. Required for using the Device Blessing feature; not available in FIDO2. |
Quick Authentication | Improves a user's authentication experience in areas with slow networks or on a low-bandwidth device. See Quick Authentication. |
Device Blessing | Allows provisioning a new device from an existing device; depends on OOB functionality. |
FIDO credential sharing across multiple mobile apps | When you have multiple mobile apps, you can designate one app to perform authentication for all of the related apps. This is possible because FIDO credentials are shared among the apps. In this scenario, the user registers once using the designated authentication app and does not have to register in any of the other apps. The designated app owns and manages the FIDO credentials for the related apps. |
WebView integration | Allows your mobile app to support authentication within an embedded WebView. See Embedding a WebView. |
Authenticator selection | Allows you to control the authenticators shown to the user when they register or authenticate. |
Manage personally identifiable information | Allows you to retrieve a user's active and inactive registrations. Enables you to delete a user's registrations and their history of FIDO operations. |
External Authentication Method | Allows you to authenticate with passwords and other authentication methods not currently supported by Digipass S3 Authentication Software. |
Authentication lifecycle
Overview
Using your app, a user must register a FIDO authenticator, SMS OTP, Email OTP, and/or Photo ID. These are referred to collectively as authentication methods. Typically, a FIDO authenticator is a biometric embedded in the device, such as a fingerprint, face, or voice authenticator.
On subsequent logins and transaction confirmations, your app prompts the user to verify their identity (or authenticate) using one or more of their registered authentication methods.
When you call the App SDK authentication methods suggested here, the Server performs Adaptive Authentication, taking into account any signals or context data that your app provides. The Server returns a list of authentication sequences appropriate for the user's current situation that satisfy your organization's criteria for strong authentication. The App SDK guides the user to complete one authentication sequence. As a result, your app need only check whether authentication succeeds or fails, then take any appropriate actions.
After getting registration and authentication working, you can expand your app's capabilities.
You can suggest that new users register authentication methods necessary for strong authentication instead of continuing to rely on passwords. This capability uses the Digipass S3 Adaptive Registration, which, like Adaptive Authentication, determines a set of vetted authentication methods and guides the user to register the methods in one registration sequence.
You can allow users to manage their registrations or rename their authentication methods after registering.
If your app's users can perform transactions, you can implement transaction confirmation using Adaptive Authentication.
Prior to deploying your app, make sure that you define FIDO policies and Adaptive Rulesets on the Server, since these objects encapsulate your organization's criteria for strong authentication. See Configure Adaptive Rulesets. Digipass S3 provides a default FIDO policy and Adaptive Ruleset that you can use initially.
Most App SDK methods require an AppSDKSessionData object that represents a user's signed-in state. For a detailed explanation of what this is and how to create it, see AppSDKSessionData.
Development approach
The goal is to help you quickly implement FIDO in your app. As a result, we cover high-functioning App SDK methods that return Views that you insert into your app's UI. These Views handle all user interaction as well as work with the Authentication Server. Digipass S3 made these Views flexible so you can replace the UIs. There are lower level APIs that you may decide to use instead, these are documented in the Client API Docs.
When you implement FIDO in your app, use the following recommended approach:
Verify that your development environment is set up correctly by successfully building Tutorial App.
Configure your Authentication Server to work with Tutorial App. You should be able to successfully register and authenticate using Tutorial App.
When you know that your environment and Auth Server configurations work correctly, use the classes and methods described here to implement registration and authentication. This allows you to focus on implementing core functionality. It also eliminates the UI as a potential source of bugs.
Expand your app's FIDO capabilities by suggesting methods to register, managing registrations, implementing out-of-band authentication, and so on using the classes and methods recommended here.
When you have FIDO functionality working in your app with your Auth Server configuration, replace the default UIs with your desired UIs. See Replacing the UI.
Creating the main objects
To implement FIDO functionality, your app primarily uses methods defined on 2 App SDK classes: NNLAdaptiveUI and NNLAppSDKPlus. Call NNLAdaptiveUI methods to perform Adaptive Registration, Adaptive Authentication, and register OTP and Photo ID authentication methods. Call NNLAppSDKPlus methods to register FIDO authenticators, manage a user's registrations, and accomplish other tasks described here. Your app needs to start by creating instances of these 2 classes.
NNLAdaptiveUI methods send requests to the endpoints for registration and authentication. These URLs must be specified in a configuration object, called AppSDKPlusConfig, that you pass to NNLAdaptiveUI's constructor, as shown below. In addition, you must specify which protocol, UAF, FIDO2, or BOTH, NNLAdaptiveUI's methods will use. The protocol type BOTH is intended to be used for Adaptive Registration and Adaptive Authentication. The code below shows how to do this.
let appConfig = AppSDKPlusConfig()
appConfig.regEndpoint = REG_SERVER_URL
appConfig.authEndpoint = AUTH_SERVER_URL
// Specify whether you are using UAF, FIDO2, or BOTH protocol. If your
// Adaptive Registration and Adaptive Authentication Rules include
// UAF and FIDO2 authenticators in their sequences, use BOTH.
appConfig.protocoltype = ProtocolType.TYPE_BOTH
let adaptiveUi = NNLAdaptiveUI(appSDKPlusConfig: appConfig)To create an instance of NNLAppSDKPlus, you also create and configure an AppSDKPlusConfig object as shown above.
let appConfig = AppSDKPlusConfig()
appConfig.regEndpoint = REG_SERVER_URL
appConfig.authEndpoint = AUTH_SERVER_URL
appConfig.protocoltype = ProtocolType.TYPE_UAF
// Most customers use embedded FIDO clients
appConfig.useRemoteClient = false
// Create the App SDK Plus instance
let sdk = NNLAppSDKPlus(appConfig: appConfig)Working example in Tutorial App
Refer to file AppDelegate.swift.
Registering
Prior to authenticating, you have to register an authentication method which could be a FIDO authenticator, SMS OTP, email OTP, or Photo ID. The App SDK provides 3 customizable UI Views so you can get registration working faster in your app.
To register FIDO UAF authenticators: Initialize the NNLAppSDKPlus for UAF and use NNLAppSDKPlus.getFidoRegistrationView(). This method returns a UIViewController, shown below. This object contains functionality to view, register, and deregister UAF authenticators.
.png?sv=2026-02-06&spr=https&st=2026-09-30T00%3A39%3A33Z&se=2026-09-30T01%3A47%3A33Z&sr=c&sp=r&sig=QQK0sQZG%2FirXvUvuWTlLbHn2AaPZQnyQgoyC86JQs0o%3D)
To register FIDO2 authenticators: Initialize the NNLAppSDKPlus for FIDO2 and use NNLAppSDKPlus.getFidoRegistrationView(). This method returns a UIViewController, shown below. This object contains functionality to register FIDO2 authenticators.
FIDO2 authentication is only available on devices that use iOS 16.0 and higher.
.jpg?sv=2026-02-06&spr=https&st=2026-09-30T00%3A39%3A33Z&se=2026-09-30T01%3A47%3A33Z&sr=c&sp=r&sig=QQK0sQZG%2FirXvUvuWTlLbHn2AaPZQnyQgoyC86JQs0o%3D)
To register non-FIDO methods such as OTP and/or Photo ID: Use NNLAdaptiveUI.getRegistrationView(). This method returns a UIViewController, shown below. This object contains functionality to view, register, and deregister non-FIDO authentication methods
.

Both OTP and Photo ID require configuration on the Server. SMS OTP and Photo ID require 3rd party services. See Configure non-FIDO authentication methods.
The list of authentication methods presented to the user are controlled by the Adaptive Rulesets (for NNLAdaptiveUI.getRegistrationView()) and FIDO policies (for NNLAppSDKPlus.getFidoRegistrationView()) on the Authentication Server. The list of methods is further refined based on the capabilities of the device. This results in showing the user the right options based on the context.
Registering FIDO authenticators
Prior to authenticating with FIDO, you have to register a FIDO authenticator credential. The UIViewController object created by calling getFidoRegistrationView() works with either FIDO UAF authenticators or FIDO2 authenticators, but not both.
When you create the NNLAppSDKPlus object, you specify the protocol. To allow users to work with both UAF and FIDO2 authenticators, create two NNLAppSDKPlus objects, each using a different protocol.
let appConfig = AppSDKPlusConfig()
appConfig.regEndpoint = REG_SERVER_URL
appConfig.authEndpoint = AUTH_SERVER_URL
appConfig.useRemoteClient = false
// We're using UAF but you can also use FIDO2
appConfig.protocoltype = ProtocolType.TYPE_UAF
// Create the App SDK Plus instance
let sdkUAF = NNLAppSDKPlus(appConfig: appConfig)After creating the NNLAppSDKPlus object, get the session object that was created during sign in, see AppSDKSessionData.
Next, create a UIViewController object by calling getFidoRegistrationView(), passing in the AppSDKSessionData object. During registration, you can allow the user to assign a recognizable name to their account, instead of using the UUID. This can only be done at registration and there is no way to rename the account later. The account name is stored locally by the authenticator, not on the Authentication Server.
After the user has entered the desired account name, pass it to NNLAppSDKPlus.getFidoRegistrationView() in its extras parameter, using ExtrasKeyUserName as the key. You can also assign the display name, a friendly local name, at this time using the key ExtrasKeyUserDisplayName.
There may be situations where you don't want to display a user name, for example, your system generates a unique randomly-generated name for each account. In this situation, use ExtrasKeyUserDisplayName to assign a "friendly" user name to the registered credential. During authentication, the device displays the "friendly" name on the platform UI and Conditional UI.
var extras = [String: String]()
extras.updateValue("ejohnson@example.com", forKey: ExtrasKeyUserName)
extras.updateValue("Emily", forKey: ExtrasKeyUserDisplayName)
let viewController =
sdkUAF.getFidoRegistrationView(app.authData?.sessionData,
extras: extras);If you need 2 Views, one for UAF and the other for FIDO2, call getFidoRegistrationView() on each of the NNLAppSDKPlus objects you created.
If the Auth Server doesn't return any UAF authenticators, the UIVIewController contains the message "No methods available to register". This can never be the case for FIDO2 authenticators.
Insert the UIViewController in your page, this should be done from the UI thread.
If your users want to register a FIDO2 authenticator on an iOS 14 or iOS 15 device, you must enable FIDO2 on older systems in your app.
You can create a custom UI for FIDO registration, refer to Replacing the FIDO Registration UI.
Working examples in Tutorial App
AppDelegate.swift - Shows how to instantiate NNLAppSDKPlus.
RegisterViewController.swift - Shows how to perform a registration operation.
Registering other (non-FIDO) authentication methods
After creating the NNLAdaptiveUI object, get the session object that was created during sign in, see AppSDKSessionData. Next, create a UIViewController object by calling getRegistrationView(), passing in the AppSDKSessionData object. getRegistrationView() uses Adaptive Registration to determine allowed authentication methods that a user can register. To pass in context data to Registration Decision Rules, use the extras parameter. See Sending Signals and Passing in Context Data.
nonFIDORegistrationVc =
adaptiveUi.getRegistrationView(app.authData?.sessionData,
withExtras: nil)If the Auth Server doesn't return any non-FIDO authentication methods, the UIVIewController contains the message "No methods available to register".
Insert the UIViewController object into your page, this should be done from the UI thread. When the user selects an authentication method, the UIViewController prompts the user to enter the necessary information, such as the email address for email OTP.
You can create a custom UI for non-FIDO registration, refer to Replacing the Non-FIDO Registration UI.
Working example in Tutorial App
Refer to file RegisterViewController.swift.
Retrieving SessionData
Sometimes you need to retrieve the SessionData object from a FIDO registration operation. To do this, create a mechanism that passes the SessionData to your callback function. Take the following steps:
Create a class that adopts the NNLIFidoRegistrationListener protocol. In this example it is called SampleFidoRegistrationListener. The functions in this class are called when a FIDO registration or deregistration is completed or fails.
Pass a NNLIFidoRegistrationListener object to NNLFidoRegistrationController.
let fidoRegVc = appsdk.getFidoRegistrationView(app.authData?.sessionData,
extras: extras)
// Set the registration/deregistration listener
fidoRegVc.getController().setListener(SampleFidoRegistrationListener())The sessionData object is a property in the AuthenticationData object passed to your onRegisterCompleted() function.
For more information on the NNLIFidoRegistrationListener, NNLFidoRegistrationController, and AuthenticationData classes, see the Client API Docs.
Working Example in Tutorial App
Refer to files RegisterViewController.swift and SampleFidoRegistrationListener.swift.
Registering for quick FIDO authentication
Users on slow networks or using low-bandwidth devices can experience a delay before seeing the first FIDO authentication screen. Before FIDO authentication can start, normally a round trip between the App SDK and the Server is required to determine what authentication methods should be used. You can improve your users' experience by enabling Quick Authentication, which bypasses the initial round trip.
When Quick Authentication is enabled during registration, the Server sends back the information that the App SDK needs to generate the challenge for authentication. You store this information so it persists. To authenticate, pass this information to the Server.
Enable Quick FIDO Authentication when you register by passing a boolean flag to NNLAppSDKPlus.getFidoRegistrationView() in extras. Use ExtrasQuickAuthEnable as the key and "true" as the value.
// Set true for the key ExtrasQuickAuthEnable in extras
extras?.updateValue("true", forKey: ExtrasQuickAuthEnable);
let fidoRegVc = sdk.getFidoRegistrationView(app.authData?.sessionData, extras: extras);The App SDK uses a QuickData object that encapsulates the necessary data to generate the challenge. If the user registered more than one FIDO authenticator while they used the View, the QuickData object is tied to the last FIDO registration that the user made. This object is valid for the lifetime of the registered authenticator. QuickData is automatically saved in the user data cache. Your app can optionally provide a name for storing and retrieving this QuickData by adding it in the following way.
// provide alternative quick data key for shared preferences
extras?.updateValue("FIDO_login", forKey: ExtrasQuickAuthDataKeyName)
Using Quick Authentication can result in inconsistent behavior because the SDK is not initially consulting a ruleset, but the Server is still enforcing the ruleset.
Quick External Authentication is also supported when you use Sign-in flow.
Digipass S3 Software must be configured for Quick Authentication. See Quick Authentication.
Working examples in Tutorial App
RegisterViewController.swift - Shows how to perform a registration operation and get quickData.
TAUTils.swift to see how to provide a name for the QuickData object.
Prompting the user to name a security key
Some users have more than one security key, so a security key name can help the user identify one. When the user is registering a security key, your app can prompt the user to name this new authenticator. When you call getFidoRegistrationView(), add the key ExtrasKeyAskSecurityKeyCredentialName with the value "true" to the extras parameter.
fido2Sdk = NNLAppSDKPlus(appConfig: config)
let extras = [ExtrasKeyAskSecurityKeyCredentialName : "true"]
let baseFIDORegVC = fido2Sdk!.getFidoRegistrationView(app.authData?.sessionData, extras: extras)Working example in Tutorial App
Refer to file RegisterViewController.swift.
Suggesting authentication methods to register
New end users may not be aware that they should register a FIDO authenticator on their device. Your app can automatically prompt users to register a FIDO authentication method after they sign in with a password. The App SDK assesses what authentication methods to suggest based on the Registration Decision Rules that you define on the Authentication Server.

The built-in Suggest Registration UI:
Displays this view. The choices depend on the active Registration Decision Rules.
If the user taps an authentication method to register, the App SDK automatically prompts the user to complete that registration. If registration is successful, the user doesn't see this UI again.
If the user taps Never ask me again, then the App SDK closes the dialog and the user is never shown this UI, even if they never register a FIDO authenticator.
If the user taps Not Now, then the App SDK closes the dialog. If the user signs in with a password in the future, they see this UI again.
When using the Sign-in flow, trigger the Suggest Registration UI by simply setting a flag. To trigger this functionality when not using the sign-in flow, call AdaptiveUI.suggestRegister() explicitly:
After creating the AdaptiveUI object, get AppSDKSessionData.
Fill in extras with additional information like context data for Registration Decision Rules, the account name, and user name. See Sending Signals and Passing in Context Data and Registering FIDO Authenticators.
Call suggestRegister(). Pass in AppSDKsessionData and extras to suggestRegister().
Task { @MainActor in
do {
authData = try await ui.suggestRegister(sessionData, extras: dextras)
}
catch {
// Convert to NSError to check the status code
let err:NSError = error as NSError
}
}suggestRegister() returns an AdaptiveResult object which contains the result of the Suggest Registration operation. For more information about error handling, see Error Handling.
If the user deletes all of their registrations, you can reset the Suggest Registration UI so it prompts the user to register an authentication method. Call the resetSuggestRegister() method:
adaptiveUI.resetSuggestRegister()
You can replace the built-in Suggest Registration UI with your own.
Working example in Tutorial App
Refer to file RegistrationTask.swift to call the Suggest Registration UI.
Refer to file RegisterViewController.swift to reset the Suggest Registration UI.
Automatically create a passkey
The App SDK can silently create a passkey under the following conditions:
the user has no passkey yet,
their device supports the WebAuthn feature "conditional-create”,
the user logged into an application using a password manager,
If the App SDK successfully creates a passkey silently, and your Server's adaptive ruleset requires no more methods for registration, then the App SDK will not show any UI. If your Server's adaptive ruleset does require the user to register more methods, then that user interface starts automatically.
To enable this feature without writing additional code, add a FIDO policy on the Authentication Server. In the Server Admin Console, navigate to Authentication > FIDO Policies and add a FIDO policy. On the Policy Details page, open the Additional Features panel. Click Allow next to Automatically Create Passkey. For more information, see Specify Desired FIDO2 Attributes.
Your application can override the Server's FIDO policy setting by setting a flag before calling a method to perform registration. Set USER_MEDIATION_CONDITIONAL as the value for the ExtrasKeyUserMediation in the extras parameter before calling any of the following methods:
AppSdk.doRegister()
AdaptiveUI.suggestRegister()
NNLAppSDKPlus.getFidoRegistrationView()
For more information, see ExtrasKeyUserMediation in the Client API Docs and examples in Tutorial app.
Working Examples in Tutorial App
Refer to file LoginViewController.swift when calling suggestRegister().
Refer to file RegisterViewController.swift when calling getFidoRegistrationView().
Sign-in
This section describes how to implement user sign-in. The Digipass S3 SDK implements the sign-in flow to simplify the integration with your application. Your app sets the authentication options and creates the View. Then when your app calls enable(AutoStart.SIGN_IN), the SDK interacts with both the user and the Authentication Server to either successfully authenticate the user or report to the user that authentication is not possible. You can create a custom UI for Authentication, see Replacing the Authentication UI.
When your app calls enable(AutoStart.SIGN_IN), the SDK opens the view with the user name field and Next button to start authentication. The Next button checks if there is an authentication sequence that only contains a single authenticator. If so, it verifies the user's identity with that authenticator. If not, then it displays a list of available authentication methods. The SDK can also optionally suggest to the user that they register an authenticator if they haven't yet done so. The enable() method returns a Promise object.
The following code performs the whole sign-in flow. A description of each step follows.
// Step 1: Set authentication options.
var authOptions : [String:String]? = nil
authOptions?.updateValue(userName, forKey: ExtrasKeyUserName)
// Step 2: Optionally set suggest registration options
var suggestRegOptions : [String:String]? = authOptions
suggestRegOptions?.updateValue("true", forKey: ExtrasKeySuggestRegEnabled)
// Step 3: Create the view for authentication.
let ui = NNLAdaptiveUI(appSDKPlusConfig:appConfig)
let authViewController : NNLAdaptiveAuthenticationView? =
ui.getAuthenticationView(nil, transactionId: nil,
authOptions: authOptions, suggestRegOptions: suggestRegOptions)
// Step 4: Trigger authentication
authViewController?.getController().enable(AutoStart.SIGN_IN).then({ authResult in
// At this point the user is signed in and the session is available
// Step 5: Process result
let sessionData = authResult?.sessionData
// You can extract username from profileData dictionary.
let profileData = authResult?.profileData
let userName = profileData?["userName"] as? String
// Get a list of authentication methods employed by the user.
let completedMethods = authResult?.completedMethods
return NSNull()
})
// Present the authViewController
self.addChild(authViewController)
self.containerView.addSubview(authViewController.view)To trigger this sign-in flow, your application takes the following steps:
Use authOptions to pass in the preferred username. The SDK uses this to automatically fill the username field on the sign-in page. Also use authOptions to pass in additional information like context data. See Sending Signals and Passing in Context Data. If you want to trigger the Quick Authentication feature, use authOptions to indicate your preferred quick mode.
(Optional) The App SDK can automatically suggest to your users that they register a passwordless authenticator after they sign in. The code above enables this Suggest Registration operation. It also copies the same authOptions that were used for the initial sign-in, but you can pass in a different set of authentication options in the suggestRegOptions to use for the Suggest Registration operation.
Create the View for authentication. As in registration, immediately insert this View into your app's view.
Trigger authentication for the user by calling enable(AutoStart.SIGN_IN).
The call to enable(AutoStart.SIGN_IN) gets the username, makes every attempt to authenticate the user, optionally suggests that the user register an authenticator, and returns a Promise object. If the user cannot successfully authenticate, it redisplays the sign-in page.
If the user successfully authenticates, the promise resolves and returns an AdaptiveResult object containing the result of the authentication operation.
AdaptiveResult includes the session data object, AppSDKSessionData, as well as additional session and username information.
The promise will not be rejected when you use this sign-in process. The SDK handles all error conditions, displays appropriate messages to the end user, and restarts the authentication process when necessary.
You may want to use your legacy sign-in flow combined with the Digipass S3 sign-in flow. To do this, your app prompts for the username and then decides whether to use your legacy authentication flow or the Digipass S3 sign-in flow. In the case where you use the Digipass S3 sign-in flow, your app then calls enable() with an AutoStart mode other than AutoStart.SIGN_IN. When you call enable() with any other AutoStart mode, the SDK skips the username prompt. In these cases you should not present the authViewController by adding it into the application’s view hierarchy. For more details, ask OneSpan customer support about Legacy Authentication.
For more information on the NNLAdaptiveUI, NNLAdaptiveAuthenticationView, NNLAuthenticationController and AdaptiveResult classes and methods, see the Client API Docs. For more information on options, see the Module called Sign in flow extras in the Client API Docs.
Working example in Tutorial App
Refer to LoginViewController.swift which shows how to call enable(AutoStart.SIGN_IN).
WebAuthn credential selection UI
To streamline the user's experience when they authenticate with a passkey, the SDK supports a WebAuthn mode called "Conditional UI". When you enable this mode, the SDK displays the user's passkey credentials on the initial sign-in screen. If the user has passkeys available on their device, the SDK autofills the passkeys in the QuickType bar. You can only take advantage of this feature when you use the Sign-in flow.
To enable Conditional UI mode, set USER_MEDIATION_CONDITIONAL as the value for the ExtrasKeyUserMediation in the authOptions parameter before calling getAuthenticationView(). Then call enable(AutoStart.SIGN_IN).
authOptions!.updateValue(USER_MEDIATION_CONDITIONAL, forKey: ExtrasKeyUserMediation);
let authViewController : NNLAdaptiveAuthenticationView? =
ui.getAuthenticationView(nil, transactionId: nil, authOptions: authOptions,
suggestRegOptions: suggestRegOptions );
authViewController?.getController().enable(AutoStart.SIGN_IN).then ({ authResult in
// At this point the user is signed in and the session is available.
let sessionData = authResult?.sessionData
return NSNull()
}).error({ reason in
// An error has occurred, get the status code.
let err = reason as? NSError;
return NSNull()
})The username field is autofilled, and the password QuickType bar now contains the passkey.

Note that this Conditional UI is triggered only if:
a passkey is available on this device,
a password is stored in the password manager to enable the autofill feature,
the user's device has passkeys enabled, and
your app has passkeys enabled.
When the end user's device supports the WebAuthn Signal API, and authentication with the selected passkey fails because the credential is no longer available on the Authentication Server, the App SDK deletes the passkey from the user's device.
Working example in Tutorial App
See file SideMenuViewController.swift
Implementing quick authentication
When using Sign-in flow, you can eliminate the initial interaction with the server by registering for Quick Authentication and then implementing it during authentication. After Quick Authentication, if the server requires additional authentications to be performed then the SDK will prompt the user to perform those additional authentications.
Before calling getAuthenticationView(), specify a value for ExtrasKeySignInQuickMode in the authOptions parameter.
var authOptions : [String:String]? = nil
let quickMode = ExtrasValueQuickFidoOnly
authOptions?.updateValue(quickMode, forKey: ExtrasKeySignInQuickMode)The table below describes the Quick Authentication modes. Choose your mode and set the corresponding value for ExtrasKeySignInQuickMode.
Value | Description |
|---|---|
ExtrasValueQuickFidoOnly | If Quick FIDO authentication is enabled for this user then the SDK prompts for FIDO authentication first. |
ExtrasValueQuickFidoOrExternal | If Quick FIDO authentication is enabled for this user then the SDK prompts for FIDO authentication first. If the user cancels, then the SDK shows a list with FIDO and External Quick Authenticators. |
ExtrasValueQuickExternalAndFido | If External Authentication is configured then this option prompts for the External Authenticator first. If this is successful, then the SDK prompts the user with a FIDO Authenticator if available. |
ExtrasValueQuickExternalOnly | If External Authentication is configured then this option prompts for the External Authenticator first. |
ExtrasValueNoQuick | Default value. No quick authentication is done. |
If you do not register a FIDO authenticator for Quick Authentication, you can still specify a Quick Mode during Sign-in flow and all subsequent FIDO authentications will bypass the initial round trip to the server. If External Authentication is not configured for the app, then modes requiring External Authentication are ignored.
If the server requires a signal to be sent during Quick Authentication, the signal must be configured as always. See Configuring Signal Generation for details.
Digipass S3 Software must be configured for Quick Authentication. See Quick Authentication.
Also see Enabling Quick FIDO Authentication in the Tutorial App.
Working example in Tutorial App
Refer to TAUtils.swift to see how to set ExtrasKeySignInQuickMode.
Provide the time used for quick auth
By default the Digipass S3 SDK relies on the user's device time when it generates a challenge for Quick Authentication. But if the user's device time is not correct, Quick Authentication can fail. To prevent that failure, your application can provide the current time to the Digipass S3 App SDK.
To provide a synchronized current time to the Digipass S3 App SDK, your application implements the NNLITimeProvider interface and calls NNLTimeProviderManager.setInstance() during initialization of your app.
class SampleTimeProvider: NNLITimeProvider {
func getCurrentTime() -> Int64 {
// Get the current time in milliseconds since the Unix epoch
let currentTimeMillis =
Int64(Date().timeIntervalSince1970) * 1000
return currentTimeMillis
}
}
startcode// during initialization of your app
NNLTimeProviderManager.setInstance(SampleTimeProvider())Working example in Tutorial App
Refer to SampleTimeProvider.swift
Sign-up
To streamline the user's experience when signing up with your app, the SDK can display a button that starts the sign-up process directly from the sign-in page.

When the user clicks Sign Up, the App SDK collects specific information from the user.

The following code performs the whole sign-up flow. A description of each step follows.
// Step 1: Set Sign-up options and call getAuthenticationView().
var signUpOptions: [String:String]? = [String:String]()
signUpOptions?.updateValue("true", forKey: ExtrasKeySignUpEnabled)
signUpOptions?.updateValue(SIGN_UP_VALUE_OPTIONAL, forKey: SIGN_UP_KEY_FULL_NAME)
signUpOptions?.updateValue(SIGN_UP_VALUE_REQUIRED, forKey: SIGN_UP_KEY_USER_NAME)
signUpOptions?.updateValue(SIGN_UP_VALUE_REQUIRED, forKey: SIGN_UP_KEY_EMAIL_ADDRESS)
signUpOptions?.updateValue(SIGN_UP_VALUE_OPTIONAL, forKey: SIGN_UP_KEY_PHONE_NUMBER)
signUpOptions?.updateValue("true", forKey: SIGN_UP_KEY_FIDO)
signUpOptions?.updateValue("true", forKey: SIGN_UP_KEY_PHOTO_ID)
let adaptiveUI = NNLAdaptiveUI(appSDKPlusConfig: appConfig)
let authenticationViewController = adaptiveUI.getAuthenticationView(nil,
transactionId: nil,
authOptions: authOptions,
suggestRegOptions: suggestRegOptions,
signUpOptions: signUpOptions)
startcode// Step 2: Create a NNLSignUpProcessor class
class SignUpProcessor : NNLISignUpProcessor {
// Initialize the processor and store the controller
init(_ controller: NNLSignUpController, extras: [AnyHashable : Any]!) {
mController = controller
}
func startSignUp(_ signUpData: [AnyHashable : Any]!, completion: ((NNLSignUpSession?, (any Error)?) -> Void)!) {
// Start account creation, choose userName, create sessionData, check
// for errors. If there is an error, call the completion with a signUpError of type
// NSError.
// If there is no error, call the completion handler with an NNLSignUpSession instance.
if ((signUpError) != nil) {
completion(nil, signUpError)
}
completion(NNLSignUpSession(username: self.mUsername, sessionData: _sessionData),nil)
}
func finishSignUp(_ sessionData: AppSDKSessionData!, completion: ((NNLSignUpSession?, (any Error)?) -> Void)!) {
// Verify that required methods are registered, update the
// sessionData and return them in a SignUpSession object.
}
}
startcode// Step 3: Create a NNLSignUpProcessorFactory class
class SignUpProcessorFactory: NNLISignUpProcessorFactory {
func createSignUpProcessor(_ controller: NNLSignUpController!,
extras: [AnyHashable : Any]!) -> NNLISignUpProcessor!
{
let processor = SignUpProcessor(controller, extras: extras)
return processor
}
}
startcode// Step 4: Notify the App SDK to use your new factory
NNLSignUpProcessorFactory.setInstance(SignUpProcessorFactory())Step 1. Call the NNLAdaptiveUI.getAuthenticationView() with the signUpOptions parameter, enabling the Sign-up process and specifying the information you want to collect from the user.
For a detailed description of signUpOptions refer to the signUpOptions Module in the Client API Docs.
Step 2. Create a class that adopts the NNLISignUpProcessor protocol by defining the two functions: startSignUp() and finishSignUp(). Because the App SDK calls these two functions asynchronously from a background thread, any function calls appearing inside startSignUp() and finishSignUp() that involve the user interface must be called from the main thread.
The startSignUp() function:
calls your application server to start the account creation.
creates a sessionData object with a JWT sessionKey that contains a custom sign_up claim in the payload.
returns an NNLSignUpSession object containing userName and sessionData.
The finishSignUp() function:
calls your application server to finish the account creation. Your application server returns the userName and sessionData to your application to sign the user in.
returns an NNLSignUpSession object containing the userName and the updated sessionData object without the sign_up claim.
In case of an error the startSignUp() method throws an error.
Step 3. Implement the NNLISignUpProcessorFactory interface by defining createSignUpProcessor() to create an instance of ISignUpProcessor.
Step 4. Set the NNLISignUpProcessorFactory instance in the AppSdk.
For more details on these classes and protocols, refer to the corresponding sections in the Client API Docs.
Creating an account and a passkey
Users running iOS 26 can sign up and create a passkey in one step. The iOS App SDK implements this with an iOS system API called “Account creation for passkeys”. Take advantage of this capability by adding the SIGN_UP_KEY_PASSKEY_ONLY key in signUpOptions before calling adaptiveUI.getAuthenticationView().
signUpOptions?.updateValue("true", forKey: SIGN_UP_KEY_PASSKEY_ONLY)When the user pushes the Sign Up button and enters their user name and email address, the App SDK creates a new account and a passkey in one step.
Note: In this flow, the user name is not known in advance. The signUpData passed to startSignUp() does not contain a value for SIGN_UP_KEY_USER_NAME. Your backend must handle that case when it creates a SignUp session object.
Working example in Tutorial App
Refer to the SignUpProcessorFactory.swift and AppDelegate.swift files.
Transaction
In Digipass S3 Software, transactions are cryptographic proof of a user's consent to a specific set of transaction details, such as purchasing merchandise totalling $385 or transferring $5,000 from the user's checking account to their money market account. The Authentication Server must be configured to support transactions, refer to Configure transactions.
The Digipass S3 SDK implements the transaction flow to simplify integration with your application. Your app creates an instance of NNLAdaptiveUI and calls transact() from the NNLAdaptiveUI and passes in sessionData, TransactionID, and the extras. Use the extras parameter to define options and context for the transaction process. The SDK interacts with both the user and the Authentication Server to either successfully complete the transaction or report that authenticating the transaction is not possible.
When your app calls transact() from NNLAdaptiveUI, the SDK displays the transaction confirmation screen with transaction details and checks if there is an authentication sequence that contains only a single authenticator. If so, it verifies the user's identity with that authenticator. If not, then it displays a list of available authentication methods. If a transaction operation is successful, the SDK returns an object of type AdaptiveResult. This AdaptiveResult object contains all the necessary information about the operation. If the transaction operation is not successful, the SDK throws an exception containing all the necessary information about the error.
The following code performs the whole transaction flow. A description of each step follows.
// Step 1: Create a dictionary for extras, put transaction details into it.
var authOptions: [String : String]? = [String:String]()
let transactionTextStr = String(format: "Authorize $%@ payment?", transactionAmount!)
// Create a key/value pair for transaction details using ExtrasKeyOptions.
let fieldValue = String(format: "{\"%@\": \"%@\"}", ExtrasKeyOptionsTransText,
transactionTextStr)
authOptions.updateValue(fieldValue, forKey: ExtrasKeyOptions)
// Step 2: Pass in user name
let sessionData = AppSDKSessionData()
sessionData?.setObject(userName, forKey: "userName")
// Step 3: Create NNLAdaptiveUI object
let adaptiveUI = NNLAdaptiveUI(appSDKPlusConfig:appConfig)
// To uniquely identify a transaction, create transactionID as a string using a UUID.
let transactionID = UUID().uuidString
// Step 4: Trigger FIDO authentication
Task {
do {
let result = try await adaptiveUI.transact(sessionData, transactionId: transactionID, extras: authOptions)
let authData = result as! AuthenticationData
let token = authData.sessionData.object(forKey: "tcToken")
// Step 5: Send confirmation token and transaction ID to backend system.
} catch {
let err:NSError = error as NSError
// Handle the cause of the returned transaction error
self.errorTransaction(reason: err)
}
}
Pass the details of the transaction to the SDK in the extras JSON object. Create a key/value pair and use the key ExtrasKeyOptions to identify this object as confirmation text.
Don't forget to include the transaction amount and any other data in extras so your Authentication Rules can evaluate the transaction confirmation. See Sending Signals and Passing in Context Data to Adaptive Rules.Use the optional sessionData to pass in the user name.
Create an NNLAdaptiveUI object.
Trigger FIDO authentication by calling NNLAdaptiveUI.transact(). Pass the sessionData and extras that were set in previous steps as parameters. The transactionId is an opaque handle provided by your app or your backend system that represents the transaction that the user will authorize.
If the end user successfully authenticates, the SDK returns an AdaptiveResult object containing the result of the authentication. The AdaptiveResult object, result in the example above, includes the AppSDKSessionData object, sessionData, as well as the username.Your app sends the confirmation token, sessionData.tcToken, and the transaction ID to your backend system. The backend system verifies the transaction confirmation token and processes the user's transaction. For more information about the tcToken, see the JWT Format section Transaction Confirmation Token
Notes:
You can create a custom UI for Transactions. See Replacing the Transaction UI.
By default, the call to NNLAdaptiveUI.transact() first tries to perform the transaction in AutoStart.AUTO_ANY mode. Then if the user cancels an authentication method or user interaction is needed, NNLAdaptiveUI.transact() calls view.showModal() and switches to AutoStart.AUTO_NONE mode.
You can specify a different AutoStart mode in the extras parameter. Use ExtrasKeyAutoStart as the key and the desired mode as its value, for example:
extras.updateValue(NNLAutoStartHelper.string(for: AutoStart.AUTO_FIDO), forKey: ExtrasKeyAutoStart)If you want NNLAdaptiveUI.transact() to behave the same way as NNLAuthenticationController.enable(AutoStart.AUTO_FIDO), define a new authentication rule in the Authentication Server customized for transaction that has a single sequence containing only the FIDO Auth method.
For more information on the NNLAdaptiveUI, AppSDKSessionData and AdaptiveResult classes and methods, see the Client API Docs. For more information about the AutoStart.AUTO_ANY mode, see the bullet on The NNLAdaptiveUI.transact() function in Upgradingthe SDK from 9.0 to 9.1.
Working example in Tutorial App
TransactionViewController.swift contains an example that demonstrates how to implement a transaction.
Implementing quick authentication for transactions
When using Transaction flow, you can eliminate the initial interaction with the server by registering for Quick Authentication and then implementing it when authenticating a transaction. After Quick Authentication, if the server requires additional authentications to be performed then the SDK prompts the user to perform those additional authentications.
Perform Quick Authentication for a transaction by first retrieving the quickData string from NNLUserDataCache as shown in the code snippet below. The username is a required parameter. If you assign a name to the QuickData object, then pass it to getQuickAuthData() as the first parameter, otherwise pass in nil.
// Retrieve the quickData string using the default name
let cache = NNLUserDataCache.getInstance()
let quickAuthData = cache?.getQuickAuthData(nil, forUsername: username)Then pass quickAuthData in extras to NNLAdaptiveUI.transact(), using the key ExtrasQuickAuthData.
var extras : [String:String]? = nil
extras!.updateValue(quickAuthData!, forKey: ExtrasQuickAuthData);
let result = try await adaptiveUI.transact(sessionData, transactionId: transactionID, extras: extras)
If the server requires a signal to be sent during Quick Authentication, the signal must be configured as always. See Configuring Signal Generation for details.
Digipass S3 Software must be configured for Quick Authentication. See Quick Authentication.containing only the FIDO Auth method.
Working example in Tutorial App
Refer to TAUtils.swift to see how to set ExtrasQuickAuthData.
Specifying which authenticators to trigger
The SDK allows your app to specify which methods to trigger automatically during authentication. For example, you may want to always trigger a registered FIDO authentication method or never trigger a password authenticator. Do this by setting an autoTriggering mode for each authentication method.
autoTriggering Mode | Description |
|---|---|
| Automatically trigger this authentication method if the user has no other choice. This is the default. |
| Always trigger the method if it is method if it is present. |
| Never trigger the method automatically. |
Create an object of class NNLMethodBehavior for each authentication method. Set the autoTriggering mode inside each object. Place these objects into an array of NNLMethodBehavior objects. Finally, pass the array to your NNLAuthenticationController object.
let externalBehavior = NNLMethodBehavior(EXTERNAL_AUTH_METHOD_NAME, methodType:EXTERNAL_AUTH_METHOD_TYPE)
externalBehavior?.autoTriggering = .NEVER
let fidoBehavior = NNLMethodBehavior(methodType: ADAPTIVE_FIDO_AUTH_TYPE)
fidoBehavior?.autoTriggering = .ALWAYS
let behaviors = [externalBehavior!, fidoBehavior!]
mAuthController?.setMethodBehavior(behaviors)For more information on the NNLMethodBehavior and NNLAuthenticationController classes, see the Client API Docs.
Working example in Tutorial App
Refer to SampleAdaptiveAuthViewController.swift
Sending signals and passing in context data
If you define Authentication Rules or Registration Decision Rules that use signals and context data, you need to ensure that this data is sent to the Authentication Server so it can use this information. The App SDK automatically sends simpler signals, like the device model and manufacturer, to the Auth Server. User location and WiFi network take more resources and time to process, so you must configure the App SDK to generate and send these signals. See Configuring Signal Generation.
To pass context data like transaction amount and transaction type to getAuthenticationView(), use extras which is a parameter for that method. extras is a dictionary of key-value pairs, so you can pass in an arbitrary amount of data.
Create an NSDictionary object containing the name and value for each piece of information you plan to send as context data. This name must match what your rules use. Identify the NSDictionary object containing your name-value pair as context data by using the key ExtrasKeyContextData.
The example below shows how to create a dictionary and insert a key-value pair for the transaction amount and transaction type. If you have a rule whose condition uses the provided location operator, you must include a context data entry for providedLocation. Pass the dictionary to getAuthenticationView().
let ctx: NSMutableDictionary = NSMutableDictionary()
// Include the transaction amount and transaction type in the context
ctx.setObject(amount!, forKey: "transactionAmount" as NSCopying)
ctx.setObject(transType!, forKey: "transactionType" as NSCopying)
// Convert the transaction data to a JSON object
let jsonData: NSData =
JSONSerialization.data(withJSONObject: ctx,
options: JSONSerialization.WritingOptions.prettyPrinted) as NSData
// Convert the JSON object to a string
let ctxString: String = NSString(data: jsonData as Data,
encoding: String.Encoding.utf8.rawValue)! as String
// Set the key in extras
var extras = [String:String]
extras.updateValue(ctxString, forKey: ExtrasKeyContextData)Managing registrations
Your app can provide a way for users to see their registered FIDO authenticators across different devices, deregister a credential, and suspend a registration. Use the NNLAppSDKPlus's manageRegistrations() method which returns a UIViewController, shown below, that you include in your app. The View shows both UAF and FIDO2 authenticators, this is independent of the protocol you specify when you create NNLAppSDKPlus. By interacting with this View, the user can view, rename, or deregister authenticators.

After creating the NNLAppSDKPlus object, get the session object that was created during sign in, see AppSDKSessionData. Next, create the UIViewController by calling manageRegistrations().
let manageVc = sdk.manageRegistrations(app.authData?.sessionData,
inFrame: frame, withExtras: extras)
//add the manageVc to your app viewInsert the UIViewController in your page, this should be done from the UI thread. If you want to offer the ability to manage non-FIDO authentication methods, use the View for registering non-FIDO authentication methods.
The view above displays a credential icon defined by the FIDO Authenticator Metadata Specification (MDS), along with additional details such as the credential name and the timestamp of its last usage. If the corresponding MDS entry does not include an icon, Digipass S3 provides default icons that are used instead - as in the screenshot above. To enable the display of MDS icons, set the value for the key ExtrasKeyOptions in the extras parameter to be "needDetails" : 4 before calling manageRegistrations().
var extras: [String: String]? = [String: String]()
extras!.updateValue("{\"needDetails\": 4}", forKey: ExtrasKeyOptions)If your app is unable to establish a network connection with the Server, you can force the offline deletion of all local UAF registrations associated with its appID. To do this, call clearLocalRegistrations() and pass in the appID as parameter. If you pass the empty string into clearLocalRegistrations() for the appID, then local registrations for all applications are deleted.
You can find the appID in the Admin Console by choosing a tenant and navigating to Configuration > Authentication Methods > FIDO UAF.
The App SDK also depends on having the appID set in the AppSDKPlusConfig object before you create the NNLAppSDKPlus object, as shown below.
let appConfig = AppSDKPlusConfig()
appConfig.regEndpoint = REG_SERVER_URL
appConfig.authEndpoint = AUTH_SERVER_URL
appConfig.appId = APP_ID
appConfig.useRemoteClient = falseIn order for your application to access the user-assigned device name on a device with iOS 16.0 or higher, you must obtain a User-Assigned Device Name Entitlement. Otherwise the device name will always be “iPhone”.
Suspend registration
To give users the ability to suspend a registered FIDO credential without removing it, set the suspendRegistrationEnabled flag to true in the client configuration file. The UIViewController will then look like the one below.

Note that the Remove button is now labeled Deactivate. If the user clicks the Deactivate button, the App SDK displays the following dialog:

The end user can re-enable a suspended registration by clicking the Enable button.
Working example in Tutorial App
Refer to file RegisterViewController.swift.
Session refresh
If your application refreshes the session token, it needs to provide the new session token to the App SDK so the App SDK can pass it to all active controllers. Below is an example of an application passing renewed session data to the App SDK.
NNLOperationResultListener.getInstance().onSessionDataUpdated?(newSessionData)The Tutorial App contains a sample implementation of session renewing logic based on user activity detection and session expiration.
Working Examples in Tutorial App
Refer to SessionManager.swift for a session renew handler.
Refer to AppDelegate.swift and SceneDelegate.swift for an example of using SessionManager.swift.
Support for passkeys in the iCloud keychain
Before iOS16 there was no native FIDO2 support in Apple devices, only the Safaribrowser supported FIDO2 authentication. This new, native support for passkeys in the iCloud keychain allows the user to register a FIDO2 authenticator on one device and use that authenticator on all of their devices. A passkey also enables your app to autofill a user's password.
Native passkey support requires a mobile device running iOS16 or higher. See https://passkeys.dev/device-support/ for current platform compatibility information. You must explicitly enable passkey support on your device and in your application.
To enable passkey support:
Enable passkeys on your device. Make sure that the iCloud keychain is On on your device. Go to Settings > Apple ID > iCloud > Passwords and Keychain. By default, the Sync this iPhone switch is on.
Enable passkeys for your application. Configure your domain’s apple-app-site-association file to include your application’s bundle ID as a webcredentials.
Configure your application:
3.a. Select your project in Xcode's Project navigator and select the app's target in the Targets list.
3.b. Click the Signing & Capabilities tab in the project editor.
3.c. Find the Associated Domains capability and click the Add button (+) to insert a service-domain placeholder.
3.d. Double-click the new placeholder and add the following:
webcredentials:<your associated domain>Passkeys are enabled by default in the Digipass S3 App SDK. To disable passkeys in the Digipass S3 App SDK, your application must call ClientConfig.setPasskeysSupport(false).
Working Example in Tutorial App
See file SettingsViewController.swift
Enabling FIDO2 on older systems
The App SDK supports FIDO2 both natively and via Safari browser. Devices running iOS 16 and higher support native FIDO2 in Apple devices through a feature called passkeys in iCloud Keychain. But devices with iOS 14 and iOS 15 must perform FIDO2 authentication using the Safari browser. To enable FIDO2 for devices running your application on iOS 14 and iOS 15:
Step 1. Assign the entire pathname of the fido.html file to AppSDKPlusConfig's fido2SupportPage property:
let appConfig = AppSDKPlusConfig()
appConfig.fido2SupportPage = <path to fido.html>
Step 2. Add a custom URL scheme in your application’s info.plist file.
In xCode, select your project and target.
Select the Info tab and scroll down to URL Types.
Enter your custom URL scheme as shown below.
.png?sv=2026-02-06&spr=https&st=2026-09-30T00%3A39%3A33Z&se=2026-09-30T01%3A47%3A33Z&sr=c&sp=r&sig=QQK0sQZG%2FirXvUvuWTlLbHn2AaPZQnyQgoyC86JQs0o%3D)
This modifies the CFBundleURLSchemes. This URL Scheme is needed by the system ASWebAuthenticationSession interface in order for it to come back with the result after completing a FIDO2 operation in fido.html.
Out-of-Band authentication
Securely authenticating users who are using a web app from your company can be challenging. You don't know if the user is the actual person or someone who has stolen your user's login credentials. Many desktop computers and laptops can't authenticate a user because they don't have a built-in fingerprint scanner or a hardware key store, like a TPM chip.
Out-of-band (OOB) authentication enables you to use a second device tied to the user, typically their cell phone, to authenticate. Your web app initiates authentication by either
Sending a push notification to your mobile app installed on the user's cell phone. When the user receives the notification, they authenticate with your mobile app using a registered FIDO authenticator, like fingerprint.
Displaying a QR code on the device running your web app. The user scans the QR code with their second device using either your mobile app or the camera app. Then the user authenticates with a registered FIDO authenticator on their second device.
While a push notification can start authentication for a user, it cannot be used to trigger registration for an authenticator. Scanning a QR can initiate both registration and authentication.
OOB requires configuring your app on the Authentication Server and, if you use push notifications, it requires using the Apple Push Notification service. See Out-of-band.
Implementation overview
To respond to push notifications in your app:
Your web app can send a push notification for OOB authentication after OOB registration, without requiring that the user be authenticated with QR-code OOB authentication first.
To implement QR code scanning in your app:
See Customizing Out-of-band Authentication for the ways you can tailor OOB authentication.
Initializing the OOB SDK
You must initialize the App SDK before you can use it to process push notifications or QR code scans as the secondary device. Before you can use the OOB SDK to scan a QR code, initialize it with a scanner view frame:
Get the instance of OOBSDK using the OOBSDK.getInstance() method.
If your app scans QR codes,
Create the scanner view frame
Call OOBSDK.setScannerViewFrame(), providing the rectangle of the frame for the scanner view. This method creates and initializes an instance of the QRScannerView class and returns a pointer to it. This pointer should be used for triggering the QR code scanner.
Call OOBSDK.setRegURL() to assign the URLS for the registration and authentication endpoints. Do this if your mobile app performs OOB registration and OOB authentication for a specific web app. The App SDK uses these endpoints and ignores the URL in the QR Code.
If you implement a mobile app, like Passport, that performs OOB registration and authentication for several web apps, then skip this step. In this situation, the mobile app must use the URL from the QR Code.
Triggering the QR code scanner
Use the following functions to start and stop QR code scanning:
Call qrScannerView.start() to trigger the QR code scanner.
Call self.qrScannerView.stop() to stop the QR code scanner
Working example in Tutorial App
Refer to file QRCodeScanViewController.swift.
Enabling push notifications
To enable push notifications with the OOB SDK, do the following:
To register an application for a push notification, call OOBSDK.registerOOBPushNotification() from inside AppDelegate’s didFinishLaunchingWithOptions() method.
Call OOBSDK.didRegisterForOOBPushNotification() from inside AppDelegate’s didRegisterForRemoteNotificationsWithDeviceToken() method.
Call OOBSDK.didFailToRegisterForOOBPushNotification() from inside AppDelegate’s didFailToRegisterForRemoteNotificationsWithError() method.
Sending custom push notifications
You can use FIDO Out-of-Band to authenticate a user during login, and also when a user confirms a transaction. In either case, your app can dynamically specify the text that the App SDK sends to the user in the push notification.
Authentication during login
By default, the Server sends the same configurable, static push notification authentication text to every user when they authenticate with a push notification. You can include the user name and other information specific to the user by customizing that text for each user. This requires a change to both the Server configuration and your client app.
Use the Server's Admin Console to change the Push Notification Authentication Text Mode to Dynamic. Login to the Admin Console, select the desired tenant and navigate to Configuration > Authentication Methods > Out-of-band. Update Push Notification Authentication Text Mode. For more information, see Step 4. in Out-of-band..
Set the custom push notification text dynamically in the extras parameter in your client application before calling getAuthenticationView().
extras?.updateValue("Custom push text", forKey:ExtrasKeyAuthNotificationText)Transaction confirmation
By default, the Server sends the same configurable, static Push Notification Transaction Text to every user when they confirm a transaction with a push notification. You can include the user name and other information specific to the user by customizing that text for each user. This requires a change to both the Server configuration and your client app.
Use the Server's Admin Console to change the Push Notification Transaction Text Mode to Dynamic. Login to the Admin Console, select the desired tenant and navigate to Configuration > Authentication Methods > Out-of-band. Update Push Notification Transaction Text Mode. For more information, see Step 4. in Out-of-band.
Before calling NNLAdaptiveUI.transact() from your client application, dynamically set the push notification transaction text as the value for the key ExtrasKeyOptions in the extras parameter. For more information, see Transaction.
Processing push notifications
To process push notifications with the OOB SDK, do the following:
To receive push notifications, set the UNUserNotificationCenter.current().delegate to self in AppDelegate’s didFinishLaunchingWithOptions() function.
Implement the following methods of UNUserNotificationCenterDelegate in AppDelegate as shown below:
func userNotificationCenter(_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: @escaping () -> Void) {
OOBSDK.didReceiveOOBPushNotification(response.notification.request.content.userInfo)
completionHandler()
}
func userNotificationCenter(_ center: UNUserNotificationCenter,
willPresent notification: UNNotification,
withCompletionHandler completionHandler:
@escaping(UNNotificationPresentationOptions) -> Void) {
OOBSDK.didReceiveOOBPushNotification(response.notification.request.content.userInfo)
completionHandler([])
}Working examples in Tutorial App
AppDelegate.swift
QRCodeScanViewController.swift
Managing pending authentications
In some cases, a push notification that your application sends to a secondary device does not arrive there. Here are some of the reasons that the push notification can end up in a pending state:
the user might have turned off push notifications for the app on their device
the push notification service has technical difficulties
the user might make several purchases quickly.
To address these situations, the App SDK provides a method that allows your application to list all pending out-of-band authentications. The user can then view this list of pending authentications and choose which one they want to approve or reject.

To take advantage of this feature:
By default this feature is turned off and it requires a configuration change on the Server to enable it. Use the Command Line Interface to the Server to set the System property oob.list.auth.enabled to true.
./nnl-mgmt.sh properties set -name oob.list.auth.enabled -value true
-tenantid SYSTEMRestart Tomcat.
In your app, create the NNLAppSDKPlus object and get the session object that was created during sign in.
Call NNLAppSDKPlus.getPendingAuthsView() and receive the instance of NNLBasePendingAuthsViewController in return.
Show the PendingAuthsView so the user can interact with it.
// 2) Create an instance of NNLAppSDKPlus.
let AppSDK = NNLAppSDKPlus(appConfig: config)
// 3) Get NNLBasePendingAuthsViewController.
let mPendingAuthsVC: NNLBasePendingAuthsViewController =
AppSDK.getPendingAuthsView(authData?.sessionData, withExtras: nil )
// 4) Show NNLBasePendingAuthsViewController.
self.addChild(mPendingAuthsVC)
self.containerView.addSubview(mPendingAuthsVC.view)By default, pending authentications on all devices are listed in the returned view. If you want the pending authentications for the current device only, set the flag currentDeviceOnly to "true" inside extras, and pass extras to getPendingAuthsView().
var extras: [String: String]? = nil
extras = [String: String]()
extras?["currentDeviceOnly"] = "true"
let mPendingAuthsVC: NNLBasePendingAuthsViewController =
AppSDK.getPendingAuthsView(authData?.sessionData, withExtras:extras)Working example in Tutorial App
See PendingAuthViewController.swift.
Specifying the QR code type to display
Unless your app has implemented push notifications, your app automatically displays a QR code if the user registers a FIDO OOB authenticator or uses FIDO OOB to authenticate. By default, the QR code type generated for registration and authentication is APP_ANY_RP which means it can be scanned by native mobile apps and OneSpan's Passport app. Your particular situation may require a different QR code type depending on the apps that need to scan it.
The table below describes the types of QR codes and what kind of apps can successfully scan it to either register or authenticate. The QR code types are ordered by the size of the QR code they generate, from largest to smallest. Larger QR codes can be scanned by a greater variety of apps but it's best to select the smallest QR code type for your situation.
QR Code Type | Description |
|---|---|
UNIVERSAL_ANY_RP | Generates a universal QR code that can be scanned successfully by mobile apps, web apps, and camera apps that can process QR codes. These apps can be deployed by different organizations. |
UNIVERSAL_RP_SPECIFIC | Generates a universal QR Code specific to apps deployed by one organization. The API Server hostname is omitted from the QR code. |
APP_ANY_RP | The default QR code type. Generates a QR Code that works with native mobile apps deployed by different organizations. The web page hostname is omitted from the QR code. |
APP_RP_SPECIFIC | Generates a QR code that can only be scanned by native mobile apps deployed by one organization. The Auth Server omits both the web page and API Server hostname from the QR code. Mobile apps that scan this QR code must hard code the registration and authentication endpoints as described in Step 3 in Initializing the OOB SDK. |
Pass in the QR code type to an App SDK method using the extras parameter as shown below. This example passes QR code type UNIVERSAL_ANY_RP to adaptiveUI.getAuthenticationView(). If the user decides to use FIDO OOB to verify their identity, the Authentication Server generates a QR code that can be scanned by any native mobile app.
var extras: [String: String]? = nil
extras = [String: String]()
extras!.updateValue(QRType_UNIVERSAL_ANY_RP, forKey: ExtrasQrType)
extras!.updateValue("https://example.com/mywebapp/oobAuth.html", forKey: ExtrasQrWebUrl)
let authViewController = adaptiveUi.getAuthenticationView(sessionData,
transactionId: nil, extras: extras)
You can pass in the QR code type to any App SDK methods that can register or authenticate using FIDO OOB such as NNLAppSDKPlus.getFidoRegistrationView(), NNLAdaptiveUI.getSuggestRegistrationView(), and NNLAdaptiveUI.getAuthenticationView(). In addition, you can pass in the QR code type to methods used for device blessing. The QR code types are defined on the AppSDKPlus.QRType class in the Client API Docs.
Processing QR codes using the camera app
Your mobile app can authenticate a user that is running a web application on a desktop browser. The web application can display a QR code and the mobile device's Camera app can scan the QR code. If your app is installed on the mobile device, the Camera then automatically opens your mobile app to process the QR code. If your app is not installed on the mobile device, the Camera app automatically opens the mobile device's browser to a webpage that can process the QR code
To use this feature:
The QR code type displayed by the web application must be a universal QR code.
Associate your mobile app with your domain for universal link usage according to Apple’s documentation. Note that custom ports (like 8443) are not allowed with associated domains.
Include a call to OOBSDK.handleUniversalLink() in your mobile app to process the QR code received by the universal link:
func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
if (userActivity.webpageURL != nil){
OOBSDK.getInstance().handleUniversalLink(userActivity.webpageURL, withDelegate: self)
}
return true
}
Please see the documentation on OOBSDK.handleUniversalLink() in the Client API Docs for more information. Working examples in Tutorial App
Refer to files:
AppDelegate.swift
SceneDelegate.swift
tutorialappplus.entitlements
Device blessing support
Device blessing is a QR code-based OOB mechanism that enables the user to initiate registration on a new device by using a previously-registered device. The following steps describe how Tutorial App uses device blessing implemented in the NNLAdaptiveUI interface:
On the previously-registered device, the user taps the Register with QR Code button in the Register tab. Use NNLAdaptiveUI.registerQr(), as shown below, to display the QR code returned by the Server.
try await adaptiveUi.registerQr(sessionData, extras: extras)On the new device, the user selects the Scan Code tab and scans the QR code displayed on the previously registered device.
When registration is completed on the new device, the previously-registered device displays the successful confirmation screen.
In the above scenario, the QR code is generated on the previously-registered device. You can also implement device blessing by generating a QR code on the new device. Please see the documentation on NNLAdaptiveUI.authenticateQr() for more information.
checkRegPossible() or checkAuthPossible() are useful for device blessing. Use them to check if a device has already been activated.
Configuring the FIDO OOB option for your users
To create the best user experience for OOB, the Server keeps track of whether your app supports QR code scanning. If your app does not include the OOB library, the App SDK automatically informs the Server that OOB is not supported. You can also enable or disable QR Code scanning for your app by configuring the Server directly using the Admin Console. The instructions below show how your app can dynamically enable or disable support for QR Code scanning using the App SDK.
The following code uses a call to getFidoRegistrationView() to inform the Server that this app does not support QR code scanning on the current device.
// Create a dictionary object called extras and assign "false" to the extras key ExtrasQrSupported. This key is defined in IAppSDKPlus.h.
var extras = [String: String]()
extras.updateValue("false", forKey: ExtrasQrSupported)
// Pass the dictionary object in the extras parameter
var view = appSdk.getFidoRegistrationView(sessionData, extras: extras)You can also pass the dictionary object in the extras parameter when you call NNLAdaptiveUI.getAuthenticationView().
var view = adaptiveUI.getAuthenticationView(nil,
transactionId: nil,
extras: extras,
suggestRegOptions: nil,
signUpOptions: nil)Embedding a WebView
The App SDK allows you to embed a WebView component into your app. Use this to render part of your app's UI using HTML, or use it to load into your app an entire web application that was developed with the NokNok Web App SDK. The WebView can trigger FIDO2, UAF, and OOB operations.
The class AppSDK2 provides two WebView methods:
-(bool) initWithWebView: (WKWebView *) pWebView;
Loads the Digipass S3 Web API into the specified WKWebView.
-(bool) processWebViewRequest: (NSURLRequest *) request;
Determines whether the given request is a valid request to the Digipass S3 Web App SDK. If the request is valid, then this processes the request and returns false. If the request is not a valid Web App SDK request, it returns true.
To embed a WebView component into your iOS app:
1. Initialize a WKWebView object and load the web application. See file webviewcontroller.swift for an example.
2. Define the delegate function below from Swift's WKNavigationDelegate protocol. The WKWebView object calls your delegate function when it completes loading a web page.
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!)4. Inside the webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) function, create an AppSDK2 instance and call the initWithWebView(WKWebView *) function to load the Digipass S3 Web API into the mWebView web page.
var appSDK2 : AppSDK2 = AppSDK2()
appSDK2.initWith(mWebView!)5. (Optional) If you want OOB functionality, then get an OOBSDK instance and call the initWithWebView(WKWebView *) method.
OOBSDK.getInstance().initWith(mWebView!)6. Swift's WKNavigationDelegate protocol defines a method called decidePolicyFor navigationAction that decides whether to allow or cancel navigation.
func webView(_ webView: WKWebView,
decidePolicyFor navigationAction: WKNavigationAction,
decisionHandler: @escaping (WKNavigationActionPolicy) -> Void)Implement this in your app, and include the following:
A call to self.appSDK2.processWebViewRequest(navigationAction.request)
If this returns false, then the URL corresponds to a Digipass S3 Web App SDK request. Call decisionHandler with the WKNavigationActionPolicy.cancel parameter.
If this returns true, then the URL does not correspond to a Digipass S3 Web App SDK request. Call decisionHandler with the WKNavigationActionPolicy.allow parameter.
(Optional) If you want OOB functionality, then include the additional call OOBSDK.getInstance().processWebViewRequest(navigationAction.request).
Take action based on the returned value as above.
Working example in Tutorial App
WebViewController.swift
Controlling authenticator selection
When you configured the Digipass S3 Server, you defined FIDO policies that enforce your organization's choice of valid UAF or FIDO2 authenticators for registration and authentication. Additionally, you can choose to filter out authenticators before they're shown to the user. You can achieve this by customizing the filter chain.
During authentication, the App SDK displays a screen to allow the end user to pick an authenticator. During registration, the App SDK displays a list of available authenticators that you can register. You can programmatically change the UI by customizing the filter chain.
Tutorial App contains an example of how to do this. This example sets SampleAuthenticatorFilterChainFactory. The sample chain consists of the filters shown below. The first 5 are default filters taken from DefaultAuthenticatorFilterChainFactory. The last filter in the chain is either the default UIAuthenticatorFilter or a custom filter that you implement. SampleUIAuthenticatorFilter is an example of such a custom filter.
DuplicateAuthenticatorFilter: Removes duplicate authenticators.
TitleBasedAuthenticatorFilter: Removes authenticators with the same title.
DisallowedAuthenticatorFilter: Removes authenticators disallowed by a FIDO policy. This filter provides correct policy processing.
CriteriaAuthenticatorFilter: Removes authenticators based on factors like authenticator modality, e.g., fingerprint or AAID. Pass in a special extension during processing to provide filtration criteria.
SelectAndRemember: Enables the App SDK to remember the user-selected authenticator and use it for subsequent authentications.
At the end of the filter chain you can add a custom filter to replace UIAuthenticatorFilter.
SampleUIAuthenticatorFilter is an example of how to implement a custom filter. It takes an index of the authenticator to select from a list and returns the selected authenticator in the resulting list. It displays a UI to the user to confirm selection. This filter demonstrates how to get parameters into custom filters and perform UI interaction during filtration.
The filter chain factory produces a list of authenticator filters which are each processed in the order that they were added. Each filter is applied on the result of the previous one. The custom AuthenticatorFilterChainFactory can add any number of existing or new custom filters.
To use standard filters included in the App SDK, your CustomFilterChainFactory implementation should extend DefaultAuthenticatorFilterChainFactory and use special helper methods to add any needed filters. The example below from the file SampleAuthenticatorFilterChainFactory.mm calls the helper method for DisallowedAuthenticatorFilter.
addDisallowedAuthenticatorFilterForAuth(filters, jsonObject);To test this functionality, uncomment the following code in TutorialAppPlus/AppDelegate.swift (this code replaces the factory class in uaf_engine module):
SampleAuthenticatorFilter.replace()There is related code in the useSampleUIFilterFunctionality method in SampleExtensions.swift which configures SampleAuthenticatorFilterChainFactory to use the custom UI. This shows how to pass filter-specific parameters via an extension. Uncomment the following line to test this functionality:
SampleExtensions.useSampleUIFilterFunctionality(ext: &extensions)See the Client API Docs for more information on the specific APIs.
Working Examples in Tutorial App
TAUtils.swift - Shows how to enable functionality for operation.
SampleExtensions.swift - Shows how to construct the criteria parameter to pass to the Authenticator Filter Factory.
Managing Personally Identifiable Information (PII)
With the advent of regulatory requirements protecting customers' private data, your apps must enable users to view and delete their sensitive data. The App SDK provides 2 methods you can use: fetchUserData() and purgeUserData(). Prior to calling these methods, create an AppSDKPlus object as described in Creating the Main Objects.
fetchUserData() retrieves the user's active and deleted FIDO authenticators as well as non-FIDO authentication methods. Pass in the AppSDKSessionData, as shown below. AppSDKSessionData contains the user name. The returned value is a JSON containing a list of Registration JSONs and a list of Method JSONs in string format. For the fields contained in Registration and Method, see the Nok Nok Data Types Used by the App SDKs Tech Note.
do {
let userData = try await sdk.fetchUserData(app.authData!.sessionData)
print("Fetch User Data: \(userData)")
} catch {
//convert to NSError to check the error code
let err = error as NSError
TAUtils.traceNestedExceptionStack(err)
}purgeUserData() hard deletes the user's FIDO authenticators, non-FIDO authentication methods, registration history, authentication history, deregistration history, and history of internal API operations. To delete user data, pass in sessionData, as shown below. This method returns a tally of deleted objects.
do {
let userData = try await sdk.purgeUserData(app.authData!.sessionData)
print("Purge User Data: %@", userData)
} catch {
//convert to NSError to check the error code
let err:NSError = error as NSError
TAUtils.traceNestedExceptionStack(err)
}Working Example in Tutorial App
Refer to the definition of fetchUserData() in the file MiscellaneousOptionsViewController.swift to see a call to sdk.fetchUserData().
Refer to the definition of purgeUserData() in the file RegisterViewController.swift to see a call to skd.purgeUserData().
Using an External Authentication Method
An External Authentication Method allows your users to authenticate with passwords or other authentication methods not currently supported by Digipass S3. In addition, you can include External Authentication Methods in an Adaptive Ruleset. When you trigger an External Authentication Method, an external entity attempts the authentication and provides an acceptable result. This section refers to this external entity as the RP Server. Digipass S3 does not support the registration of an External Authentication Method.
To use an External Authentication Method, define a new custom class to perform the external authentication, define a new factory class, override createMethodUI() in your factory class, and configure the API Server. Here are the details:
Define a custom class that adopts the NNLIExternalAuthLiveData protocol and is responsible for the external authentication UI. The App SDK calls the onGetCredential() method of your custom class. Your onGetCredential() method presents the UI if needed and asks the user for verification data. Your class then creates a credential and sends the credential in the NNLExternalAuthMethodUi object to process. Digipass S3 supports two ways to create the credential:
JWT External Authentication - Your class calls the RP Server to verify the user. The RP Server returns a JWT as the credential. Your class then passes the JWT in the NNLExternalAuthMethodUi object to process through the processCredential() method. If your class fails to get the credential, use the onError() method to send an error to the App SDK.
Password External Authentication - Your class gets the username and the password and sends them back to the API Server in an NNLExternalAuthMethodUi object through the processCredentialDict() method. The API Server then calls the RP Server to verify the userName and password.
Define a new factory class that is a subclass of NNLMethodUIFactory. For an example, see class SampleMethodUiFactory in Tutorial App Plus. Set an object of your new factory class to be the NNLMethodUIFactory instance.
let defaultFactory = NNLMethodUIFactory.getInstance()
let factory = SampleMethodUiFactory (factory: defaultFactory)
NNLMethodUIFactory.setInstance(factory)In your new class SampleMethodUIFactory, override createMethodUI() and other methods. Your factory’s createMethodUI() returns an NNLExternalAuthMethodUi object. The initialization of NNLExternalAuthMethodUi requires a LiveData object from the class you implemented in Step 1.
Configure the Server to use your External Authentication Method. See External Authentication Method.
Working examples in Tutorial App
SampleMethodUiFactory.swift
PasswordAuthUi.swift
SideMenuViewController.swift
TAUtils.swift
Integrating FIDO into a watch app
The integration process differs depending on whether or not you’ve already developed a watch app.
Creating a new watch app with FIDO capabilities
Create your watch app in Swift and then perform the following steps:
Add a new target for the watch into your application in the Xcode project. Ensure that Complication support was added to your watch target during creation.
Embed the following xcframeworks from FrameworksWatch folder into watch target in Xcode:
watchKitAppExtension.xcframework
uaf_engine.xcframework
uaf_asm.xcframework
uaf_extensions.xcframework
appsdk.xcframework
appsdkplus.xcframework
nnl_util.xcframework
SilentASM.xcframework
Using Finder, replace the contents of YourAppWatchKitApp folder with the contents of TutorialWatchApp/WatchKitApp. Also replace YourAppWatchKitAppExtension folder with the contents of TutorialWatchApp/WatchKitAppExtension folder.
Add the following files from the YourAppWatchKitAppExtension folder to your watch project in Xcode.
ActionInterfaceController.swift
TutorialUserSelectionUI.swift
WatchSampleAuthenticatorFilter.h/m
Add images and image assets from the YourAppWatchKitApp and YourAppWatchKitAppExtension folders to your project in Xcode.
To customize elements of the watch app UI, modify the following files and folders in the Xcode project:
Watch app | Location in Xcode project |
|---|---|
App screens | YourAppWatchKitApp/Interface.storyboard (file) |
Screen image | YourAppWatchKitApp/images (folder) |
App icons | YourAppWatchKitApp/Assets.xcassets (file) |
Complication icons | YourAppWatchKitAppExtension/Assets.xcassets (file) YourAppWatchKitAppExtension/images (folder) |
You have an existing watch app
To add watch authenticator support to an existing watch app, modify it using the following steps:
Add the watchKitAppExtension.framework into your watch target.
During initialization of your watch app, activate WCSession and call WatchKitAppSDK.sharedInstance()!.activate(self) to activate WatchKitAppSDK.
you should set the WCSessionDelegate delegate after this call.
Implement WCSessionDelegate::didReceiveMessage(). When you receive a message from the iPhone, call watchKitAppExtension.framework() to parse and process the requests. In the session(session: WCSession, didReceiveMessage message: [String : AnyObject], replyHandler: ([String : AnyObject]) -> Void) function, you should call WatchKitAppSDK.sharedInstance.process(message, replyHandler: replyHandler).
Add UI handling. Prepare the response for the watchASM and call the reply handler. Here is a sample for the case when the user taps Authorize on the watch. For the deny case, use WC_FAIL instead of WC_SUCCESS.
// Prepare return data
var returnValue: [String : Any] = [:]
returnValue[WC_AUTHORIZE_RESPONSE] = WC_SUCCESS
// Call the reply handler
WatchKitAppSDK.sharedInstance().mReplyHandler(returnValue)If your watch app has no Complications support, then add Complications support to the watch target and replace YourAppWatchKitAppExtension/ComplicationsController.swift file with WatchApp/WatchKitAppExtension/ComplicationsController.swift.
Adding standalone watch app support
To add standalone watch app support to an existing watch app, modify it using the following steps:
Embed the following xcframeworks from the FrameworksWatch folder into the watch target in Xcode:
uaf_engine.xcframework
uaf_asm.xcframework
uaf_extensions.xcframework
appsdk.xcframework
appsdkplus.xcframework
nnl_util.xcframework
SilentASM.xcframework
To implement operations in your watch app, refer to the following functions in the TutorialWatchAppPlus Extension/InterfaceController.swift file:
doLogin() - for login operation.
doRegister() - for registration operation.
doAuthenticate() - for authentication operation.
doDeregister() - for deregistration operation.
Working example in Tutorial App
Refer to file InterfaceController.swift.