---
title: Configure an iOS app for OATH MFA
description: PingOne Advanced Identity Cloud PingAM iOS
component: orchsdks
page_id: orchsdks:journey:use-cases/oath/ios/index
canonical_url: https://developer.pingidentity.com/orchsdks/journey/use-cases/oath/ios/index.html
revdate: Tue, 25 Mar 2025 11:00:37 +0100
keywords: ["DaVinci", "Flows", "Tutorial", "Source Code", "Integration", "SDK", "iOS"]
section_ids:
  step_1_adding_core_dependencies: Step 1. Adding core dependencies
  swift_package_manager: Swift Package Manager
  cocoapods: CocoaPods
  step_2_initializing_the_oath_client: Step 2. Initializing the OATH Client
  step_3_managing_oath_credentials: Step 3. Managing OATH credentials
  creating_oath_credentials: Creating OATH credentials
  getting_oath_credentials: Getting OATH credentials
  updating_oath_credentials: Updating OATH credentials
  deleting_oath_credentials: Deleting OATH credentials
  step_4_generating_oath_based_one_time_passcodes: Step 4. Generating OATH-based one-time passcodes
  generating_hotp_codes: Generating HOTP codes
  generating_totp_codes: Generating TOTP codes
  step_5_closing_the_oath_client: Step 5. Closing the OATH client
  handling_errors: Handling errors
  storage: Customizing credential storage
  customizing_the_default_keychain_based_storage: Customizing the default keychain-based storage
  implementing_your_own_storage_mechanism: Implementing your own storage mechanism
---

# Configure an iOS app for OATH MFA

[icon: circle-check, set=far]PingOne Advanced Identity Cloud [icon: circle-check, set=far]PingAM [icon: apple, set=fab]iOS

This page guides you through configuring your iOS application to support OATH-based Multi-Factor Authentication (MFA) using the **OATH** module.

It covers dependency setup, OATH client initialization, credential management, passcode generation, and custom storage options.

## Step 1. Adding core dependencies

You can use Swift Package Manager (SPM) or CocoaPods to add dependencies to your iOS project.

### Swift Package Manager

1. With your project open in **Xcode**, select File > Add Package Dependencies.

2. In the search bar, enter the Orchestration SDK for iOS repository URL: `https://github.com/ForgeRock/ping-ios-sdk`.

3. Select the `ping-ios-sdk` package, and then click Add Package.

4. In the Choose Package Products dialog, ensure that the `PingOath` library is added to your target project.

5. Click Add Package.

6. In your project, import the relevant dependencies:

   ```swift
   import PingOath
   ```

### CocoaPods

