CyberArk Identity SDK for iOS
This topic describes how to integrate CyberArk Identity multi-factor authentication with your iOS mobile app.
Overview
The CyberArk Identity SDK for iOS provides a high-performance framework to help you quickly and easily integrate CyberArk's Multi-factor Authentication (MFA) services within your own mobile app. Using the CyberArk Identity SDK, you can integrate a rich set of authentication factors, such as QR Code authentication and push authentication, to provide users with a seamless and secure end user experience.
Before you begin
Make sure you have the following before you integrate the CyberArk Identity SDK
- Access to CyberArk Identity tenant
- Familiarity with Starting the Authentication Process
and Advancing the Authentication - Software requirements:
- Apple iOS 11.0 and above
- Xcode 12.0 and above
- Swift 5.0 and above
About this guide
This guide provides resources for integrating the CyberArk Identity SDK. The instructions help you leverage CyberArk's MFA platform. It also includes a demo app (iOS SDK demo) that illustrates how to add advanced authentication to web-apps and MFA mechanisms(push notification and QR Code) and a tutorial to get started.
Integrating the CyberArk Identity SDK
Step 1: Create an OAuth 2.0 Client
If you developed a public or confidential app to access CyberArk Identity services on behalf of an end-user, you need to create an OAuth 2.0 Client.
For instructions on creating an OAuth 2.0 Client application in the Admin Portal, refer to
Authorization (Auth) Code Flow with PKCE.
For public apps, such as native apps, the Authorization Code Flow with PKCE is recommended. To do this, select List (Apps > Web Apps > OAuth2 Client > General Usage > List) and add Allowed Clients in the Admin Portal.
Step 2: Download the CyberArk Identity SDK
Download iOS sample app from GitHub and then unzip the file to your location. The sample app comes with the Identity.xcframework integrated.
Step 3: Adding Identity.xcframework into your Xcode project
In order to add the Identity.xcframework to your Xcode project follow the below steps:
- Create an Xcode project
- Right-click on the project explorer, and then select Add Files to your project
- Browse to locate the SDK package(IdentityIntegrationApp > IdentitySDK > Identity.xcframework) which you have downloaded as part of sample app, and then click Add
- Check the option Copy items if needed, and then click Finish
Step 4: Embed Identity.xcframework into the project target
Make sure that the framework is embedded into your app’s binary. To embed Identity.xcframework, refer to the following steps:
- In Xcode, click the project root to navigate to your project settings.
- Make sure that your target is selected, and that the General tab is open.
- Select Embed & Sign for Frameworks.Libraries and Embedded.


Step 5: Create a plist file
To customize your app with CyberArk Identity SDK features, create a custom plist file with the name IdentityConfiguration.plist. The plist file contains configuration information that is essential to receive a callback from CyberArk Identity. Copy the XML snippet below and configure it with your account information:
<plist version="1.0">
<dict>
<key>clientid</key>
<string>{YOUR_CLIENT_ID}</string>
<key>domainoauth</key>
<string>{YOUR_TENANT_URL}</string>
<key>systemurl</key>
<string>{YOUR_SYSTEM_URL}</string>
<key>applicationid</key>
<string>{YOUR_APPLICATION_ID}</string>
<key>redirecturi</key>
<string>{URLSCHEME}://{bundleidentifier}</string>
<key>scope</key>
<string>{YOUR_SCOPE}</string>
<key>threshold</key>
<integer>{YOUR_THRESHOLD}</integer>
<key>responsetype</key>
<string>code</string>
<key>loginurl</key>
<string>{your_loginurl}</string>
<key>widgetid</key>
<string>{your_widgetid}</string>
<key>mfatenanturl</key>
<string>{your_mfatenanturl}</string>
</dict>
</plist>
Parameters | Description |
---|---|
| The client ID of your app. This is provided when you register your app in the Admin Portal. |
| The authorization server where your tenant is hosted. |
| Your tenant URL. This is provided when you register your iOS app in the Admin Portal. |
| A unique key used to build the OAuth2 endpoint URL. |
| The URL that you register when OAuth 2.0 is added in the Admin Portal. In other words, this is the same redirect URI that your app uses when requesting the auth code. |
| The specific scopes that your app requests in the authorization flow. |
| By default, the threshold value is configured to 60. |
| The type of response requested from the authorization server. This must be set to code for authorization code flow. |
| |
| Configured widget in the tenant portal |
|
Step 6: Add the URL Scheme to the project
After creating the plist file, define a callback (redirect URL scheme) in the app, which helps the app to exchange the authorization codes for access tokens. To add the callback URL scheme, refer the following steps:
- Goto Xcode, select the root project >> target >> Info
- Expand the URL Types section and set the Identifier value to $(PRODUCT_BUNDLE_IDENTIFIER) and URL Scheme to unique URL scheme with the desired name.


