Skip to content

IDK usage

Getting started

To access APIs for the Digidentity IDK, the package/module must first be imported:

// Android
import com.digidentity.idk.DigidentityIDK
// iOS
import DigidentityIDK

The IDK instance must be configured before its APIs can be called. The IDK is provided as a singleton instance, so there is no need to cache it: once configured, it lives until the app is closed.

// Android
DigidentityIDK.configure(context, "client secret")

or

// Android
DigidentityIDK.configure(context, "client secret", null);

Parameters

  • context: an instance of the android.content.Context.
  • secret: the string representation of the client secret provided by Digidentity. This is unique to the application and can not be modified.
  • idkLogger: An instance of the com.digidentity.idk.logger.IDKLogger. Can be null.
// iOS
try DigidentityIDK.sharedInstance.configure(with: "client secret", delegate: self)

Parameters

  • secret: the string representation of the client secret provided by Digidentity. This is unique to the application and can not be modified.
  • delegate: used when the IDK requests the application to provide data and perform tasks. Please see IDKDelegate documentation for more information

Should the configuration fail, an error/exception will be thrown. This indicates that the client secret value may be incomplete or modified.

Completion closures

Some APIs return a value and accept either a success or failure closure parameter. In these cases, the result value will be returned before the completion closures are called.

Important: Closures may capture all objects referenced in their scope, leading to cycle references.

IDK errors

The IDK uses the common enum DigidentityIDKError as the error type for all of its APIs, however some of the APIs may return specific, context-dependent errors. These errors are documented next to their respective API. Below are some common errors which may be returned by any API:

  • notConfigured: the IDK has not been configured. The IDK must be configured before calling any of its APIs
  • internalError: an internal error has occurred. A message value is included alongside the error only to provide further information
  • flowCancelled: the flow has been dismissed or cancelled by the user
  • flowClosed: the user clicked the close button in the Scan QR flow
  • invalidInput: this error is context-dependent. If an API returns this error, more information can be found in the API's documentation
  • unknownLoginState: the backend returned a login state which the IDK could not handle. Please inform our IDK developers should this occur
  • deviceNotSupported: the IDK detected that the operating system or device is not supported

Some of the errors are only possible if feature flags are set: - userForgotPIN: the feature flag customForgotPinButton is set and the user tapped the "Forgot your PIN?" button - serviceRegistrationNotAllowed: the feature flag disableServiceRegistration is set and the user reached a point when a IDK service registration is required - operationNotAllowed: the feature flag disableManualAccountAddition is set and the user reached a point when a manual IDK account addition is required

When an error occurs during a flow, the IDK attempts to present the user with options to recover from the error where possible. For example, the IDK may propose to try an operation again, send the user to app settings, or advise to contact customer support. The error is reported to the caller only in the case that the error was not possible to resolve (causing the flow to fail). When an IDK flow fails, in some cases an error dialog may be displayed to inform the user of what happened. In such cases, the failure closure is called only on the dialog dismiss action.

IDKDelegate (iOS)

The delegate is an object which helps the IDK perform tasks on behalf of the app. The delegate must be passed to the IDK via an argument when the IDK is configured. The IDK instance keeps a weak reference to the delegate in order to avoid a cycle reference, therefore it is the responsibility of the app to own the delegate object.

The IDK depends on Firebase Messaging which in turn relies on the Firebase Messaging token (FCM token) to establish a push notification communication channel. When the token is needed, the IDK asks the delegate to provide the token from the app's side. This call can occur multiple times and at any arbitrary moment.

// iOS
func firebaseTokenProvider(for idk: IDKProtocol, completion: @escaping (FirebaseToken?, Error?) -> Void) {
    // pass the completion closure to Firebase:
    Messaging.messaging().token(completion: completion)
}

Parameters

  • idk: the IDK instance itself (matching the shared singleton)
  • completion: this can usually be passed directly to Firebase Messaging (matching its signature)

Localization

The IDK currently supports Dutch and English localizations. The UI language is detected from the current device settings automatically. Where the device is set to a language that is not supported, the IDK will fall back to English.

UI customization

The UI of the IDK can be customized to match the visual style of your app through the json theme file where all supported visual attributes are set.

Android

