Integrate authentication widget for strong MFA

This topic describes how to integrate the authentication widget along with OIDC Authorization PKCE grant into the mobile app using the iOS SDK

Provide an authentication and authorization flow with strong MFA by embedding the authentication widget into the SDK's Android mobile app.

Prerequisites

Step 1: Setup the OpenID connect custom app

Step 2: Create authentication rule

Create an authentication rule that challenges the user to authenticate with MFA when the mobile app requests it. Refer to the following link for steps and instructions for creating an authentication rule in the Admin Portal.
https://docs.cyberark.com/Product-Doc/OnlineHelp/Idaptive/Latest/en/Content/CoreServices/Authenticate/MFA-AdminPortal.htm

To find your authentication Policies in the Admin Portal, navigate Core Services > Policies > Authentication Policy.

Step 3: Configure authentication Widget:

📘

Please refer to https://docs.cyberark.com/Product-Doc/OnlineHelp/idaptive/Latest/en/Content/Widgets/Create-Authentication-Widget.htm to get information on how to use widgets

To find your authentication widgets in the Admin Portal, navigate to Web apps > Widgets. Refer to the following figures for an example.

19201920

Link the OIDC app with the authentication widget

19061906

How it works?

12251225

Integrate authentication widget with mobile app

The authentication client must have a configuration to interact with the CyberArk Identity Server. The iOS app redirects the user to Authentication Widget for authentication. The iOS app must redirect the user (browser) to make a secure HTTP call to the authorization endpoint with the following parameters to request authorization.

Step 1: Add the URL Scheme to the project

Define a callback (redirect and resource URL scheme) in the app, which helps the app to exchange the authorization codes for access tokens. To add the callback URL scheme, refer to the following steps:

  1. Goto Xcode, select the root project >> target >> Info
  2. Expand the URL Types section and set the Identifier value to $(PRODUCT_BUNDLE_IDENTIFIER) and URL Scheme to a unique URL scheme with the desired name.
13791379

Step 2: Create a builder object as shown below:

guard let config = plistValues(bundle: Bundle.main, plistFileName: "IdentityConfiguration") 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)
                    .set(authWidgetHostURL: config.authwidgethosturl)
                    .set(authWidgetID: config.authwidgetId)
                    .set(authWidgetResourceURL: config.authwidgetresourceURL)
                    .build() else { return }

CyberArkAuthProvider.launchAuthWidget(account: account)

Step 3: Define a callback to receive the widget response

func addAuthWidgetResourceURLObserver(){
        CyberArkAuthProvider.didReceiveResurceURLCallback = { (status, message) in
            if status {
                DispatchQueue.main.async {
                    self.dismiss(animated: true) {
                        self.doLogin()
                    }
                }
            } else {
                DispatchQueue.main.async {
                    self.dismiss(animated: true) {
                        self.showAlert(message: message)
                    }
                }
            }
        }

Step 4: Launch the authentication widget URL in a Safari view controller

The Identity iOS SDK is enabled with the ability to open the CyberArk Identity sign-in web page to perform the user authentication in a browser window.

After authentication, the app redirects the user back to the app to exchange the received authorization code for an access token and/or refresh token.

10381038

Step 5: Use device storage for access, refresh, and ID tokens

Save the access token, refresh token, and ID token in device storage (SharedPreference) using Keystore encryption.
The Keystore encryption model is as follows:

KeyChainWrapper.standard.save(key: KeyChainStorageKeys.accessToken.rawValue, data: accessToken.toData() ?? Data())
            }
KeyChainWrapper.standard.save(key: KeyChainStorageKeys.refreshToken.rawValue, data: refreshToken.toData() ?? Data())
            }
let date = Date().epirationDate(with: expiresIn)
KeyChainWrapper.standard.save(key: KeyChainStorageKeys.access_token_expiresIn.rawValue, data: Data.init(from: date))

Step 6: To get Refresh the token

Use the refresh token to obtain a new access token.

val refreshTokenResponseHandler: LiveData<ResponseHandler<RefreshTokenModel>> =
    CyberArkAuthProvider.refreshToken(cyberArkAccountBuilder).start(this, refreshTokenData)

Refer to Refresh Token for details.

Step 7: Obtain User Info from the ID token

An ID token is an artifact that confirms that the user has been authenticated. An ID Token is a JWT Token, which is a cryptographically signed Base64-encoded JSON object. Here is an example of the decoded ID token

ID Token Payload:
{
"auth_time": <authentication_time>,
"is": "<issuer_identifier>",
"given_name": "<user_name>",
"iat": <ID_token_issued_time>,
"aud": "<audience_this_ID_token_is_intended_for>",
"name": "<user_name>",
"email": "<user_email_address>",
"family_name": "<user_name>",
"preferred_username": "<user_name>",
"unique_name": "<user_name>",
"exp": <ID_token_expiry_time>,
"sub": "<An_identifier_for_user>",
"email_verified":
}

Step 8: Retrieve User Info using the Access token

Use the CyberArkAuthProvider class to retrieve user info flow. The retrieve user info flow pattern is as follows:

guard let config = plistValues(bundle: Bundle.main, plistFileName: "IdentityConfiguration") 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.fetchUserInfo()

Step 9: Define a callback to receive the user info response

func addObserver(){
        CyberArkAuthProvider.didReceiveUserInfo = { (status, message, response) in
            DispatchQueue.main.async {
                self.activityIndicator.stopAnimating()
                if status {
                    self.setup(info: response)
                } else {
                    self.showAlert(message: message)
                }
            }
            
        }
    }