Authorization work flow using CyberArk Identity SDK features with examples.
Start the authorization flow
First, the authentication client must have a configuration (above section) to interact with the CyberArk Identity Server. The iOS app redirects the user to CyberArk Identity for authentication. To request authorization, the iOS app must redirect the user (from browser) to make a secure HTTP call to the authorization endpoint.
Step 1: Import the CyberArk Identity SDK
import Identity
Step 2: Use the below code snippet to configure the account.
guard let config = plistValues(bundle: Bundle.main) else { return }
//CyberarkAccount
guard let account = CyberArkAuthProvider.webAuth()?
.set(clientId: config.clientId)
.set(domain: config.domain)
.set(redirectUri: config.redirectUri)
.set(applicationID: config.applicationID)
.set(presentingViewController: self)
.setCustomParam(key: "", value: "")
.set(scope: config.scope)
.set(webType: .sfsafari)
.set(systemURL: config.systemurl)
.build() else { return }
CyberArkAuthProvider.login(account: account)


Step 3: Define a callback to receive the access token as a result of step 2 on successful authentication.
//This method should be called either in viewDidLoad() or
//anywhere else before calling the login method in the step 2.
func addObserver(){
CyberArkAuthProvider.didReceiveAccessToken = { (status, message, response) in
if status {
DispatchQueue.main.async {
self.dismiss(animated: true) {
self.save(response: response)
// Navigate to the required screen.
}
}
} else {
// Handle errors here if any.
}
}
}
Step 4: Save the access and refresh tokens to secure storage (for example : Keychain)
Save the access and refresh tokens to a secured storage, one of the best ways is Keychain Sharing. To implement Keychain Sharing, refer the following steps:
- Goto Xcode, select the root project >> target >> Signing & Capabilities
- Click on + Capability and double click on Keychain sharing option to add the capability to the project if it is not added.
- Use the following code snippet to store the tokens to the Keychain.


func save(response: AccessToken?) {
do {
if let accessToken = response?.access_token {
try KeyChainWrapper.standard.save(key: KeyChainStorageKeys.accessToken.rawValue, data: accessToken.toData() ?? Data())
}
if let refreshToken = response?.refresh_token {
try KeyChainWrapper.standard.save(key: KeyChainStorageKeys.refreshToken.rawValue, data: refreshToken.toData() ?? Data())
}
if let expiresIn = response?.expires_in {
let date = Date().epirationDate(with: expiresIn)
try KeyChainWrapper.standard.save(key: KeyChainStorageKeys.access_token_expiresIn.rawValue, data: Data.init(from: date))
}
} catch {
// Handle errors here
}
}
Step 5: Handle the response returned during the Login
It is required to handle the response received from the internal browser(Safari Controller), there should be a call back to handle the response in the app when the user is redirected to app.
func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
for context in URLContexts {
do {
process(with: context.url)
} catch let error {
//Handle error here
}
}
}
func process(with url: URL) {
CyberArkAuthProvider.resume(url: url)
}
Adding MFA
You can add an MFA experience into your mobile app. To do this, you can use one of the following methods:
- Authentication with QR Code
- Authentication with Push notification
Authentication with QR Code
Authenticating with a QR code requires a username and password, and to leverage the QR code ability the mobile device must be enrolled. Use the below code snippet to enroll the device.
let enrollProvider = EnrollmentProvider()
/// To enroll the device
func enrollDevice() {
do {
guard let config = plistValues(bundle: Bundle.main) else { return }
guard let data = try KeyChainWrapper.standard.fetch(key: KeyChainStorageKeys.grantCode.rawValue), let code = data.toString() , let refreshTokenData = try KeyChainWrapper.standard.fetch(key: KeyChainStorageKeys.refreshToken.rawValue),let refreshToken = refreshTokenData.toString() else {
return
}
enrollProvider.enroll(baseURL: config.systemurl)
} catch {
//Handle error case here
}
}
Add an observer to receive the enrolment response
The following observer code snippet allows the app to get the response from the CyberArk Identity enrolment API.
func addEnrollObserver(){
enrollProvider.didReceiveEnrollmentApiResponse = { (result, message) in
if result {
// Handle sucess case here
}else {
// Handle error case
}
}
}


Scan using a QR code
let provider = QRAuthenticationProvider()
// To Scan the QR code
func scanQRCode() {
provider.authenticateWithQRCode(presenter: self, completion: { [weak self] result in
switch result {
case .success(_):
//Handle success case here
case .failure(let error):
//Handle error case here
}
})
}


