iOS
Guides

iOS Guides

Mobile SDK v1.6.10 for CA Mobile API Gateway v4.1.00
< SEE ALL DOCS

What’s New for Mobile SDK

For new features and bug fixes, see Release Notes.

Mobile SDK Libraries

The following Mobile SDK libraries provide simple and secure access to the services of the CA Mobile API Gateway (MAG).

Mobile SDK Services

MASFoundation

  • Authenticates users
  • Handles user, app, and device registration, requesting and locally storing certificates, keys, and token credentials for accessing the protected APIs.

MASUI

  • Provides resources to implement a user login dialog, Social Login, One-Time Password, and Proximity Login (QR code and BLE)

MASConnecta

  • Enables bi-directional and real-time interactions between users.
  • Provides communication with iOT devices using the MQTT protocol.

MASIdentityManagement

  • Searches for users and/or groups in identity providers.
  • Manages users and adhoc groups.
  • Communicates with different identity providers using the SCIM protocol.

MASStorage

  • Saves and retrieves data locally from a device or in a private cloud.
  • Provides encryption for private cloud storage.
  • Option to enable encryption for local storage.
  • Stores data based on various permissions for users and apps.

More Info

Support and Prerequisites

Supported Versions

  • Supported Platform: iOS 9.0 - 11.0
  • OS: MAC OS X 10.10+ (not a PC)
  • IDE: Xcode 7.2+ (free from iTunes app store)

Note: Our Mobile SDK is tested only on devices using an iOS official version. If devices are jailbroken or the OS is unlocked (rooting the device), the SDK may not work as expected.

Prerequisites

If you have purchased the product, get started!

If you have an Administrator… Easy. Just tell your Admin which Mobile SDK libraries that you need for your app. Then, ask your Admin for the app configuration file (called msso_config.json), which handle communication between the Mobile SDK and MAGs. (You’ll add it to your project in later steps.)

If you ARE the Administrator…

Complete these tasks:

Sample msso_config.json file

The is an example of a valid msso_config.json file that was exported from a MAG.

{
    "server":
    {
        "hostname": "gatewayhostname.example.com",
        "port": 8443,
        "prefix": "",
        "server_certs":
        [
            [
                "-----BEGIN CERTIFICATE-----","MIIDADCCAeigAwIBAgIIW3j/9QFwgk8wDQYJKoZIhvcNAQEMBQAwHjEcMBoGA1UEAxMTbWF0LWRl","=","-----END CERTIFICATE-----"
            ]
        ]
    },
    
    "mag":
    {
        "system_endpoints":
        {
            "device_register_endpoint_path": "/connect/device/register",
            "device_remove_endpoint_path": "/connect/device/remove",
            "client_credential_init_endpoint_path": "/connect/client/initialize"
        },
        
        "oauth_protected_endpoints":
        {
            "enterprise_browser_endpoint_path": "/connect/enterprise/browser",
            "device_list_endpoint_path": "/connect/device/list"
        },
        
        "mobile_sdk":
        {
            "sso_enabled": true,
            "location_enabled": true,
            "location_provider": "network",
            "msisdn_enabled": true,
            "enable_public_key_pinning": false,
            "trusted_public_pki":false,
            "trusted_cert_pinned_public_key_hashes" :[],
            "client_cert_rsa_keybits": 1024
        },
        
        "ble":
        {
            "msso_ble_service_uuid":"980c2f36-bde3-11e4-8dfc-aa07a5b089db",
            "msso_ble_characteristic_uuid":"980c34cc-bde3-11r6-8dfc-aa07a5b093db",
            "msso_ble_rssi": -35
        }
    },
    
    "oauth":
    {
        "client":
        {
            "organization": "Example Organization Inc.",
            "description": "Example App",
            "client_name": "ExampleApp",
            "client_type": "confidential",
            "registered_by": "ExampleUser",
            "client_ids":
            [
                {
                    "client_id": "819455f6-9a7e-4ee0-b159-f85b25758112",
                    "client_secret":"",
                    "scope": "openid msso phone profile address email",
                    "redirect_uri": "https://ios.ssosdk.ca.com/ios",
                    "environment": "iOS",
                    "status": "ENABLED",
                    "registered_by": "ExampleUser"
                }
            ]
        },
        
        "system_endpoints":
        {
            "authorization_endpoint_path": "/auth/oauth/v2/authorize",
            "token_endpoint_path": "/auth/oauth/v2/token",
            "token_revocation_endpoint_path": "/auth/oauth/v2/token/revoke",
            "usersession_logout_endpoint_path": "/connect/session/logout",
            "usersession_status_endpoint_path": "/connect/session/status"
        },
        
        "oauth_protected_endpoints":
        {
            "userinfo_endpoint_path": "/openid/connect/v1/userinfo"
        }
    },

    "custom":
    {
        "oauth_demo_protected_api_endpoint_path":"/oauth/v2/protectedapi/foo",
        "mag_demo_products_endpoint_path":"/protected/resource/products"
    }
}

Create an App: Choose a Method

There are several ways to create your first app.

Create your app using… Benefits
Using Mobile SDK App Templates
  • CocoaPods-ready (just run pod install for the latest libraries)
  • Includes predefined settings, basic methods, and UI components
  • Ideal for exploring the methods, or building a real app
No templates, from scratch
  • Create an app from scratch for maximum control
  • Use this method for integrating existing apps
  • Download using binaries or CocoaPods
Mobile Developer Console Installation (GUI) *requires Admin set up
  • Wizard to guide you through creating your first app
  • One-button download for app configuration file updates
  • Quick install and easy configuration to grant secure developer access

Create an App Using Mobile SDK Templates

This is the easiest way to create your first app (for real, or just to explore). If you want to connect to a live MAG using the templates, you’ll need a valid app configuration file. The templates include:

  • Predefined Xcode settings for the Mobile SDK
  • A CocoaPods podfile with the latest SDK libraries
  • App templates
    • MAS Basic
      Includes a basic startup method, but no authentication methods or default login screen. Perfect for creating an app from scratch.
    • MAS Starter
      Same functionality as basic, but adds authentication and login methods, a branded splash screen, a default login screen, and other building blocks. Perfect if you want more preconfiguration.

To get started, all you need is: Install CocoaPods

Download and Install the Mobile SDK App Templates

  1. Quit (not close) the Xcode application.
  2. Download the Mobile SDK App Templates and open the file.
  3. Click the Install icon in the distribution window.
    If you see the message, “Install can’t be opened because it is from an unidentified developer”, click OK. Then go to your Mac System Preferences, Security & Privacy, and click Open Anyway.
  4. Follow the wizard, accept the defaults, and confirm the installation is successful.
    When closing the installer, select ‘Keep’ if you want to save the installer for later use.

Create and Run Your App

Step 1: Select a template and create a new project

  1. Open Xcode (or quit and reopen if you didn’t do this in the step, Download and Install the Mobile SDK App Templates).
  2. On the Xcode welcome page, select Create a new Xcode project.
  3. On the “Choose a template” page, scroll down and select the CA MAS Basic or CA MAS Starter template, and click Next.
  4. On the new project screen, complete the following fields. For example:
    • Product Name: BasicApp
    • Organization: CA Technologies Inc
    • Organization Identifier: com.ca
    • Language: Objective-C
    • Devices: Universal
  5. Click Next.
  6. On the folder location screen, select a folder for saving your project and click Create.

Step 2: Add the Mobile SDK libraries and build the project

  • If you are building a real app, we recommend importing all of the libraries.
  • The minimum library to create an app is MASFoundation (login: authentication and authorization).
  • If you are building a Messaging or Pub/Sub app using MASConnecta, you must also use the MASIdentityManagement libraries to get multiple users.
  1. Open a Terminal and navigate to your project folder.
  2. Enter pod install
    This installs the MASFoundation library into your project (under Pods).
  3. Per the pod install note, close your existing Xcode project before the next step.
  4. In the same terminal window, open the workspace.
    For example, open BasicApp.xcworkspace/
  5. Build your project: Product, Build.
    You’ll see the confirmation, Build Succeeded.
  6. Run your project: Product, Run.
    Your basic application is now up and running! The “MAS.start Error” is informational to alert you that if you want to connect to the MAG, you need a valid msso_config.json file from your Admin.

BasicApp

  1. Click OK (MAS Basic), or Done (MAS Starter).
  2. If you are using the MASStorage library, you must link the libsqlite3.tbd library in the project (General tab, Linked Frameworks and Libraries).

Run Your App on a Live MAG

To register your app with a live MAG:

  1. Verify that your Admin has configured the MAG Prerequisites.
  2. In Xcode, drag and drop the msso_config.json file anywhere in the app.
    On the confirmation dialog, verify that Destination: ‘Copy Items if Needed’ is checked.
  3. Compile and build your app (Start button on the top left of the toolbar). If successful, you will see the following message:

MASstartSucceed

  1. In the left panel of Xcode, expand BasicApp, Classes.
    You’ll the see class: MASMainViewController.h. This is a UIViewController lifecycle method that is called automatically during application start up. When the Mobile SDK starts, it uses values in the msso_config.json to initialize itself and communicate with the MAG. The first time this class is run, it verifies the application information and registers the device with the MAG.

Your app is now connected to the MAG server!

You can now:

Create App from Scratch or Integrate an Existing App into the Mobile SDK

If you have an existing app that you want to integrate into the Mobile SDK, or simply want to have full control over app set up, these steps are for you.

Step 1: Download the Mobile SDK and add to Xcode

You can add the libraries by downloading the binaries, or using CocoaPods. Note the following:

  • MASFoundation is the only required library
  • For a complete mobile solution, install all of the SDK libraries
  • If you are implementing Messaging or Pub/Sub, the MASIdentityManagement library is required to get multiple users

Binaries

  1. Download binaries.
  2. Unzip the file.
  3. In Xcode, click the General tab, and scroll down to Embedded Binaries.
  4. Click the plus sign (+), and on the dialog, click Add Other.
  5. Navigate to your unzipped file and select the libraries you want to add, and click Open.
    In the dialog, “Destination” field, select the option Copy items if needed.
  6. Verify that the frameworks you selected are displayed in “Embedded Binaries” and “Linked Frameworks and Libraries.”

Embedded_binaries

  1. Click the tab, Build Phases.
  2. Click the plus sign (+), and select, New Run Script Phase.
  3. Open the Run Script folder.
  4. Copy and paste the following script in the “Type a script…” field.
    (This script removes the simulator file which is required to successfully deploy your app to the Apple Store.)
APP_PATH="${TARGET_BUILD_DIR}/${WRAPPER_NAME}"
 
# This script loops through the frameworks embedded in the application and
# removes unused architectures.
find "$APP_PATH" -name '*.framework' -type d | while read -r FRAMEWORK
do
FRAMEWORK_EXECUTABLE_NAME=$(defaults read "$FRAMEWORK/Info.plist" CFBundleExecutable)
FRAMEWORK_EXECUTABLE_PATH="$FRAMEWORK/$FRAMEWORK_EXECUTABLE_NAME"
echo "Executable is $FRAMEWORK_EXECUTABLE_PATH"
 
EXTRACTED_ARCHS=()
 
for ARCH in $ARCHS
do
echo "Extracting $ARCH from $FRAMEWORK_EXECUTABLE_NAME"
lipo -extract "$ARCH" "$FRAMEWORK_EXECUTABLE_PATH" -o "$FRAMEWORK_EXECUTABLE_PATH-$ARCH"
EXTRACTED_ARCHS+=("$FRAMEWORK_EXECUTABLE_PATH-$ARCH")
done
 
echo "Merging extracted architectures: ${ARCHS}"
lipo -o "$FRAMEWORK_EXECUTABLE_PATH-merged" -create "${EXTRACTED_ARCHS[@]}"
rm "${EXTRACTED_ARCHS[@]}"
 
echo "Replacing original executable with thinned version"
rm "$FRAMEWORK_EXECUTABLE_PATH"
mv "$FRAMEWORK_EXECUTABLE_PATH-merged" "$FRAMEWORK_EXECUTABLE_PATH"
 
done
  1. Import the following Mobile SDK library header file to the classes (or to the .pch file if your project has one).

