This codelab introduces Multi-Factor Authentication (MFA) concepts using the RDNA iOS SDK framework. You'll learn how the SDK's challenge-response system works, understand different authentication flows, and master the terminology needed for implementing secure authentication in native iOS applications.

What You'll Learn

What You'll Need

The RDNA iOS SDK implements Multi-Factor Authentication through a challenge-response mechanism. This system ensures secure, step-by-step verification of user identity through multiple factors.

Core MFA Concepts

Challenge-Response Pattern

The MFA system works on a fundamental challenge-response pattern:

  1. SDK Issues Challenge: The SDK identifies what information it needs
  2. Callback/Event Triggered: Your iOS app receives an event (via delegate, closure, or NotificationCenter)
  3. User Interaction: App collects required information from user through UIKit views
  4. API Response: App responds with appropriate SDK API call
  5. Flow Continuation: SDK processes response and continues or completes flow

MFA Flow Initialization

After successful SDK initialization, the authentication process begins immediately:

SDK Initialization Complete → getUser Challenge → MFA Flow Begins

Challenge Categories

The MFA system uses several categories of challenges:

Category

Purpose

Examples

Identity Verification

Establish user identity

checkuser(getUser)

Multi-Factor Verification

Verify user through multiple channels

otp(getActivationCode)

Device/Password Authentication

Authenticate using device capabilities/password verification

pass(getUserConsentForLDA/getPassword)

Device Management

Handle new device scenarios

verifyauth(addNewDeviceOptions)

Session Management

Complete authentication flows

onUserLoggedIn

Callback/Event-Driven Architecture in iOS

The MFA system is completely event-driven using iOS patterns:

iOS Callback/Event Handling Options:

  1. Delegate Pattern: Implement SDK delegate protocol methods
  2. Closure-Based Callbacks: Register closure handlers for specific events
  3. NotificationCenter: Observe SDK notifications (if supported)

The RDNA iOS SDK supports three distinct prelogin authentication flows, each designed for specific user scenarios and security requirements.

1. Activation Flow

Purpose: First-time user registration and device setup

When Used:

Key Characteristics:

Flow Outcome: User gets registered successfully with device enrollment.

2. Login Flow (Same Device)

Purpose: Authenticate returning users on previously registered devices

When Used:

Key Characteristics:

Flow Outcome: Quick authentication leveraging device trust and biometrics.

3. Login Flow (New Device)

Purpose: Authenticate users on iOS devices they haven't registered before

When Used:

Key Characteristics:

Flow Outcome: User authenticated with new device added to registered devices.

Each MFA challenge follows a consistent event → API response pattern in iOS. Understanding these mappings is crucial for implementing authentication flows with the RDNA iOS SDK.

Universal Challenge Pattern

Every challenge in the MFA system follows this pattern:

SDK Challenge Callback/Event → iOS UI Collection (UIViewController/Views) → API Response → Flow Continuation

Core Challenge Mappings

1. User Identification Challenge

Challenge Name

checkuser

Callback/Event Name

getUser

API Name

setUser

Purpose

Collect and verify user identifier

When Triggered

Always first challenge after initialization

User Input

Username, email, or user identifier

iOS Implementation Pattern:

// Callback/Event handling (Delegate)
func sdkDidRequestUser() {
    // Show UIViewController with username input
    showUsernameViewController()
}

// Callback/Event handling (Closure)
rdnaService.onGetUser = { [weak self] in
    self?.showUsernameViewController()
}

// API response
func handleUsernameSubmit(_ username: String) {
    rdnaClient.setUser(username) { result in
        DispatchQueue.main.async {
            switch result {
            case .success:
                // Wait for next challenge
                break
            case .failure(let error):
                self.handleError(error)
            }
        }
    }
}

Flow Pattern:

SDK → getUser event → Show UITextField/UIViewController → User enters username → setUser API → Next challenge

2. OTP Verification Challenge

Challenge Name

otp

Callback/Event Name

getActivationCode

API Name

setActivationCode

Purpose

Verify user through out-of-band authentication

When Triggered

After successful user identification

User Input

OTP code, activation code, or verification code

iOS Implementation Pattern:

// Callback/Event handling
func sdkDidRequestActivationCode() {
    showOTPViewController()
}