Get the Refresh token
Use the the following refresh token code snippet to obtain a new access token:
CyberArkAuthProvider.sendRefreshToken()
func addRefreshTokenObserver(){
CyberArkAuthProvider.didReceiveRefreshToken = { (status, message, response) in
if status {
//Handle success case here
self.save(response: response)
} else {
//Handle error case here
}
}
}
Refer to Refresh Token for details.
Use the Keychain wrapper
Use the keychain wrapper to store the access tokens securely. Refer to the code snippet below:
func save(response: AccessToken?) {
do {
try KeyChainWrapper.standard.delete(key: KeyChainStorageKeys.accessToken.rawValue)
try KeyChainWrapper.standard.delete(key: KeyChainStorageKeys.access_token_expiresIn.rawValue)
if let accessToken = response?.access_token {
try KeyChainWrapper.standard.save(key: KeyChainStorageKeys.accessToken.rawValue, data: accessToken.toData() ?? Data())
}
if let expiresIn = response?.expires_in {
let date = Date().epirationDate(with: expiresIn)
try KeyChainWrapper.standard.save(key: KeyChainStorageKeys.access_token_expiresIn.rawValue, data: Data.init(from: date))
}
} catch {
//Handle error case here
}
}
Use push notifications for authentication
CyberArk Identity MFA delivers push notifications to a user’s pre-registered device using APNS (Apple Push Notification Service). Using push notifications, users can immediately allow or deny access to web applications.
To configure your app to receive push notifications for all your registered devices, refer to the following instructions:
Step 1: Enable remote notifications
- Click the project to navigate to your project settings.
- Select the Capability tab and find Background Modes.
- Double-click to add Background Modes to Signing & Capabilities.
- From Background Modes, select the Remote Notifications option.