On Android this is done by applying the IDK theme Gradle plugin and configuring a json theme file as its source file. For instructions please refer to the "IDK theme Gradle plugin setup" section of the Android setup. You can find an example of a json theme file here

iOS

On iOS this is done by passing the Theme object to the IDK. The theme object is a json theme file mentioned earlier. You can find an example here.

Note: Customization is not mandatory: by default the IDK uses its built in Digidentity visual style.

The IDK supports automatic light/dark interface style switching. The theme file consists of three main parts (validation occurs during Theme object initialization): - colors: allows customization for colors in both the light and dark interface styles - dimensions: allows standard size customization for points used in the IDK view layout - textStyles: allows customization for font properties in captions

Important: There are no size validations for these values, so please ensure layouts are correct on all supported device screen sizes after customization.

// iOS
let themeFileURL: URL = ...
try let customTheme = Theme(themeFileURL)
DigidentityIDK.sharedInstance.setTheme(customTheme)

Customize font

Using a custom font can be achieved in three steps: 1. Add the custom font to your Xcode project. 2. Register the custom font by adding the font file name (with extension) to the Info.plist under the key Fonts provided by application (UIAppFonts). 3. Finally, in the json theme file, change fontFamily and style for the textStyles for which you want to use this custom font. The fontFamily and style should match with how they're named in the Font Book app. For example including Times New Roman Regular should be done as: "fontFamily": "Times New Roman" and "style": "Regular".

Handling notifications

Registering for notifications

The IDK depends on Firebase Messaging. When Firebase delivers a new FCM token to the app, please provide the token to the IDK:

// Android
val token: String = ...
DigidentityIDK.handleNewFirebaseToken(token = token)

or

// Android
final String token = ...
DigidentityIDK.handleNewFirebaseToken(token);
// iOS
func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String?) {
    if let token = fcmToken {
        DigidentityIDK.sharedInstance.handleNewFirebaseToken(token)
    }
}

Processing notifications

Android

The IDK must be allowed to handle push notifications received by the app. Where messages are intended for the IDK, the IDK will handle them appropriately, otherwise the failure closure is called with notDigidentityNotification error to give the app a chance to handle the notification itself.

// Android
val context: Context = ...
val remoteMessage: RemoteMessage = ...
DigidentityIDK.handleNotification(
    context = context,
    message = remoteMessage,
    onSuccess = {
        // optional: respond to successful completion by user
    },
    onFailure = { digidentityIdkError ->
        // optional: handle error
    }
)

or

// Android
final Context context = ...
final RemoteMessage remoteMessage = ...
DigidentityIDK.handleNotification(
    context,
    remoteMessage,
    () -> {
        // optional: respond to successful completion by user
    },
    (digidentityIdkError) -> {
        // optional: handle error
    }
);

iOS

The IDK must be allowed to handle push notifications received by the app. Where messages are intended for the IDK, the IDK will handle them appropriately, otherwise failure is called with notDigidentityNotification error to give the app a chance to handle the notification itself.

// iOS
func userNotificationCenter(_ center: UNUserNotificationCenter,
                            willPresent notification: UNNotification,
                            withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
    if notification.request.trigger is UNPushNotificationTrigger {
        // Received remote notification while app is foreground. This should not be presented.
        let userInfo = notification.request.content.userInfo
        DigidentityIDK.sharedInstance.onRemoteNotificationReceived(
            payload: userInfo,
            success: {
                // Notification is handled and the subsequent flow has successfully finished.
            },
            failure: { error in
                // Notification handling failed or the subsequent flow execution failed.
                switch error {
                case .notDigidentityNotification:
                    // Not a Digidentity notification
                    break
                default:
                    break
                }
            },
            presentation: { viewController in
                // Present view controller
        })

        completionHandler([])
    } else {
        // Received a local notification, which should be presented
        completionHandler([.sound, .list, .banner])
    }
}

In some cases the IDK may need to present a notification to the user, in which case a local notification may be sent via UNUserNotificationCenter. Pass the local notification to the IDK to be handled:

// iOS
func userNotificationCenter(_ center: UNUserNotificationCenter,
                            didReceive response: UNNotificationResponse,
                            withCompletionHandler completionHandler: @escaping () -> Void) {
    let notificationRequest = response.notification.request
    let userInfo = notificationRequest.content.userInfo

    if notificationRequest.trigger is UNPushNotificationTrigger {
        // Tapped on remote notification
        DigidentityIDK.sharedInstance.onRemoteNotificationReceived(
            payload: userInfo,
            success: {
                // Notification is handled and the subsequent flow has successfully finished.
            },
            failure: { error in
                // Notification handling failed or the subsequent flow execution failed.
            },
            presentation: { navigationController in
                // Present navigation controller
        })
    } else {
        // Tapped on local notification
        DigidentityIDK.sharedInstance.onLocalNotificationReceived(
            payload: userInfo,
            success: {
                // Notification is handled and the subsequent flow has successfully finished.
            },
            failure: { error in
                // Notification handling failed or the subsequent flow execution failed.
                switch error {
                case .notDigidentityNotification:
                    // Not a Digidentity notification
                    break
                default:
                    break
                }
            },
            presentation: { viewController in
                // Present view controller
        })
    }

    completionHandler()
}

Both onRemoteNotificationReceived and onLocalNotificationReceived have the same signature and the following applies to both methods:

Parameters

  • payload: notification payload to be handled
  • success: success closure. If no screen presentation required, called after notification is processed, otherwise called after presented flow succeeds and is dismissed
  • failure: failure closure. If no screen presentation required, called after notification failed to process, otherwise called after presented flow fails and is dismissed. Delivers an error value such as the following:
    • notDigidentityNotification: messages are not intended for the IDK
  • presentation: presentation closure with UINavigationController as parameter. Called if IDK requires a screen to be presented. Screen dismisses itself after initiated flow is over

IDK flows

IDK flows are APIs which provide content screens completing specific tasks for the user. On iOS a screen may be represented as UIViewController or UINavigationController. This allows the IDK to navigate the user through a sequence of screens. The IDK controls IDK screen content and provides screens to the app, while the IDK screen presentation is controlled by the app itself.

Handling URLs

The IDK can handle URL input using Firebase Dynamic Links for URL resolution. For this to function, the URL format must be supported by both the IDK and the app (registered Universal Links for iOS). Depending on the content of the URL, the IDK may perform one of the following actions:

  • start passwordless login flow
  • start evidence upload flow
  • start smart card creation flow
  • start TOTP creation flow

Android

// Android
val context: Context = ...
val intent: Intent = ...
val handleDeepLinkIntent = DigidentityIDK.handleDeepLink(
    context = context,
    intent = intent,
    onSuccess = { handleUriResult ->
        ...
    },
    onFailure = { digidentityIdkError ->
        ...
    }
)
context.startActivity(handleDeepLinkIntent)

or

// Android
final Context context = ...;
final Intent intent = ...;
final Intent handleDeepLinkIntent = DigidentityIDK.handleDeepLink(
    context,
    intent,
    (handleUriResult) -> {
        ...;

        return null;
    },
    (digidentityIdkError) -> {
        ...;

        return null;
    }
);
context.startActivity(handleDeepLinkIntent);

Parameters

  • intent: an intent, which contains a Firebase Dynamic Link.
  • onSuccess: success callback obtains a result value indicating which flow was started.
  • onFailure: failure callback obtains an error value such as the following:
    • CameraPermissionDenied: user denied permission to access the device camera. In this case the flow could not continue without camera access
    • UriNotRecognized: the IDK could not recognize the provided URI. In this case the unrecognized URI is provided in the associated value. The IDK is only able to handle supported URIs
    • UriActionNotSupported: the URL has been recognized, but is not currently supported by the IDK build
    • InternalFailure: the URL has been recognized, but the IDK could not process it. The IDK displays an error dialog in this case
    • EntityExists: the URL has been recognized as an 'add TOTP' action, but the TOTP already exists

Return value

This action returns an Activity Intent before running the callbacks.

iOS

// iOS
let someURL = ...

let idkController = DigidentityIDK.sharedInstance.handleURL(
    url: someURL,
    calledFromExternalApp: true,
    success: { [weak self] result in
        self?.dismissIDK()
    },
    failure: { [weak self] error in
        self?.dismissIDK()
        self?.handleIDKError(error)
    }
)

