PingAuthMigration

May 7, 2026 · View on GitHub

Swift Version iOS Version License

Ping Identity

PingAuthMigration

PingAuthMigration migrates OATH (TOTP/HOTP) and Push credentials from the legacy FRAuthenticator SDK Keychain storage to the modern PingOath / PingPush storage format.

Getting Started

Prerequisites

Installation

To integrate the module into your iOS project, add the following dependency to your Package.swift or Podfile file.

Swift Package Manager

dependencies: [
    .package(url: "https://github.com/ForgeRock/ping-ios-sdk.git", from: "<version>")
]

Then add the PingAuthMigration product to your target's dependencies.

CocoaPods

pod 'PingAuthMigration', '~> <version>'

Import the Module

import PingAuthMigration

Usage

Quick Start — Fire and Forget

Call this once at app startup (e.g., in AppDelegate.application(_:didFinishLaunchingWithOptions:) or a SwiftUI App.init):

import PingAuthMigration

Task {
    let didMigrate = await AuthMigration.migrateIfNeeded()
    if didMigrate {
        print("FRAuthenticator credentials migrated successfully")
    }
}

migrateIfNeeded() returns true if migration ran, false if no legacy data was found.

With Progress Tracking

import PingAuthMigration
import PingCommons

Task {
    let stream = AuthMigration.start { config in
        config.logger = LogManager.logger          // Optional logger
        config.cleanupLegacyData = true            // Delete legacy data after migration (default)
    }

    for await progress in stream {
        switch progress {
        case .started:
            showLoadingIndicator()

        case .inProgress(let step, let current, let total):
            updateProgressBar(Float(current) / Float(total))
            updateStatus("Step \(current)/\(total): \(step)")

        case .stepCompleted(let step):
            print("Completed: \(step)")

        case .success(let message):
            hideLoadingIndicator()
            print(message)  // e.g. "Migrated 3 OATH + 1 Push credentials"

        case .error(let step, let error):
            hideLoadingIndicator()
            handleMigrationError(step: step, error: error)
        }
    }
}

Check Before Migrating

let needed = await AuthMigration.isMigrationNeeded()
if needed {
    // Show a migration progress screen before continuing
}

Configuration Options

PropertyTypeDefaultDescription
accessGroupString?nilKeychain access group used by the legacy SDK
loggerLogger?nilMigration diagnostic logger
cleanupLegacyDataBooltrueDelete legacy Keychain entries after migration
oathStorage(any OathStorage)?nilCustom OATH storage (defaults to OathKeychainStorage)
pushStorage(any PushStorage)?nilCustom Push storage (defaults to PushKeychainStorage)

Migration Scope

Legacy TypeNew TypeNotes
TOTPMechanismOathCredential (.totp)Period, digits, algorithm preserved
HOTPMechanismOathCredential (.hotp)Counter preserved
PushMechanismPushCredentialAuth endpoint query params stripped
Notification entriesSkippedNot migrated
Device token entriesSkippedNot migrated

Error Handling

case .error(let step, let error):
    if let migrationError = error as? AuthMigrationError {
        switch migrationError {
        case .noLegacyDataFound:
            // Normal on clean installs or after first migration
            break
        case .failedToDecryptLegacyData(let inner):
            logger.e("Decryption failed: \(inner)")
        case .failedToSaveOathCredentials(let inner):
            logger.e("OATH save failed: \(inner)")
        default:
            logger.e("Migration error: \(migrationError.localizedDescription)")
        }
    }

Testing

xcodebuild test \
  -scheme PingTestHost \
  -workspace SampleApps/Ping.xcworkspace \
  -destination 'platform=iOS Simulator,name=iPhone 16,OS=18.3.1' \
  -only-testing:AuthMigrationTests

License

This software may be modified and distributed under the terms of the MIT license. See the LICENSE file for details.

© Copyright 2025-2026 Ping Identity Corporation. All rights reserved.