// API response
func handleOTPSubmit(_ code: String) {
    rdnaClient.setActivationCode(code) { result in
        DispatchQueue.main.async {
            switch result {
            case .success:
                // Wait for next challenge
                break
            case .failure(let error):
                self.handleError(error)
            }
        }
    }
}

Flow Pattern:

SDK → getActivationCode event → Show OTP input UI → User enters code → setActivationCode API → Next challenge

3. Password Authentication Challenge

Challenge Name

pass

Callback/Event Name

getPassword

API Name

setPassword

Purpose

Traditional password-based authentication

When Triggered

When LDA is NOT available on device

User Input

User password

iOS Implementation Pattern:

// Callback/Event handling
func sdkDidRequestPassword() {
    showPasswordViewController()
}

// API response
func handlePasswordSubmit(_ password: String) {
    rdnaClient.setPassword(password) { result in
        DispatchQueue.main.async {
            switch result {
            case .success:
                // Authentication complete
                break
            case .failure(let error):
                self.handleError(error)
            }
        }
    }
}

Flow Pattern:

SDK → getPassword event → Show UITextField (secure entry) → User enters password → setPassword API → Authentication complete

4. Local Device Authentication (LDA) Challenge

Challenge Name

pass

Callback/Event Name

getUserConsentForLDA

API Name

setUserConsentForLDA

Purpose

Biometric or device-based authentication (Face ID, Touch ID)

When Triggered

When LDA IS available on device

User Input

Consent for biometric authentication

iOS Implementation Pattern:

// Callback/Event handling
func sdkDidRequestLDAConsent() {
    showBiometricConsentDialog()
}

// API response
func handleBiometricConsent(_ granted: Bool) {
    rdnaClient.setUserConsentForLDA(granted) { result in
        DispatchQueue.main.async {
            switch result {
            case .success:
                // SDK will trigger native biometric prompt
                break
            case .failure(let error):
                self.handleError(error)
            }
        }
    }
}

Flow Pattern:

SDK → getUserConsentForLDA event → Show UIAlertController consent → User grants → setUserConsentForLDA API → iOS biometric prompt (Face ID/Touch ID)

iOS Biometric Integration:

5. New Device Verification Challenge

Challenge Name

verifyauth

Callback/Event Name

addNewDeviceOptions

API Name

performVerifyAuth

Purpose

Verify legitimacy of new device

When Triggered

During new device login flow

User Input

Selection of verification method

iOS Implementation Pattern:

// Callback/Event handling
func sdkDidRequestDeviceVerification(options: [VerificationOption]) {
    showDeviceVerificationViewController(options: options)
}

// API response
func handleVerificationMethodSelected(_ method: VerificationOption) {
    rdnaClient.performVerifyAuth(method: method) { result in
        DispatchQueue.main.async {
            switch result {
            case .success:
                // Verification process continues
                break
            case .failure(let error):
                self.handleError(error)
            }
        }
    }
}

Flow Pattern:

SDK → addNewDeviceOptions event → Show UITableViewController with options → User selects method → performVerifyAuth API → Verification process

6. Authentication Completion Callback/Event

Challenge Name

N/A (Completion Callback/Event)

Callback/Event Name

onUserLoggedIn

API Name

NA

Purpose

Signal successful authentication completion and user logged in

When Triggered

After all challenges successfully completed

User Input

None (automatic)

iOS Implementation Pattern:

// Callback/Event handling (Delegate)
func sdkDidCompleteLogin(userInfo: UserInfo) {
    DispatchQueue.main.async {
        self.navigateToMainViewController(userInfo: userInfo)
    }
}

// Callback/Event handling (Closure)
rdnaService.onUserLoggedIn = { [weak self] userInfo in
    DispatchQueue.main.async {
        self?.navigateToMainViewController(userInfo: userInfo)
    }
}

Flow Pattern:

Final challenge completed → onUserLoggedIn event → Navigate to main app screen (UINavigationController push/modal present)

Understanding key MFA terminology is essential for implementing and troubleshooting authentication flows in iOS applications.

Authentication Terms

Challenge

A security step that the SDK requires the user to complete. Each challenge represents a specific verification requirement.

Example: The checkuser challenge for which SDK triggers getUser event requires the user to provide their username.

