Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/signing-sdk/face-auth-ios/llms.txt

Use this file to discover all available pages before exploring further.

Registration combines a real-time face liveness check with FIDO2 passkey creation in a single, SDK-managed flow. When the user completes registration, the SDK verifies that a live face is present (preventing photo and video spoofing), then creates a WebAuthn credential bound to your rpId domain and stores it in the device’s Secure Enclave. Your backend receives a requestId that you can use to correlate the event server-side.

What registration does

  1. Face liveness detection — the SDK captures and analyzes the user’s face to confirm it is live, not a photo or video replay.
  2. Passkey creation — a FIDO2/WebAuthn credential is generated on-device, tied to the rpId you supply in TadSigningConfig.
  3. Backend confirmation — the SDK calls your apiBaseUrl and returns a requestId to your completion handler on success.

Required inputs

ParameterTypeDescription
bankIdStringA stable identifier for the user in your system (e.g. a bank customer ID).
dto[String: String]Optional extra fields sent to the backend. Pass ["otp": otp] to include a one-time password.
configTadSigningConfigSDK configuration (API URL, public key, rpId, service name, proxy settings).
modeTadSigningModeMust be .register for the registration flow.
The dto dictionary is optional. Pass [:] when you do not need to include an OTP or other extra fields. The demo uses otp.isEmpty ? [:] : ["otp": otp] to handle both cases cleanly.

Presenting the view controller

1

Build the dto payload

Construct the optional dictionary that accompanies the registration request. If your flow uses a one-time password as an additional factor, include it here.
let dto = otp.isEmpty ? [:] : ["otp": otp]
2

Instantiate TadSigningViewController

Create the view controller with your config, the user’s bankId, the dto payload, and mode: .register. Provide a trailing closure to receive the result.
let signingVC = TadSigningViewController(
    config:  SDKConfig.shared,
    bankId:  bankId,
    dto:     dto,
    mode:    .register
) { signingResult in
    isLoading = false
    switch signingResult {
    case .success(let jwt, let requestId):
        // jwt is empty for registration — use requestId only
        print("Registered. requestId: \(requestId)")
    case .failure(let code, let message):
        print("Error \(code.rawValue): \(message)")
    }
}
3

Resolve the top view controller

The SDK view controller must be presented from the currently active UIViewController. Use a helper to walk the presentation chain.
private func topViewController() -> UIViewController? {
    guard let scene = UIApplication.shared.connectedScenes
        .compactMap({ $0 as? UIWindowScene })
        .first(where: { $0.activationState == .foregroundActive }),
          let root = scene.windows.first(where: { $0.isKeyWindow })?.rootViewController
    else { return nil }

    var top = root
    while let presented = top.presentedViewController { top = presented }
    return top
}
4

Present the view controller

Present signingVC modally from the top view controller. The SDK manages its own UI, camera access, and dismissal.
guard let vc = topViewController() else { return }
isLoading = true
vc.present(signingVC, animated: true)

Handling the result

The completion closure receives a TadSigningResult. On success, requestId identifies the registration event on your backend.
The jwt value is empty for registration. Do not attempt to decode or verify it. Only requestId carries meaningful information after a successful registration.
switch signingResult {
case .success(let jwt, let requestId):
    // jwt == "" — ignore it for registration
    print("Registration successful. requestId: \(requestId)")
case .failure(let code, let message):
    print("Registration failed — \(code.rawValue): \(message)")
}
Store or transmit requestId to your server to confirm the registration event and link the new passkey to the user’s account.

Associated Domains requirement

Passkeys are bound to the rpId domain via the WebAuthn specification. Your app must declare an Associated Domains entitlement matching your rpId so the OS will permit passkey creation. In your entitlements file (or via Xcode’s Signing & Capabilities tab), add:
<key>com.apple.developer.associated-domains</key>
<array>
    <string>webcredentials:signing.tadi.uz</string>
</array>
Replace signing.tadi.uz with your production rpId. The domain must also serve an apple-app-site-association file at https://<rpId>/.well-known/apple-app-site-association that lists your app’s bundle ID.

Permissions

The SDK requires camera access for face liveness detection. Add the following key to your Info.plist:
<key>NSCameraUsageDescription</key>
<string>Required for face liveness check during biometric sign-in</string>

Build docs developers (and LLMs) love