Sample

    #import <MASFoundation/MASFoundation.h>
    #import <MASConnecta/MASConnecta.h>
    #import <MASIdentityManagement/MASIdentityManagement.h>
    #import <MASStorage/MASStorage.h>
    
    import MASFoundation
    import MASConnecta
    import MASIdentityManagement
    import MASStorage
    

    CocoaPods using Podfile

    Use the CocoaPods instructions to create a podfile. Here are the Mobile SDK libraries:

    To integrate Mobile SDK libraries using CocoaPods

    Specify libraries in your Podfile in Xcode. To get library updates for the latest release, replace the Podfile with the following:

    target 'MyAppProjectName' do
    pod 'MASFoundation'
    pod 'MASUI'
    pod 'MASConnecta'
    pod 'MASIdentityManagement'
    pod 'MASStorage'
    
    end
    

    Then, run the following command using the command prompt from the folder of your project:$ pod install

    Step 2: Configure Xcode properties for the Mobile SDK

    1. Open info.plist.
    2. At the end of the table, right-click Add Row
    3. Name the new row App Transport Security Settings
    4. Set the type to Dictionary
    5. Expand the folder, App Transport Security Settings, and click ‘+’ to add the sub line: Allow Arbitrary Loads
    6. Set the type to Boolean
    7. Set the value to YES
    8. Close the Allow Arbitrary Loads folder
    9. Right-click Add Row
    10. Set row to: Privacy - Location When In Use Usage Description
    11. Set the type to String
    12. Set the value to Using Location in iOS8+
    13. If you are using the MASStorage library, link the libsqlite3.tbd library in the project (General, Linked Frameworks and Libraries).
    14. Select the Build Setting tab, go to Linking section, expand Other Linker Flags, and add -ObjC in the Debug and Release fields.

    Step 3: Add the msso_config.json file

    Drag and drop the app configuration file to your app. (If your implementation requires multiple MAGs, you will have multiple files.)

    Important: The msso_config.json file must use a valid JSON format with the required data. If the file is not found, you’ll get an error message and your app will not run. Do not change any of the contents without assistance from your Admin; if you remove or alter required values, your app may not be able to connect or interact with the MAG.

    (Optional) Rename the msso_config.json file

    You can rename the msso_config.json configuration file. Just make sure that you use the .json extension, and you change the name before you start the library processes.

      #import "AppDelegate.h"
      
      #import <MASFoundation/MASFoundation.h>
      
      
      @implementation AppDelegate
      
      - (BOOL)application:(UIApplication *)application
          didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
      {
        //
        // You MUST do this before calling start for it to take effect.
        // Calling it after does nothing and results in it looking 
        // for the default named file when start is called.
        //
        // Also, it doesn't matter if you include the file extension or
        // not. It only looks at the filename portion and enforces the
        // .json extension internally.
        //
        [MAS setConfigurationFileName:@"customConfig"];
        
          //
          // Start MAS
          //
          [MAS start:^(BOOL completed, NSError *error)
          {
              ... 
          }];
      
          return YES;
      }
      
      @end
      
      
      import UIKit
      import MASFoundation
      @UIApplicationMain
      class AppDelegate: UIResponder, UIApplicationDelegate {
       
          var window: UIWindow?
       
          
          func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
              // Override point for customization after application launch
              //
              // You MUST do this before calling start for it to take effect.
              // Calling it after does nothing and results in it looking 
              // for the default named file when start is called.
              //
              // Also, it doesn't matter if you include the file extension or
              // not. It only looks at the filename portion and enforces the
              // .json extension internally.
              //
      
              MAS.setConfigurationFileName("customconfig")
       
              //
              // Start MAS
              //
       
              MAS.start {(completed, error) in
                 ... 
              }
              return true
          }
      }
      

      That’s it! Build and run your project. If the msso_config.json file is valid, your app will connect to the MAG.

      MASstartSucceed

      Start the SDK

      After your project is properly configured, you must start the SDK to establish a secure connection with the backend services. The startup process includes: initialize necessary services for library (such as geo-location, BLE, and network services), and load configuration.

      Ideally, SDK startup should be processed before app startup (during the splash/loading screen of your app). We recommended that you process any communication with the backend services upon successful completion of the startup method. Otherwise, the secure communication is not guaranteed and may fail.

      SDK Initialization States

      Before initializing the SDK, it is useful to check the current state of the SDK using MASSate.

        MASState currentState = [MAS MASState];
        
        let masState = MAS.masState()
        

        This is a static method that is available even before initializing SDK.

        The following states can be returned during the lifecycle of the SDK:

        /**
        *  SDK is not initialized. No configuration file is available in local file system 
        *  based on the default configuration file name, no in the keychain storage.
        */
        MASStateNotConfigured = -1,
        /**
        *  SDK is not initialized. Active configuration is available in the local file system or keychain storage.
        */
        MASStateNotInitialized,
        /**
        *  SDK services are loaded.  This state is invoked only one time for the app's lifecycle.
        */
        MASStateDidLoad,
        /**
        *  SDK is preparing to start initializing all elements required to operate.
        */
        MASStateWillStart,
        /**
        *  SDK is successfully initialized and is fully functional.
        */
        MASStateDidStart,
        /**
        *  SDK is preparing to stop the lifecycle and is shut down all elements and services.
        */
        MASStateWillStop,
        /**
        *  SDK is properly stopped. Restart the SDK. 
        */
        MASStateDidStop,
        /**
        *  SDK is forcibly stopped. 
        */
        MASStateIsBeingStopped
        

        Start with standard method

          //Initializing the SDK.
          [MAS start:^(BOOL completion, NSError *error) {
              //Your code here!
          }];
          
          //Initializing the SDK
          MAS.start {(completed, error) in
              //Your code here!
          }
          

          This method starts the SDK with the currently-active configuration. A currently-active configuration is: 1) the last successfully used configuration, 2) the default JSON configuration file (i.e. msso_config.json in your app bundle) or 3) the custom JSON configuration file defined in [MAS setConfigurationFileName:].

          Recommended for: Most environments, including production.

          Start with default configuration

            //Initializing the SDK.
            [MAS startWithDefaultConfiguration:YES completion:^(BOOL completed, NSError *error)
                //Your code here!
            }];
            
            //Initializing the SDK.
            MAS.start(withDefaultConfiguration: completed) { (completed, error) in
                //Your code here!
            }
            

            This method starts the SDK with the currently-active configuration, or the default configuration (depending on the parameter). If you specify the YES parameter, this overwrites the currently-active configuration with the default configuration (if two configurations are different.). If you pass the NO parameter, this behaves the same as [MAS start:];. If the SDK is already started, this method: stops the SDK, then restarts it with the custom JSON object.

            Recommended for: Development environments where configurations change often.

            Start using custom JSON

              //Your custom JSON object
              NSDictionary *jsonObject = @{....};
              
              //Initializing the SDK with custom JSON object
              [MAS startWithJSON: jsonObject completion:^(BOOL completed, NSError *error) {
                  //Your code here!
              }];
              
              //Your custom JSON object
              let json = [………]
              
              //Initializing the SDK with custom JSON object
              MAS.start(withJSON:json) { (completed, error) in
                 //Your code here!         
              }
              

              This method starts the SDK using the custom JSON object in, NSDictionary. This method overwrites the currently-active configuration with the custom JSON object, and stores it as the active configuration. If the SDK is already started, this method: stops SDK, then it restarts it with the custom JSON object.

              Recommended for: Using multiple MAG servers so you can dynamically change the configuration during runtime. Note: The backend servers must have a version of the product that supports dynamic client configuration.

              Start using file URL

                NSString *jsonPath = [[NSBundle mainBundle] pathForResource:@"your_file_name"
                ofType:@"json"];
                NSURL *thisURL = [NSURL fileURLWithPath:jsonPath];
                
                //
                // OR
                //
                
                NSURL *thisURL = [NSURL URLWithString:@"https://ENROLLMENT_URL"];
                
                //Initializing the SDK with file URL of JSON configuration or enrollment URL.
                [MAS startWithURL:thisURL completion:^(BOOL completed, NSError *error) {
                    //Your code here!
                }];
                
                let jsonPath = Bundle.main.path(forResource: "your_file_name", ofType: "json")
                let thisURL = URL.init(string: jsonPath!)
                
                //
                // OR
                //
                
                let thisURL = URL.init(string: "https://ENROLLMENT_URL")
                //Initializing the SDK with file URL of JSON configuration or enrollment URL
                
                MAS.start(with: thisURL) { (completed, error) in
                    //Your code here!
                }
                

                The custom file can also be defined in NSURL format, which indicates the path of the custom file. This method overwrites the currently-active configuration with the custom JSON file, and stores it as the active configuration. If the SDK is already started, this method: stops the SDK, then restarts it with the custom JSON file.

                Recommended for: Using multiple MAG servers so you can dynamically change the configuration during runtime. Note: The backend servers must have a version of the product that supports dynamic configuration.

                Null object for NSURL

                Null can still be passed in NSURL parameter for this initialization method to allow developers to handle multiple scenarios. After null is passed in as NSURL parameter, the Mobile SDK is initialized using the currently-active configuration which behaves same as [MAS start:];. If the SDK is already started, this method stops the SDK, then restarts.

                Enrollment URL

                Using an enrollment URL dynamically initializes the SDK without having the binary of the msso_config.json within the app bundle. This provides secure bootstrapping in case you want to expose sensitive data in the app bundle.

                Recommended: Primarily for implementing secure initialization of the Mobile SDK. It can also be used to implement a solution to dynamically update the msso_config.json file without having to reinstall the app when the file is updated.

                How it Works

                The Mobile SDK retrieves the msso_config.json configuration using an enrollment URL to a target MAG server. You can provide enrollment URL to the Mobile SDK through app linking with an application’s custom URL scheme, or any other method. After the Mobile SDK retrieves the enrollment URL, it makes a request to the enrollment URL to download the msso_cconfig.json file, and then stores it in the secure keychain storage.

                The enrollment URL is a customizable endpoint that contains the following data: publicKeyHash for SSL pinning (required by the Mobile SDK), and a signed JWT to secure session data. For example:

                https://mobile-staging-sandbox.l7tech.com:8443/connect/device/config?sessionData=eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9[…….]HMuAtMeG-MrA&subjectKeyHash=kqWXaaCtYDm2Xbmd0VdL-hDF8szTTuNRe6Dk_EY9-64%3D

                The enrollment URL can be in the following format:

                • WWW: the enrollment URL from the MAG server. SDK validates subjectKeyHash
                • File path: file path URL in the app bundle or device file system
                • Null: SDK initializes with default or currently-active config

                This initialization method overwrites the currently-active configuration with the configuration received using the enrollment URL, and stores it as the active configuration. If the SDK is already started, this method: stops the SDK, then restarts it with the configuration retrieved via the enrollment URL.

                About Implementation

                As stated, the enrollment URL must be generated from the MAG server and passed to Mobile SDK using a mutually-agreed upon process. The URL can be generated and passed in several ways, for example:

                • A system administrator can request an enrollment URL from the MAG server and distribute it to the end user using SMS or email. The app can handle the enrollment URL through app linking with a custom URL scheme and the SDK is initialized.
                • The application layer can handle the request for the enrollment URL from the MAG server. The app can perform user authentication without going through the SDK.

                Whatever method you choose for the enrollment URL, customizations are required in the Mobile SDK and on the MAG server. Depending on your implementation and workflow, you may need to reauthenticate users multiple times to create a secure solution.

                To get started:

                • Talk with your Admin and determine 1) your enrollment URL process 2) the user authentication workflows
                • For the POST request (to request the enrollment URL), see the SDK Reference section
                • When implementing your app, use MAS.state to check states of the Mobile SDK during the enrollment process to ensure success
                • For security reasons, the enrollment URL can only be used once (like one-time password).

                Update Scopes for the Client App

                When new scopes are added to the API, a new master client key must also be generated on the MAG.

                To update scopes for a client app, follow these steps:

                1. Get the updated msso_config.json file with the new scope from your Admin.
                2. Change the mobile client app to call [MAS startWithDefaultConfiguration] instead of [MAS start]

                Keychain Synchronization

                The Mobile SDK uses iOS Keychain Storage to secure credentials and sensitive data on mobile devices. When a mobile device is backed up to iCloud or transferred to another device, some of the data stored in keychain storage is also backed up or transferred to iCloud or another device.

                By default, the Mobile SDK enforces all app credentials and data to remain only on the device; during backup, credentials are not synchronized to iCloud or transferred to another device. Use the following method to bypass the default and sync credentials and data to the iCloud.

                Before initializing the Mobile SDK, configure the keychain synchronization option:

                • YES: Mobile SDK credentials in keychain storage are sync’ed with iCloud
                • NO: Mobile SDK credentials in keychain storage are NOT sync’ed with iCloud (default)
                  [MAS setKeychainSynchronizable:YES or NO];
                  //
                  //	Initialize SDK
                  //
                  [MAS start:...];
                  
                  MAS.setKeychainSynchronizable(true)
                  //
                  //    Initialize SDK
                  //
                  MAS.start(....)
                  

                  Login: User Authentication and Authorization

                  This tutorial describes how to authenticate and authorize users for login.


                  Get sample app used in video.

                  Library: MASFoundation, MASUI

                  Description: Authenticate and authorize users to log in to your app. This corresponds to the MAG and backend services.

                  Login and Password

                  To authenticate a user with basic credentials:

                  + (void)loginWithUserName:(NSString *)userName
                  password:(NSString *)password
                  completion:(MASCompletionErrorBlock)completion;
                  
                    [MASUser loginWithUserName:@"userName" password:@"password" completion:^(BOOL completed, NSError *error) {
                        //
                        // User login completion
                        //        
                    }];
                    
                    MASUser.login(withUserName: String:"userName", password: "password") { (completed, error) in
                        //
                        // User login completion
                        //
                    }
                    

                    To listen for the following notifications:

                    MASUserWillAuthenticateNotification
                    MASUserDidFailToAuthenticateNotification
                    MASUserDidAuthenticateNotification
                    

                    To log out a currently-authenticated user using an asynchronous request:

                    - (void)logoutWithCompletion:(MASCompletionErrorBlock)completion;
                    
                      [[MASUser currentUser] logoutWithCompletion:^(BOOL completed, NSError *error) {
                          //
                          // User logout completion
                          //     
                      }];
                      
                      MASUser.current()?.logout(completion: { (completed, error) in
                          //
                          // User logout completion
                          // 
                      })
                      

                      To listen for the following notifications:

                      MASUserWillLogoutNotification
                      MASUserDidFailToLogoutNotification
                      MASUserDidLogoutNotification
                      

                      Request user information

                      To request additional information on currently authenticated user:

                      - (void)requestUserInfoWithCompletion:(MASUserResponseErrorBlock)completion;
                      
                        [[MASUser currentUser] requestUserInfoWithCompletion:^(MASUser *user, NSError *error) {
                            //
                            // User information request completion
                            //        
                        }];
                        
                        MASUser.current()?.requestInfo(completion: { (masUser, error) in       
                            //
                            // User information request completion
                            //     
                         })
                        

                        To listen for the following notifications:

                        MASUserWillUpdateInformationNotification
                        MASUserDidFailToUpdateInformationNotification
                        MASUserDidUpdateInformationNotification
                        

                        Fingerprint Sessions Lock

                        Library: MASFoundation

                        Description: An alternative to using passwords for login authentication, many phones support unlocking phones using fingerprint recognition. The Mobile SDK supports fingerprint authentication using local device authentication only. The user’s fingerprint is compared against the image that is stored in the secure area on the chipset. If the unique characteristics of the fingerprints match, the user is authenticated, and the phone is unlocked. Local device authentication using fingerprints can store multiple fingerprints, including the owner and people who the owner trusts.

                        Important! Currently, the Mobile SDK does not support fingerprint using multi-factor authentication, which is often mandated in government and enterprises (FIDO protocol). Specifically, the Mobile SDK does not match the device’s fingerprint against an image that is stored securely on a server, and where the original fingerprint was scanned using a third-party fingerprint scanner. If you use the local device authentication using fingerprints, understand the inherent security limitations for this feature that are documented by your device vendor.

                        Support: iOS 9+ with Fingerprint and/or Passcode enabled device

                        Sample App:
                        Get sample app

                        Device Lock/Unlock Session

                        The Mobile SDK supports session lock/unlock using device Fingerprint Session Lock, and device Passcode. Because the app user can use one, both, or no locking method at all, you need to handle all of these scenarios. If the device is configured without any lock method, the Mobile SDK returns an error that device lock security is missing.

                        Let’s look at a user interaction with the Mobile SDK if you have configured both Fingerprint Session Lock and Passcode:

                        1. The device prompts user for fingerprint login.
                        2. If the user fails fingerprint authentication (three times), the device prompts for authentication with Passcode.
                        3. If the user fails Passcode authentication (five times) the device OS locks out the user for 30 seconds.

                        Fingerprint Implementation Highlights

                        • [[MASUser currentUser] lockSessionWithCompletion:] - Lock the user session.
                        • [MASUser currentUser].isSessionLocked - Verify that the user session is locked.
                        • [[MASUser currentUser] unlockSessionWithCompletion:] - Unlock the user session.
                        • [[MASUser currentUser] unlockSessionWithUserOperationPromptMessage:completion:] - Unlock the user session with operation prompt message.
                        • [[MASUser currentUser] removeSessionLock] - Remove the user session lock.

                        Note: If you store multiple fingerprints on the device, all users can access the app and any API call. If you implement fingerprint with Single Sign-On enabled, all apps using SSO are blocked and require a fingerprint match to unlock.

                        Lock User Session

                          //
                          // Lock the application whenever it is inactive.
                          //
                          // in AppDelegate.m or any other ViewController
                          //
                          - (void)applicationWillResignActive:(UIApplication *)application
                          {
                              //
                              // Only lock the session when [MASUser currentUser] exists, and is authenticated
                              //
                              if ([MASUser currentUser] && [MASUser currentUser].isAuthenticated)
                              {
                                  [[MASUser currentUser] lockSessionWithCompletion:^(BOOL completed, NSError *error) {
                                      if (completed)
                                      {
                                          // session lock successful
                                      }
                                      else {
                                          // session lock error
                                      }       
                                  }];
                              }
                          }
                          
                          //
                          // Lock the application whenever it is inactive.
                          //
                          // in AppDelegate.swift or any other ViewController
                          //
                              func applicationWillResignActive(_ application: UIApplication) {
                          //
                          // Only lock the session when [MASUser currentUser] exists, and is authenticated
                          //
                          if MASUser.current() != nil && (MASUser.current()?.isAuthenticated)! {
                              MASUser.current()?.lockSession(completion: { (completed, error) in
                                  if completed == true {
                                      // session lock successful
                                  }
                                  else {
                                      // session lock error
                                  }
                              })
                          }
                          

                          Unlock User Session

                          A complete implementation of this workflow is provided in AppDelegate.m or any ViewController.

                            //
                            // Unlock the application whenever it is active.
                            //
                            // in AppDelegate.m or any other ViewController
                            //
                            - (void)applicationDidBecomeActive:(UIApplication *)application
                            {
                                //
                                // Unlock the user's session when [MASUser currentUser] exists, and session is locked
                                //
                                if ([MASUser currentUser] && [MASUser currentUser].isSessionLocked)
                                {
                                    //
                                    // Unlock the session with operation prompt message 
                                    //
                                    // (optionally, also can be unlocked without the message)
                                    //
                                    [[MASUser currentUser] unlockSessionWithUserOperationPromptMessage:@"Unlock the application" completion:^(BOOL completed, NSError *error) {
                                        if (completed && !error)
                                        {
                                            // session unlock successful
                                        }
                                        else {
                                            // session unlock failure
                                        }      
                                    }];
                                }
                            }
                            
                            
                            //
                            // Unlock the application whenever it is active.
                            //
                            // in AppDelegate.swift or any other ViewController
                            //
                            func applicationDidBecomeActive(_ application: UIApplication) {
                                //
                                // Unlock the user's session when [MASUser currentUser] exists, and session is locked
                                //
                                if MASUser.current() != nil && (MASUser.current()?.isSessionLocked)! {
                                    //
                                    // Unlock the session with operation prompt message 
                                    //
                                    // (optionally, also can be unlocked without the message)
                                    //
                                    MASUser.current()?.unlockSession(completion: { (completed, error) in
                                        if completed {
                                            // session unlock successful
                                        }
                                        else {
                                            // session unlock failure
                                        }
                                    })
                                }
                            }
                            
                            

                            Verify Locked User Session

                              if ([MASUser currentUser] && [MASUser currentUser].isSessionLocked)
                              {
                                  //
                                  // User session is currently locked
                                  //
                              }
                              
                              if MASUser.current() != nil && (MASUser.current()?.isSessionLocked)! 
                              {
                                  //
                                  // User session is currently locked
                                  //
                              }
                              

                              Remove Locked User Session

                                //
                                // If the app is in an unexpected error state, call removeSessionLock() to remove the encrypted ID token.
                                //
                                if ([MASUser currentUser] && [MASUser currentUser].isSessionLocked)
                                {
                                    //
                                    // remove session lock
                                    //
                                    [[MASUser currentUser] removeSessionLock];
                                }
                                
                                //
                                // If the app is in an unexpected error state, call removeSessionLock() to remove the encrypted ID token.
                                //
                                 
                                if MASUser.current() != nil && (MASUser.current()?.isSessionLocked)! 
                                {
                                    //
                                    // remove session lock
                                    //
                                    MASUser.current()?.removeSessionLock()
                                }
                                

                                Proximity Login (BLE and QR Code)

                                The MASFoundation library provides different ways to share an authenticated user with another device’s apps. It allows you to log into an enterprise app on one device, then use that device to log into the same or a different app on another device. For example, you provide user credentials to log into an app on a cell phone, then use the cell phone to log into a tablet. The devices can be running different operating systems. The Proximity Login authentication supports PKCE extension to OAuth.

                                Limitation: Currently, only the registered user on the device can use proximity login to authorize and provide access to another device.

                                Quick Response Code (QR Code)

                                Library: MASFoundation, MASUI

                                Description: Authenticate the user using a bar code.

                                MASFoundation provides a public interfaces to enable proximity login through the QR Code image. During registration or authentication, an app can display QR Code for other registered and authenticated devices to scan, and the app proceeds with the code received from the server.

                                MASProximityLoginQRCode

                                The MASProximityLoginQRCode object handles all necessary API calls to the backend services for QR Code proximity login.

                                This class provides a QR Code image so you can display it on the login screen, or any other form of registration or authentication process. It also handles polling requests to the backend services based on configurations.

                                Configure startup

                                To start the object, you can configure the following settings. Default values are used if you start the object with only the authentication provider object.

                                • initDelay: Delay before polling request for authorization. Default = 10 seconds.
                                • pollingInterval: Interval for polling requests. Default = 5 seconds.
                                • pollingLimit: Limit number of times to poll requests. Default = 6 times.

                                Startup Methods

                                - (instancetype)initWithAuthenticationProvider:(MASAuthenticationProvider *)provider initialDelay:(NSNumber *)initDelay pollingInterval:(NSNumber *)pollingInterval pollingLimit:(NSNumber  *)pollingLimit;
                                
                                - (instancetype)initWithAuthenticationProvider:(MASAuthenticationProvider *)provider;
                                
                                Start and stop QR code proximity login
                                - (UIImage *)startDisplayingQRCodeImageForProximityLogin; 
                                
                                • startDisplayingQRCodeImageForProximityLogin method creates a QR Code image based on the authentication provider, and starts the timer for polling requests.
                                - (void)stopDisplayingQRCodeImageForProximityLogin;
                                
                                • stopDisplayingQRCodeImageForProximityLogin method stops the timer for polling requests.
                                Notifications

                                You can register the MAG to monitor status update notifications for QR code proximity login.

                                • MASDeviceDidReceiveAuthorizationCodeFromProximityLoginNotification : this notification will be sent to you when the SDK receives the authorization code from backend service. The notification contains authorizationCode as string in userInfo object of the notification.
                                • MASDeviceDidReceiveErrorFromProximityLoginNotification : this notification will be sent to you when the SDK encounters any error during QR Code proximity login. The notification contains a detailed error description.
                                • MASProximityLoginQRCodeDidStartDisplayingQRCodeImage : this notification will be sent to you when the SDK creates the QR Code image and start the polling requests.
                                • MASProximityLoginQRCodeDidStopDisplayingQRCodeImage : this notification will be sent to you when the SDK determines to stop polling requests from backend service for either App reaches the pollingLimit or received the authorizationCode.
                                MASProximityLoginDelegate

                                You can also implement MASProximityLoginDelegate methods for events (instead of subscribing to notifications). Notification and delegation methods do the same thing.

                                - (void)didReceiveAuthorizationCode:(NSString *)authorizationCode;
                                
                                • didReceiveAuthorizationCode: method is invoked when the SDK receives the authorizationCode from the backend service. You can use this authorizationCode to register or authenticate your app. Optionally, you can subscribe to the notification MASDeviceDidReceiveAuthorizationCodeFromProximityLoginNotification to receive the authorizationCode.
                                - (void)didReceiveProximityLoginError:(NSError *)error;
                                
                                • didReceiveProximityLoginError: method is invoked when the SDK encounters an error. You can implement this method to
                                • shoot QR Code/BLE Proximity Login.
                                Workflow
                                Authenticate the device
                                  //
                                  // 1. Create the MASProximityLoginQRCode object
                                  //
                                  MASAuthenticationProvider *qrCodeAuthProvider = [[MASAuthenticationProviders currentProviders] retrieveAuthenticationProviderForProximityLogin];
                                  MASProximityLoginQRCode *qrCodeProximityLogin = [[MASProximityLoginQRCode alloc] initWithAuthenticationProvider:qrCodeAuthProvider];
                                  
                                  //
                                  // 2. Create QR code image and display it
                                  //
                                  UIImage *qrCodeImage = [qrCodeProximityLogin startDisplayingQRCodeImageForProximityLogin];
                                  
                                  //
                                  // 3. Prior to steps 1 and 2, implement MASProximityLoginDelegate method or subscribe to the notification to handle the events. 
                                  // 
                                  
                                  //
                                  // 4. After the authorization code is received, use the code to register/authenticate the user.
                                  //
                                  
                                  //
                                  // 5. Whenever the MASProximityLoginQRCodeDidStopDisplayingQRCodeImage is received, stop displaying QR Code image 
                                  //
                                  
                                  //
                                  // 1. Create the MASProximityLoginQRCode object
                                  //
                                  let qrCodeAuthProvider = MASAuthenticationProviders.current()?.retrieveAuthenticationProviderForProximityLogin()
                                  let qrCodeProximityLogin = MASProximityLoginQRCode.init(authenticationProvider: qrCodeAuthProvider!)
                                   
                                  //
                                  // 2. Create QR code image and display it
                                  //
                                  let qrCodeImage = qrCodeProximityLogin?.startDisplayingQRCodeImageForProximityLogin()
                                   
                                  //
                                  // 3. Prior to steps 1 and 2, implement MASProximityLoginDelegate method or subscribe to the notification to handle the events. 
                                  // 
                                   
                                  //
                                  // 4. After the authorization code is received, use the code to register/authenticate the user.
                                  //
                                   
                                  //
                                  // 5. Whenever the MASProximityLoginQRCodeDidStopDisplayingQRCodeImage is received, stop displaying QR Code image 
                                  //
                                  
                                  Scan the device
                                    //
                                    // 1. Implement Camera viewController to capture QR Code data as string
                                    //
                                    
                                    //
                                    // 2. With camera viewController, capture the QR Code data
                                    //
                                    
                                    //
                                    // 3. With the captured QR Code string (which is the URL), use the following method to authorize the login:
                                    //
                                    [MASProximityLoginQRCode authorizeAuthenticateUrl:code completion:^(BOOL completed, NSError *error) {
                                    
                                        if (!completed || error)
                                        {
                                            //
                                            // Handle the error if it is not successful
                                            //
                                        }
                                        else {
                                            //
                                            // If it is successful, scanning device does not have to do anything from this point on.
                                            //
                                        }
                                    
                                        return;
                                    }];
                                    
                                    //
                                    // 1. Implement Camera viewController to capture QR Code data as string
                                    //
                                     
                                    //
                                    // 2. With camera viewController, capture the QR Code data
                                    //
                                     
                                    //
                                    // 3. With the captured QR Code string (which is the URL), use the following method to authorize the login:
                                    //
                                    MASProximityLoginQRCode.authorizeAuthenticateUrl(String: code){ (completed, error) in
                                    
                                        if error != nil 
                                        {
                                            //
                                            // Handle the error if it is not successful
                                            //
                                        }
                                        else 
                                        {
                                            //
                                            // If it is successful, scanning device does not have to do anything from this point on.
                                            //
                                        }
                                    
                                        return
                                    }
                                    
                                    

                                    Bluetooth Smart Low Energy Technology (BLE)

                                    Library: MASFoundation, MASUI

                                    Description: Enable BLE wireless technology in devices with low power requirements. For example: proximity sensors, heart rate monitors, and fitness devices.

                                    The MASFoundation provides a public interface to enable proximity login through the BLE connection between two devices. During registration or authentication, an app scans for an advertising session from other registered and authenticated devices (to authorize the app), and proceeds with the code received from the authenticated device.

                                    BLE proximity login supports these devices:

                                    Central device: A device that must be registered or authenticated, and scans for advertising session from other devices.

                                    Peripheral device: A device that has already been registered and authenticated, and advertises the authenticated session to other devices.

                                    MASProximityLoginDelegate

                                    To use the BLE proximity login feature, you must implement the following required method: MASProximityLoginDelegate. You can also implement other methods and/or subscribe notifications.

                                    MASBLEServiceState

                                    MASBLEServiceState is the enumeration value in MASProximityLoginDelegate and indicates the service state for both the central and peripheral devices.

                                    typedef NS_ENUM(NSInteger, MASBLEServiceState) {
                                    MASBLEServiceStateUnknown = -1,
                                    MASBLEServiceStateCentralStarted,
                                    MASBLEServiceStateCentralStopped,
                                    MASBLEServiceStateCentralDeviceDetected,
                                    MASBLEServiceStateCentralDeviceConnected,
                                    MASBLEServiceStateCentralDeviceDisconnected,
                                    MASBLEServiceStateCentralServiceDiscovered,
                                    MASBLEServiceStateCentralCharacteristicDiscovered,
                                    MASBLEServiceStateCentralCharacteristicWritten,
                                    MASBLEServiceStateCentralAuthorizationSucceeded,
                                    MASBLEServiceStateCentralAuthorizationFailed,
                                    MASBLEServiceStatePeripheralSubscribed,
                                    MASBLEServiceStatePeripheralUnsubscribed,
                                    MASBLEServiceStatePeripheralStarted,
                                    MASBLEServiceStatePeripheralStopped,
                                    MASBLEServiceStatePeripheralSessionAuthorized,
                                    MASBLEServiceStatePeripheralSessionNotified
                                    };
                                    

                                    These statuses are sent as notifications (as shown in the previous workflow diagram).

                                    Implement BLE required method
                                    - (void)handleBLEProximityLoginUserConsent:(MASCompletionErrorBlock)completion deviceName:(NSString *)deviceName;
                                    

                                    This method is required on the peripheral device. This method is invoked when it requires permission from an app user to grant a permission to share the session to a central device.

                                    Sample Code: Request permission from app user

                                      - (void)handleBLEProximityLoginUserConsent:(MASCompletionErrorBlock)completion deviceName:(NSString *)deviceName
                                      {
                                          __block MASCompletionErrorBlock blockCompletion = completion;
                                      
                                          // Construct AlertController
                                          UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"BLE Proximity Login"
                                          message:[NSString stringWithFormat:@"Grant a permission to share your session with %@?", deviceName]
                                          preferredStyle:UIAlertControllerStyleAlert];
                                          // Construct Grant action
                                          UIAlertAction *grantAction = [UIAlertAction actionWithTitle:@"Grant"
                                          style:UIAlertActionStyleDefault
                                          handler:^(UIAlertAction * _Nonnull action) {
                                              blockCompletion(YES, nil);
                                          }];
                                          // Construct Deny action
                                          UIAlertAction *denyAction = [UIAlertAction actionWithTitle:@"Deny"
                                          style:UIAlertActionStyleDestructive
                                          handler:^(UIAlertAction * _Nonnull action) {
                                              blockCompletion(NO, nil);
                                          }];
                                          // Add grant action
                                          [alertController addAction:grantAction];
                                          // Add deny action
                                          [alertController addAction:denyAction];
                                          // Present Alert
                                          [self presentViewController:alertController animated:YES completion:nil];
                                      }
                                      
                                      func handleBLEProximityLoginUserConsent(completion: @escaping MASCompletionErrorBlock, deviceName:String) 
                                      {
                                          let blockCompletion: MASCompletionErrorBlock = completion
                                          
                                          // Construct AlertController
                                          let alertController: UIAlertController = UIAlertController.init(title: "BLE Proximity Login", message: "Grant a permission to share your session with \(deviceName)?", preferredStyle: UIAlertControllerStyle.alert)
                                          //Construct Grant action
                                          var grantAction = UIAlertAction.init(title: "Grant", style: UIAlertActionStyle.default) { (action) in
                                              blockCompletion(true, nil)
                                          }
                                          var denyAction = UIAlertAction.init(title: "Deny", style: UIAlertActionStyle.destructive) { (action) in
                                              blockCompletion(false, nil)
                                          }
                                          // Add grant action
                                          alertController.addAction(grantAction)
                                          // Add deny action
                                          alertController.addAction(denyAction)
                                          // Present Alert
                                          self.presentViewController(alertController, animated:true, completion:nil)
                                      }
                                      
                                      Implement BLE optional methods
                                      - (void)didReceiveAuthorizationCode:(NSString *)authorizationCode;
                                      
                                      • didReceiveAuthorizationCode: method is invoked when the SDK receives authorizationCode from the backend service. Use this authorizationCode to register or authenticate your app. You can also subscribe to MASDeviceDidReceiveAuthorizationCodeFromProximityLoginNotification to receive the authorizationCode as notification.
                                      - (void)didReceiveBLEProximityLoginStateUpdate:(MASBLEServiceState)state;
                                      
                                      • didReceiveBLEProximityLoginStateUpdate: method is invoked when the SDK sends status updates for all of the states defined in MASBLEServiceState.
                                      - (void)didReceiveProximityLoginError:(NSError *)error;
                                      
                                      • didReceiveProximityLoginError: method is invoked when the SDK encounters an error. Use this method to troubleshoot BLE/QR Code proximity login.
                                      Notifications

                                      You can register the following notifications for status updates on BLE proximity login.

                                      • MASDeviceDidReceiveAuthorizationCodeFromProximityLoginNotification : this notification is sent to you when the SDK receives the authorization code from backend service. The notification contains authorizationCode as string in userInfo object of the notification.
                                      • MASDeviceDidReceiveErrorFromProximityLoginNotification : this notification is sent to you when the SDK encounters any error during BLE proximity login. The notification contains a detailed error description.
                                      MASDevice (BLE)
                                      MASProximityLoginDelegate setter/getter
                                      + (id<MASSessionProximityLogine>)proximityLoginDelegate;
                                      + (void)setProximityLoginDelegate:(id<MASProximityLoginDelegate>)delegate;
                                      
                                      • These methods are the delegation setter/getter for proximity login.
                                      Start MASDevice as central device
                                      - (void)startAsBluetoothCentral;
                                      - (void)startAsBluetoothCentralWithAuthenticationProvider:(MASAuthenticationProvider *)provider;
                                      - (void)stopAsBluetoothCentral;
                                      
                                      • startAsBluetoothCentral: : this method starts the device that is acting as the BLE central device. This method uses the MASAuthenticationProvider in [MASAuthenticationProviders currentProviders].
                                      • startAsBluetoothCentralWithAuthenticationProvider: : this method starts the device acting as the BLE central device with MASAuthenticationProvider in the parameter.
                                      • stopAsBluetoothCentral: : this method stops the device acting as the BLE central device and stops scanning the peripheral device.
                                      Start MASDevice as peripheral device
                                      - (void)startAsBluetoothPeripheral;
                                      - (void)stopAsBluetoothPeripheral;
                                      
                                      • startAsBluetoothPeripheral: : this method starts the device that is acting as the BLE peripheral device, and advertises the registered/authenticated session. Any device that is not registered or authenticated cannot act as a peripheral device or you get an error when you call this method.
                                      • stopAsBluetoothPeripheral: : this method stops the device that is acting as the BLE peripheral device, and stops advertising the session.
                                      Workflow
                                      Authenticate the device (peripheral device)
                                        //
                                        // Prerequisites: device is already registered and authenticated
                                        //
                                        
                                        //
                                        // 1. Before start implementing BLE in the peripheral device, verify that you implemented the required method 
                                        // in MASProximityLoginDelegate (i.e. requesting permission for users to grant access 
                                        // to share their session to other devices).
                                        //
                                        
                                        //
                                        // 2. Start [MASDevice currentDevice] as the peripheral device by calling the following method.
                                        //
                                        [[MASDevice currentDevice] startAsBluetoothPeripheral];
                                        
                                        //
                                        // 3. Stop [MASDevice currentDevice] as the peripheral device when you have to.
                                        //
                                        [[MASDevice currentDevice] stopAsBluetoothPeripheral];
                                        
                                        //
                                        // Prerequisites: device is already registered and authenticated
                                        //
                                        
                                        //
                                        // 1. Before start implementing BLE in the peripheral device, verify that you implemented the required method 
                                        // in MASProximityLoginDelegate (i.e. requesting permission for users to grant access 
                                        // to share their session to other devices).
                                        //
                                        
                                        //
                                        // 2. Start [MASDevice currentDevice] as the peripheral device by calling the following method.
                                        //
                                        MASDevice.current()?.startAsBluetoothPeripheral()
                                         
                                        //
                                        // 3. Stop [MASDevice currentDevice] as the peripheral device when you have to.
                                        //
                                        MASDevice.current()?.stopAsBluetoothCentral()
                                        
                                        Central device
                                          //
                                          // 1. Implement the MASProximityLoginDelegate's delegation method to receive the authorizationCode from 
                                          // the peripheral device
                                          //
                                          - (void)didReceiveAuthorizationCode:(NSString *)authorizationCode
                                          {
                                              //
                                              // handle your authorizationCode to use it for device registration or user authentication.
                                              //
                                          }
                                          
                                          //
                                          // Or, you can also subscribe to NSNotification for receiving authorizationCode.
                                          //  You can handle the authorization code in this notification's userInfo
                                          //
                                          [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didReceiveAuthorizationCode:) name:MASDeviceDidReceiveAuthorizationCodeFromProximityLoginNotification  object:nil];
                                          
                                          //
                                          // Authorization code from the notification can be retrieved as follows:
                                          //
                                          NSNotification *thisNotification = notification that you received..
                                          NSString *authCode = [thisNotification.object objectForKey:@"code"];
                                          
                                          //
                                          // 2. Start [MASDevice currentDevice] as the central device by calling the following method.
                                          //  Verify that you added this line to start the BLE as part of your registration/authentication process
                                          // (i.e. in your login screen).   
                                          //
                                          [[MASDevice currentDevice] startAsBluetoothCentral];
                                          
                                          //
                                          // 3. Stop [MASDevice currentDevice] as the central device after the registration/authentication is completed.
                                          //
                                          [[MASDevice currentDevice] stopAsBluetoothCentral];
                                          
                                          
                                          //
                                          // 1. Implement the MASProximityLoginDelegate's delegation method to receive the authorizationCode from 
                                          // the peripheral device
                                          //
                                          func didReceiveAuthorizationCode(String: authorizationCode)
                                          {
                                              //
                                              // handle your authorizationCode to use it for device registration or user authentication.
                                              //
                                          }
                                           
                                          //
                                          // Or, you can also subscribe to NSNotification for receiving authorizationCode.
                                          //  You can handle the authorization code in this notification's userInfo
                                          //
                                          NotificationCenter.default.addObserver(self, selector: didReceiveAuthorizationCode, name: MASDeviceDidReceiveAuthorizationCodeFromProximityLoginNotification, object: nil)
                                          
                                          //
                                          // Authorization code from the notification can be retrieved as follows:
                                          //
                                          var notification =  notification
                                          var authCode = notification.objectForKey("authCode") 
                                          //
                                          // 2. Start [MASDevice currentDevice] as the central device by calling the following method.
                                          //  Verify that you added this line to start the BLE as part of your registration/authentication process
                                          // (i.e. in your login screen).   
                                          //
                                          MASDevice.current()?.startAsBluetoothCentral()
                                           
                                          //
                                          // 3. Stop [MASDevice currentDevice] as the central device after the registration/authentication is completed.
                                          //
                                          MASDevice.current()?.stopAsBluetoothCentral()
                                          
                                          
                                          Known BLE issues

                                          See the section, Troubleshoot for known BLE issues.

                                          Single Sign-On

                                          Library: MASFoundation

                                          Description: A session and user authentication process that allows a user to enter a single username and password to access multiple apps. This include mobile and web apps.

                                          For Mobile Apps Only

                                          Follow these steps to share secure credentials among mobile apps using the same MAG:

                                          1. Enable your own app by selecting the main project, the main app target, select the Capabilities tab, enable the Key Sharing option.
                                          2. Add two groups:
                                          • One specifically for your app in the first slot
                                          • A shared keychain group identifier to enable single sign-on

                                          Note: The prefix of the group identifier must match the prefix of the bundle identifier of the app. Also, all apps that share the keychain must have their bundle identifiers match this prefix. The suffix should be ‘singleSignOn’

                                          The shared keychain group identifier that you specify in both apps must be the same and use the same prefix as the participating apps, for example: com.ca.singleSignOn. The following screenshot shows an example in one of the apps:

                                          Shared Keychain Setting

                                          That’s it! The MASFoundation library detects your shared keychain group settings (if set) and responds accordingly.

                                          For Mobile and Web Apps (Enterprise Browser)

                                          Library: MASFoundation

                                          Description: Enable single sign-on to a trusted group of enterprise-approved apps on a device. This include mobile and web apps.

                                          Mobile apps are installed on the device and registered with the SDK. Web apps are accessed through WebView and are not installed on the device. WebSSO with SAML tokens are used for approved web apps.

                                          Enterprise Browser

                                          Social Login

                                          Library: MASFoundation, MASUI

                                          Description: Access social login platforms. With Mobile SSO and social login enabled, users log in only once with their social account credentials, and can access multiple enterprise and third-party applications from a single mobile device. The Social Login authentication supports PKCE extension to OAuth.

                                          The MASUI library contains UI resources files for Social Login. You can customize the UI dialog and login buttons by overwriting the social login provider images. MASFoundation provides the underlying logic to the backend services. The following social network site platforms are supported:

                                          • Google
                                          • Salesforce
                                          • LinkedIn
                                          • Facebook

                                          Social Login

                                          Create a Social Login Dialog

                                          The Mobile SDK uses the SFViewController interface to integrate with all provider social login SDKs. It handles the native social provider’s authentication method whether tokens (id, access), or authoritzation code. This provides better security and a better user experience.

                                          Follow these steps to create a social login dialog:

                                          Note: If you implemented social login using a previous version of the Mobile SDK, you must follow these steps to update your social login feature. The Mobile SDK no longer supports social login using the Mobile SDK native WebView.

                                          1. Register the URLs for all social login providers (URL scheme) in your app. The URLs must be a unique. For example:

                                          Sample URL Scheme: /auth/oauth/v2/authorize

                                          {
                                          "idp": "all",
                                          "providers": [
                                          {
                                          "provider": {
                                          "id": "facebook",
                                          "auth_url": "https://<unique_name>"
                                          }
                                          },
                                          {
                                          "provider": {
                                          "id": "google",
                                          "auth_url": "https://<unique_name>"
                                          }
                                          },
                                          {
                                          "provider": {
                                          "id": "salesforce",
                                          "auth_url": "https://<unique_name>"
                                          }
                                          },
                                          {
                                          "provider": {
                                          "id": "linkedin",
                                          "auth_url": "https://<unique_name>"
                                          }
                                          },
                                          {
                                          "provider": {
                                          "id": "enterprise",
                                          "auth_url": "https://<unique_name>"
                                          }
                                          },
                                          {
                                          "provider": {
                                          "id": "qrcode",
                                          "auth_url": "https://<unique_name>",
                                          "poll_url": "https://<unique_name>"
                                          }
                                          }
                                          ]
                                          }
                                          
                                          1. Ask your MAG administrator to register your URL scheme in OAuth Manager on the server side. Admins add the URLs using the redirect_uri parameter in OAuth Manager.

                                          2. Get the updated msso_json.config file with the URL Schemes.

                                          Important! If the URL scheme is not registered properly on your application or in OAuth Manager, Social Login will not work.

                                          1. Add the SFSafariViewController view controller method to display the social login web page.
                                            Load the third-party authentication provider’s URL webpage in SFSafariViewController with the URL in MASAuthenticationProvider.

                                          SFSafariViewController

                                            - (void)presentFacebookLogin:(MASAuthenticationProvider *)provider {
                                            
                                                if ([provider.identifier isEqualToString:@"facebook"])
                                                {
                                                    SFSafariViewController *viewController = [[SFSafariViewController alloc] initWithURL:provider.authenticationUrl];
                                            
                                                    //
                                                    // Set MASAuthorizationResponseDelegate
                                                    //
                                                    [[MASAuthorizationResponse sharedInstance] setDelegate:self];
                                            
                                                    //
                                                    // present the view controller
                                                    //
                                                    [self.navigationController presentViewController:viewController animated:YES completion:nil];
                                                }
                                            }
                                            
                                            func presentFacebookLogin(MASAuthenticationProvider: provider) {
                                            
                                                if provider.identifier == "facebook" {
                                                    var safariViewController = SFSafariViewController.init(url: provider.authenticationUrl)
                                                    //
                                                    // Set MASAuthorizationResponseDelegate
                                                    //
                                                    MASAuthorizationResponse.sharedInstance()?.delegate = self
                                                
                                                    //
                                                    // present the view controller
                                                    //
                                                    self.navigationController().presentViewController(viewController, animated:true)
                                                }
                                            }
                                            
                                            1. Add the MASAuthorizationResponse method to handle app interactions.

                                            The MASAuthorizationResponse class handles incoming interaction from other social login apps (Safari), parses and conveys the authorization code, and handles errors that may occure when processing the designated class.

                                            In AppDelegate’s [UIApplicationDelegate application:openURL:options:] method of your application, the MASAuthorizationResponse method must be invoked to properly handle incoming response from social login providers.

                                            MASAuthorizationResponse

                                              - (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey, id> *)options
                                              {
                                                  [[MASAuthorizationResponse sharedInstance] application:app openURL:url options:options];
                                              
                                                  return YES;
                                              }
                                              
                                              func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool 
                                              {
                                                  MASAuthorizationResponse.sharedInstance()?.application(app, open: url, options: [UIApplicationOpenURLOptionsKey : Any])
                                                  return true
                                              }
                                              
                                              1. Add a method to handle the authorization response by the MAG server.

                                              There are two methods to you can choose to handle the response from social login providers for authorization code (and any processing errors). You can either subscribe the NSNotification, or implement MASAuthorizationResponseDelegate protocols.

                                              MASAuthorizationResponseDelegate

                                              If you implement the delegation methods, you must set the delegation to the designated view controller or class. Then, implement the following methods to handle the authorization response.

                                              /**
                                              *  Delegation method to notify with authorization code when the authentication process is done.
                                              *
                                              *  @param code NSString of authorization code
                                              */
                                              - (void)didReceiveAuthorizationCode:(NSString *)code;
                                              
                                              /**
                                              *  Delegation method to notify when an error is encountered during the authentication process.
                                              *
                                              *  @param error NSError object of the encountered error
                                              */
                                              - (void)didReceiveError:(NSError *)error;
                                              

                                              In you are using the custom login view controller or designated class, implement the following methods to handle login with authorization code in the response.

                                                - (void)didReceiveAuthorizationCode:(NSString *)code {
                                                
                                                    //
                                                    // Use the authorization code to login the user
                                                    //
                                                    [MASUser loginWithAuthorizationCode:code completion:^(BOOL completed, NSError *error) {
                                                
                                                        //
                                                        // handle the result of login here
                                                        //        
                                                    }];
                                                }
                                                
                                                - (void)didReceiveError:(NSError *)error {
                                                
                                                    //
                                                    // Handle an error
                                                    //
                                                }
                                                
                                                func didReceiveAuthorizationCode(code: String) {
                                                
                                                    //
                                                    // Use the authorization code to login the user
                                                    //
                                                    MASUser.login(withAuthorizationCode: code) { (completed, error) in
                                                
                                                        //
                                                        // handle the result of login here
                                                        //
                                                    }
                                                }
                                                
                                                func didReceiveError(error: Error) {
                                                    //
                                                    // Handle an error
                                                    //
                                                }
                                                
                                                NSNotification to handle authorization response

                                                If use are subscribing to NSNotification to handle the authorization response, subscribe: MASAuthorizationResponseDidReceiveAuthorizationCodeNotification and MASAuthorizationResponseDidReceiveErrorNotification. Each notification delivers the respective contents to the objects listening to these notifications.

                                                  - (void)viewDidLoad {
                                                      //
                                                      // Subscribe the notification
                                                      //
                                                  
                                                      [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didReceiveAuthorizationCodeFromSocialLogin:)
                                                      name:MASAuthorizationResponseDidReceiveAuthorizationCodeNotification
                                                      object:nil];
                                                  }
                                                  
                                                  - (void)didReceiveAuthorizationCodeFromSocialLogin:(NSNotification *)notification {
                                                  
                                                      NSString *authorizationCode = [notification.object objectForKey:@"code"];
                                                  
                                                      //
                                                      // Use the authorization code to login the user
                                                      //
                                                      [MASUser loginWithAuthorizationCode:authorizationCode completion:^(BOOL completed, NSError *error) {
                                                  
                                                          //
                                                          // handle the result of login here
                                                          //        
                                                      }];
                                                  }
                                                  
                                                  func viewDidLoad() {
                                                      //
                                                      // Subscribe the notification
                                                      //
                                                      NotificationCenter.default.addObserver(observer: self, selector: #selector(barcodeRead(barcode:)), name: MASAuthorizationResponseDidReceiveAuthorizationCodeNotification, object: nil)
                                                  }
                                                  
                                                  
                                                  func didReceiveAuthorizationCodeFromSocialLogin(notification: Notification) {
                                                      var authorizationCode = notification.object.objectForKey("code")
                                                      //
                                                      // Use the authorization code to login the user
                                                      //
                                                      MASUser.login(withAuthorizationCode: authorizationCode) { (completed, error) in
                                                          //
                                                          // handle the result of login here
                                                          //      
                                                      }
                                                  }
                                                  

                                                  Login Screen Designer

                                                  Description: CA Mobile SDK now provides browser-based login page via Safari View Controller giving the app developer flexibility to choose between native, or browser-based login page. If you have suite of mobile applications, browser-based login lets you dynamically change the login template of all the apps without modifying them individually. Using browser-based login flow, iOS opens Safari View Controller, and the user is directed to customized login page for user authentication.

                                                  Prerequisite: Ensure to meet the following requirements:

                                                  1. Install CA Mobile Developer Console (MDC). For information about how to install MDC, see the CA Mobile Developer Console Installation section in the CA Mobile API Gateway documentation.

                                                  2. Create a login template in MDC for an app that is created on the iOS platform.

                                                  3. Download the msso_config.json configuration.

                                                  4. Add a callback code in your app. Add the following code to the AppDelegate.m file of your app to get the redirection, and allow MASFoundation to handle it.

                                                    - (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey, id> *)options
                                                    {
                                                        return [[MASAuthorizationResponse sharedInstance] application:app openURL:url options:options];
                                                    }
                                                    
                                                    func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool 
                                                    {
                                                        return MASAuthorizationResponse.sharedInstance()?.application(app, open: url, options: [UIApplicationOpenURLOptionsKey : Any])
                                                    }
                                                    
                                                    1. Expose the redirect URI in the info.plist file of your app. The redirect_uri is available in the oauth section of the msso_config.json file. The msso_config.json file is created as part of app creation on CA Mobile Developer Console. Open the downloaded msso_config.json file and Search for “redirect_uri”. Copy the redirect_uri value to the info.plist file of your app.

                                                    A sample code of the oauth section from the msso_config.json file with a redirect_uri is as follows:

                                                    
                                                    oauth": {
                                                         "client": {
                                                             "organization": "CA",
                                                             "description": "What is the app used for?",
                                                             "client_name": "Aditya Test1",
                                                             "client_type": "confidential",
                                                             "registered_by": "admin",
                                                             "client_custom": {
                                                                 "RAS_templateId": "3d0d1a31-7fbd-4a8a-bf05-049000bbcd35"
                                                             },
                                                             "client_ids": [
                                                                 {
                                                                     "account_plan_mapping_ids": "",
                                                                     "client_secret": "",
                                                                     "client_key_custom": {},
                                                                     "redirect_uri": "camssoras://com.ca.ras",
                                                                     "service_ids": "",
                                                                     "client_id": "062e0aaf-c6e8-4420-8de2-49e8eb4a06c8",
                                                                     "status": "ENABLED",
                                                                     "environment": "Android",
                                                                     "scope": "openid msso phone profile address email user_role msso_client_register msso_register mas_messaging mas_storage mas_identity mas_identity_retrieve_users mas_identity_create_users mas_identity_update_users mas_identity_delete_users mas_identity_retrieve_groups mas_identity_create_groups mas_identity_update_groups mas_identity_delete_groups",
                                                                     "registered_by": "admin"
                                                                 }
                                                    
                                                    

                                                    Enable or Disable the Browser-Based Authentication to access the protected API

                                                    Enable browser-based authentication in your app to load a templatized login dialog in a browser.

                                                     /*
                                                    @param enable Boolean value that indicates whether Browser Based Authentication is enabled or not.
                                                    
                                                    */
                                                    
                                                    +(void)enableBrowserBasedAuthentication:(BOOL)enable;
                                                    
                                                    By default, browser-based authentication is disabled.
                                                    

                                                    You can alternatively, launch a browser with a templatized URL.

                                                    Launch a browser to authenticate user

                                                    The API retrieves the templatized login page from MAG and display in a browser. This template has options to login and the API returns the login status.

                                                    /*
                                                    @param completion The MASCompletionErrorBlock block that receives the results. On a successful completion, the user available via [MASUser currentUser] has been updated with the new information.
                                                    */
                                                    
                                                    +(void)initializeBrowserBasedAuthenticationWithCompletion:(MASCompletionErrorBlock _Nullable)completion;
                                                    

                                                    Create Your Own Login Dialog

                                                    Library: MASFoundation

                                                    Description: Create customized login dialog with selected features of your choice.

                                                    Although the MASUI library provides the functionality for the authentication process along and other features (ProximityLogin, and Social Login), you can still create your own login dialog with customized User Interface, branding, and set of features that you want.

                                                    There are two types of flow in MASFoundation: MASGrantFlowClientCredentials, and MASGrantFlowPassword. By default, MASFoundation library sets the flow to MASGrantFlowClientCredentials. In client credentials flow, after you set the grant flow to client credentials, simply invoke an API. Mobile SDK does all of the registration and authentication required.

                                                    You can set the flow with following methods:

                                                      //
                                                      // Set GrantFlow to Client Credentials
                                                      //
                                                      [MAS setGrantFlow:MASGrantFlowClientCredentials] 
                                                      
                                                      or
                                                      
                                                      //
                                                      // Set GrantFlow to Password
                                                      //
                                                      [MAS setGrantFlow:MASGrantFlowPassword];
                                                      
                                                      //
                                                      // Set GrantFlow to Client Credentials
                                                      //
                                                      MAS.setGrantFlow(MASGrantFlow.clientCredentials)
                                                      
                                                      or
                                                      
                                                      //
                                                      // Set GrantFlow to Password
                                                      //
                                                      MAS.setGrantFlow(MASGrantFlow.password)
                                                      

                                                      You can also check the type of flow by calling the follow method:

                                                        //
                                                        //  Returns the current GrantFlow
                                                        //
                                                        [MAS grantFlow];
                                                        
                                                        //
                                                        //  Returns the current GrantFlow
                                                        //
                                                        MAS.grantFlow()
                                                        

                                                        MASGrantFlowClientCredentials

                                                        For MASGrantFlowClientCredentials, MASFoundation library always guarantees that the library is authenticated against client. In this scenario, entering username and password is not required; however, if you would like to manage an access to a certain feature for user, you can overwrite the client authenticated session with user authentication.

                                                        You should check the current authentication status and perform the authenticated based on your need. [MASApplication currentApplication].authenticationStatus returns MASAuthenticationStatus enumeration value which represents type of authentication status for current session.

                                                        Example:

                                                          //
                                                          // If the current session is not user authentication
                                                          //
                                                          if ([MASApplication currentApplication].authenticationStatus != MASAuthenticationStatusLoginWithUser)
                                                          {
                                                              //
                                                              // Do your own thing for User Interface, and perform login with given username and password
                                                              //
                                                              [MASUser loginWithUserName:@"username" password:@"password" completion:^(BOOL completed, NSError *error) {
                                                                  //
                                                                  // authentication succeed or fail
                                                                  //
                                                              }];
                                                          }
                                                          
                                                          //
                                                          // If the current session is not user authentication
                                                          //
                                                          if (MASApplication.current()?.authenticationStatus != MASAuthenticationStatus.loginWithUser) 
                                                          {
                                                              //
                                                              // Do your own thing for User Interface, and perform login with given username and password
                                                              //
                                                              MASUser.login(withUserName: "username", password: "password", completion: { (completed, error) in
                                                                  //
                                                                  // authentication succeed or fail
                                                                  //
                                                              })
                                                          }
                                                          

                                                          MASGrantFlowPassword

                                                          For MASGrantFlowPassword, MASFoundation library invokes MASUserLoginBlock whenever user credential is required to perform authentication.

                                                          Important: If MASUI.framework is included in the project, you should set the boolean flag to handle user authentication by MASUI to false by following code [MAS setWillHandleAuthentication:NO];. Otherwise, the login dialog from MASUI is prompted whenever the user authentication is required and MASUserLoginBlock will not be invoked.

                                                          Important: Although it is still possible to explicitly login a user with [MASUser loginWithUsername:password:completion:], you must implement MASUserLoginBlock as there are many cases where the session can be invalidated from backend services or the session expires during the network requests.

                                                          When you implement MASUserLoginBlock, you have two options to authenticate the user. You can implement one of them based on your needs and workflow.

                                                          MASBasicCredentialsBlock
                                                          typedef void (^MASBasicCredentialsBlock)(NSString *userName, NSString *password, BOOL cancel, MASCompletionErrorBlock);
                                                          

                                                          MASBasicCredentialsBlock is used to authenticate the user with basic user credentials. You can implement your own User Interface to retrieve username and password for user’s account, and perform the authentication.

                                                          MASAuthorizationCodeCredentialsBlock
                                                          typedef void (^MASAuthorizationCodeCredentialsBlock)(NSString *authorizationCode, BOOL cancel, MASCompletionErrorBlock);
                                                          

                                                          MASAuthorizationCodeCredentialsBlock is used to authenticate the user with authorization code which can be retrieved from Proximity Login or Social Login.

                                                          Example:

                                                            [MAS setUserLoginBlock::^(MASBasicCredentialsBlock basicBlock, MASAuthorizationCodeCredentialsBlock authorizationCodeBlock) {
                                                                //
                                                                // Do your own thing for User Interface, and perform login with given credentials
                                                                //
                                                            
                                                                //
                                                                // For basic credentials
                                                                //
                                                                basicBlock(@"username", @"password", NO, ^(BOOL completed, NSError *error){
                                                                    //
                                                                    // authentication succeed or fail
                                                                    //
                                                                });
                                                            
                                                                // Or
                                                            
                                                                //
                                                                // authorization code credential
                                                                //
                                                                authorizationCodeBlock(@"authorization code", NO, ^(BOOL completed, NSError *error){
                                                                    //
                                                                    // authentication succeed or fail
                                                                    //
                                                                });
                                                            }];
                                                            
                                                            //
                                                            // Do your own thing for User Interface, and perform login with given credentials
                                                            //
                                                            MAS.setUserAuthCredentials { (authCredentials, false) in {
                                                                //
                                                                // authentication succeed or fail
                                                                //
                                                            }
                                                            

                                                            Services

                                                            After users securely log into your app, the rest is all about connecting the data to users. This includes sending, receiving, storing data, and managing who gets the data. This section describes the three libraries to help you do this: MASConnecta, MASIdentityManagement, and MASStorage.

                                                            Messaging

                                                            This tutorial describes how to send and receive messages.


                                                            Get sample app used in video.

                                                            Library: MASConnecta

                                                            Description: Enable user messaging and Pub/Sub.

                                                            To create a Messaging solution, you need the MASConnecta library. After MASConnecta is added to a project, some objects from the MASFoundation library automatically displays messaging methods. This saves you development time and makes the code cleaner. You write just a few lines of code, and the library automatically handles all of the settings for the connection to the server.

                                                            Import the MASConnecta.h header file to any class that you want to use or to the .pch file if your project has one.

                                                              #import <MASConnecta/MASConnecta.h>
                                                              
                                                              import MASConnecta
                                                              

                                                              Send messages

                                                                //Authenticated users have the ability to send messages (Text, Data, Image) to a user
                                                                
                                                                MASUser *myUser = [MASUser currentUser];
                                                                MASUser *userB = Some user retrieved from the server
                                                                
                                                                [myUser sendMessage:@"Hello World" toUser:userB completion:^(BOOL success, NSError * _Nullable error) {
                                                                
                                                                NSLog(@"Message Sent : %@\nerror : %@", success ? @"YES":@"NO", error);
                                                                }];
                                                                
                                                                //Authenticated users have the ability to send messages (Text, Data, Image) to a user
                                                                
                                                                let myUser = MASUser.current()        
                                                                let userB  = Some user from server
                                                                myUser?.sendMessage("Hello World" as NSObject, to: userB, completion: { (success, error) in
                                                                
                                                                    print("Message Sent : \(success? "YES" : "NO")\nerror : \(error ?? "" as! Error)")
                                                                })
                                                                
                                                                  //Authenticated users can send messages (Text, Data, Image) to a user on a specific topic
                                                                  
                                                                  MASUser *myUser = [MASUser currentUser];
                                                                  MASUser *userB = Some user retrieved from the server
                                                                  
                                                                  //
                                                                  // Get image from App Bundle
                                                                  //
                                                                  NSString* filePath = [[NSBundle mainBundle] pathForResource:@"image" ofType:@"jpg"];
                                                                  NSData *message = [NSData dataWithContentsOfFile:filePath];
                                                                  
                                                                  //
                                                                  // Create MASMessage object
                                                                  //
                                                                  MASMessage *messageImage = [[MASMessage alloc] initWithPayloadData:message contentType:@"image/jpeg"];
                                                                  //
                                                                  // Send Message to Recipient
                                                                  //
                                                                  [myUser sendMessage:messageImage toUser:userB onTopic:@"vacations" completion:^(BOOL success, NSError * _Nullable error) {
                                                                  
                                                                  NSLog(@"Message Sent : %@\nerror : %@", success ? @"YES":@"NO", error);
                                                                  }];
                                                                  
                                                                  //Authenticated users can send messages (Text, Data, Image) to a user on a specific topic
                                                                  
                                                                  let myUser:MASUser = MASUser.current()
                                                                  let userB:MASUser = Some user from server 
                                                                  
                                                                  //
                                                                  // Get image from App Bundle
                                                                  //
                                                                  let filePath = Bundle.main.path(forResource: "image", ofType: "jpg")
                                                                  var message: Data? = nil
                                                                  if let aPath = Data(contentsOfFile: filePath ?? "") {
                                                                      message = aPath as Data
                                                                  }
                                                                  //
                                                                  // Create MASMessage object
                                                                  //
                                                                  let messageImage = MASMessage(payloadData: message, contentType: "image/jpeg")
                                                                  //
                                                                  // Send Message to Recipient
                                                                  //
                                                                  myUser?.sendMessage(messageImage!, to: userB, onTopic: "Vacation", completion: {(success, error) in
                                                                      print("Message Sent : \(success ? "YES" : "NO")\nerror : \(error ?? "" as! Error)")
                                                                  })
                                                                  

                                                                  Start listening to messages

                                                                  Start listening to my messages

                                                                    - (void)viewDidLoad
                                                                    {
                                                                        //
                                                                        //Get the current authenticated user
                                                                        //
                                                                        MASUser *myUser = [MASUser currentUser];
                                                                    
                                                                        //
                                                                        //Listen to Messages sent to my User
                                                                        //
                                                                        [myUser startListeningToMyMessages:^(BOOL success, NSError *error) {
                                                                    
                                                                            if (success) {
                                                                    
                                                                                NSLog(@"Success subscribing to myUser topic!");
                                                                            }
                                                                            else {
                                                                    
                                                                                NSLog(@"%@",error.localizedDescription);
                                                                            }
                                                                        }];
                                                                    }
                                                                    
                                                                    override func viewDidLoad()
                                                                    {
                                                                        //
                                                                        //Get the current authenticated user
                                                                        //
                                                                        let myUser = MASUser.current()!
                                                                    
                                                                        //
                                                                        //Listen to Messages sent to my User
                                                                        //
                                                                        myUser.startListening(toMyMessages: {(success , error)  in
                                                                    
                                                                            if success {
                                                                    
                                                                                print("Success subscribing to myUser topic!")
                                                                            } else {
                                                                    
                                                                                print(error?.localizedDescription as Any)
                                                                            }
                                                                        })
                                                                    }
                                                                    

                                                                    Stop listening to messages

                                                                    Stop listening to my messages

                                                                      - (void)viewDidLoad
                                                                      {
                                                                          //
                                                                          //Get the current authenticated user
                                                                          //
                                                                          MASUser *myUser = [MASUser currentUser];
                                                                      
                                                                          //
                                                                          //Stop Listening to Messages sent to my User
                                                                          //
                                                                          [myUser stoplisteningToMyMessages:nil];
                                                                      }
                                                                      
                                                                      override func viewDidLoad() {
                                                                          //
                                                                          //Get the current authenticated user
                                                                          //
                                                                          let myUser = MASUser.current()
                                                                      
                                                                          //
                                                                          //Stop Listening to Messages sent to my User
                                                                          //
                                                                          myUser?.stopListening(toMyMessages: nil)
                                                                      }
                                                                      

                                                                      Handle incoming messages

                                                                      Use notifications

                                                                        - (void)viewDidLoad
                                                                        {
                                                                            [[NSNotificationCenter defaultCenter] addObserver:self
                                                                            selector:@selector(didReceiveMessageNotification:)
                                                                            name:MASConnectaMessageReceivedNotification
                                                                            object:nil];
                                                                        }
                                                                        
                                                                        - (void)didReceiveMessageNotification:(NSNotification *)notification
                                                                        {    
                                                                            //
                                                                            //Get the Message Object from the notification
                                                                            //
                                                                            __weak typeof(self) weakSelf = self;
                                                                        
                                                                            dispatch_async(dispatch_get_main_queue(), ^{
                                                                        
                                                                                MASMessage *myMessage = notification.userInfo[MASConnectaMessageKey];
                                                                        
                                                                                [weakSelf.profileImage setImage:myMessage.payloadTypeAsImage];
                                                                                [weakSelf.messagePayload setText:myMessage.payloadTypeAsString];
                                                                            });   
                                                                        }
                                                                        
                                                                        override func viewDidLoad() {
                                                                          NotificationCenter.default.addObserver(self, 
                                                                          selector: #selector(self.didReceiveMessageNotification(notification:)), 
                                                                          name: NSNotification.Name(rawValue: MASConnectaMessageSentNotification), 
                                                                          object: nil)
                                                                        }
                                                                        
                                                                        @objc  func didReceiveMessageNotification(notification: NSNotification){
                                                                            //
                                                                            //Get the Message Object from the notification
                                                                            //
                                                                            weak var weakSelf = self
                                                                        
                                                                            DispatchQueue.main.async(execute: {() -> Void in
                                                                        
                                                                                var myMessage = notification.userInfo![MASConnectaMessageKey] as? MASMessage
                                                                        
                                                                                weakSelf.profileImage.image = myMessage.payloadTypeAsImage
                                                                                weakSelf.messagePayload.text = myMessage.payloadTypeAsString
                                                                        }
                                                                        

                                                                        Pub/Sub

                                                                        Library: MASConnecta

                                                                        Description: Enable user Pub/Sub.

                                                                        For Pub/Sub, you need the MASConnecta library. The MASConnecta library exposes objects that let you: 1) create your own MQTTClient object 2) optionally establish connection with a host using SSL/TLS, 3) set up login and password, and 4) subscribe or publish directly to a topic.

                                                                        For example:

                                                                          - (void)viewDidLoad
                                                                          {
                                                                              [super viewDidLoad];
                                                                          
                                                                              //
                                                                              //Creating a new MQTT client
                                                                              //
                                                                              MASMQTTClient *client = [[MASMQTTClient alloc] initWithClientId:@"myClientID" cleanSession:YES];
                                                                          
                                                                              //
                                                                              //Connecting the mqtt client to a host
                                                                              //
                                                                              [client connectWithHost:@"mas.ca.com" withPort:8883 enableTLS:YES completionHandler:^(MQTTConnectionReturnCode code) {
                                                                          
                                                                                  //Your code here
                                                                              }];
                                                                          
                                                                              //
                                                                              //Handling messages that arrive
                                                                              //
                                                                              [client setMessageHandler:^(MASMQTTMessage *message) {
                                                                          
                                                                                  //Your code here
                                                                              }];
                                                                          
                                                                              //
                                                                              //Subscribing to a topic
                                                                              //
                                                                              [client subscribeToTopic:@"caTopic" withQos:ExactlyOnce completionHandler:^(NSArray *grantedQos) {
                                                                          
                                                                                  //Your code here
                                                                              }];
                                                                          
                                                                              //
                                                                              //Publishing a message to a topic
                                                                              //
                                                                              [client publishString:@"Hello World" toTopic:@"caTopic" withQos:ExactlyOnce retain:YES completionHandler:^(int mid) {
                                                                          
                                                                                  //Your code here
                                                                              }];
                                                                          }
                                                                          
                                                                          override func viewDidLoad() {
                                                                              super.viewDidLoad()
                                                                              
                                                                              //
                                                                              //Creating a new MQTT client
                                                                              //
                                                                              let client = MASMQTTClient(clientId: "myClientID", cleanSession: true)
                                                                          
                                                                              //
                                                                              //Connecting the mqtt client to a host
                                                                              //
                                                                              client.connect(withHost: "mas.ca.com", withPort: 8883, enableTLS: true, completionHandler: {(_ code: MQTTConnectionReturnCode) -> Void in
                                                                          
                                                                                  //Your code here
                                                                              })
                                                                          
                                                                              //
                                                                              //Handling messages that arrive
                                                                              //
                                                                              client.messageHandler = {(_ message: MASMQTTMessage) -> Void in
                                                                          
                                                                                  //Your code here
                                                                              }
                                                                          
                                                                              //
                                                                              //Subscribing to a topic
                                                                              //
                                                                              client.subscribe(toTopic: "caTopic", withQos: MQTTQualityOfService.ExactlyOnce, completionHandler: {(_ grantedQos: [Any]) -> Void in
                                                                          
                                                                                  //Your code here
                                                                              })
                                                                          
                                                                              //
                                                                              //Publishing a message to a topic
                                                                              //
                                                                              client.publishString("Hello World", toTopic: "caTopic", withQos: MQTTQualityOfService.ExactlyOnce, retain: true, completionHandler: {(_ mid: Int32) -> Void in
                                                                          
                                                                                  //Your code here
                                                                              })
                                                                          }
                                                                          

                                                                          User and Group Management

                                                                          This tutorial describes how to manage users and groups in identity providers.


                                                                          Get sample app used in video.

                                                                          Library: MASIdentityManagement

                                                                          Description: Securely access users and groups from enterprise identity providers like LDAP, MSAD, and others. Supports creating groups on the fly (called ad hoc groups) for collaborative apps. The underlying protocol is SCIM.

                                                                          Import the MASIdentityManagement.h header file to any class that you want to use or to the .pch file if your project has one.

                                                                            #import <MASIdentityManagement/MASIdentityManagement.h>
                                                                            
                                                                            import MASIdentityManagement
                                                                            

                                                                            Manage Users

                                                                            Retrieve a user by username
                                                                              //Retrieve a MASUser object that matches the given userName
                                                                              [MASUser getUserByUserName:sampleUserName completion:^(MASUser *user, NSError *error) {
                                                                              
                                                                                  //your code here            
                                                                              }];
                                                                              
                                                                              //Retrieve a MASUser object that matches the given userName
                                                                              MASUser.getUserByUserName("Sampl username", completion: { (user, error) in
                                                                              
                                                                                  // your code here
                                                                              })
                                                                              
                                                                              Retrieve a user by id
                                                                                //Retrieve a MASUser object that matches the given objectId
                                                                                [MASUser getUserByObjectId:sampleUserObjectId completion:^(MASUser *user, NSError *error) {
                                                                                
                                                                                    //your code here            
                                                                                }];
                                                                                
                                                                                //Retrieve a MASUser object that matches the given objectId
                                                                                MASUser.getByObjectId("Sample ID", completion: { (user, error) in
                                                                                
                                                                                    // your code here
                                                                                })
                                                                                

                                                                                Manage Groups

                                                                                Following list of code examples demonstrate basic CRUD operations of ad-hoc group (created on the fly). More advanced operations with using filter request can be found in the next section.

                                                                                Create a group
                                                                                  MASGroup *newGroup = [MASGroup group]; 	// create new MASGroup object
                                                                                  newGroup.groupName = @"New group's name"; // set the group name
                                                                                  newGroup.owner = [MASUser currentUser].userName; // set the owner of the group to a current user
                                                                                  
                                                                                  [newGroup saveInBackgroundWithCompletion:^(MASGroup *group, NSError *error) {
                                                                                  
                                                                                      //your code here
                                                                                  }];
                                                                                  
                                                                                  var newGroup = MASGroup()
                                                                                  // create new MASGroup object
                                                                                  newGroup.groupName = "New group's name"
                                                                                  // set the group name
                                                                                  newGroup.owner = MASUser.current()?.userName
                                                                                  // set the owner of the group to a current user
                                                                                  
                                                                                  newGroup.saveInBackground(completion: {(newGroup, error) -> Void in
                                                                                  
                                                                                      //your code here
                                                                                  })
                                                                                  
                                                                                  Retrieve all groups
                                                                                    //Retrieve all groups in Identity Management system
                                                                                    [MASGroup getAllGroupsWithCompletion:^(NSArray *groupList, NSError *error, NSUInteger totalResults){
                                                                                    
                                                                                        //your code here
                                                                                    }];
                                                                                    
                                                                                    //Retrieve all groups in Identity Management system
                                                                                    MASGroup.getAllGroups(completion:{(groupList, error, totalResult) in
                                                                                    
                                                                                        //your code here
                                                                                    })
                                                                                    
                                                                                    Retrieve a group by group display name
                                                                                      //Retrieve an MASGroup object that matches the given displayName
                                                                                      [MASGroup getGroupByGroupName:@"groupName" completion:^(MASGroup *group, NSError *error) {
                                                                                      
                                                                                          //your code here
                                                                                      }];
                                                                                      
                                                                                      //Retrieve an MASGroup object that matches the given displayName
                                                                                      MASGroup.getGroupByGroupName("sample groupdname", completion:{ (group, error) in
                                                                                      
                                                                                       // your code here
                                                                                      })
                                                                                      
                                                                                      Retrieve a group by id
                                                                                        //Retrieve an MASGroup object that matches the given objectId
                                                                                        [MASGroup getGroupByObjectId:@"objectId" completion:^(MASGroup *group, NSError *error) {
                                                                                        
                                                                                            //your code here
                                                                                        }];
                                                                                        
                                                                                        //Retrieve an MASGroup object that matches the given objectId
                                                                                        MASGroup.getByObjectId("Sample group ID", completion: { (group, error) in
                                                                                        
                                                                                            // your code here
                                                                                        })
                                                                                        
                                                                                        Add a member to a group
                                                                                          MASGroup *thisGroup = retrieve the group object to add a member;
                                                                                          MASUser *thisUser = retrieve the user object to be added;
                                                                                          
                                                                                          //Add the member to the group
                                                                                          [thisGroup addMember:thisUser completion:^(MASGroup *group, NSError *error) {
                                                                                          
                                                                                              //your code here
                                                                                          }];
                                                                                          
                                                                                          let thisGroup:MASGroup = retrieve the group object to add a member
                                                                                          let thisUser:MASUser = retrieve the group object to add a member
                                                                                          
                                                                                          //Add the member to the group      
                                                                                          thisGroup.addMember(thisUser, completion:  { (group, error) in
                                                                                          
                                                                                              // your code here
                                                                                          })
                                                                                          
                                                                                          Remove a member from a group
                                                                                            MASGroup *thisGroup = retrieve the group object to remove a member;
                                                                                            MASUser *thisUser = retrieve the user object to be removed;
                                                                                            
                                                                                            //Remove the member from the group
                                                                                            [thisGroup removeMember:thisUser completion:^(MASGroup *group, NSError *error) {
                                                                                            
                                                                                                //your code here
                                                                                            }];
                                                                                            
                                                                                            let thisGroup:MASGroup = retrieve the group object to add a member
                                                                                            let thisUser:MASUser = retrieve the group object to add a member
                                                                                            
                                                                                            //Remove the member from the group      
                                                                                            thisGroup.removeMember(thisUser, completion:  { (group, error) in
                                                                                            
                                                                                                // your code here
                                                                                            })
                                                                                            
                                                                                            Delete a group
                                                                                              MASGroup *thisGroup = retrieve the group to delete;
                                                                                              
                                                                                              //Delete the group
                                                                                              [thisGroup deleteInBackgroundWithCompletion:^(BOOL success, NSError *error) {
                                                                                              
                                                                                                  //your code here
                                                                                              }];
                                                                                              
                                                                                              let thisGroup:MASGroup = retrieve the group to delete;
                                                                                              
                                                                                              //Delete the group
                                                                                              thisGroup.deleteInBackground(completion: { (success, error) in
                                                                                              
                                                                                                  // your code here
                                                                                              })
                                                                                              

                                                                                              FilteredRequest for Users and Groups

                                                                                              The MASFilteredRequest class is a convenience request builder to make the interaction with the Identity Management system extremely easy and void of errors. The Identity Management service is able to use a set of conditions for querying both users and groups consisting of the following conditionals:

                                                                                              • eq - the filter is equal to the attribute value from the Identity Manager.
                                                                                              • ne - the filter is not equal to the attribute value from the Identity Manager.
                                                                                              • co - the filter is contained within the attribute value from the Identity Manager.
                                                                                              • sw - the filter starts with the attribute value from the Identity Manager.
                                                                                              • ew - the filter ends with the attribute value from the Identity Manager.
                                                                                              • pr - the attribute is present in the results from the Identity Manager.
                                                                                              • gt - the filter is greater than the attribute value from the Identity Manager.
                                                                                              • ge - the filter is greater than or equal to the attribute value from the Identity Manager.
                                                                                              • lt - the filter is less than the attribute value from the Identity Manager.
                                                                                              • le - the filter is less than or equal to the attribute value from the Identity Manager.

                                                                                              Include/Exclude To include or exclude attributes, you need to provide a list of attributes in the query, containing either the key attributes=[comma separated attribute list], or excludedAttributes=[comma separated attribute list]. However, the FilteredRequestBuilder can be used to perform the URL formatting for you.

                                                                                              To read users from the MAS Identity Management service, use the getUsersByFilter api along with the MASFilteredRequest:

                                                                                              Create FilteredRequest
                                                                                                //
                                                                                                // Create FilteredRequest
                                                                                                //
                                                                                                MASFilteredRequest *filteredRequest = [MASFilteredRequest filteredRequest];
                                                                                                
                                                                                                //      
                                                                                                // Create Filter object. 
                                                                                                // Set the attribute, 'userName' or 'displayName', and the filter. For example, a filter of 'sm'
                                                                                                // would match all users with the userName such as 'Smith', 'Asmil', 'Smeel',
                                                                                                // or all groups with the displayName such as 'Small', 'Smart group'
                                                                                                // etc.
                                                                                                //
                                                                                                MASFilter *filter = [MASFilter filterByAttribute:@"userName" contains:@"sm"];
                                                                                                filteredRequest.filter = filter;
                                                                                                
                                                                                                // 
                                                                                                // Set the sortOrder, either descending or ascending.
                                                                                                // sortOrder is an enumeration value with MASFilteredRequestSortOrderDescending or 
                                                                                                // MASFilteredRequestSortOrderAscending.
                                                                                                //
                                                                                                filteredRequest.sortOrder = MASFilteredRequestSortOrderDescending;
                                                                                                
                                                                                                //
                                                                                                // Set the pagination for the request.
                                                                                                //
                                                                                                filteredRequest.paginationRange = NSMakeRange(0, 10);
                                                                                                
                                                                                                //
                                                                                                // Create FilteredRequest
                                                                                                //
                                                                                                let filteredRequest = MASFilteredRequest()
                                                                                                
                                                                                                //      
                                                                                                // Create Filter object. 
                                                                                                // Set the attribute, 'userName' or 'displayName', and the filter. For example, a filter of 'sm'
                                                                                                // would match all users with the userName such as 'Smith', 'Asmil', 'Smeel',
                                                                                                // or all groups with the displayName such as 'Small', 'Smart group'
                                                                                                // etc.
                                                                                                //
                                                                                                let filter = MASFilter(byAttribute: "userName", contains: "sm")
                                                                                                filteredRequest.filter = filter
                                                                                                
                                                                                                // 
                                                                                                // Set the sortOrder, either descending or ascending.
                                                                                                // sortOrder is an enumeration value with MASFilteredRequestSortOrderDescending or 
                                                                                                // MASFilteredRequestSortOrderAscending.
                                                                                                //
                                                                                                filteredRequest.sortOrder = MASFilteredRequestSortOrder.descending
                                                                                                
                                                                                                //
                                                                                                // Set the pagination for the request.
                                                                                                //
                                                                                                filteredRequest.paginationRange = NSMakeRange(0,10)
                                                                                                
                                                                                                Use FilteredRequest for retrieving users

                                                                                                By using the FilteredRequest above, users can easily be retrieved that match with the filter request. There are many other ways of retrieving users without creating a filteredRequest object with several combinations of filter attributes. For more details on the documentation, please refer to the iOS reference.

                                                                                                  MASFilteredRequest *filteredRequest = filteredRequest object;
                                                                                                  
                                                                                                  //
                                                                                                  // Retrieve an array of MASUser objects based on the filterRequest
                                                                                                  //
                                                                                                  [MASUser getUsersByFilteredRequest:filteredRequest completion:^(NSArray *userList, NSError *error, NSUInteger totalResults) {
                                                                                                  
                                                                                                      if (error)
                                                                                                      {
                                                                                                          // Handle error case here
                                                                                                      }
                                                                                                      else {
                                                                                                          // Handle successful request here
                                                                                                      }
                                                                                                  }];
                                                                                                  
                                                                                                  let filteredRequest = MASFilteredRequest() 
                                                                                                  
                                                                                                  //
                                                                                                  // Retrieve an array of MASUser objects based on the filterRequest
                                                                                                  //
                                                                                                  MASUser.getUsersBy(filteredRequest, completion: { (userList, error, totalResult) in
                                                                                                  
                                                                                                      if (error != nil) 
                                                                                                      {
                                                                                                          //  handle error case here
                                                                                                      } 
                                                                                                      else {
                                                                                                          // handle succesful request here
                                                                                                      }
                                                                                                  })
                                                                                                  
                                                                                                  Use FilteredRequest for retrieving groups

                                                                                                  By using the FilteredRequest above, groups can easily be retrieved that match with the filter request. There are many other ways of retrieving groups without creating a filteredRequest object with several combinations of filter attributes. For more details on the documentation, please refer to the iOS reference.

                                                                                                    MASFilteredRequest *filteredRequest = filteredRequest object;
                                                                                                    
                                                                                                    //
                                                                                                    // Retrieve an array of MASGroup objects based on the filterRequest
                                                                                                    //
                                                                                                    [MASGroup getGroupsByFilteredRequest:filteredRequest completion:^(NSArray *groupList, NSError *error, NSUInteger totalResults) {
                                                                                                    
                                                                                                        if (error)
                                                                                                        {
                                                                                                            // Handle error case here
                                                                                                        }
                                                                                                        else {
                                                                                                            // Handle successful request here
                                                                                                        }
                                                                                                    }];
                                                                                                    
                                                                                                    let filteredRequest = MASFilteredRequest()
                                                                                                    
                                                                                                    //
                                                                                                    // Retrieve an array of MASGroup objects based on the filterRequest
                                                                                                    //
                                                                                                    MASGroup.getGroupsBy(filteredRequest) { (groupList, error, totalResult) in
                                                                                                    
                                                                                                        if (error != nil) 
                                                                                                        {
                                                                                                            //  handle error case here
                                                                                                        } 
                                                                                                        else {
                                                                                                            // handle succesful request here
                                                                                                        }
                                                                                                    }
                                                                                                    

                                                                                                    Storage

                                                                                                    This tutorial describes how to use local and private cloud storage.


                                                                                                    Get sample app used in video.

                                                                                                    Library: MASStorage

                                                                                                    Description: Securely store data locally on the device or in a private cloud.

                                                                                                    Note: As of version 1.1.00, you must link the libsqlite3.tbd library to the project in Xcode for MASStorage to work.

                                                                                                    Note: Data synchronization is not supported in this release. If your app requires running without a network, use the local storage option.

                                                                                                    Import the MASStorage.h header file to any class that you want to use or to the .pch file if your project has one.

                                                                                                    For example:

                                                                                                      #import <MASStorage/MASStorage.h>
                                                                                                      
                                                                                                      import MASStorage
                                                                                                      

                                                                                                      The datakey that you use for MAS Storage supports only upper and lowercase alphanumeric characters, plus these characters: - \ . _

                                                                                                      Storage (Local)

                                                                                                      Store/Retrieve local data is done using different permission types. You control permissions using the following enumeration values for the MASLocalStorageSegment:

                                                                                                      • MASLocalStorageSegmentApplication This segment is used to store data pertinent only to the Application. For example, you can store the application configuration. Data stored here is shared across users within the app.

                                                                                                      • MASLocalStorageSegmentApplicationForUser This segment is used to store data pertinent to a specific user within the app. For example, in a game app, you can store the user game score or user game state.

                                                                                                      Enable local storage for the app

                                                                                                      To start using local storage in your app, the SDK needs to enable it. The following process creates the LocalStorage DB with a predefined schema.

                                                                                                        - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
                                                                                                            //
                                                                                                            // Enable Local Storage in the App
                                                                                                            //
                                                                                                            [MAS enableLocalStorage];
                                                                                                        
                                                                                                            //
                                                                                                            // Start the SDK
                                                                                                            //
                                                                                                            [MAS start:nil];
                                                                                                        
                                                                                                            return YES;
                                                                                                        }
                                                                                                        
                                                                                                        func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
                                                                                                            //
                                                                                                            // Enable Local Storage in the App
                                                                                                            //
                                                                                                            MAS.enableLocalStorage()
                                                                                                        
                                                                                                            //
                                                                                                            // Start the SDK
                                                                                                            //
                                                                                                            MAS.start()
                                                                                                        
                                                                                                            return true
                                                                                                        }
                                                                                                        
                                                                                                        Save an object to local storage

                                                                                                        Any object conforming to NSData or NSString can be saved into the local storage.

                                                                                                          - (void)saveObjectMethod
                                                                                                          {
                                                                                                              NSString *myObject = @"someString";
                                                                                                              NSString *key = @"someKey";
                                                                                                              NSString *type = @"objectType";
                                                                                                          
                                                                                                              //
                                                                                                              // Save object to Local Storage
                                                                                                              //
                                                                                                              [MASLocalStorage saveObject:myObject withKey:key type:type mode:MASLocalStorageSegmentApplication completion:^(BOOL success, NSError *error) {
                                                                                                          
                                                                                                                  //Your code here
                                                                                                              }];
                                                                                                          }
                                                                                                          
                                                                                                          func saveObjectMethod () 
                                                                                                          {
                                                                                                              let myObject = "someString"
                                                                                                              let key = "someKey"
                                                                                                              let type = "objectType"
                                                                                                              
                                                                                                              //
                                                                                                              // Save object to Local Storage
                                                                                                              //
                                                                                                              MASLocalStorage.save(myObject as NSObject, withKey: key, type: type, mode: MASLocalStorageSegment.application) { (completed, error) in
                                                                                                          
                                                                                                                  //Your code here
                                                                                                              }
                                                                                                          }
                                                                                                          
                                                                                                          Save an object to local storage using encryption

                                                                                                          Using encryption, saves any object conforming to NSData or NSString. It uses a password, that is set by parameter in the method, to encrypt the object before saving it in the local storage.

                                                                                                            - (void)saveEncryptedObjectMethod
                                                                                                            {
                                                                                                                NSString *myObject = @"someString";
                                                                                                                NSString *key = @"someKey";
                                                                                                                NSString *type = @"objectType";
                                                                                                            
                                                                                                                //
                                                                                                                // Save object to Local Storage
                                                                                                                //
                                                                                                                [MASLocalStorage saveObject:myObject withKey:key type:type mode:MASLocalStorageSegmentApplication password:@"S0m3Pwd!" completion:^(BOOL success, NSError *error) {
                                                                                                            
                                                                                                                    //Your code here
                                                                                                                }];
                                                                                                            }
                                                                                                            
                                                                                                            func saveEncrytpedObjectMethod () 
                                                                                                            {
                                                                                                                let myObject = "someString"
                                                                                                                let key = "someKey"
                                                                                                                let type = "objectType"
                                                                                                                
                                                                                                                //
                                                                                                                // Save object to Local Storage
                                                                                                                //
                                                                                                                MASLocalStorage.save(myObject as NSObject, withKey: key, type: type, mode: MASLocalStorageSegment.application, passwd: "S0m3Pwd!") { (completed, error) in
                                                                                                            
                                                                                                                    //Your code here
                                                                                                                }
                                                                                                            }
                                                                                                            
                                                                                                            Find an object from local storage
                                                                                                              - (void)findObjectMethod
                                                                                                              {
                                                                                                                  //
                                                                                                                  // Find Local Storage Data
                                                                                                                  //
                                                                                                                  [MASLocalStorage findObjectUsingKey:key mode:MASLocalStorageSegmentApplication completion:(void (^)(MASObject *object, NSError *error))completion; {
                                                                                                              
                                                                                                                      //Your code here
                                                                                                                  }];
                                                                                                              }
                                                                                                              
                                                                                                              func findObjectMethod () {
                                                                                                                  //
                                                                                                                  // Find Local Storage Data
                                                                                                                  //
                                                                                                                  MASLocalStorage.findObjects(usingMode: MASLocalStorageSegment.application) { (response, error) in
                                                                                                              
                                                                                                                      //Your code here
                                                                                                                  }
                                                                                                              }
                                                                                                              
                                                                                                              Delete an object from local storage
                                                                                                                - (void)deleteObjectMethod
                                                                                                                {
                                                                                                                    //
                                                                                                                    // Delete object from local storage
                                                                                                                    //
                                                                                                                    [MASLocalStorage deleteObjectUsingKey:key mode:MASLocalStorageSegmentApplication completion:^(BOOL success, NSError *error) {
                                                                                                                
                                                                                                                        //Your code here
                                                                                                                    }];
                                                                                                                }
                                                                                                                
                                                                                                                func deleteObjectMethod () {
                                                                                                                    //
                                                                                                                    // Delete object from local storage
                                                                                                                    //
                                                                                                                    MASLocalStorage.deleteObject(usingKey: key, mode: MASLocalStorageSegment.application) { (response, error) in
                                                                                                                    
                                                                                                                        //Your code here
                                                                                                                    }
                                                                                                                }
                                                                                                                
                                                                                                                Retrieve all objects from local storage
                                                                                                                  - (void)findObjectsMethod
                                                                                                                  {
                                                                                                                      //
                                                                                                                      // Find all Local Storage Data by Mode
                                                                                                                      //
                                                                                                                      [MASLocalStorage findObjectsUsingMode:MASLocalStorageSegmentApplication completion:^(NSArray *objects, NSError *error) {
                                                                                                                  
                                                                                                                          //Your code here
                                                                                                                      }];
                                                                                                                  }
                                                                                                                  
                                                                                                                  func findObjectsMethod() 
                                                                                                                  {
                                                                                                                      //
                                                                                                                      // Find all Local Storage Data by Mode
                                                                                                                      //
                                                                                                                      MASLocalStorage.findObjects(usingMode: MASLocalStorageSegment.application) { (response, error) in
                                                                                                                  
                                                                                                                          //Your code here
                                                                                                                      }
                                                                                                                  }
                                                                                                                  
                                                                                                                  Use notifications
                                                                                                                    - (void)viewDidLoad
                                                                                                                    {
                                                                                                                        [super viewDidLoad];
                                                                                                                    
                                                                                                                        //Add Observer for Notifications from the Mobile SDK
                                                                                                                        [[NSNotificationCenter defaultCenter] addObserver:self
                                                                                                                        selector:@selector(didSaveToLocalStorageNotification:)
                                                                                                                        name:MASStorageOperationDidSaveToLocalStorageNotification
                                                                                                                        object:nil];
                                                                                                                    }
                                                                                                                    
                                                                                                                    override func viewDidLoad() 
                                                                                                                    {
                                                                                                                        super.viewDidLoad()
                                                                                                                    
                                                                                                                        //Add Observer for Notifications from the Mobile SDK
                                                                                                                        NotificationCenter.default.addObserver(self, 
                                                                                                                        selector: #selector(didSaveToLocalStorageNotification()), 
                                                                                                                        name:MASStorageOperationDidSaveToLocalStorageNotification, 
                                                                                                                        object: nil)
                                                                                                                    }
                                                                                                                    

                                                                                                                    Storage (Cloud)

                                                                                                                    Store/Retrieve cloud data is done using different permission types. You control permissions using the following enumeration values for the MASLocalStorageSegment:

                                                                                                                    • MASCloudStorageSegmentUser This segment is used to store data pertinent only to a specific user. For example, if you want to extend the SCIM user profile. Data stored using this segment can be accessed by the same user via different apps.

                                                                                                                    • MASCloudStorageSegmentApplication This segment is used to store data pertinent only to the Application. For example, you can store the application configuration. Data stored here is shared across users within the app.

                                                                                                                    • MASCloudStorageSegmentApplicationForUser This segment is used to store data pertinent to a specific user within the app. For example, in a game app, you can store the user game score or user game state.

                                                                                                                    Save an object to cloud storage

                                                                                                                    Any object conforming to NSDate or NSString can be saved into the cloud storage.

                                                                                                                      - (void)saveObjectMethod
                                                                                                                      {
                                                                                                                          NSString *myObject = @"someString";
                                                                                                                          NSString *key = @"someKey";
                                                                                                                          NSString *type = @"objectType";
                                                                                                                      
                                                                                                                          //
                                                                                                                          // Save object to Cloud Storage
                                                                                                                          //
                                                                                                                          [MASCloudStorage saveObject:myObject withKey:key type:type mode:MASCloudStorageSegmentApplication completion:^(BOOL success, NSError *error) {
                                                                                                                      
                                                                                                                              //Your code here
                                                                                                                          }];
                                                                                                                      }
                                                                                                                      
                                                                                                                      func saveObjectMethod () 
                                                                                                                      {
                                                                                                                          let myObject = "someString"
                                                                                                                          let key = "someKey"
                                                                                                                          let type = "objectType"
                                                                                                                          
                                                                                                                          //
                                                                                                                          // Save object to Cloud Storage
                                                                                                                          //
                                                                                                                          MASCloudStorage.save(myObject as NSObject, withKey: key, type: type, mode: MASCloudStorageSegment.application) { (completed, error) in
                                                                                                                      
                                                                                                                              //Your code here
                                                                                                                          }
                                                                                                                      }
                                                                                                                      
                                                                                                                      Find an object from cloud storage
                                                                                                                        - (void)findObjectMethod
                                                                                                                        {
                                                                                                                            //
                                                                                                                            // Find Cloud Storage Data
                                                                                                                            //
                                                                                                                            [MASCloudStorage findObjectUsingKey:key mode:MASCloudStorageSegmentApplication completion:(void (^)(MASObject *object, NSError *error))completion; {
                                                                                                                        
                                                                                                                                //Your code here
                                                                                                                            }];
                                                                                                                        }
                                                                                                                        
                                                                                                                        func findObjectMethod () 
                                                                                                                        {
                                                                                                                            //
                                                                                                                            // Find Cloud Storage Data
                                                                                                                            //
                                                                                                                            MASCloudStorage.findObjects(usingMode: MASCloudStorageSegment.application) { (response, error) in
                                                                                                                        
                                                                                                                                //Your code here
                                                                                                                            }
                                                                                                                        }
                                                                                                                        
                                                                                                                        Delete an object from cloud storage
                                                                                                                          - (void)deleteObjectMethod
                                                                                                                          {
                                                                                                                              //
                                                                                                                              // Delete object from Cloud Storage
                                                                                                                              //
                                                                                                                              [MASCloudStorage deleteObjectUsingKey:key mode:MASCloudStorageSegmentApplication completion:^(BOOL success, NSError *error) {
                                                                                                                          
                                                                                                                                  //Your code here
                                                                                                                              }];
                                                                                                                          }
                                                                                                                          
                                                                                                                          func deleteObjectMethod () 
                                                                                                                          {
                                                                                                                              //
                                                                                                                              // Delete object from cloud storage
                                                                                                                              //
                                                                                                                              MASCloudStorage.deleteObject(usingKey: key, mode: MASCloudStorageSegment.application) { (response, error) in
                                                                                                                          
                                                                                                                                  //Your code here
                                                                                                                              }
                                                                                                                          }
                                                                                                                          
                                                                                                                          Retrieve all objects from cloud storage
                                                                                                                            - (void)findObjectsMethod
                                                                                                                            {
                                                                                                                                //
                                                                                                                                // Find all Cloud Storage Data by Mode
                                                                                                                                //
                                                                                                                                [MASCloudStorage findObjectsUsingMode:MASCloudStorageSegmentApplication completion:^(NSArray *objects, NSError *error) {
                                                                                                                            
                                                                                                                                    //Your code here
                                                                                                                                }];
                                                                                                                            }
                                                                                                                            
                                                                                                                            func findObjectsMethod() 
                                                                                                                            {
                                                                                                                                //
                                                                                                                                // Find all Cloud Storage Data by Mode
                                                                                                                                //
                                                                                                                                MASCloudStorage.findObjects(usingMode: MASCloudStorageSegment.application) { (response, error) in
                                                                                                                            
                                                                                                                                    //Your code here
                                                                                                                                }
                                                                                                                            }
                                                                                                                            

                                                                                                                            UI Templates

                                                                                                                            The MASUI library contains graphic and xib files to speed up development time. The library provides the following UI components:

                                                                                                                            • User Login Dialog
                                                                                                                            • Social Login
                                                                                                                            • One Time Password

                                                                                                                            User Login Dialog

                                                                                                                            Social Login

                                                                                                                            To use the user login dialog, drag and drop MASUI.framework and MASUIResources.bundle into your project. After the MASUI library is added to the project, MASFoundation automatically detects the presence of the MASUI library and processes the user login as needed.

                                                                                                                            MASUI provides the following method to enable or disable the login dialog. If MASUI is disabled to handle the authentication, MASUserLoginBlock is invoked to retrieve the username and password.

                                                                                                                            /**
                                                                                                                            * Set the handling state of the authentication UI by this library.
                                                                                                                            *
                                                                                                                            * @param handle YES if you want the library to enable it, NO if not.
                                                                                                                            *     YES is the default.
                                                                                                                            */
                                                                                                                            + (void)setWillHandleAuthentication:(BOOL)handle;
                                                                                                                            

                                                                                                                            The user login dialog prompts whenever MASFoundation realizes that user authentication is required or you call the following method to present the login screen.

                                                                                                                              //
                                                                                                                              // Display the login screen
                                                                                                                              //
                                                                                                                              [[MASUser currentUser] presentLoginViewControllerWithCompletion:^(BOOL completed, NSError *error){
                                                                                                                              
                                                                                                                              }];
                                                                                                                              
                                                                                                                              //
                                                                                                                              // Display the login screen
                                                                                                                              //
                                                                                                                              MASUser.presentLoginViewController { (completed, error) in
                                                                                                                                  
                                                                                                                              }
                                                                                                                              

                                                                                                                              Customize User Login Dialog

                                                                                                                              To customize the user login dialog, you need the MASUI library and create your own login view controller, which inherits MASBaseLoginViewController. After creating your own user login dialog, you simply set the default user login screen in MASUI library, so that the custom login screen prompts whenever the user authentication is required.

                                                                                                                              Important! Do not modify properties of MASBaseLoginViewController for any reason. These properties are used internally to process the authentication. Changing them can result in several types of app failures.

                                                                                                                              MASBaseLoginViewController

                                                                                                                              Before implementing the custom login dialog, implement the following methods and also invoke the parent’s method.

                                                                                                                              - (void)viewWillReload;

                                                                                                                              This method is invoked before the user login screen prompt. This method prepares the data source of the UI and other logic with the latest authentication information.

                                                                                                                                - (void)viewWillReload
                                                                                                                                {
                                                                                                                                    [super viewWillReload];
                                                                                                                                
                                                                                                                                    //
                                                                                                                                    // Prepare for data source and other logic
                                                                                                                                    //    
                                                                                                                                }
                                                                                                                                
                                                                                                                                func viewWillReload() 
                                                                                                                                {
                                                                                                                                    super.viewWillReload()
                                                                                                                                
                                                                                                                                    //
                                                                                                                                    // Prepare for data source and other logic
                                                                                                                                    //
                                                                                                                                }
                                                                                                                                

                                                                                                                                - (void)viewDidReload;

                                                                                                                                This method is invoked after the user login screen prompt. This method reloads all UI elements and other logic.

                                                                                                                                  - (void)viewDidReload
                                                                                                                                  {
                                                                                                                                      [super viewDidReload];
                                                                                                                                  
                                                                                                                                      //
                                                                                                                                      // Reload all UI elements and logic
                                                                                                                                      //    
                                                                                                                                  }
                                                                                                                                  
                                                                                                                                  func viewDidReload() 
                                                                                                                                  {
                                                                                                                                      super.viewDidReload()
                                                                                                                                  
                                                                                                                                      //
                                                                                                                                      // Reload all UI elements and logic
                                                                                                                                      //
                                                                                                                                  }
                                                                                                                                  

                                                                                                                                  - (void)loginWithUsername:(NSString *)username password:(NSString *)password completion:(MASCompletionErrorBlock)completion;

                                                                                                                                  This method can be invoked with the given user credentials to perform user authentication. Note that successful authentication of this method does not dismiss the user login screen; it must be dismissed explicitly by calling the dismiss method.

                                                                                                                                    // 
                                                                                                                                    // On your custom IBAction, or other places to perform authentication
                                                                                                                                    //
                                                                                                                                    - (IBAction)onLoginButtonSelected:(id)sender
                                                                                                                                    {
                                                                                                                                        [self loginWithUsername:@"username" password:@"password" completion:^(BOOL completed, NSError *error){
                                                                                                                                            //
                                                                                                                                            // handle the result
                                                                                                                                            //
                                                                                                                                        }];
                                                                                                                                    }
                                                                                                                                    
                                                                                                                                    // 
                                                                                                                                    // On your custom IBAction, or other places to perform authentication
                                                                                                                                    //
                                                                                                                                    @IBAction func onLoginButtonSelected(_ sender: Any) {
                                                                                                                                        MASUser.login(withUserName: "userName", password: "password") { (completed, error) in
                                                                                                                                            //
                                                                                                                                            // handle the result
                                                                                                                                            //
                                                                                                                                        }
                                                                                                                                    }
                                                                                                                                    

                                                                                                                                    - (void)loginWithAuthorizationCode:(NSString *)authorizationCode completion:(MASCompletionErrorBlock)completion;

                                                                                                                                    This method can be invoked with the given authorization code to perform user authentication. Note that successful authentication of this method does not dismiss the user login screen; it must be dismissed explicitly by calling the dismiss method.

                                                                                                                                      // 
                                                                                                                                      // On your custom IBAction or other places to perform authentication
                                                                                                                                      //
                                                                                                                                      - (IBAction)onLoginButtonSelected:(id)sender
                                                                                                                                      {
                                                                                                                                          [self loginWithAuthorizationCode:@"auth_code" completion:^(BOOL completed, NSError *error){
                                                                                                                                      
                                                                                                                                              //
                                                                                                                                              // handle the result
                                                                                                                                              //
                                                                                                                                          }];
                                                                                                                                      }
                                                                                                                                      
                                                                                                                                      // 
                                                                                                                                      // On your custom IBAction or other places to perform authentication
                                                                                                                                      //
                                                                                                                                      @IBAction func onLoginButtonSelected(_ sender: Any) {
                                                                                                                                          MASUser.login(withAuthorizationCode: "authCode") { (completed, error) in
                                                                                                                                      
                                                                                                                                              //
                                                                                                                                              // handle the result
                                                                                                                                              //
                                                                                                                                          }
                                                                                                                                      }
                                                                                                                                      

                                                                                                                                      - (void)cancel;

                                                                                                                                      This method can be invoked when users want to cancel the authentication process or close the user login screen. By invoking this method, the user login screen is dismissed.

                                                                                                                                        - (void)cancel
                                                                                                                                        {
                                                                                                                                            [super cancel];
                                                                                                                                        
                                                                                                                                            //
                                                                                                                                            // Do what you have to do upon cancel
                                                                                                                                            //    
                                                                                                                                        }
                                                                                                                                        
                                                                                                                                        func cancel() 
                                                                                                                                        {
                                                                                                                                            super.cancel()
                                                                                                                                        
                                                                                                                                            //
                                                                                                                                            // Do what you have to do upon cancel
                                                                                                                                            //
                                                                                                                                        }
                                                                                                                                        

                                                                                                                                        - (void)dismissLoginViewControllerAnimated:(BOOL)animated completion: (void (^)(void))completion;

                                                                                                                                        This method can be invoked to dismiss the user login screen upon successful authentication, or other times that you want to dismiss the login screen.

                                                                                                                                          //
                                                                                                                                          // Dismiss the view controller
                                                                                                                                          //
                                                                                                                                          [self dismissLoginViewControllerAnimated:YES completion:nil];
                                                                                                                                          
                                                                                                                                          //
                                                                                                                                          // Dismiss the view controller
                                                                                                                                          //
                                                                                                                                          self.dismiss(animated: true, completion: nil)
                                                                                                                                          

                                                                                                                                          Set the custom login screen as default

                                                                                                                                          After creating the custom login screen, you must create a view controller object and set it as the default login screen in the MASUI library with the following method.

                                                                                                                                          Important: Note that the user login dialog view controller object is reusedfor multiple user logins. It is important to properly organize the lifecycle of the UI elements before and after the user authentication with the provided methods.

                                                                                                                                          /**
                                                                                                                                          Set the custom login view controller for handling by MASUI library
                                                                                                                                          
                                                                                                                                          @param viewController view controller object that inherited MASBaseLoginViewController
                                                                                                                                          */
                                                                                                                                          + (void)setLoginViewController:(MASBaseLoginViewController *)viewController;
                                                                                                                                          

                                                                                                                                          Social Login

                                                                                                                                          The social login feature is included in the user login dialog (described in the previous section). No configuration is required to set up the social login.

                                                                                                                                          Session Lock Screen

                                                                                                                                          Social Login

                                                                                                                                          Social Login

                                                                                                                                          Session lock screen is provided by simply dropping the MASUI.framework and MASUIResource.bundle into your project. MASFoundation detects the presence of the MASUI library and presents the session lock screen upon the API call.

                                                                                                                                          /**
                                                                                                                                          Display currently set lock screen view controller in MASUI for locked session
                                                                                                                                          
                                                                                                                                          @param completion MASCompletionErrorBlock to notify the result of the displaying lock screen.
                                                                                                                                          */
                                                                                                                                          + (void)presentSessionLockScreenViewController:(MASCompletionErrorBlock)completion;
                                                                                                                                          

                                                                                                                                          Customize session lock screen

                                                                                                                                          You can customize the session lock screen with custom logic for locking and unlocking the session, and for branding. The session lock screen must inherit from the MASViewController (MASUI library). For more information about fingerprint session lock APIs in the MASFoundation library, see Fingerprint Sessions Lock section.

                                                                                                                                          After creating your own screen, simply set the default session lock screen in the MASUI library; your own screen then prompts when you call to present the session lock screen.

                                                                                                                                          /**
                                                                                                                                          Set custom lock screen view controller that inherits MASViewController
                                                                                                                                          
                                                                                                                                          @param viewController MASViewController of the lock screen
                                                                                                                                          */
                                                                                                                                          + (void)setLockScreenViewController:(MASViewController *)viewController
                                                                                                                                          

                                                                                                                                          One Time Password

                                                                                                                                          To use the One Time Password dialogs, drag and drop the MASUI.framework and MASUIResources.bundle into your project. The MASFoundation detects the presence of the MASUI library and processes the One Time Password.

                                                                                                                                          OTP Delivery Channel Dialog

                                                                                                                                          DeliveryChanner

                                                                                                                                          MASUI provides the following method to enable or disable the OTP Delivery Channel dialog.

                                                                                                                                          /**
                                                                                                                                          * Set the handling state of the OTP authentication UI by this framework.
                                                                                                                                          *
                                                                                                                                          * @param handle YES if you want the framework to enable it, NO if not.
                                                                                                                                          *     YES is the default.
                                                                                                                                          */
                                                                                                                                          + (void)setWillHandleOTPAuthentication:(BOOL)handle;
                                                                                                                                          

                                                                                                                                          If MASUI is disabled to handle the OTP authentication, MASOTPChannelSelectionBlock is invoked to retrieve the OTP delivery channels.

                                                                                                                                          One Time Password Dialog

                                                                                                                                          OTP

                                                                                                                                          MASUI provides the following method to enable or disable the One Time Password dialog.

                                                                                                                                          /**
                                                                                                                                          * Set the handling state of the OTP authentication UI by this framework.
                                                                                                                                          *
                                                                                                                                          * @param handle YES if you want the framework to enable it, NO if not.
                                                                                                                                          *     YES is the default.
                                                                                                                                          */
                                                                                                                                          + (void)setWillHandleOTPAuthentication:(BOOL)handle;
                                                                                                                                          

                                                                                                                                          If MASUI is disabled to handle the OTP authentication, MASOTPCredentialsBlock is invoked to retrieve the one time password.

                                                                                                                                          Access APIs

                                                                                                                                          This section provides methods to call APIs. It includes one-time request features like geolocation and One-Time Password (OTP).

                                                                                                                                          This tutorial describes how to access protected APIs.


                                                                                                                                          Get sample app used in video.

                                                                                                                                          Send HTTP Requests to APIs

                                                                                                                                          If you have custom endpoints installed on the MAG, you can make direct and secure https calls to them using the following methods. If you are making requests to APIs on external servers that are not MAGs, follow these instructions.

                                                                                                                                          Parameters

                                                                                                                                          The endpoint

                                                                                                                                          The endpoint passed to these methods should be a relative path. For example, if your full URL is https://somegatewayhost:port/some/endpoint, you should only pass the /some/endpoint. MASFoundation should already be setup for network connection to the base URL https://somegatewayhost:port

                                                                                                                                          The parameters

                                                                                                                                          Parameters are encoded according various standards defined for the HTTP verb type.

                                                                                                                                          The headers

                                                                                                                                          Headers only need to be those that are custom to a call, if any. MASFoundation will add any necessary security credentials internally.

                                                                                                                                          The response type

                                                                                                                                          The default expected response type is JSON, but in case the API is sending a different type, the SDK allows you to easily change that per the MASResponseType enumeration.

                                                                                                                                          The completion block

                                                                                                                                          The MASResponseInfoErrorBlock block defined in MAS returns (NSDictionary *responseInfo, NSError *error).

                                                                                                                                          Within the responseInfo are two keys:

                                                                                                                                          // The key for the header info dictionary that was returned in the response:
                                                                                                                                          MASResponseInfoHeaderInfoKey
                                                                                                                                          
                                                                                                                                          // The key for the body info dictionary that was returned in the response, if any:
                                                                                                                                          MASResponseInfoBodyInfoKey
                                                                                                                                          

                                                                                                                                          The request methods

                                                                                                                                          This method makes HTTP DELETE calls to an endpoint.

                                                                                                                                          Parameters are encoded into the endpoint URL as query parameters.

                                                                                                                                          + (void)deleteFrom:(NSString *)endPointPath
                                                                                                                                          withParameters:(NSDictionary *)parameterInfo
                                                                                                                                          andHeaders:(NSDictionary *)headerInfo
                                                                                                                                          responseType:(MASResponseType)responseType
                                                                                                                                          completion:(MASResponseInfoErrorBlock)completion;
                                                                                                                                          

                                                                                                                                          This method makes HTTP GET calls to an endpoint.

                                                                                                                                          Parameters are encoded into the endpoint URL as query parameters.

                                                                                                                                          + (void)getFrom:(NSString *)endPointPath
                                                                                                                                          withParameters:(NSDictionary *)parameterInfo
                                                                                                                                          andHeaders:(NSDictionary *)headerInfo
                                                                                                                                          responseType:(MASResponseType)responseType
                                                                                                                                          completion:(MASResponseInfoErrorBlock)completion;
                                                                                                                                          

                                                                                                                                          This method makes HTTP POST calls to an endpoint.

                                                                                                                                          Parameters are encoded into the HTTP body.

                                                                                                                                          + (void)postTo:(NSString *)endPointPath
                                                                                                                                          withParameters:(NSDictionary *)
                                                                                                                                          andHeaders:(NSDictionary *)headerinfo
                                                                                                                                          responseType:(MASResponseType)responseType
                                                                                                                                          completion:(MASResponseInfoErrorBlock)completion;
                                                                                                                                          

                                                                                                                                          This method makes HTTP PUT calls to an endpoint.

                                                                                                                                          Parameters are encoded into the HTTP body.

                                                                                                                                          + (void)putTo:(NSString *)endPointPath
                                                                                                                                          withParameters:(NSDictionary *)parameterInfo
                                                                                                                                          andHeaders:(NSDictionary *)headerInfo
                                                                                                                                          responseType:(MASResponseType)responseType
                                                                                                                                          completion:(MASResponseInfoErrorBlock)completion;
                                                                                                                                          

                                                                                                                                          Build request with MASRequestBuilder and MASRequest

                                                                                                                                          The iOS Mobile SDK provides an alternative way of sending HTTP requests to APIs. Similar to the Android Mobile SDK, the iOS Mobile SDK can make API requests through MASRequestBuilder.

                                                                                                                                          MASRequestBuilder programmatically, and progressively builds MASRequest object which can be used to invoke an API.

                                                                                                                                          MASRequestBuilder

                                                                                                                                          The MASRequestBuildercan construct MASRequest object with options that can be modified for the particular request. This lets you customize and programmatically build a request and call the MAS.invoke method after MASRequest is built.

                                                                                                                                          // Construct the MASRequestBuilder
                                                                                                                                          MASRequestBuilder *builder = [[MASRequestBuilder alloc] initWithHTTPMethod:@"GET"];
                                                                                                                                           
                                                                                                                                          // Add parameters
                                                                                                                                          [builder setEndPoint:@"https://somegatewayhost:port/some/endpoint"];
                                                                                                                                          [builder setIsPublic:YES];
                                                                                                                                          [builder setQuery:@{@"key":@"queryValue"}];
                                                                                                                                          [builder setHeader:@{@"key":@"headerValue"}];
                                                                                                                                          [builder setBody:@{@"key":@"bodyValue"}];
                                                                                                                                          [builder setRequestType:MASRequestResponseTypeTextPlain];
                                                                                                                                           
                                                                                                                                          // Build the MASRequest
                                                                                                                                          MASRequest *request = [builder build];
                                                                                                                                           
                                                                                                                                          // Invoke the endpoint using the request
                                                                                                                                          [MAS invoke:request completion:^(NSHTTPURLResponse *response, NSDictionary * responseInfo, NSError *error) {
                                                                                                                                              
                                                                                                                                              // Handle error
                                                                                                                                              if(error)
                                                                                                                                              {
                                                                                                                                                  NSLog(@"MAS.invoke - completed with error: %@", error)
                                                                                                                                                  return;
                                                                                                                                              }
                                                                                                                                              
                                                                                                                                              // Handle success
                                                                                                                                              NSLog(@"Result : %@", responseInfo);
                                                                                                                                          }];
                                                                                                                                          
                                                                                                                                          MASRequest methods

                                                                                                                                          MASRequest also provides static methods to construct the request directly with MASRequestBuilder in the parameter of the block.

                                                                                                                                          HTTP DELETE method
                                                                                                                                          MASRequest *request = [MASRequest deleteFrom:^(MASRequestBuilder *builder) {
                                                                                                                                              builder.endPoint = @"https://somegatewayhost:port/some/endpoint";
                                                                                                                                          }];
                                                                                                                                          
                                                                                                                                          HTTP GET method
                                                                                                                                          MASRequest *request = [MASRequest getFrom:^(MASRequestBuilder *builder) {
                                                                                                                                              builder.endPoint = @"https://somegatewayhost:port/some/endpoint";
                                                                                                                                          }];
                                                                                                                                          
                                                                                                                                          HTTP POST method
                                                                                                                                          MASRequest *request = [MASRequest postTo:^(MASRequestBuilder *builder) {
                                                                                                                                              builder.endPoint = @"https://somegatewayhost:port/some/endpoint";
                                                                                                                                          }];
                                                                                                                                          
                                                                                                                                          HTTP PUT method
                                                                                                                                          MASRequest *request = [MASRequest putTo:^(MASRequestBuilder *builder) {
                                                                                                                                              builder.endPoint = @"https://somegatewayhost:port/some/endpoint";
                                                                                                                                          }];
                                                                                                                                          
                                                                                                                                          Response type with MASRequest

                                                                                                                                          Unlike regular CRUD request methods through MAS.h,[MAS getFrom:..], API request with MASRequest returns raw response payload as requested. There is no need for extracting the response body from NSDictionary.

                                                                                                                                          MASRequest *request = ...;
                                                                                                                                          [MAS invoke: request completion:^(NSHTTPURLResponse *response, id responseObject, NSError *error) {}];
                                                                                                                                          

                                                                                                                                          As shown above, response object is a pointer to any type. Mobile SDK automatically parses the payload to the requested data type, and does typecasting before returning the responseObject. The responseObject can be declared as requested response type. Available response types and returning classes are as follows:

                                                                                                                                          • application/json: NSDictionary
                                                                                                                                          • application/xml: NSXMLParser
                                                                                                                                          • plain/text: NSString

                                                                                                                                          The following code shows how to invoke an API with a resulting plain text response.

                                                                                                                                          // Create the request
                                                                                                                                          MASRequest *request = [MASRequest getFrom:^(MASRequestBuilder *builder) {
                                                                                                                                              //Add parameters
                                                                                                                                              builder.endPoint = @"/protected/resource/products";
                                                                                                                                              builder.body = @{@"operation":@"listProducts"};
                                                                                                                                              builder.responseType = MASRequestResponseTypeTextPlain;
                                                                                                                                          }];
                                                                                                                                           
                                                                                                                                          // Invoke the endpoint using the request
                                                                                                                                          [MAS invoke:request completion:^(NSHTTPURLResponse *response, NSString *responseInfo, NSError *error) {
                                                                                                                                          
                                                                                                                                              // Handle error
                                                                                                                                              if(error)
                                                                                                                                              {
                                                                                                                                                  NSLog(@"MAS.invoke - completed with error: %@", error)
                                                                                                                                                  return;
                                                                                                                                              }
                                                                                                                                              
                                                                                                                                              // Handle success
                                                                                                                                              NSLog(@"Result : %@", responseInfo);
                                                                                                                                          }];
                                                                                                                                          

                                                                                                                                          The following code shows how to invoke an API by sending a URL query parameter with a GET request, with a resulting JSON response.

                                                                                                                                          // Create the request
                                                                                                                                          MASRequest *request = [MASRequest getFrom:^(MASRequestBuilder *builder) {
                                                                                                                                              //Add parameters
                                                                                                                                              builder.endPoint = @"/protected/resource/products";
                                                                                                                                              builder.body = @{@"operation":@"listProducts"};
                                                                                                                                              builder.requestType = MASRequestResponseTypeWwwFormUrlEncoded;
                                                                                                                                          }];
                                                                                                                                           
                                                                                                                                          // Invoke the endpoint using the request
                                                                                                                                          [MAS invoke:request completion:^(NSHTTPURLResponse *response, NSDictionary * responseInfo, NSError *error) {
                                                                                                                                          
                                                                                                                                              // Handle error
                                                                                                                                              if(error)
                                                                                                                                              {
                                                                                                                                                  NSLog(@"MAS.invoke - completed with error: %@", error)
                                                                                                                                                  return;
                                                                                                                                              }
                                                                                                                                              
                                                                                                                                              // Handle success
                                                                                                                                              NSLog(@"Result : %@", responseInfo);
                                                                                                                                          }];
                                                                                                                                          

                                                                                                                                          JWT Signing with MASRequest

                                                                                                                                          MASRequestBuilder also lets you easily sign a request parameter with a JWT signature.

                                                                                                                                          The following example demonstrates a HTTP POST request with body parameters and signs the parameters with a JWT signature.

                                                                                                                                          // NSError object to check issues in the signature
                                                                                                                                          NSError *signError;
                                                                                                                                           
                                                                                                                                          // Create a POST request with JWT signature
                                                                                                                                          MASRequestBuilder *builder = [[MASRequestBuilder alloc] initWithHTTPMethod:@"POST"];
                                                                                                                                          [builder setEndPoint:@"https://somegatewayhost:port/some/endpoint"];
                                                                                                                                          [builder setBody:@{@"key":@"bodyValue"}];
                                                                                                                                          //	Simply mark that it needs to be signed
                                                                                                                                          [builder setSignWithError:&signError];
                                                                                                                                          [builder setRequestType:MASRequestResponseTypeTextPlain];
                                                                                                                                          MASRequest *request = [builder build];
                                                                                                                                           
                                                                                                                                          // Invoke the endpoint when signing was successful
                                                                                                                                          if(!signError)
                                                                                                                                          {
                                                                                                                                              [MAS invoke:request completion:nil];
                                                                                                                                          }
                                                                                                                                          else {
                                                                                                                                          	// handle signing error
                                                                                                                                          }
                                                                                                                                          

                                                                                                                                          The example below shows a slightly different way of building HTTP POST request signing with MASClaims object.

                                                                                                                                          // Construct JWT Claims
                                                                                                                                          MASClaims *claims = [MASClaims claims];
                                                                                                                                          claims.content = @{@"contentKey" : @"the contents claim"};
                                                                                                                                          claims.contentType = @"application/json";
                                                                                                                                           
                                                                                                                                          // NSError object to check issues in the signature
                                                                                                                                          NSError *signError;
                                                                                                                                           
                                                                                                                                          // Create a POST request with JWT signature
                                                                                                                                          MASRequestBuilder *builder = [[MASRequestBuilder alloc] initWithHTTPMethod:@"POST"];
                                                                                                                                          [builder setEndPoint:@"https://somegatewayhost:port/some/endpoint"];
                                                                                                                                          //	Sign with MASClaims
                                                                                                                                          [builder setSignWithClaims:claims error:&signError];
                                                                                                                                          [builder setRequestType:MASRequestResponseTypeTextPlain];
                                                                                                                                          MASRequest *request = [builder build];
                                                                                                                                           
                                                                                                                                          // Invoke the endpoint when signing was successful
                                                                                                                                          if(!signError)
                                                                                                                                          {
                                                                                                                                              [MAS invoke:request completion:nil];
                                                                                                                                          }
                                                                                                                                          else {
                                                                                                                                          	// handle signing error
                                                                                                                                          }
                                                                                                                                          

                                                                                                                                          Geolocation

                                                                                                                                          This tutorial describes how to access protected APIs using geolocation.


                                                                                                                                          Get sample app used in video.

                                                                                                                                          Library: n/a

                                                                                                                                          Description: Access to protected APIs can be based on the physical location of the application user. The application passes the physical location information to the MAG in the http header of an access request. Within the http header, location is expressed using latitude/longitude coordinates of the host device. SDK will prompt users to consent to access location information at runtime. Includes the location information in all requests when enabled.

                                                                                                                                          To enable: Add NSLocationAlwaysUsage Description (location service always in use), or NSLocationWheinUseUsageDescription (location service on demand) to the info.plist.

                                                                                                                                          Dependencies: Admin must enable geolocation in the policy. In the msso_config.json file, the Admin must set mag.mobile_sdk.location_enabled to true to enable it from the SDK. If the value is set to false, the SDK does not ask for location permission, and does not include geolocation information in header.

                                                                                                                                          One-Time Password (OTP)

                                                                                                                                          This tutorial describes how to access protected APIs using One-Time Password (OTP).


                                                                                                                                          Get sample app used in video.

                                                                                                                                          Library: MASFoundation

                                                                                                                                          Description: Authenticate the user for a single transaction or session.

                                                                                                                                          Two-factor authentication is an extra layer of security for your protected APIs. It is designed to ensure that an authorized person only has access to protected APIs. This enhances security of the protected APIs.

                                                                                                                                          The OTP is sent to you through your trusted OTP delivery channels. The SDK prompts the users to provide the OTP to access the protected APIs.

                                                                                                                                          To access the protected APIs, you will need to provide two pieces of information - the security credentials (that MASFoundation adds internally), and the OTP that is sent to you.

                                                                                                                                          Because security credentials alone (that MASFoundation adds internally) is no longer enough to access your protected APIs, two-factor authentication dramatically enhances the security of your protected APIs.

                                                                                                                                          OTP Delivery Channels: When you access the protected API, the SDK prompts you to select the preferred delivery channel to send the OTP. The generated OTP is sent to you through the preferred channel.

                                                                                                                                          Invalid OTP Attempts: Providing an invalid OTP to access the protected API prompts you to resend the OTP. A new OTP is sent to you through the preferred OTP delivery channel.

                                                                                                                                          After three consecutive invalid OTP attempts to access the protected API, you are blocked to use it for the next configurable time. Access to the protected API is resumed after the preconfigured time.

                                                                                                                                          Expired OTP Attempt: Your attempts to access the protected API after the defined expiry time is denied. Retry to access the protected API.

                                                                                                                                          MASOTPChannelSelectionBlock

                                                                                                                                          Just like MASUserLoginBlock, MASFoundation library invokes MASOTPChannelSelectionBlock when it detects that the OTP channel selection is required. MASOTPChannelSelectionBlock is invoked with a list of available OTP channels, and MASOTPGenerationBlock sends requests to the server to generate OTP.

                                                                                                                                          If MASUI library is included in the project, MASUI's default OTP channel dialog prompts for channel selection. It is possible to bypass the MASUI's default dialog and create a custom channel selection dialog with the following configuration (if MASUI is included; by default it is set to true):

                                                                                                                                            [MAS setWillHandleOTPAuthentication:NO];
                                                                                                                                            
                                                                                                                                            MAS.willHandleOTPAuthentication(false)
                                                                                                                                            

                                                                                                                                            For the custom OTP channel selection dialog, the following example can be used:

                                                                                                                                              //
                                                                                                                                              // Configure to handle OTP by custom UI if MASUI library is included
                                                                                                                                              // Place this line of code in AppDelegate or in your app's initialization 
                                                                                                                                              //
                                                                                                                                              [MAS setWillHandleOTPAuthentication:NO];
                                                                                                                                              
                                                                                                                                              //
                                                                                                                                              // OTP Channel selection block is invoked when SDK needs
                                                                                                                                              //
                                                                                                                                              [MAS setOTPChannelSelectionBlock:^(NSArray *supportedOTPChannels, MASOTPGenerationBlock otpGenerationBlock) {
                                                                                                                                                  //
                                                                                                                                                  // Select the channel
                                                                                                                                                  //
                                                                                                                                                  NSString *selectedChannel = [supportedOTPChannels objectAtIndex:0];
                                                                                                                                              
                                                                                                                                                  otpGenerationBlock(@[selectedChannel], NO, ^(BOOL completed, NSError *error){
                                                                                                                                              
                                                                                                                                                      //
                                                                                                                                                      // Handle result of channel selection
                                                                                                                                                      //     
                                                                                                                                                  });
                                                                                                                                              }];
                                                                                                                                              
                                                                                                                                              //
                                                                                                                                              // Configure to handle OTP by custom UI if MASUI library is included
                                                                                                                                              // Place this line of code in AppDelegate or in your app's initialization
                                                                                                                                              //
                                                                                                                                              MAS.setWillHandleAuthentication(false)
                                                                                                                                              
                                                                                                                                              //
                                                                                                                                              // OTP Channel selection block is invoked when SDK needs
                                                                                                                                              //
                                                                                                                                              MAS.setOTPChannelSelectionBlock { (supportedOTPChannels, otpGenerationBlock) in
                                                                                                                                                  //
                                                                                                                                                  // Select the channel
                                                                                                                                                  //
                                                                                                                                                  let selectedChannel = supportedOTPChannels[0]
                                                                                                                                                  otpGenerationBlock([selectedChannel], false, { (completed, error) in
                                                                                                                                                      //
                                                                                                                                                      // Handle result of channel selection
                                                                                                                                                      //
                                                                                                                                                  })
                                                                                                                                              }
                                                                                                                                              

                                                                                                                                              MASOTPCredentialsBlock

                                                                                                                                              After OTP channel is selected, MASFoundation library invokes MASOTPCredentialsBlock to retrieve OTP credential that was generated for the selected channel.

                                                                                                                                              If MASUI library is included in the project, MASUI's default OTP credential dialog prompts. It is possible to bypass MASUI's default dialog and create a custom OTP credential dialog with following configuration (if MASUI is included; by default it is set to true):

                                                                                                                                                [MAS setWillHandleOTPAuthentication:NO];
                                                                                                                                                
                                                                                                                                                MAS.setWillHandleOTPAuthentication(false)
                                                                                                                                                

                                                                                                                                                For custom OTP credential dialog, following example can be used.

                                                                                                                                                  //
                                                                                                                                                  // OTP credential block is invoked when SDK needs
                                                                                                                                                  //
                                                                                                                                                  [MAS setOTPCredentialsBlock:^(MASOTPFetchCredentialsBlock otpBlock) {
                                                                                                                                                      //
                                                                                                                                                      // Create UI to retrieve OTP credential from the user
                                                                                                                                                      //
                                                                                                                                                      NSString *otpCredential = @"...user input...";
                                                                                                                                                  
                                                                                                                                                      otpBlock(otpCredential, NO, ^(BOOL completed, NSError *error){
                                                                                                                                                  
                                                                                                                                                          //
                                                                                                                                                          // Handle result of OTP credential
                                                                                                                                                          //     
                                                                                                                                                      });
                                                                                                                                                  }];
                                                                                                                                                  
                                                                                                                                                  //
                                                                                                                                                  // OTP credential block is invoked when SDK needs
                                                                                                                                                  //
                                                                                                                                                  MAS.setOTPCredentialsBlock { (otpBlock, error) in
                                                                                                                                                      //
                                                                                                                                                      // Create UI to retrieve OTP credential from the user
                                                                                                                                                      //
                                                                                                                                                      let otpCredentials = "...user input..."
                                                                                                                                                      
                                                                                                                                                      otpBlock(otpCredentials, false, {(completed, error) in
                                                                                                                                                  
                                                                                                                                                          //
                                                                                                                                                          // Handle result of OTP credential
                                                                                                                                                          //
                                                                                                                                                      })
                                                                                                                                                  }
                                                                                                                                                  

                                                                                                                                                  Sign Requests Using JWT

                                                                                                                                                  Description: JWT is used for passing a signed message between the Mobile SDK and the MAG server. For Mobile Single Sign On, after a user logs in, the same token can be used to access other resources and services. JWT also provides message exchange and verification so you can verify that senders of information are, who they say they are. Implementing JWT ensures that hackers cannot tamper with data during message transmission, and cannot hack into one app, and then access all the user’s other apps.

                                                                                                                                                  Note: Admins can validate a signed JWT by creating a custom MAG policy described here: Validate Data Recipients using JWT.

                                                                                                                                                  Supported

                                                                                                                                                  • Mobile SDK methods to sign a JSON object
                                                                                                                                                  • Admins can validate a signed JWT using their own custom policy
                                                                                                                                                  • Signing PUT or POST data
                                                                                                                                                  • Requests signing using a secret key (RS256 hashing algorithm of private key and validate signature with public/private key pair)

                                                                                                                                                  JWT Data

                                                                                                                                                  A JWT consists of a header, payload, and signature. The Mobile SDK injects JWT reserved claims (key/value pairs) into the payload:

                                                                                                                                                  • Issuer (iss)
                                                                                                                                                  • Audience (aud)
                                                                                                                                                  • Subject (sub)
                                                                                                                                                  • JWT unique identifier (jti)
                                                                                                                                                  • Issued at time (iat)
                                                                                                                                                  • Not before (nbf)

                                                                                                                                                  Above JWT reserved claims’ values are automatically populated upon MASClaims object construction or at the time of sigining JWT; however, all of reserved claims are read-write properties which you can change on your needs.

                                                                                                                                                  You can also add custom claim to JWT with following properties and method:

                                                                                                                                                  Content (content claim) The content to be signed. Content must be serializable object (i.e. NSData, NSString, NSDictionary, NSarray, etc.). UIImage or other custom objects cannot get set to content directly; you must first serialize an object into NSData first, then assign it to a content claim.

                                                                                                                                                  Content-type (contentType claim) Type of content. For example, text/plain, application/json, application/x-www-form-urlencoded

                                                                                                                                                  Custom Claim Custom claim can be declared with following method:

                                                                                                                                                  - (void)setValue:(id)value forClaimKey:(NSString *)claimKey error:(NSError **)error
                                                                                                                                                  
                                                                                                                                                  • value: the value of the claim
                                                                                                                                                  • claimKey: the name of the claim
                                                                                                                                                  • error: an error reference which will be returned if there is any error while setting the value of the claim

                                                                                                                                                  Value of the claim must be either NSString, NSNumber, NSDictionary, or NSArray class. All other types of value will be rejected and return an error to its reference in the method.

                                                                                                                                                  Request JSON Object

                                                                                                                                                  JWT Custom Claims Example

                                                                                                                                                    // An error reference
                                                                                                                                                    NSError *claimError;
                                                                                                                                                    
                                                                                                                                                    // Construct default claim and add custom claim
                                                                                                                                                    MASClaims *claims = [MASClaims claims];
                                                                                                                                                    [claims setValue:@"value of custom claim" forClaimKey:@"customClaim" error:&claimError];
                                                                                                                                                    
                                                                                                                                                    // An error reference
                                                                                                                                                    var claimError: Error
                                                                                                                                                    
                                                                                                                                                    // Construct default claim and add custom claim
                                                                                                                                                    let claims = MASClaims.init()
                                                                                                                                                    claims.setValue("value of custom claim", forClaimKey: "customClaim", error: claimError)
                                                                                                                                                    

                                                                                                                                                    Sign Using Device’s Registered Private Key

                                                                                                                                                      // Construct MASClaims
                                                                                                                                                      MASClaims *claims = [MASClaims claims];
                                                                                                                                                      claims.content = @{@"contentKey" : @"content payload"};
                                                                                                                                                      claims.contentType = @"application/json";
                                                                                                                                                      
                                                                                                                                                      NSError *signingError;
                                                                                                                                                      NSString *jwt = [MAS signWithClaims:claims error:&signingError];
                                                                                                                                                      
                                                                                                                                                      // Construct MASClaims
                                                                                                                                                      let claims = MASClaims.init()
                                                                                                                                                      claims.content = ["contentKey":"contentPayload"]
                                                                                                                                                      claims.contentType = "application/json"
                                                                                                                                                      
                                                                                                                                                      var signingError:Error
                                                                                                                                                      var jwt = MAS.sign(with: claims)
                                                                                                                                                      

                                                                                                                                                      Sign Using Custom Private Key

                                                                                                                                                        //	Construct MASClaims
                                                                                                                                                        MASClaims *claims = [MASClaims claims];
                                                                                                                                                        claims.content = @{@"contentKey" : @"content payload"};
                                                                                                                                                        claims.contentType = @"application/json";
                                                                                                                                                        
                                                                                                                                                        // Load private key in NSData format from PEM Private Key or P12 Private Key
                                                                                                                                                        NSData *privateKey = ; // custom private key
                                                                                                                                                        
                                                                                                                                                        NSError *signingError;
                                                                                                                                                        NSString *jwt = [MAS signWithClaims:claims error:&signingError];
                                                                                                                                                        
                                                                                                                                                        //    Construct MASClaims
                                                                                                                                                        let claims = MASClaims.init()
                                                                                                                                                        claims.content = ["contentKey":"contentPayload"]
                                                                                                                                                        claims.contentType = "application/json"
                                                                                                                                                        
                                                                                                                                                        // Load private key in NSData format from PEM Private Key or P12 Private Key
                                                                                                                                                        var privateKey =   // custom private key
                                                                                                                                                        
                                                                                                                                                        var signingError:Error
                                                                                                                                                        var jwt = MAS.sign(with: claims, privateKey: privateKey)
                                                                                                                                                        

                                                                                                                                                        Send Signed JWT in Request

                                                                                                                                                          //	Construct MASClaims
                                                                                                                                                          MASClaims *claims = [MASClaims claims];
                                                                                                                                                          claims.content = @{@"contentKey" : @"content payload"};
                                                                                                                                                          claims.contentType = @"application/json";
                                                                                                                                                          
                                                                                                                                                          NSError *signingError;
                                                                                                                                                          NSString *jwt = [MAS signWithClaims:claims error:&signingError];
                                                                                                                                                          
                                                                                                                                                          //
                                                                                                                                                          //	Send POST request with text plain request type
                                                                                                                                                          //
                                                                                                                                                          [MAS postTo:YOUR_CUSTOM_ENDPINT withParameter:@{@"jwt":jwt} andHeaders:nil requestType:MASRequestResponseTypeTextPlain responseType:MASRequestResponseTypeJson completion:^(NSDictionary* responseInfo, NSError* error) {
                                                                                                                                                          
                                                                                                                                                              //
                                                                                                                                                              //	Handle your response
                                                                                                                                                              //
                                                                                                                                                          }];
                                                                                                                                                          
                                                                                                                                                          //    Construct MASClaims
                                                                                                                                                          var claims = MASClaims.init()
                                                                                                                                                          claims.content = ["contentKey" : "contentPayload"]
                                                                                                                                                          claims.contentType = "application/json"
                                                                                                                                                          
                                                                                                                                                          var error:Error
                                                                                                                                                          var jwt = MAS.sign(with: claims)
                                                                                                                                                          
                                                                                                                                                          //
                                                                                                                                                          //    Send POST request with text plain request type
                                                                                                                                                          //
                                                                                                                                                          MAS.post(to: YOUR_CUSTOM_ENDPOINT, withParameters: ["jwt": jwt], andHeaders: nil) { (responseInfo, error) in
                                                                                                                                                          
                                                                                                                                                              //
                                                                                                                                                              //    Handle your response
                                                                                                                                                              //
                                                                                                                                                          }
                                                                                                                                                          

                                                                                                                                                          SSL Pinning

                                                                                                                                                          SSL pinning is a feature that avoids “man in the middle” attacks where someone can issue a false SSL certificate and gain access to modify data or snoop network traffic. SSL pinning is implemented on the MAG server side in the msso_config.json file and through policy. The Mobile SDK validates the MAG certificates to ensure mutual trust. The Mobile SDK handles the certificate validation under the covers. As a developer, you don’t need to do anything special.

                                                                                                                                                          Support and Limitations

                                                                                                                                                          • SSL pinning is mandatory for all HTTPS connections in iOS; it cannot be turned off
                                                                                                                                                          • Only certificates and public key hashes that are generated by the MAG in the msso_config.json file are supported
                                                                                                                                                          • The SDK validates that certificates in the certificate chain exist, and that they have the correct hashing algorithm or RSA bit
                                                                                                                                                          • SSL pinning failures result in the following error in the Mobile SDK: Error Message: Invalid pinning information for security configuration. At least one pinning information should be provided or public PKI should be trusted., Error Code: 100212.

                                                                                                                                                          Best Practices

                                                                                                                                                          Here are some best practices to jumpstart your app development experience.

                                                                                                                                                          App Testing

                                                                                                                                                          The MAG server secures device registration and re-registration with this simple logic: only the previously-registered user or client can perform the re-registration. This logic (which resides in policy), is perfect for production environments. However, in Mobile SDK 1.5 and earlier, this caused “device already registered” errors during app testing with multiple users and uninstalling/reinstalling the app.

                                                                                                                                                          In this release, the Mobile SDK generates a new device identifier after uninstall/reinstall, which reduces the likelihood that you’ll get this error.

                                                                                                                                                          But if you get this error, follow these steps to delete unwanted registered device entries in MAG Manager. If you don’t have experience with MAG Manager, work with your Admin.

                                                                                                                                                          1. Log into the MAG Manager. For example: https://your_hostname/instanceModifier/mag/manager
                                                                                                                                                          2. Find your registered device.
                                                                                                                                                            If you don’t know the device user, enter “*” in the “Lookup values for user” field.
                                                                                                                                                          3. Find your device identifier by calling this method in the Mobile SDK: [MASDevice currentDevice].identifier.
                                                                                                                                                          4. Map the device identifier to the OU attribute in MAG Manager (for example: OU=08f8ce12096fcf9d1a1779e4f9dc5fe15519fa2b4ace2af904cf954cc5f5c4e5), Registered Name (DN) column.
                                                                                                                                                          5. Click “Delete Device” to delete the device.

                                                                                                                                                          Note: It’s not likely, but it’s possible that the policy for device registration is incorrectly configured, so check with your Admin if you continue to get “device already registered” errors. See Configure Device Registration

                                                                                                                                                          Note: If you are using the default client credential registration, multiuser mode must be enabled on the MAG server.

                                                                                                                                                          Debug the SDK

                                                                                                                                                          Configure app for network monitoring

                                                                                                                                                          MAS always monitors the network reachability status of the MAG URL. If your app needs monitoring, here’s how to hook your app into monitoring.

                                                                                                                                                            #import "AppDelegate.h"
                                                                                                                                                            
                                                                                                                                                            #import <MASFoundation/MASFoundation.h>
                                                                                                                                                            
                                                                                                                                                            
                                                                                                                                                            @implementation AppDelegate
                                                                                                                                                            
                                                                                                                                                            - (BOOL)application:(UIApplication *)application
                                                                                                                                                            didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
                                                                                                                                                            {
                                                                                                                                                                //
                                                                                                                                                                // You can set the MAG monitor block like this.  It is recommended 
                                                                                                                                                                // to set this before starting MAS
                                                                                                                                                                //
                                                                                                                                                                [MAS setGatewayMonitor:^(MASNetworkMonitoringStatus status)
                                                                                                                                                                {
                                                                                                                                                                    ... do something ...
                                                                                                                                                                }];
                                                                                                                                                            
                                                                                                                                                                //
                                                                                                                                                                // Start MAS
                                                                                                                                                                //
                                                                                                                                                                [MAS start:^(BOOL completed, NSError *error)
                                                                                                                                                                {
                                                                                                                                                                    ... 
                                                                                                                                                                }];
                                                                                                                                                            
                                                                                                                                                                return YES;
                                                                                                                                                            }
                                                                                                                                                            
                                                                                                                                                            @end
                                                                                                                                                            
                                                                                                                                                            import UIKit
                                                                                                                                                            import MASFoundation
                                                                                                                                                            
                                                                                                                                                            @UIApplicationMain
                                                                                                                                                            class AppDelegate: UIResponder, UIApplicationDelegate {
                                                                                                                                                            
                                                                                                                                                                var window: UIWindow?
                                                                                                                                                            
                                                                                                                                                                func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
                                                                                                                                                                    //
                                                                                                                                                                    // You can set the MAG monitor block like this.  It is recommended
                                                                                                                                                                    // to set this before starting MAS
                                                                                                                                                                    //
                                                                                                                                                                    MAS.setGatewayMonitor { (status) in
                                                                                                                                                                        ... do something ...
                                                                                                                                                                    }
                                                                                                                                                            
                                                                                                                                                                    //
                                                                                                                                                                    // Start MAS
                                                                                                                                                                    //
                                                                                                                                                                    MAS.start { (completed, error) in
                                                                                                                                                                        ...
                                                                                                                                                                    }
                                                                                                                                                            
                                                                                                                                                                    return true
                                                                                                                                                                }
                                                                                                                                                            }
                                                                                                                                                            

                                                                                                                                                            Configure status notifications

                                                                                                                                                            You can register the MAG to monitor status update notifications. The notification is defined in MASConstants as shown below:

                                                                                                                                                            MASGatewayMonitorStatusUpdateNotification
                                                                                                                                                            

                                                                                                                                                            Conveniences

                                                                                                                                                            To determine if the network connection to the MAG is currently reachable:

                                                                                                                                                              [MAS gatewayIsReachable];
                                                                                                                                                              
                                                                                                                                                              MAS.gatewayIsReachable()
                                                                                                                                                              

                                                                                                                                                              To determine the current status as a string at any time:

                                                                                                                                                                [MAS gatewayMonitoringStatusAsString]
                                                                                                                                                                
                                                                                                                                                                MAS.gatewayMonitoringStatusAsString()
                                                                                                                                                                

                                                                                                                                                                Application startup: what happens?

                                                                                                                                                                On initial installation of your app, the following happens:

                                                                                                                                                                • Loads information in the msso_config.json file
                                                                                                                                                                • Configures the networking and starts the connection
                                                                                                                                                                • Validates the app to receive and store application-level credentials
                                                                                                                                                                • Depending upon the grant flow type, registers the device to receive and store device-specific credentials, and optionally authenticates the user, and receives and stores user specific credentials

                                                                                                                                                                On subsequent startups of an already-installed app, the process is repeated. However, if the stored credentials are still valid, the validation and/or registration to the server is not necessary and is skipped. It checks only that currently-stored credentials are still valid, and requests a refresh (if necessary).

                                                                                                                                                                Stop and reset the device

                                                                                                                                                                To stop all processes in the library, use the following method:

                                                                                                                                                                + (void)stop:(MASCompletionErrorBlock)completion;
                                                                                                                                                                

                                                                                                                                                                Reset all app, device, and user credentials

                                                                                                                                                                To reset all app, device, and user credentials in memory, or in the local and shared group keychains, use the following method:

                                                                                                                                                                + (void)resetWithCompletion:(MASCompletionErrorBlock)completion;
                                                                                                                                                                

                                                                                                                                                                Note: We recommend that you add a warning UI component or similar to indicate to the user exactly what they are doing, with a confirmation before proceeding with this action.

                                                                                                                                                                Note: This only resets the credentials on the device. To reset and deregister the device record on the MAG, call [[MASDevice currentDevice] deregisterWithCompletion:].

                                                                                                                                                                Note: You must restart your app to get new registration of the app, device and user authentication.

                                                                                                                                                                Deregister a device

                                                                                                                                                                To remove the device’s record from the MAG:

                                                                                                                                                                - (void)deregisterWithCompletion:(MASCompletionErrorBlock)completion;
                                                                                                                                                                

                                                                                                                                                                Use this feature with caution because it may not be easy for end users to use if you make it publicly available. We suggest a warning UI component or similar to indicate to the user exactly what they are doing, with a confirmation before proceeding with this action.

                                                                                                                                                                Note: You must restart your app to get new registration of the app, device and user authentication.

                                                                                                                                                                To listen for the following notifications:

                                                                                                                                                                MASDeviceWillDeregisterNotification
                                                                                                                                                                MASDeviceDidFailToDeregisterNotification
                                                                                                                                                                MASDeviceDidDeregisterNotification
                                                                                                                                                                

                                                                                                                                                                Notifications

                                                                                                                                                                During SDK startup, if the SDK detects the server switch from an old configuration to a new configuration, MASWillSwitchGatewayServerNotification and MASDidSwitchGatewayServerNotification are sent. You can optionally observe these notifications to handle any necessary operation that you may wish to do within your app.

                                                                                                                                                                The SDK determines the server switch by these configuration values: hostname, port, and prefix.

                                                                                                                                                                Handle errors

                                                                                                                                                                All errors that occur during SDK startup are returned in the completion block of the method.

                                                                                                                                                                  //Initializing the SDK.
                                                                                                                                                                  [MAS start:^(BOOL completion, NSError *error) {
                                                                                                                                                                      if (error)
                                                                                                                                                                      {
                                                                                                                                                                          //Handle error here
                                                                                                                                                                          if ([error.domain isEqualToString:MASFoundationErrorDomain])
                                                                                                                                                                          {
                                                                                                                                                                              //MASFoundation error domain   
                                                                                                                                                                          }
                                                                                                                                                                          else if ([error.domain isEqualToString:MASFoundationErrorDomainLocal])
                                                                                                                                                                          {
                                                                                                                                                                              //MASFoundation local error domain  
                                                                                                                                                                          }
                                                                                                                                                                      }
                                                                                                                                                                  }];
                                                                                                                                                                  
                                                                                                                                                                  //Initializing the SDK.
                                                                                                                                                                  MAS.start { (completed, error) in
                                                                                                                                                                      if (error != nil) 
                                                                                                                                                                      {
                                                                                                                                                                          //Handle error here
                                                                                                                                                                          if (error.domain == MASFoundationErrorDomain) 
                                                                                                                                                                          {
                                                                                                                                                                              //MASFoundation error domain
                                                                                                                                                                          }
                                                                                                                                                                          else if (error.domain == MASFoundationErrorDomainLocal) 
                                                                                                                                                                          {
                                                                                                                                                                              //MASFoundation local error domain
                                                                                                                                                                          }
                                                                                                                                                                      }
                                                                                                                                                                  }
                                                                                                                                                                  

                                                                                                                                                                  All errors that are returned from the startup process should contain proper error message descriptions in error.localizedDescription and error.userInfo.

                                                                                                                                                                  MASFoundationErrorDomain
                                                                                                                                                                  objectivec
                                                                                                                                                                  static NSString *const MASFoundationErrorDomain = @"com.ca.MASFoundation:ErrorDomain";
                                                                                                                                                                  

                                                                                                                                                                  This error is returned when the SDK fails during communications: app registration, device registration, or user authentication with backend services. This can be caused by invalid configuration values, or misconfiguration on the backend services.

                                                                                                                                                                  The error should contain: 1) explanation of the error, 2) the backend services’ specific error code and error response in error.userInfo.

                                                                                                                                                                  MASFoundationErrorDomainLocal
                                                                                                                                                                  objectivec
                                                                                                                                                                  static NSString *const MASFoundationErrorDomainLocal = @"com.ca.MASFoundation.localError:ErrorDomain";
                                                                                                                                                                  

                                                                                                                                                                  This error is returned when the SDK fails because of client SDK configuration issues. The most common issues are: invalid JSON configuration file, misconfigured device settings (i.e. geolocation, BLE, or other permissions), or network issues.

                                                                                                                                                                  MASFoundationErrorDomainTargetAPI
                                                                                                                                                                  objectivec
                                                                                                                                                                  static NSString *const MASFoundationErrorDomainTargetAPI = @"com.ca.MASFoundation.targetAPI:ErrorDomain";
                                                                                                                                                                  

                                                                                                                                                                  This error is returned only when a custom endpoint on a backend service fails.

                                                                                                                                                                  Get notifications

                                                                                                                                                                  Optionally, you can observe the lifecycle notifications of the app registration, device registration and user authentication. These notifications are defined in MASConstants as shown below:

                                                                                                                                                                  // SDK initialization lifecycle notifications
                                                                                                                                                                  MASWillStartNotification
                                                                                                                                                                  MASDidFailToStartNotification
                                                                                                                                                                  MASDidStartNotification
                                                                                                                                                                  MASWillStopNotification
                                                                                                                                                                  MASDidFailToStopNotification
                                                                                                                                                                  MASDidStopNotification
                                                                                                                                                                  
                                                                                                                                                                  // SDK initialization server switch notifications
                                                                                                                                                                  MASWillSwitchGatewayServerNotification
                                                                                                                                                                  MASDidSwitchGatewayServerNotification
                                                                                                                                                                  
                                                                                                                                                                  // Device de-registration notifications
                                                                                                                                                                  MASDeviceWillDeregisterNotification
                                                                                                                                                                  MASDeviceDidFailToDeregisterNotification
                                                                                                                                                                  MASDeviceDidDeregisterNotification
                                                                                                                                                                  
                                                                                                                                                                  // User authentication notifications
                                                                                                                                                                  MASUserWillAuthenticateNotification
                                                                                                                                                                  MASUserDidFailToAuthenticateNotification
                                                                                                                                                                  MASUserDidAuthenticateNotification
                                                                                                                                                                  MASUserWillLogoutNotification
                                                                                                                                                                  MASUserDidFailToLogoutNotification
                                                                                                                                                                  MASUserDidLogoutNotification
                                                                                                                                                                  MASUserWillUpdateInformationNotification
                                                                                                                                                                  MASUserDidFailToUpdateInformationNotification
                                                                                                                                                                  MASUserDidUpdateInformationNotification
                                                                                                                                                                  
                                                                                                                                                                  

                                                                                                                                                                  Troubleshoot Your App

                                                                                                                                                                  Your App and the MAG Server

                                                                                                                                                                  This section describes some of the issues that arise between your app and the MAG server.

                                                                                                                                                                  msso_config.json file

                                                                                                                                                                  The msso_config.json file is how the Mobile SDK communicates with the MAG server. It contains OAuth scope values that provide permissions to operations and access to resources for your app. If the file has missing or incorrect scopes, this can cause errors.

                                                                                                                                                                  Scope help for Admins:

                                                                                                                                                                  Apps and Backend Policies

                                                                                                                                                                  When troubleshooting your app, it is helpful to know the methods that have associated policy and configuration on the MAG server. Your Admin can use this list to find the root cause.

                                                                                                                                                                  Mobile Policies

                                                                                                                                                                  • Enterprise Browser: Policy configuration
                                                                                                                                                                  • Geolocation: Policy configuration: enable/disable.
                                                                                                                                                                  • BLE: Policy configuration: enable/disable. BLE values can be changed by Admin in msso_config.json
                                                                                                                                                                  • QR Code: Policy configuration: enable/disable.
                                                                                                                                                                  • NFC: Policy configuration: enable/disable.
                                                                                                                                                                  • OTP: Policy configuration
                                                                                                                                                                  • UI Templates: No policy configuration
                                                                                                                                                                  • Messaging and Pub/Sub: Policy configuration
                                                                                                                                                                  • Users and Groups: Policy configuration
                                                                                                                                                                  • Storage, Cloud: Policy configuration
                                                                                                                                                                  • Storage, Local: No policy configuration

                                                                                                                                                                  Error Codes

                                                                                                                                                                  Error Codes

                                                                                                                                                                  An iOS error is returned with three components to help identify the problem:

                                                                                                                                                                  • Error domain – one of the following MASFoundation domains described below
                                                                                                                                                                  • Error code
                                                                                                                                                                  • Error description

                                                                                                                                                                  Error Domain=com.ca.MASFoundation:ErrorDomain Code=1000202 “Username or password invalid” UserInfo={status-code=401} The error code is returned in the response header in the x-ca-err field: x-ca-err = 1000202.

                                                                                                                                                                  MASFoundation

                                                                                                                                                                  The MASFoundation domain indicates a MAG server endpoint error. These errors occur during SDK startup, and include errors on registering the client, registering the device, and authenticating the user.

                                                                                                                                                                  static NSString *const MASFoundationErrorDomain = @"com.ca.MASFoundation:ErrorDomain";
                                                                                                                                                                  

                                                                                                                                                                  MASFoundation.localError

                                                                                                                                                                  The MASFoundation.localError domain indicates a client side error, for example:

                                                                                                                                                                  • Invalid or missing JSON format of msso_config.json
                                                                                                                                                                  • No geolocation available or unauthorized geolocation
                                                                                                                                                                  • No internet access
                                                                                                                                                                  • Invalid id_token format or id_token expiration when the client first receives the id_token
                                                                                                                                                                  • App missing or invalid URL problems in Enterprise Browser
                                                                                                                                                                  static NSString *const MASFoundationErrorDomainLocal = @"com.ca.MASFoundation.localError:ErrorDomain";
                                                                                                                                                                  

                                                                                                                                                                  MASFoundation.targetAPI

                                                                                                                                                                  The MASFoundation.targetAPI domain indicates a target endpoint error. These are errors thrown by the app that are outside the range 200-299. You define these errors. However, the following errors are reserved:

                                                                                                                                                                  • xxxx990 Access Token Expired

                                                                                                                                                                  Although the access_token is accepted by the MAG server, the app server considers the token expired. This can occur when the MAG server and the app server are not synchronized. In this case, the Access Token is expired, the token is removed from the keychain, and the process flow is repeated – this time without an access token. With no access token, a refresh token is issued.

                                                                                                                                                                  • xxxx991 Access Token Not Granted

                                                                                                                                                                  The API requires a SCOPE value that the request does not contain.

                                                                                                                                                                  • xxxx992 No Access Token

                                                                                                                                                                  The access_token was not included in the request, or the same access_token was included more than once in the same request.

                                                                                                                                                                  • xxxx993 Token is disabled

                                                                                                                                                                  The associated client is disabled.

                                                                                                                                                                  • xxxx000 Unknown
                                                                                                                                                                  static NSString *const MASFoundationErrorDomainTargetAPI = @"com.ca.MASFoundation.targetAPI:ErrorDomain";
                                                                                                                                                                  

                                                                                                                                                                  Reset the App

                                                                                                                                                                  During app testing (or other administrative/devops use cases), you may need to reset the app and clean up the local cache on the device. Conditions that can lead to resetting the app include:

                                                                                                                                                                  • You get a ‘Device Unknown’ error message
                                                                                                                                                                  • The device record has been removed on the MAG
                                                                                                                                                                  • You get an error message that the device is already registered

                                                                                                                                                                  Use the following method to deregister the device and remove the record on MAG. Note that all apps associated with the device are deregistered.

                                                                                                                                                                  Deregister a device

                                                                                                                                                                    [[MASDevice currentDevice] deregisterWithCompletion:^(BOOL completed, NSError *error) {
                                                                                                                                                                        if (completed && error != nil)
                                                                                                                                                                        {
                                                                                                                                                                            // The device is successfully deregistered.
                                                                                                                                                                        }
                                                                                                                                                                        else {
                                                                                                                                                                            //Handle the error
                                                                                                                                                                        }
                                                                                                                                                                    }];
                                                                                                                                                                    
                                                                                                                                                                    MASDevice.current()?.deregister(completion: { (completed, error) in
                                                                                                                                                                        if (completed && error != nil) {
                                                                                                                                                                            // The device is successfully deregistered.
                                                                                                                                                                        }
                                                                                                                                                                        else {
                                                                                                                                                                            //Handle the error
                                                                                                                                                                        }
                                                                                                                                                                    })
                                                                                                                                                                    

                                                                                                                                                                    iTunes Store Operation failed

                                                                                                                                                                    If you installed the Mobile SDK using the binaries, and you did not add our custom script to remove the simulator architecture, you get the following error when you deploy your app to the Apple Store:

                                                                                                                                                                    Archive validation failed with errors: iTunes Store Operation failed. Unsupported Architectures. Your executable contains unsupported architecture '[x86_64, i386]

                                                                                                                                                                    To fix this, follow steps 7-10 to remove the simulator file, recompile your project, and submit again.

                                                                                                                                                                    SSL Pinning Validation Failed

                                                                                                                                                                    ErrorDomain Code=100212"SSL pinning validation failed: ensure the target domain’s MASSecurityConfiguration is correctly configured."

                                                                                                                                                                    This error means that the server security configuration in the MASSecurityConfiguration object for the hostname:portnumber is not valid or is missing. See Create the MASSecurityConfiguration object.

                                                                                                                                                                    Errors on Specific iOS Devices

                                                                                                                                                                    If you have SDK errors that are occurring only on specific iOS devices, environments or settings, verify that devices have a supported version of the platform. The Mobile SDK is tested only on devices using official iOS platform versions. Next, verify that devices have not been jailbroken, and the OS has not been unlocked. The Mobile SDK can behave in unexpected ways if any of these conditions are true.

                                                                                                                                                                    General Errors

                                                                                                                                                                    MQTT broker is unresponsive

                                                                                                                                                                    Ask your Admin to verify that the broker is running. The broker may need to be restarted.

                                                                                                                                                                    Requests are not being handled

                                                                                                                                                                    If you see the error: Error Subscribing to Topic: (128), ask your Admin to check the server-side message topic format for unsupported characters (+ and #).

                                                                                                                                                                    Invalid Data Key

                                                                                                                                                                    **Error:**Invalid data key

                                                                                                                                                                    MAS Storage only supports datakeys that use upper and lowercase alphanumeric characters, plus these additional characters: - \ . __

                                                                                                                                                                    SDK Sample App Fails

                                                                                                                                                                    Error: code –999

                                                                                                                                                                    This error occurs when the sample app fails to connect to the MAG server. It is a MAG certificate configuration issue that must be resolved by your Admin. You may need an updated msso_config.json file.

                                                                                                                                                                    Disable PKCE

                                                                                                                                                                    Proof Key for Code Exchange (PKCE) provides an extra layer of security for your app. It is enabled by default and works with proximity login. Your Admin does not need to enable the feature on the MAG server. In the enabled state, the Mobile SDK responds to authentication requests or not, based on the policy that is configured by your Admin using OAuth Toolkit. We recommend leaving this feature enabled. However, if you have a specific use case to disable it, go to Reference documentation and change the state: [MAS enablePKCE:YES];

                                                                                                                                                                    Starting the SDK

                                                                                                                                                                    Authentication errors

                                                                                                                                                                    If you get invalid token, unauthorized, or other authentication errors, it may be due to a MAG server change. Your Admin must change a client parameter (documented in the 4.0 Release Notes) to allow more than one token per user/client (default). Without making the server changes, the Mobile SDK will not allow the same user to log in to multiple apps instances.

                                                                                                                                                                    MAS start method error

                                                                                                                                                                    This method performs the necessary validations for the SDK to properly work. If something is missing or invalid, the method returns an error with a description of the problem. Common problems are:

                                                                                                                                                                    • You forgot to add the msso_config.json file to the project
                                                                                                                                                                    • You forgot to reset the simulator after replacing the msso_config.json file with the new one that has different host

                                                                                                                                                                    Cannot identify location error

                                                                                                                                                                    In the Simulator, manually set the location.

                                                                                                                                                                    Device is already registered error

                                                                                                                                                                    This error usually occurs during app testing when you are hitting valid MAG server policy logic that prohibits re-registration for production environments. Specifically,

                                                                                                                                                                    • The device is already registered (mag-identifier and username match an existing registration)
                                                                                                                                                                    • MAG server is not configured to accept registration updates

                                                                                                                                                                    For details, see App Testing

                                                                                                                                                                    Registered device is invalid error

                                                                                                                                                                    Any of the following:

                                                                                                                                                                    • Existing registration was found but could not be updated
                                                                                                                                                                    • Certificate DN is already registered
                                                                                                                                                                    • Certificate DN is too long and exceeds the maximum length

                                                                                                                                                                    Device is not registered error

                                                                                                                                                                    Verify that the SharedKeychain is enabled in the project Capabilities tab.

                                                                                                                                                                    TLS connection version error

                                                                                                                                                                    The iOS requires TLS v1.2 as minimum. The default MAG installation is configured with TLS 1.0. Add a KEY into the app info.plist so MAG can successfully connect.

                                                                                                                                                                    SDK tries to connect to old host instead of the new one from the msso_config.json file

                                                                                                                                                                    The SDK relies on the keychain to store information. To reload data for a new msso_config.json file, the keychain must be cleaned. Follow these steps based on where you are working:

                                                                                                                                                                    • In the Simulator Select “Reset content and Settings” from the Simulator top menu bar.

                                                                                                                                                                    • In the Device Implement the code to call deregisterWithCompletion from the currentDevice object. That is: MASDevice *myDevice = [MASDevice currentDevice] [myDevice deregisterWithCompletion:...

                                                                                                                                                                    Identity Management

                                                                                                                                                                    Identity Management methods are not working

                                                                                                                                                                    The currentUser may be set to ‘NOT nil’, which will not work. To use Messaging and/or Identity Management features, a valid authenticated user is required.

                                                                                                                                                                    User Authentication

                                                                                                                                                                    MASUser loginWithUsername error

                                                                                                                                                                    A user is trying to log in after a different user has already logged in. From the currentUser, call logout to clean the session, and try again.

                                                                                                                                                                    BLE Issues

                                                                                                                                                                    The following are known issues with proximity login across Android and iOS devices.

                                                                                                                                                                    iOS (peripheral device) and Android (central device)

                                                                                                                                                                    When the iOS device is acting as a peripheral device (advertising authenticated session), and the Android device is acting as the central device (scanning for the authenticated session), the iOS device gets the following error. The Android device should still be able to register/authenticate the device.

                                                                                                                                                                    Error Domain=com.ca.MASFoundation.localError:ErrorDomain Code=48 “BLE authorization failed due to no subscribed central device.” UserInfo={NSLocalizedDescription=BLE authorization failed due to no subscribed central device.}

                                                                                                                                                                    iOS (central device) and Android (peripheral device)

                                                                                                                                                                    When the iOS device is acting as the central device (scanning for the authenticated session), and the Android device is acting as the peripheral device (advertising the authenticated session), the iOS device gets the following error. However, the iOS device should still be able to register/authenticate the device/user.

                                                                                                                                                                    Error Domain=com.ca.MASFoundation.localError:ErrorDomain “No attribute is found.” UserInfo={NSLocalizedDescription=No attribute is found.}

                                                                                                                                                                    Video Tutorials

                                                                                                                                                                    Click to view the first video, and others in the playlist.

                                                                                                                                                                    ADVANCED USE CASES

                                                                                                                                                                    This section provides solutions that solve specific and immediate customer requests. They may not have the tight coupling between backend and the SDK that we normally provide, but they work. They just require more collaboration between Admins and developers to implement. Hope you find them useful!

                                                                                                                                                                    Send HTTP Requests to External APIs

                                                                                                                                                                    You can send HTTP requests to APIs hosted in others servers (another MAG or other public server). The MASSecurityConfiguration object registers the external server as a trusted source.

                                                                                                                                                                    For how to use this feature, see Blog: How to Make Secure Calls to APIs from External Servers

                                                                                                                                                                    Support

                                                                                                                                                                    The Mobile SDK supports:

                                                                                                                                                                    • Sending requests to external APIs with these security features: SSL pinning method, evaluate the certificate against root certificates on device, default credentials injection on API calls
                                                                                                                                                                    • Only certificate signature algorithm SHA256 with RSA 2048 bits
                                                                                                                                                                      (SSL pinning will fail if you use other algorithms.)

                                                                                                                                                                    Create the MASSecurityConfiguration object

                                                                                                                                                                    To send HTTP requests to another MAG or public server, you must configure the MASSecurityConfiguration object. The following security settings are per hostname and port number. The only required attribute is the host; if the host is not set, the request fails with an SSL pinning error.

                                                                                                                                                                    Attribute Description Required? Default
                                                                                                                                                                    host NSURL object containing the schema, hostname, and port number. Yes Null
                                                                                                                                                                    isPublic Boolean value that includes (or not), credentials from the primary gateway to the target host for network communication. No false
                                                                                                                                                                    certificates The array of string that contains the pinned certificate (like in the msso_config.json file). No Null
                                                                                                                                                                    publicKeyHashes String value of the pinned public key hashes in base64 format. No Null
                                                                                                                                                                    trustPublicPKI Boolean value that validates the trusted server (or not) against the iOS trusted root certificates. No false
                                                                                                                                                                    validateDomainName Boolean value that validates (or not) the domain name of the certificate on the trusted server. No false

                                                                                                                                                                    Example

                                                                                                                                                                      //
                                                                                                                                                                      // Create a URL from string with schema, target host and port
                                                                                                                                                                      //
                                                                                                                                                                      NSURL *url = [NSURL URLWithString:@"https://itunes.apple.com:443"];
                                                                                                                                                                      
                                                                                                                                                                      //
                                                                                                                                                                      // Configure security settings for the target host
                                                                                                                                                                      //
                                                                                                                                                                      MASSecurityConfiguration *securityConfiguration = [[MASSecurityConfiguration alloc] initWithURL:url];
                                                                                                                                                                      securityConfiguration.isPublic = YES;
                                                                                                                                                                      securityConfiguration.certificates = @[@""-----BEGIN CERTIFICATE-----"",@"MIIDa+Xyz....XqAj",....,@"9QFwgk8yz....XqZ=",@"Aa+TQz....SxB",@"-----END CERTIFICATE-----"];
                                                                                                                                                                      securityConfiguration.publicKeyHashes = @[@"Ha+Xyz....XqU="];
                                                                                                                                                                      
                                                                                                                                                                      NSError *error = nil;
                                                                                                                                                                      [MASConfiguration setSecurityConfiguration:securityConfiguration error:&error];
                                                                                                                                                                      
                                                                                                                                                                      if (error)
                                                                                                                                                                      {
                                                                                                                                                                      	// check for an error
                                                                                                                                                                      }
                                                                                                                                                                      else {
                                                                                                                                                                      	// security configuration is successfully set
                                                                                                                                                                      }
                                                                                                                                                                      
                                                                                                                                                                      //
                                                                                                                                                                      // Create a URL from string with schema, target host and port
                                                                                                                                                                      //
                                                                                                                                                                      var url = URL.init(string: "https://itunes.apple.com:443")
                                                                                                                                                                      
                                                                                                                                                                      //
                                                                                                                                                                      // Configure security settings for the target host
                                                                                                                                                                      //
                                                                                                                                                                      var securityConfiguration = MASSecurityConfiguration.init(url: url!)
                                                                                                                                                                      securityConfiguration.isPublic = true
                                                                                                                                                                      
                                                                                                                                                                      securityConfiguration.certificates = ["-----BEGIN CERTIFICATE-----","MIIDa+Xyz....XqAj",....,"9QFwgk8yz....XqZ=","Aa+TQz....SxB","-----END CERTIFICATE-----"]
                                                                                                                                                                      securityConfiguration.publicKeyHashes = ["Ha+Xyz....XqU="]
                                                                                                                                                                      
                                                                                                                                                                      var error:Error
                                                                                                                                                                      MASConfiguration.setSecurity(securityConfiguration, error: error)
                                                                                                                                                                      if (error != nil)
                                                                                                                                                                      {
                                                                                                                                                                          // check for an error
                                                                                                                                                                      }
                                                                                                                                                                      else
                                                                                                                                                                      {
                                                                                                                                                                          //security configuration is successfully set
                                                                                                                                                                      }
                                                                                                                                                                      

                                                                                                                                                                      Invoke an API from external server

                                                                                                                                                                      The only difference between making HTTP requests to a MAG, versus to external servers, is that you must provide the full URL instead of the relative path. For example, if your full URL is https://somegatewayhost:port/some/endpoint, you would pass this value as endPointPath parameter in the SDK CRUD methods (for example, MAS.getFrom / MAS.postTo / MAS.deleteFrom).

                                                                                                                                                                      Example

                                                                                                                                                                        //
                                                                                                                                                                        // Configure security settings for the target host
                                                                                                                                                                        //
                                                                                                                                                                        [MAS getFrom:@"https://itunes.apple.com/search?term=red+hot+chili+peppers&entity=musicVideo" withParameters:nil andHeaders:nil requestType:MASUnitRequestResponseTypeJsonValue responseType:MASRequestResponseTypeJson completion:^(NSDictionary *responseInfo, NSError *error) {
                                                                                                                                                                        
                                                                                                                                                                            //
                                                                                                                                                                            // Handle completion
                                                                                                                                                                            //
                                                                                                                                                                        }];
                                                                                                                                                                        
                                                                                                                                                                        //
                                                                                                                                                                        // Configure security settings for the target host
                                                                                                                                                                        //
                                                                                                                                                                        MAS.getFrom("https://itunes.apple.com/search?term=red+hot+chili+peppers&entity=musicVideo", withParameters: nil, andHeaders: nil, request: MASRequestResponseType.json, responseType: MASRequestResponseType.json) { (responseInfo, error) in
                                                                                                                                                                            
                                                                                                                                                                            //
                                                                                                                                                                            // Handle completion
                                                                                                                                                                            //
                                                                                                                                                                        }
                                                                                                                                                                        

                                                                                                                                                                        Validate Data Recipients Using JWT

                                                                                                                                                                        This solution describes how to use JSON Web Token (JWT) to validate recipients using the Mobile SDK and MAG.

                                                                                                                                                                        Why JWT?

                                                                                                                                                                        JWT is an open standard (RFC 7519) that is used to pass information between parties for web apps. Here is a quick summary of why JWT is used for mobile apps.

                                                                                                                                                                        Benefit Description
                                                                                                                                                                        Self contained The tokens are designed to be compact, self-contained, and URL-safe. A JWT consists of a header, payload (usually user information), and a signature.
                                                                                                                                                                        JWT


                                                                                                                                                                        JWTs can be signed using a secret using an HMAC algorithm or a public/private key pair using algorithms such as RSA. For HMAC, the secret is known on the client and the server. For RSA, the signing party owns the private key, and the validating party knows the public key.
                                                                                                                                                                        Easily passed JWTs can be passed as a header or parameter for authenticating users and securing data.
                                                                                                                                                                        Multiple programming language support JWT supports many programming languages including .NET, Python, Node.js, PHP, Java, Ruby, Go, Haskell, and JavaScript.

                                                                                                                                                                        More JWT Resources

                                                                                                                                                                        Business Value

                                                                                                                                                                        Modern apps must be able to access and co-mingle with other third-party app data. JWT can validate that “someone is who they say they are,” which adds another layer of security beyond mutual SSL and OAuth. Key capabilities include:

                                                                                                                                                                        • Authentication for MSSO
                                                                                                                                                                          User authentication is the most common use case for using JWT. JWT is widely used by Mobile Single Sign On (MSSO) because of its small overhead and ease-of-use across different domains. For example, after a user logs in, the token that is used for login can permit the user to access other resources, routes, and services.

                                                                                                                                                                        • Message exchange and verification
                                                                                                                                                                          JWT allows secure data exchange between parties. Implementing JWT can stop MSSO-related, “man in the middle” hacking scenarios. For example, a user logs in to app A, a hacker accesses app A, and then gets access to all other apps (B, C, and so on). Signed JTW ensures that the content has not been tampered with. And if you are implementing digital signatures, JWT is used to pass the signed message between the Mobile SDK and the MAG server.

                                                                                                                                                                        Use Cases

                                                                                                                                                                        Let’s say a bank offers a mobile banking app. And a user wants to transfer money from their account to a brother’s account. Or the user wants to withdraw a huge amount of money to buy a house. The mobile banking app needs a “funds transfer” API. From a bank perspective, mutual SSL and OAuth may not be enough. Banks may want to validate that the transfer is being made from the correct sender (and not someone impersonating the sender). In this example, using JWT for digital signatures can provide this extra layer of security. Just like anyone can read a contract to buy a house,but the user signature on the contract is the validation of trust for the final transaction.

                                                                                                                                                                        Support and Limitations

                                                                                                                                                                        The Mobile SDK includes:

                                                                                                                                                                        • Methods for developers to create signed JWT (iOS and Android)
                                                                                                                                                                          • MAS.sign(MASClaims)
                                                                                                                                                                          • MAS.sign(MASClaims, privateKey)

                                                                                                                                                                        For help with server-side JSON Web Token assertions, see the CA API Gateway documentation.

                                                                                                                                                                        Limitations

                                                                                                                                                                        The Mobile SDK generates a key pair, and verifies the JWT. To fully implement JWT, Admins must create their own policy and solution. Specifically, this version does not support these client and server features:

                                                                                                                                                                        • Methods to generate your own key pair (private/public)
                                                                                                                                                                        • Methods to verify a signed JWT
                                                                                                                                                                        • Methods to encrypt the signed JWT
                                                                                                                                                                        • Signing a payload to more than one MAG server
                                                                                                                                                                        • Other hashing algorithms for signing payloads other than RS256
                                                                                                                                                                        • Signing query parameters
                                                                                                                                                                        • Signing using fingerprint private key

                                                                                                                                                                        Implementation

                                                                                                                                                                        All workflows for iOS validate that:

                                                                                                                                                                        • Messages are created by an authenticated sender; the sender cannot deny having sent the message
                                                                                                                                                                        • Message content is not altered during transit after receiving a JWT
                                                                                                                                                                        Sign doc: with default private key

                                                                                                                                                                        The following diagram shows a workflow for signing a document using the default private key.

                                                                                                                                                                        Custom Private Key

                                                                                                                                                                        In this workflow:

                                                                                                                                                                        1. The client generates the private/public key pairs
                                                                                                                                                                        2. The client sends a CSR and receives the signed public key from the MAG server (at the end of the registration flow)
                                                                                                                                                                        3. The client uses the payload to create a signed JWT using its private key
                                                                                                                                                                        4. The client sends the JWT to the MAG server
                                                                                                                                                                        5. The MAG server validates the JWT with the registered public key
                                                                                                                                                                        Sign doc: with custom private key

                                                                                                                                                                        The following diagram shows the workflow for signing a document using device registration using a custom private key.

                                                                                                                                                                        Default Private Key

                                                                                                                                                                        JWT Claims

                                                                                                                                                                        The Mobile SDK injects the following default claims and values into the JWT; they represent a minimum set to validate the JWT. All claims are optional and customizable; the developer can override all defaults.

                                                                                                                                                                        Claim Description MAG Example
                                                                                                                                                                        iss Issuer of the JWT. The MAG identifier and the client id.
                                                                                                                                                                        device://{mag-identifier}/{client_id}
                                                                                                                                                                        aud Audience or recipient(s) of the JWT. The MAG server.
                                                                                                                                                                        https://mag2.ca.com
                                                                                                                                                                        sub Subject of the JWT (user who is associated with the action). ‘username’ or ‘client_id’ based on the currently-authenticated session.
                                                                                                                                                                        exp Expiration time. Do not process JWT claim on/after this time. 148761933.
                                                                                                                                                                        Default = 5 minutes from when the request was issued.
                                                                                                                                                                        jti A unique identifier for the JWT. A randomly -generated UUID.
                                                                                                                                                                        4c3c2eca-f7a3-11e6-bc64-92361f002672
                                                                                                                                                                        iat Issued at time. Time when the JWT was created. 1487619335
                                                                                                                                                                        nbf Not before time. Do not process JWT claim before this time. 1487619335
                                                                                                                                                                        content Payload data that you want to sign using Put, Post, Delete. {“test”:“value”}
                                                                                                                                                                        contentType Data content type. application/json

                                                                                                                                                                        Sample Payloads

                                                                                                                                                                        Data Format Content Type
                                                                                                                                                                        { “test”: “value” } application/json

                                                                                                                                                                        Server Received JWT

                                                                                                                                                                        Header
                                                                                                                                                                        {“alg”:“RS256”}

                                                                                                                                                                        Payload

                                                                                                                                                                        {
                                                                                                                                                                          "iss": "device://${mag-identifier}/${client_id}",
                                                                                                                                                                          "aud": "https://mag2.ca.com",
                                                                                                                                                                          "sub": "karek",
                                                                                                                                                                          "exp": 148761933,
                                                                                                                                                                          "jti": "4c3c2eca-f7a3-11e6-bc64-92361f002672",
                                                                                                                                                                          "iat": 1487619335,
                                                                                                                                                                          "content": {
                                                                                                                                                                            "test": "value"
                                                                                                                                                                          },
                                                                                                                                                                          "content-type" : "application/json"
                                                                                                                                                                        }
                                                                                                                                                                        
                                                                                                                                                                        Data Format Content Type
                                                                                                                                                                        “test” text/plain

                                                                                                                                                                        Header
                                                                                                                                                                        {“alg”:“RS256”}

                                                                                                                                                                        Payload

                                                                                                                                                                        {
                                                                                                                                                                          "iss": "device://${mag-identifier}/${client_id}",
                                                                                                                                                                          "aud": "https://mag2.ca.com",
                                                                                                                                                                          "sub": "karek",
                                                                                                                                                                          "exp": 148761933,
                                                                                                                                                                          "jti": "4c3c2eca-f7a3-11e6-bc64-92361f002672",
                                                                                                                                                                          "iat": 1487619335,
                                                                                                                                                                          "content": "test",
                                                                                                                                                                          "content-type" : "text/plain"
                                                                                                                                                                        }
                                                                                                                                                                        
                                                                                                                                                                        Data Format Content Type
                                                                                                                                                                        Bytes application/octet-stream or none

                                                                                                                                                                        Header
                                                                                                                                                                        {“alg”:“RS256”}

                                                                                                                                                                        Payload

                                                                                                                                                                        {
                                                                                                                                                                          "iss": "device://${mag-identifier}/${client_id}",
                                                                                                                                                                          "aud": "https://mag2.ca.com",
                                                                                                                                                                          "sub": "karek",
                                                                                                                                                                          "exp": 148761933,
                                                                                                                                                                          "jti": "4c3c2eca-f7a3-11e6-bc64-92361f002672",
                                                                                                                                                                          "iat": 1487619335,
                                                                                                                                                                          "content": "<base 64 encoded>",
                                                                                                                                                                          "content-type" : "image/png"
                                                                                                                                                                        }
                                                                                                                                                                        
                                                                                                                                                                        Data Format Content Type
                                                                                                                                                                        fromAcc=1234&toAcc=2345&amount=100 application/x-www-form-urlencoded

                                                                                                                                                                        Header
                                                                                                                                                                        {“alg”:“RS256”}

                                                                                                                                                                        Payload

                                                                                                                                                                        {
                                                                                                                                                                          "iss": "device://${mag-identifier}/${client_id}",
                                                                                                                                                                          "aud": "https://mag2.ca.com",
                                                                                                                                                                          "sub": "karek",
                                                                                                                                                                          "exp": 148761933,
                                                                                                                                                                          "jti": "4c3c2eca-f7a3-11e6-bc64-92361f002672",
                                                                                                                                                                          "iat": 1487619335,
                                                                                                                                                                          "content": {
                                                                                                                                                                            "fromAcc": [
                                                                                                                                                                              "1234"
                                                                                                                                                                            ],
                                                                                                                                                                            "toAcc": [
                                                                                                                                                                              "2345"
                                                                                                                                                                            ],
                                                                                                                                                                            "amount": [
                                                                                                                                                                              "100"
                                                                                                                                                                            ]
                                                                                                                                                                          },
                                                                                                                                                                          "content-type": "application/x-www-form-urlencoded"
                                                                                                                                                                        }
                                                                                                                                                                        

                                                                                                                                                                        Frequently Asked Questions

                                                                                                                                                                        Can I combine the workflows (default private key and custom private key)?
                                                                                                                                                                        Yes. Both these methods can be combined. Claims are overridden in the order they are called.

                                                                                                                                                                        Do I need to implement any new assertions to consume the JWT that the Mobile SDK generates?
                                                                                                                                                                        No. You can use the existing CA API Gateway assertions, Encode/Decode Json Web Token.