1. If you do not already have CocoaPods, install the [latest version](https://guides.cocoapods.org/using/getting-started.html).

2. If you do not already have a Podfile, in a terminal window, run the following command to create a new [Podfile](https://guides.cocoapods.org/syntax/podfile.html):

   ```
   pod init
   ```

3. Add the relevant dependencies to your Podfile:

   ```
   pod 'PingOath'
   ```

4. Run the following command to install pods:

   ```
   pod install
   ```

## Step 2. Initializing the OATH Client

To use the **OATH** module you must initialize the OATH client in your application by calling the `createClient()` method:

Initializing the OATH client with default config

```swift
// Create an OATH client
let client = try await OathClient.createClient { config in
    config.logger = LogManager.logger
    config.enableCredentialCache = false
}
```

The properties you can use to customize OATH client configuration are as follows:

* *enableCredentialCache*

  Whether to enable in-memory caching of credentials.

  By default, this is set to `false` for security reasons, as an attacker could potentially access cached credentials from memory dumps.

* *timeout*

  The timeout for network operations, in seconds.

  Default value is `15`.

* *storage*

  The storage implementation to use for OATH credentials.

  If `nil`, the default `OathKeychainStorage` is used.

  Learn more in [Customizing credential storage](#storage).

* *policyEvaluator*

  The policy evaluator to use for credential policy validation.

  If `nil`, the default `MfaPolicyEvaluator` is used.

* *encryptionEnabled*

  Whether data encryption is enabled for storing credentials.

  Default is `true`.

* *logger*

  The logger instance used for logging messages.

  Defaults to a global logger instance.

  Learn more in [Logging](../../../customization/logging/index.html).

## Step 3. Managing OATH credentials

The **OATH** module relies on a set of credentials, that you can create, retrieve, update, and delete.

The credentials contain details such as the service and user they relate to, and details about how to generate the HOTP or TOTP key.

### Creating OATH credentials

The OATH module lets the user register their device for OATH-based multi-factor authentication (MFA).

The information required to register a device is contained in a specially-encoded URI, which your client application decodes to create the credentials.

This URI is often delivered by QR codes that the client can scan, or directly in the callback output by the [OATH Registration node](https://docs.pingidentity.com/auth-node-ref/latest/oath-registration.html).

Use the `addCredentialFromUri()` method to create OATH credentials and register an MFA device:

Creating OATH credentials

```swift
let uri = "otpauth://hotp/Example:user@example.com?secret=JBSWY3DPEHPK3PXP&counter=0"

let credential = try await client.addCredentialFromUri(uri)
```

### Getting OATH credentials

You can get a list of all the registered OATH credentials, or get an individual credential, by passing the credential ID as a parameter.

* All OATH credentials

* Specific OATH credential

Getting all OATH credentials

```swift
do {
    let credentials = try await Task.detached(priority: .userInitiated) {
        try await client.getCredentials()
    }.value
} catch {
    throw AppError.oathError("Failed to load credentials: \(error.localizedDescription)")
}
```

Getting a specific OATH credential

```swift
let credential = try await Task.detached(priority: .userInitiated) {
    try await client.getCredential(credentialId)
}.value
```

### Updating OATH credentials

You can update the properties of a stored credential with new values, by using the `saveCredential()` method. Pass the updated credential object into the method as a parameter:

Updating an OATH credential

```swift
for credential in credentials {
    var updated = credential
    updated.displayIssuer = "Example.com Checking Account"
    updated.displayAccountName = "Babs Jensen"
    _ = try await client.saveCredential(updated)
}
```

### Deleting OATH credentials

Use the `deleteCredential()` method to remove individual credentials from the client device. Pass the credential ID into the method as a parameter:

Deleting an OATH credential

```swift
do {
    let removed = try await Task.detached(priority: .userInitiated) {
        try await client.deleteCredential(credentialId)
    }.value
    return removed
} catch {
    throw AppError.oathError("Failed to remove credential: \(error.localizedDescription)")
}
```

## Step 4. Generating OATH-based one-time passcodes

To perform OATH-based multi-factor authentication the user needs to enter the correct one-time passcode.

Your client app needs to generate these one-time passcodes and display them to the user.

### Generating HOTP codes

Use the `generateCode` method to create an HOTP code using the details within the specified credential:

Generating an HOTP for an OATH credential

```swift
// Generate code (counter increments automatically)
let code1 = try await client.generateCode(hotpCredential.id)  // Counter = 1
let code2 = try await client.generateCode(hotpCredential.id)  // Counter = 2
```

### Generating TOTP codes

For TOTP codes, the `code` object you generate contains timing and progress information that you can use to customize the user interface:

Generating a TOTP with timing and progress information

```swift
// Get code with timing and validity information
let codeInfo = try await client.generateCodeWithValidity(credential.id)

print("Code: \(codeInfo.code)")
print("Time remaining: \(codeInfo.timeRemaining) seconds")
print("Progress: \(codeInfo.progress * 100)%")
```

## Step 5. Closing the OATH client

You can close the client, clean up any temporary files, and regain the memory used by calling the `close()` method:

Closing an OATH client

```swift
// Close the OATH client and clean up
try await client.close()
```

## Handling errors

The **OATH** module provides comprehensive error handling:

Handling errors

```swift
do {
    let credential = try await client.addCredentialFromUri(uri)
} catch let error as OathError {
    switch error {
    case .invalidUri(let message):
        print("Invalid URI: \(message)")
    case .credentialNotFound(let id):
        print("Credential not found: \(id)")
    case .credentialLocked(let id):
        print("Credential is locked: \(id)")
    case .codeGenerationFailed(let message, let underlying):
        print("Code generation failed: \(message)")
    case .initializationFailed(let message, let underlying):
        print("Client initialization failed: \(message)")
    }
} catch let error as OathStorageError {
    switch error {
    case .storageFailure(let message, let underlying):
        print("Storage error: \(message)")
    }
}
```

## Customizing credential storage

The **OATH** module needs to store the credentials it uses on the client device.

By default, it uses an iOS keychain services-based implementation, which you can customize.

You can also provide your own storage mechanism, by implementing the `OathStorage` interface.

### Customizing the default keychain-based storage

The **OATH** module uses the `OathKeychainStorage` implementation for storing OATH credentials by default.

You can customize this keychain-based default as follows:

Customizing the `OathKeychainStorage` implementation

```swift
// Create a custom storage instance with specific parameters
let customStorage = OathKeychainStorage(
    service: "com.myapp.oath",
    accessGroup: "group.myapp",
    accessibility: kSecAttrAccessibleWhenUnlockedThisDeviceOnly
)

// Create the client with the custom storage
let client = try await OathClient.createClient { config in
    config.storage = customStorage
    config.enableCredentialCache = false
}
```

The properties you can customize are as follows:

* `service`

  The keychain service identifier.

  Defaults to "com.pingidentity.oath".

* `accessGroup`

  Optional keychain access group for shared access.

* `accessibility`

  Keychain accessibility level.

  Defaults to `kSecAttrAccessibleWhenUnlockedThisDeviceOnly`.

### Implementing your own storage mechanism

You can implement a custom storage solution as alternative to the default `OathKeychainStorage` by implementing the `OathStorage` interface:

Implementing custom OATH credential storage

```swift
class MyCustomStorage: OathStorage {

  func storeOathCredential(_ credential: OathCredential) async throws {
    // Store an OATH credential.
  }

  func retrieveOathCredential(credentialId: String) async throws -> OathCredential? {
    // Retrieve an OATH credential by its ID.
  }

  func getAllOathCredentials() async throws -> [OathCredential] {
    // Get all OATH credentials.
  }

  func removeOathCredential(credentialId: String) async throws -> Bool {
    // Remove an OATH credential by its ID.
  }

  func clearOathCredentials() async throws {
    // Clear all OATH credentials from the storage.
  }
}
```