self.present(idkController, animated: true)

Parameters

  • url: url to handle
  • calledFromExternalApp:: flag that to inform that the provided url was a deeplink opened from external app (default: false)
  • success: success closure delivers a result value indicating which flow was started.
  • failure: failure closure delivers an error value such as the following:
    • cameraPermissionDenied: user denied permission to access the device camera. In this case the flow could not continue without camera access
    • noUniquelyMatchedFirebaseDynamicLinkError: firebase could not uniquely match the provided URL to the current device (please refer to the Firebase documentation for more details)
    • urlNotRecognized: the IDK could not recognize the provided URL. In this case, the unrecognized URL is provided in the associated value. The IDK is only able to handle supported URLs
    • urlActionNotSupported: the URL has been recognized, but is not currently supported by the IDK build
    • invalidInput: the URL has been recognized, but the IDK could not process it. The IDK displays an error dialog in this case
    • entityExists: the URL has been recognized as an 'add TOTP' action, but the TOTP already exists

Return value

This action returns UINavigationController before running closures, presented as a full screen.

Scan QR code fullscreen

The IDK can handle input in QR code form if the QR code contains a supported IDK URL. The purpose of QR code scanning is to extract an encoded URL and handle it according to the Handle URL API.

Android

// Android
val context: Context = ...
val title: Int? = ...
val scanQrCodeFlowIntent = DigidentityIDK.scanQRCodeFlow(
    context = context,
    title = title,
    onSuccess = { handleUriResult ->
        ...
    },
    onFailure = { digidentityIdkError ->
        ...
    }
)
context.startActivity(scanQrCodeFlowIntent)

or

// Android
final Context context = ...;
final int title = ...;
final Intent scanQrCodeFlowIntent = DigidentityIDK.scanQRCodeFlow(
    context,
    title,
    (handleUriResult) -> {
        ...;

        return null;
    },
    (digidentityIdkError) -> {
        ...;

        return null;
    }
);
context.startActivity(scanQrCodeFlowIntent);

Parameters

  • title: an optional string resource ID, which is used as a QR scanner screen title.
  • onSuccess: success callback obtains a result value indicating which flow was started.
  • onFailure: failure callback obtains an error value such as the following:
    • CameraPermissionDenied: user denied permission to access the device camera. In this case the flow could not continue without camera access
    • UriNotRecognized: the IDK could not recognize the provided URI. In this case the unrecognized URI is provided in the associated value. The IDK is only able to handle supported URIs
    • UriActionNotSupported: the URI has been recognized, but is not currently supported by the IDK build
    • InternalFailure: the URI has been recognized, but the IDK could not process it. The IDK displays an error dialog in this case
    • EntityExists: the URI has been recognized as an 'add TOTP' action, but the TOTP already exists

Return value

This action returns an Activity Intent before running the callbacks.

iOS

// iOS
let idkController = IDK.sharedInstance.qrScanFlow(
    success: { [weak self] result in
        self?.dismissIDK()
    },
    failure: { [weak self] error in
        self?.dismissIDK()
        self?.handleIDKError(error)
    }
)

self.present(idkController, animated: true)

Parameters

  • success: the success closure delivers a result value indicating which flow was started
  • failure: the failure closure delivers an error value such as the following:
    • cameraPermissionDenied: user denied permission to access the device camera after the IDK displayed dialogs with a request and explanation. In this case, the user must go to system settings and grant camera access permission
    • noUniquelyMatchedFirebaseDynamicLinkError: firebase could not uniquely match the provided URL to the current device (please refer to the Firebase documentation for more details)
    • urlNotRecognized: the IDK could not recognize the provided URL. In this case, the unrecognized URL is provided in the associated value. The IDK is only able to handle supported URLs
    • urlActionNotSupported: the URL has been recognized, but is not currently supported by the IDK build
    • invalidInput: the URL has been recognized, but the IDK could not process it. The IDK displays an error dialog in this case
    • entityExists: the URL has been recognized as an 'add TOTP' action, but the TOTP already exists

Return value

This action returns UINavigationController before running closures, presented as a full screen.

Below is an example of how this may appear in the app:

Scan QR code fullscreen