Callback/Event

A callback sent by the SDK to your iOS application when a challenge needs to be addressed. Callback/Events are delivered via delegates, closures, or NotificationCenter.

Example: The getActivationCode event indicates the SDK needs an OTP from the user.

iOS Callback/Event Delivery:

API Response

The SDK method your iOS application calls to provide the required information for a challenge.

Example: Calling rdnaClient.setUser("john.doe") { result in ... } responds to the getUser challenge.

iOS API Patterns:

Flow

A complete sequence of challenges from start to successful authentication.

Example: Activation Flow might include: getUser → getActivationCode → getUserConsentForLDA → onUserLoggedIn

Technical Terms

LDA (Local Device Authentication)

Device-based authentication using iOS biometrics (Face ID, Touch ID) or device passcode.

iOS-Specific Details:

Biometric Availability:

// SDK checks automatically, but you can verify:
import LocalAuthentication

let context = LAContext()
var error: NSError?
if context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) {
    // Face ID or Touch ID available
}

Multi-Factor Authentication (MFA)

Security method requiring users to provide two or more verification factors.

iOS Implementation Benefits:

Flow-Specific Terms

Activation

The initial registration process where a user establishes their identity and enrolls their first iOS device.

iOS Specifics:

Device Registration

The process of adding an iOS device to a user's list of trusted devices.

iOS Device Identifiers:

Device Trust

The security relationship between a user account and a registered iOS device, enabling streamlined future authentication.

Session

The authenticated period between successful login and logout, managed through device tokens stored in iOS Keychain.

iOS Session Management:

Error Handling Terms

Long Error Code

Detailed numeric error code providing specific information about what went wrong.

Short Error Code

Generic error category for broad error classification.

Error String

Human-readable error message describing the issue and potential solutions.

Points to Consider for API Calls

Critical Timing Rules:

  1. Only call APIs in response to events - Never call MFA APIs without receiving the corresponding challenge event
  2. Sequential processing - Wait for current API to complete before handling next challenge
  3. No duplicate calls - Don't retry API calls unless explicitly handling errors
  4. Main thread UI updates - Always dispatch UI changes to main queue after API callbacks

Correct Flow:

// ✅ Correct: Callback/Event-driven with proper threading
func sdkDidRequestUser() {
    DispatchQueue.main.async {
        self.showUsernameInput()
    }
}

func handleUsernameSubmit(_ username: String) {
    rdnaClient.setUser(username) { result in
        DispatchQueue.main.async {
            // Wait for next event from SDK
            self.handleResult(result)
        }
    }
}

Incorrect Flow:

// ❌ Incorrect: API called before event
rdnaClient.setUser("username") { ... }  // No event received yet

// ❌ Incorrect: Duplicate calls
rdnaClient.setUser("username") { ... }
rdnaClient.setUser("username") { ... }  // Called again

// ❌ Incorrect: UI update off main thread
func sdkDidRequestUser() {
    self.showUsernameInput()  // May be on background thread
}

iOS Best Practices:

// Use weak self to prevent retain cycles
rdnaService.onGetUser = { [weak self] in
    guard let self = self else { return }
    DispatchQueue.main.async {
        self.showUsernameViewController()
    }
}

// Handle errors gracefully
rdnaClient.setUser(username) { result in
    DispatchQueue.main.async { [weak self] in
        switch result {
        case .success:
            // Wait for next challenge
            break
        case .failure(let error):
            self?.showError(error)
        }
    }
}

Congratulations! You've mastered the fundamentals of Multi-Factor Authentication (MFA) with the RDNA iOS SDK:

Challenge-Response Architecture: Understanding how SDK events map to API responses in iOS ✅ Flow Types: Activation, Same Device Login, and New Device Login characteristics ✅ Callback/Event Integration: iOS-specific patterns (Delegates, Closures, NotificationCenter) ✅ API Patterns: Proper response patterns with completion handlers and error handling ✅ MFA Terminology: Essential security and iOS-specific technical concepts ✅ Best Practices: Threading, memory management, and implementation guidelines for iOS

Key Concepts Mastered

iOS-Specific Knowledge

You now understand:

MFA Foundation Knowledge

You now understand:

Next Steps

With this foundation, you're ready for:

References