Step 2: Configure Apple Push Notification services (APNs)
private let categoryIdentifier = "MfaNotificationCategoryId"
private let kMfaNotificationApproveActionID = "MfaNotificationApproveActionId"
private let kMfaNotificationDenyActionID = "MfaNotificationDenyActionId"
let application = UIApplication.shared
UNUserNotificationCenter.current().requestAuthorization(options: [
.badge, .sound, .alert
]) { granted, _ in
guard granted else { return }
self.registerCustomActions()
}
application.registerForRemoteNotifications()
private func registerCustomActions() {
let accept = UNNotificationAction(
identifier: kMfaNotificationApproveActionID,
title: "Approve",options: UNNotificationActionOptions(rawValue: 0))
let reject = UNNotificationAction(
identifier: kMfaNotificationDenyActionID,
title: "Deny",options: UNNotificationActionOptions(rawValue: 0))
let category = UNNotificationCategory(
identifier: categoryIdentifier,
actions: [accept, reject],
intentIdentifiers: [],options: .customDismissAction)
UNUserNotificationCenter.current()
.setNotificationCategories([category])
}
Step 3: Send the device token to your backend server
func application(
_ application: UIApplication,
didRegisterForRemoteNotificationsWithDeviceToken
deviceToken: Data) {
guard let config = plistValues(bundle: Bundle.main, plistFileName: "IdentityConfiguration") else { return }
CyberArkAuthProvider.handlePushToken(token: deviceToken, baseURL: config.domain)
}
Step 4: Implement support for handling incoming remote notifications
func userNotificationCenter(_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler:
@escaping () -> Void) {
notificationcompletionHandler = completionHandler
let identity = response.notification
.request.content.categoryIdentifier
let userInfo = response.notification.request.content.userInfo
guard identity == categoryIdentifier, let action = NotificationActionIdentifier(rawValue: response.actionIdentifier) else {
Notification.Name.handleNotification.post(userInfo: userInfo)
completionHandler()
return
}
switch action {
case .accept:
self.performChallengeby(isAccepted: true, userInfo: userInfo, withCompletionHandler: nil)
case .reject:
self.performChallengeby(isAccepted: false, userInfo: userInfo, withCompletionHandler: nil)
}
}
}
Step 5: Handle the push notification MFA challenge
Below is the snippet that Invokes the API based on the approve or deny action.
extension AppDelegate {
func performChallengeby(isAccepted: Bool, userInfo: [AnyHashable : Any]? = nil, withCompletionHandler completionHandler:
CheckNotificationResult? ){
let userInfo = userInfo?["payload"] as! [AnyHashable: Any]
let info = userInfo["Options"] as! [AnyHashable: Any]
let challenge = info["ChallengeAnswer"]
handleChallange(isAccepted: isAccepted, challenge: challenge as! String, withCompletionHandler: completionHandler)
}
func handleChallange(isAccepted: Bool, challenge: String, withCompletionHandler completionHandler:
CheckNotificationResult?) {
guard let config = plistValues(bundle: Bundle.main, plistFileName: "IdentityConfiguration")
else { return }
mfaProvider.handleMFAChallenge(isAccepted: isAccepted, challenge: challenge, baseURL: config.domain, withCompletionHandler: completionHandler)
}
func addMFAObserver(){
mfaProvider.didReceiveMFAApiResponse = { (result, message) in
if let handler = self.notificationcompletionHandler {
handler()
}
}
}
}
Configure APNS Certificates
To leverage the push notification functionality, you need to set the APNS certificate in the tenant level. The following APIs need to be invoked to set the APNS and to delete the APNS certificates.
You can also follow the tutorial to generate the APNS certificate.
Tenant Configuration:
- ClientAppKey: Mobile.IosCustomApp.ClientAppKey
- APNSCert : Base64 string representation of the APNS certificate of p12 format
- APNSPass: Password associated with the APNS Certificate
- APNSTopic: The topic or client bundleid related to client application with which the push notification needs to be sent.
Set the APNS certificate
API: {tenantUrl}/Mobile/SetApnsCertForClientApp
This API sets the APNS Developer certificate so you can send push notifications to a mobile device. The storage of the APNSCert and other details happen at a Tenant level. The APNSCert details related to ClientKey are used to send the push notification. Earlier ClientAppKeys are ignored when sending the push notification.
You need to have APNSCertificate management rights to use this API.
API | Description | Tenant Configuration |
---|---|---|
ClientAppKey | The client application key is a unique identifier for the client application that needs to be registered for APNS notifications. By default, this is the latest certificate. | Mobile.IosCustomApp.ClientAppKey |
APNSCert | Base64 string representation of the APNS certificate of p12 format. | {ClientAppKeyValue}_APNSCert |
APNSPass | The password associated with the APNS certificate. | {ClientAppKeyValue}_APNSPass |
APNSTopic | The topic or client packageId related to client application with which the push notification needs to be sent. | {ClientAppKeyValue}_APNSTopic |
{
"ClientAppKey":"bundleidentifier",
"APNSCert":"hlsdfjflsdkjf",
"APNSPass":"password”
"APNSTopic":"bundleidentifier"
}
Delete the APNS certificate
API: {tenantUrl}/Mobile/DeleteApnsCertForClientApp
This API deletes the APNS Developer certificate. All of the configuration set as part of the SetApnCertForClientApp API is deleted as a result
You need to have APNSCertificate management rights (Device Management Rights) to use this API.
ClientAppKey
Client key is a unique identifier for the client application and needs to be deleted in order to remove APNS notifications.
Strong biometric authentication
You can also leverage the CyberArk Identity SDK to implement MFA using biometric identification in the login process. Configuring the CyberArk Identity SDK provides an app with the ability to:
- Invoke a strong biometric authentication dialog for users at app startup.
- Handle strong biometric challenges for users, storing and retrieving the tokens as needed.
Invoke Biometric utility instance
Use the following code snippet to invoke a biometric dialog when the app starts up:
BiometricsAuthenticator().authenticateUser{ (response) in{ (response) in
switch response {
case .success(let success):
//Handle success case here
case .failure(let error):
//Handle error case here
}
}
Close the session from the browser
Use the closeSession()
method to close the session as shown in the code snippet below:
func closeSession() {
guard let config = plistValues(bundle: Bundle.main) else { return }
guard let account = CyberArkAuthProvider.webAuth()?
.set(clientId: config.clientId)
.set(domain: config.domain)
.set(redirectUri: config.redirectUri)
.set(applicationID: config.applicationID)
.set(presentingViewController: self)
.setCustomParam(key: "", value: "")
.set(webType: .sfsafari)
.build() else { return }
CyberArkAuthProvider.closeSession(account: account)
}
Declare a logout observer to receive the logout session result, and then clear the keychain data using KeyChainWrapper
. Use the following code snippet for the logout observer:
func addLogoutObserver(){
CyberArkAuthProvider.didReceiveLogoutResponse = { (result, message) in
if result {
DispatchQueue.main.async {
self.dismiss(animated: true) {
try KeyChainWrapper.standard.delete(key: KeyChainStorageKeys.accessToken.rawValue)
try KeyChainWrapper.standard.delete(key: KeyChainStorageKeys.grantCode.rawValue)
try KeyChainWrapper.standard.delete(key: KeyChainStorageKeys.refreshToken.rawValue)
try KeyChainWrapper.standard.delete(key: KeyChainStorageKeys.access_token_expiresIn.rawValue)
}
}
}
}
}
Updated 2 months ago