Skip to Content

Logon

Overview
Registering using the Logon Plugin
SAPUI5 and the Logon Plugin
Customizing the Logon Plugin’s UI
Timing and Performance
Registering using an X.509 Certificate on Android with SMP(SP15+)
Registering using an X.509 Certificate on Android with SAP Cloud Platform Mobile Services(SP15+)
Multiple Users

Overview

The Logon plugin manages the onboarding or registration process with a SAP Cloud Platform Mobile Services or SMP 3.0 server. When an application onboards to to a server, it receives a registration ID (also known as an application connection ID). In all subsequent requests to the server, the registration ID is sent via a cookie named X-SMP-APPCID that is set following a successful registration. This enables tracing functionality for each app instance.

The server integrates with common security providers such as HTTP/HTTPS Authentication, Directory Service (LDAP), SAML, or x.509 User Certificate. The Logon plugin provides a registration screen where the user can enter values needed to connect to the server. The server will validate the registration using one of the security providers and the connection details will be stored in the Logon plugin’s secure data vault. A basic authentication challenge can be handled automatically by the AuthProxy plugin using the stored registration credentials retrieved from the Logon plugin’s data vault.

The data vault used by the Logon plugin is separate from storage provided with the Encrypted Storage plugin and is used to store user names, passwords, keys and certificates while the Encrypted Storage plugin is better suited to storing application data.

For additional details on the Logon plugin, see C:\SAP\MobileSDK3\KapselSDK\docs\api\sap.Logon.html or Using the Logon Plugin.

For additional information on the underlying component that is exposed via the Logon plugin, the Developing with MAF Logon section explains how to use the MAF Logon in a native application.

Registering using the Logon Plugin

If you have not already done so, complete the previous section, Creating an Apache Cordova Project.

  • Add the Logon plugin and dialogs plugin.The following steps assume that a KAPSEL_HOME environment variable has been set to the location of the KapselSDK.
    Note the way to access an environment variable on windows is with a % sign while on a Mac it is a $.

    on Windows
    cordova plugin add kapsel-plugin-logon --searchpath %KAPSEL_HOME%/plugins
    
    on a Mac
    cordova plugin add kapsel-plugin-logon --searchpath $KAPSEL_HOME/plugins
    
    cordova plugin add cordova-plugin-dialogs
    

    An added plugin can be removed by calling

    cordova plugin remove plugin_id
  • A list of the plugins now included in the project can be seen by entering the below command.
    cordova plugins
    cordova-plugin-device 1.1.6 "Device"
    cordova-plugin-dialogs 1.3.3 "Notification"
    cordova-plugin-whitelist 1.3.2 "Whitelist"
    kapsel-plugin-authproxy 3.15.2 "AuthProxy"
    kapsel-plugin-corelibs 3.15.2 "CoreLibs"
    kapsel-plugin-i18n 3.15.2 "i18n"
    kapsel-plugin-inappbrowser 3.15.2 "InAppBrowser"
    kapsel-plugin-logon 3.15.2 "Logon"
    kapsel-plugin-ui5 3.15.2 "ui5"
    

    Notice that the plugins that the Logon plugin depends on are added if they are not present. This is defined in

    C:\Kapsel_Projects\KapselGSDemo\plugins\kapsel-plugin-logon\plugin.xml
  • This sample requires the application previously created in Configuring a Kapsel App in the Management Cockpit.
  • Replace www\index.html with the following contents.
    <!DOCTYPE html>
    <html>
        <head>
            <!--<meta http-equiv="Content-Security-Policy" content="default-src http://10.7.171.234:8080 'self' 'unsafe-inline' data: gap: https://ssl.gstatic.com 'unsafe-eval'; style-src 'self' 'unsafe-inline'; media-src *">-->
            <meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width">
            <script type="text/javascript" charset="utf-8" src="datajs-1.1.2.min.js"></script>
            <script type="text/javascript" charset="utf-8" src="serverContext.js"></script>
            <script type="text/javascript" charset="utf-8" src="cordova.js"></script>
            <script>
                var t0 = Number(location.href.split("startTime=")[1]); //Time passed in from main activity.
                var t1 = performance.now();
                var t2; //onLoad
                var t3; //deviceready
                var readStartTime;
                var logonInitTime;
                var unlockTime;
                var resumeTime;
                
                var applicationContext = null;
                
                
                window.onerror = onError;
                
                function onError(msg, url, line) {
                    var idx = url.lastIndexOf("/");
                    var file = "unknown";
                    if (idx > -1) {
                        file = url.substring(idx + 1);
                    }
                    alert("An error occurred in " + file + " (at line # " + line + "): " + msg);
                    return false; //suppressErrorAlert;
                }
                
                function init() {
                    updateStatus2("EventLogging: deviceready called");
                    t3 = performance.now();
                    console.log("EventLogging Perf: " + ((t3 - t1)/1000).toFixed(3) + " seconds from onLoad until deviceready");
    
                    if (sap.Logger) {
                        sap.Logger.setLogLevel(sap.Logger.DEBUG);  //enables the display of debug log messages from the Kapsel plugins.
                        sap.Logger.debug("EventLogging: Log level set to DEBUG");
                    }
                    
                    if (navigator.notification) { // Override default HTML alert with native dialog. alert is not supported on Windows
                        window.alert = navigator.notification.alert;
                    }
                    
                    register();
                }
                
                function logonSuccessCallback(result) {
                    updateStatus2("logonSuccessCallback called");
                    var endTime = performance.now();
                    var endTime2 = new Date();
                    console.log("EventLogging Perf: " + ((endTime - logonInitTime)/1000).toFixed(3) + " seconds for logon.init to complete");
                    console.log("EventLogging Perf: " + ((endTime - t1)/1000).toFixed(3) + " seconds from onload till datavault is unlocked");
                    console.log("EventLogging Perf: " + ((endTime2 - t0)/1000).toFixed(3) + " seconds from main activity till datavault is unlocked.  Requires modifying MainActivity.java");  //loadUrl(launchUrl + "?startTime=" + new java.util.Date().getTime());
                    console.log("EventLogging:  logonSuccessCallback " + JSON.stringify(result));
                    applicationContext = result;
                    showScreen("MainDiv");
                }
        
                //used when the logon plugin does not register with a server
                function logon2SuccessCallback(result) {
                    console.log("EventLogging: logon2SuccessCallback " + JSON.stringify(result));
                    applicationContext = result;
                    applicationContext.applicationEndpointURL = "https://sapes4.sapdevcenter.com/sap/opu/odata/IWFND/RMTSAMPLEFLIGHT";
                    showScreen("MainDiv");
                }
    
                function logonErrorCallback(error) {   //this method is called if the user cancels the registration.
                    alert("An error occurred:  " + JSON.stringify(error));
                    if (device.platform == "Android") {  //Not supported on iOS
                        navigator.app.exitApp();
                    }
                }
            
                function read() {
                    updateStatus2("Read request started");
                    readStartTime = performance.now();
                    if (!applicationContext) {
                        alert("Register or unlock before proceeding");
                        return;
                    }
                    clearTable();
                    sUrl = applicationContext.applicationEndpointURL + "/CarrierCollection?$format=json";  //JSON format is less verbose than atom/xml
                    var oHeaders = {};
                    //oHeaders['X-SMP-APPCID'] = applicationContext.applicationConnectionId;  //not needed as this will be sent by the logon plugin
                    
                    var request = {
                        headers : oHeaders,
                        requestUri : sUrl,
                        method : "GET"
                    };
    
                    if (device.platform == "windows") { //provided by the authproxy and logon plugins on Android and iOS but not on Windows
                        request.user = applicationContext.registrationContext.user;
                        request.password = applicationContext.registrationContext.password;
                    }
                    OData.read(request, readSuccessCallback, errorCallback);
                }
                
                function readSuccessCallback(data, response) {
                    var endTime = performance.now();
                    updateStatus2("Read " + data.results.length + " records in " + ((endTime - readStartTime)/1000).toFixed(3) + " seconds");
                    var carrierTable = document.getElementById("carrierTable");
                    
                    for (var i = data.results.length -1; i >= 0; i--) {
                        var row = carrierTable.insertRow(1);
                        var cell1 = row.insertCell(0);
                        var cell2 = row.insertCell(1);
                        cell1.innerHTML = data.results[i].carrid;
                        cell2.innerHTML = data.results[i].CARRNAME;
                    }
                }
                
                function clearTable() {
                    var carrierTable = document.getElementById("carrierTable");
                    while(carrierTable.rows.length > 1) {
                        carrierTable.deleteRow(1);
                    }
                }
                
                function errorCallback(e) {
                    alert("An error occurred: " + JSON.stringify(e));
                    showScreen("MainDiv");
                }
                
                function register() {
                    logonInitTime = performance.now();
                    updateStatus2("Calling Logon.init");
                    sap.Logon.init(logonSuccessCallback, logonErrorCallback, appId, context);
                    //sap.Logon.initPasscodeManager(logon2SuccessCallback, errorCallback, appId);  //initialize the datavault without registering with an SMP server
                }
                
                function unRegister() {
                    updateStatus2("Calling deleteRegistration");
                    showScreen("LoadingDiv");
                    sap.Logon.core.deleteRegistration(logonUnregisterSuccessCallback, errorCallback);
                    //sap.Logon.deletePasscodeManager(logonUnregisterSuccessCallback, errorCallback);  //Used when the datavault is initialized with initPasscodeManager
                    clearTable();
                }
    
                function logonUnregisterSuccessCallback(result) {
                    updateStatus2("Successfully Unregistered");
                    console.log("EventLogging: logonUnregisterSuccessCallback " + JSON.stringify(result));
                    applicationContext = null;
                    register();
                }
                
                function lock() {
                    sap.Logon.lock(logonLockSuccessCallback, errorCallback);
                    clearTable();
                }
    
                function logonLockSuccessCallback(result) {
                    console.log("EventLogging: logonLockSuccessCallback " + JSON.stringify(result));
                    showScreen("LockedDiv");
                }
    
                function unlock() {
                    unlockTime = performance.now();
                    sap.Logon.unlock(unlockSuccessCallback, errorCallback);
                }
    
                function unlockSuccessCallback(result) {
                    updateStatus2("unLockSuccessCallback called");
                    var endTime = performance.now();
                    console.log("EventLogging Perf: " + ((endTime - unlockTime)/1000).toFixed(3) + " seconds to unlock the datavault");
                    showScreen("MainDiv");
                }
    
                function managePasscode() {
                    sap.Logon.managePasscode(managePasscodeSuccessCallback, errorCallback);
                }
    
                function managePasscodeSuccessCallback() {
                    console.log("EventLogging: managePasscodeSuccess");
                }
                
                function showScreen(screenIDToShow) {
                    var screenToShow = document.getElementById(screenIDToShow);
                    screenToShow.style.display = "block";
                    var screens = document.getElementsByClassName('screenDiv');
                    for (var i = 0; i < screens.length; i++) {
                        if (screens[i].id != screenToShow.id) {
                            screens[i].style.display = "none";
                        }
                    }
                }
    
                function updateStatus2(msg) {
                    var d = new Date();
                    document.getElementById('statusID2').innerHTML = msg + " at " + addZero(d.getHours()) + ":" + addZero(d.getMinutes()) + ":" + addZero(d.getSeconds());
                    console.log("EventLogging: " + msg + " at " + addZero(d.getHours()) + ":" + addZero(d.getMinutes()) + ":" + addZero(d.getSeconds() + "." + addZero(d.getMilliseconds())));
                }
                
                function addZero(input) {
                    if (input < 10) {
                         return "0" + input;
                    }
                    return input;
                }
    
                function onLoad() {
                    console.log("EventLogging: onLoad");
                    t2 = performance.now();
                    var endTime = new Date();
                    console.log("EventLogging Perf: " + ((endTime - t0)/1000).toFixed(3) + " seconds between MainActivity and onLoad");
                }
    
                function onBeforeUnload() {
                    console.log("EventLogging: onBeforeUnLoad");
                }
    
                function onUnload() {
                    console.log("EventLogging: onUnload");
                }
    
                function onPause() {
                    console.log("EventLogging: onPause");
                }
    
                function onResume() {
                    resumeTime = performance.now();
                    console.log("EventLogging: onResume");
                }
    
                function onSapResumeSuccess() {
                    console.log("EventLogging: onSapResumeSuccess");
                    var endTime = performance.now();
                    console.log("EventLogging Perf: " +  ((endTime - resumeTime)/1000).toFixed(3)  + " seconds from onresume to onSapResumeSuccess");
                }
    
                function onSapLogonSuccess() {
                    console.log("EventLogging: onSapLogonSuccess");
                }
    
                function onSapResumeError(error) {
                    console.log("EventLogging: onSapResumeError " + JSON.stringify(error));
                }
                
                document.addEventListener("deviceready", init, false);
                document.addEventListener("pause", onPause, false);
                document.addEventListener("resume", onResume, false);
                document.addEventListener("onSapResumeSuccess", onSapResumeSuccess, false);
                document.addEventListener("onSapLogonSuccess", onSapLogonSuccess, false);
                document.addEventListener("onSapResumeError", onSapResumeError, false);
            </script>
            
        </head>
        <body onload="onLoad()" onunload="onUnload()" onbeforeunload="onBeforeUnload()">
            <div class="screenDiv" id="LoadingDiv">
                <h1>Loading ...</h1>
            </div>
            
            <div class="screenDiv" id="LockedDiv" style="display: none">
                <h1>Locked</h1>
                <button id="unlock2" onclick="unlock()">Unlock</button>
            </div>
            
            <div class="screenDiv" id="MainDiv" style="display: none">
                <h1>Logon Sample</h1>
                <button id="read" onclick="read()">Read</button>
                <button id="unregister" onclick="unRegister()">Unregister</button>
                <button id="lock" onclick="lock()">Lock</button>
                <button id="unlock" onclick="unlock()">Unlock</button>
                <button id="managePasscode" onclick="managePasscode()">Manage Passcode</button><br>
                <span id="statusID2"></span>
                <table id="carrierTable"><tr><th>Carrier ID</th><th>Carrier Name</th></tr></table>
            </div>
        </body>
    </html>
    ​

    Create a file named serverContext.js in the www folder and copy the content from below.

    var appId = "com.kapsel.gs"; // Change this to app id on server
    
    // Optional initial connection context
    var context = {
        "serverHost": "10.7.168.212", //Place your SMP 3.0 server name here
        //"serverHost": "hcpms-i82XXXXtrial.hanatrial.ondemand.com", //SAP Cloud Platform Mobile Services
        "https": false,  //true for SAP Cloud Platform Mobile Services
        "serverPort": "8080",  //443 for SAP Cloud Platform Mobile Services
        //"multiUser": true,
        //"useLocalStorage": false,
        "user": "i82XXXX",          //For demo purposes, specify the user name and password you wish to register with here to save typing on the device 
        "password": "XXXXX",        //Note, if you wish to use this user name and password to be passed to the backend OData producer, choose Basic as the SSO mechanism
                                    //The AuthProxy plugin with the Logon plugin can respond to 401 Authentication challenges if the same credentials used to register are also used to make OData requests
                                    //Once set the credentials can be changed by calling sap.Logon.changePassword()
        "passcode": "password",     //Hardcoding passwords and unlock passcodes are strictly for ease of use during development
                                    //Passcode can be changed by calling sap.Logon.managePasscode()
        "unlockPasscode": "password",
        "passcode_CONFIRM": "password",
        "oldPasscode" : "password",
        "communicatorId": "REST",
        
        //"auth": [ { "type": "saml2.web.post" } ], //Indicates that a redirect to a SAML IDP should be used for registration
        //"refreshSAMLSessionOnResume": "skip",  // Useful for offline apps when you may not wish for a saml check to be performed when the app is resumed since you may be offline
        
        "custom": {
            "hiddenFields": ["farmId", "resourcePath", "securityConfig", "serverPort", "https"]
        }
    };
    

    Edit the serverHost, user, and password variables in serverContext.js.

    Note if you are using an Android emulator and your server is running on the same machine, 10.0.2.2 can be used to reach the host machine from the emulator.

    Note, that the application ID is com.kapsel.gs and is passed in as a parameter to the method sap.Logon.init(). The logonSuccessCallback method stores an object containing the logon parameters into the applicationContext variable.

    function logonSuccessCallback(result) {
        ...
        applicationContext = result;
        showScreen("MainDiv")
    }
    
  • Place a copy of datajs-1.1.2.min.js into the www folder. For additional details on datajs and OData see Appendix A: OData.
  • If you are targeting Windows 10, add the following to your config.xml file.
    <preference name="windows-target-version" value="10.0" />
    
  • Prepare, build and deploy the app with the following commands.
    cordova run android
    or
    cordova run windows -- --archs=x64 
    or
    cordova run windows --device -- --archs=arm --phone
    or
    cordova run ios
    
  • Alternatively, use Android Studio, Xcode or Visual Studio to deploy and run the project after first running the below command.
    cordova prepare
  • The registration screen allows a user to enter the connection information for their SAP Mobile Platform server. As of SP15, this screen will return the following error messages.
    • Not connected to network.
    • Please check your credentials.
    • Server certificate failed validation.
    • Server unreachable.
    • Secure channel error. (occurs if http is used on an https port or vice versa)


    In serverContext.js, the context object can be used to set default values such as port to be used.
    If you need to enter text on an Android device and wish to use your computers keyboard try the following.

    adb shell input text some_text_to_enter

    The hiddenFields which was added to the context object in SP 13 can be used to hide fields. Notice above that serverPort and whether to use HTTPS or not are not shown.

    var context = {
        "serverHost": "10.7.186.3", //Place your SMP 3.0 server name here
        "https": "false",
        "serverPort": "8080",
        "custom": {
            "hiddenFields": ["farmId", "resourcePath", "securityConfig", "serverPort", "https"]
        }
    };
    

    For additional security, the application can have a passcode screen. The client password policy such as password length and retry limit can be centrally managed in the management cockpit. It is also possible to disable this feature via the custom property disablePasscode as shown in the customization section below.

    The Logon plugin’s data vault which stores the entered credentials needs to be unlocked when the application opens and is locked when it closes. The data vault will also lock if it the app is in the background for a certain length of time. This is configurable via the lock timeout setting.

    As of SP14, it is possible to use a fingerprint instead of a passcode to unlock the app. The following instructions demonstrate this on an Android 7.1 emulator. A few issues were found using an earlier emulator so it may be best to use the latest available emulator or a device. Note, a passcode is still required as a fallback in case the device is unable to successfully scan your fingerprint. For additional instructions see Understanding Fingerprint Authentication.
    Settings > Security> Fingerprint.

    The following command simulates a fingerprint touch.

    adb -e emu finger touch 1

    Alternatively, from the emulators extended controls menu select Fingerprint > Touch Sensor.

     



    Provide the previously entered fingerprint to unlock.

    adb -e emu finger touch 1
    

    It is possible to use the Logon plugin’s data vault without using its features for registering with an SMP server using the sap.Logon.initPasscodeManager method. See the index.html in the Encrypted Storage plugin for a complete example.

  • Try out the rest of the application. The following are a few more screenshots showing the lock screen and the results of performing a read.
  • Note, if the server host, port or application id changes; sap.Logon.core.reset() must be called followed by calling init sap.Logon.init().In SP10 of the SMP server a new setting was added called Automatic Removal which means that registrations that have not been used for a specified time will be deleted automatically.
  • If the registration fails one quick test to verify basic connectivity to the SMP 3.0 server from the device or emulator is to open the following URL from a browser on the device or emulator.
    http://SMP_Server_IP_Address:8080/sapui5/ or http://SMP_Server_IP_Address:8080 
    

    If the above URL opens correctly in a browser on the machine that the SMP server is running on but not from the browser on another machine or on a mobile device there is likely something such as a firewall preventing communication over port 8080.

  • When the app is opened, the deviceready event fires and the code in the init() method is called including a call to sap.Logon.init() which will open either the registration screen, the unlock screen, or if the no passcode option is enabled, the first screen of the app.
  • Note that this example contains some additional logging and timing of events using the term EventLogging. Event listeners for pause, resume, onload, onunload, onbeforeunload, onSapResumeSuccess, on SapLogonSuccess and onSapResumeError events have been added. See also Events.
  • Note that on iOS versions greater than 6, the status bar can overlay the contents of the WebView. The org.apache.cordova.statusbar plugin can be added to prevent this. See iOS 7 Status Bar Overlays the App.

SAPUI5 and the Logon Plugin

In SP14, a new plugin named kapsel-plugin-ui5 contains a minimal set of the SAPUI5 libraries that are used by the logon plugin.

A change was also made to use the belize theme where in previous versions of the SDK, the blue_crystal theme was used. This is specified in the following file:

C:\SAP\MobileSDK3\KapselSDK\plugins\logon\www\common\assets\ui\iab.html

The plugin will install UI5 to the resources folder at the root of the www folder if the following file is not found.

C:\Kapsel_Projects\KapselGSDemo\www\resources\sap-ui-core.js

If you want to use your own copy of UI5, delete the resources folder in the www folder and add your own copy of UI5 there. Run cordova prepare to update the platforms with that copy.

If your application is always online and you wish to reduce the size of your app, you could change iab.html to load SAPUI5 from saoui5.hana.ondemand.com. This may also improve the speed of building and deploying your app during development.

src="../../../resources/sap-ui-core.js"
to
src="https://sapui5.hana.ondemand.com/resources/sap-ui-core.js"

Note, this requires removing the logon plugin, then adding it back after making this change. Also delete the all the contents in the www\resources folder of your application and replace sap-ui-core.js with an empty file. The size of the app prior to making this change was about 12 MB vs 5 MB after.

Customizing the Logon Plugin’s UI

The text that is displayed as part of the Logon’s UI can be modified as the strings are loaded from the file i18n.properties.

C:\SAP\MobileSDK3\KapselSDK\plugins\logon\www\common\assets\i18n\i18n_en.properties

Make a change to the above file and then remove and add back the logon plugin as shown below.

cordova plugin remove kapsel-plugin-logon
cordova plugin add kapsel-plugin-logon --searchpath %KAPSEL_HOME%/plugins
cordova run android

See also the Localization section of this guide.

As of SP13, the custom field in the context object can be used to further customize the screens of the Logon plugin as shown in the following example context.

var context = {
    "serverHost": "10.7.168.124", //Place your SMP 3.0 server name here
    "https": "false",
    "serverPort": "8080",
        "custom": {
            "hiddenFields": ["farmId", "resourcePath", "securityConfig", "serverPort", "https"],
            "backgroundImage": "../../../nightsky.jpg",
            "copyrightLogo":"../../../logo.png",
            "styleSheet": "../../../custom.css",
            "copyrightMsg": ["Copyright @2016 Mycompany Inc.", "All rights reserved."],
            "hideLogoCopyright" : false,
            "disablePasscode": true
        }
    }
};

Note, the above files such as nightsky.jpg should be placed in www folder and the ../ above is necessary as they are referenced from iab.html within the logon plugin.

The contents of the custom.css file follow.

/* company logo */
#panel_footer_hbox > .sapMFlexItem:not(#copyright) {
    /* uncomment to hide logo */
    /*display: none;*/

    /* uncomment to right align logo */
    /*position: absolute;
    right: 0px;*/
}

#logo {
    /* change size of logo */
    height: 40px !important;
}



/* copyright message */
#copyright{
    /* uncomment to hide copyright message */
    /*visibility: hidden;*/
    
    /* uncomment to right align text */
    /*text-align: right;*/

    /* uncomment to left align copyright message */
    /*left: 0px;*/

}

/* copyright message first line */
#copyright1{    
    /* change text color */
    color: white;
    
    /* change text size */
    font-size: 20px;
}
/* copyright message second line */
#copyright2 {
    /* change text color */
    color: yellow;

The background and logo images and custom.css files should be placed in the www folder.

Prepare, build and deploy the app with the following command.

cordova run android
or
cordova run windows -- --archs=x64  
or
cordova run ios

Notice that the background, and copyright logo and text have been customized. Also, note that the option to set a passcode screen is not shown after pressing the OK button due to the context option disablePasscode being set to true.

Timing and Performance

Note that this example contains some additional logging and timing of events using the term EventLogging. To enable the capturing of the time taken from the when the app is initially started till the datavault is unlocked, make the following modification.
On Android, modify the following file and change the below line to pass in to the index.html the time the app was started.

C:\Kapsel_Projects\KapselGSDemo\platforms\android\src\com\kapsel\gs\MainActivity.java
//loadUrl(launchUrl);
loadUrl(launchUrl + "?startTime=" + new java.util.Date().getTime());

With this change, the time taken from the MainActivity which should occur shortly after the user clicks on the app icon till the time the logonSucccessCallback is called can be calculated. In the below example, the time taken is about 2 1/2 seconds.

The time taken by the datavault to unlock can be reduced (using SP15 or greater is recommended for Android) by modifying the useLocalStorage setting in the serverContext.js file as shown below.

"useLocalStorage": true,


Notice above that the time taken from clicking on the app icon till the datavault unlocks has decreased from 2 1/2 seconds to 1.3 seconds.
For further details see Storing configuration data outside of the datavault.

Registering using an X.509 Certificate on Android with SMP(SP15+)

In SP15, a built in certificate provider was added to the Logon plugin for Android. The certificate provider enables the logon plugin to perform a registration against a SMP or SAP Cloud Platform Mobile Services server using a certificate to identify the user. The following instructions will demonstrate using it. Additional details are available at Using the X.509 Certificate Provider Interface to Integrate with Third-Party Certificate Providers.

  • Configure the app com.kapsel.gs to use the Certificate Authentication provider.

    Set Validated Certificate Is Identity to ON and Certificate Attribute as Principal to be CN which is the Common Name field from the user’s certificate.
  • Configure the SMP server to trust certificates signed by the certificate authority that signed the user certificates by ensuring that the CA that signed the user certificates is listed in the Shared Keystore Entries.
  • Modify the serverContext.js file to use port 8082 and ensure the fully qualified machine name is used for the host.
  • Add the following code to the start of the JavaScript code in index.html just below the line var resumeTime = null;
    var operation = {};
    
    var appConfig = {};
    appConfig.appID = appId;
    appConfig.isForSMP = true;
    appConfig.certificate = "com.sap.SystemCertProvider";
    
    var context2 = {};
    context2.smpConfig = context;
    context2.appConfig = appConfig;
    context2.operation = operation;
    
    var appDelegate = {
        onRegistrationSuccess: logonSuccessCallback,
        onRegistrationError: logonErrorCallback,
    };
    
  • Comment out the sap.Logon.init method and replace it with the following code.
    operation.logonView = sap.logon.LogonJsView;
    sap.Logon.startLogonInit(context2, appDelegate);
    
  • Run the app and notice that the user is prompted to select a certificate to be used for the registration.

Registering using an X.509 Certificate on Android with SAP Cloud Platform Mobile Services(SP15+)

The steps are the same as above except the Management Cockpit of the SAP Cloud Platform Mobile Services has a different UI and an additional step to upload the CA is required.

  • Configure the app com.kapsel.gs to use the Certificate Authentication provider.
  • Configure the server to trust certificates signed by the certificate authority that signed the user certificates.
    Download the Neo Environment SDK from SAP Cloud Platform Neo Environment SDK.
    For additional details see the following links.
    Configuring X.509 Certificate Authentication
    upload-keystore

        keytool -import -trustcacerts -alias sapsso -file c:\certs\SSO_CA.cer -keystore hcpms_trusted_ca.jks
        neo.bat upload-keystore --account i8XXXXtrial --application sapmobile:hcpms --user i82XXXX --location ./hcpms_trusted_ca.jks --host hanatrial.ondemand.com
    

    Note above that the application name should not be changed as this is the name of the SAP Cloud Platform Mobile Service.

  • Modify the serverContext.js file and change the serverHost to use the new domain for the application.

Multi User (SP14+)

Implemented on iOS and Windows only.
Android has multi user built-in to the Android OS via Settings -> Users.
To try it out, follow the instructions below.

  • Uncomment the line “multiUser”: true in the context object in serverContext.js
    Ensure that the disablePasscode option is either not present or set to false.
    Ensure that the option useLocalStorage is either not present or set to false.
  • Add the following code to the start of the JavaScript code in index.html just below the line var resumeTime = null;
    var appConfig = {};
    appConfig.appID = appId;
    appConfig.isForSMP = true;
    
    var operation = {};
    
    var context2 = {};
    context2.smpConfig = context;
    context2.appConfig = appConfig;
    context2.operation = operation;
    
    var appDelegate = {
        onRegistrationSuccess: logonSuccessCallback,
        onRegistrationError: logonErrorCallback,
    };
    
  • Comment out the sap.Logon.init method and replace it with the following code:
    operation.logonView = sap.logon.LogonJsView;
    sap.Logon.startLogonInit(context2, appDelegate);
    
  • Add the following method:
    function signOutSuccessCallback() {
        console.log("signOutSuccess");
        unlock();  //shows the pick user screen
    }
  • Add in a new button to enable the sign out.
    <button id="signOut" onclick="sap.logon.Core.deactivateCurrentUser(signOutSuccessCallback, errorCallback)">Sign Out</button>
  • Delete the app from the device or emulator before redeploying.


Back to Getting Started with Kapsel

To report this post you need to login first.

21 Comments

You must be Logged on to comment or reply to a post.

  1. Tim Lu

    Hello Daniel,

    I got some problems when I create an ios project and add the logon plugin follow the Registering using the Logon Plugin.I run the project in xcode8,it throw an error Terminating app due to uncaught exception ‘DataVaultException’, reason: ‘Does Not Exist’.Could you give me some suggestions to solve this problem?

    the cordova version is 6.1.1,kapsel version is 13.

    Thanks & regards
    Tim

    (0) 
    1. Daniel Van Leeuwen Post author

      I have seen this problem when deploying to an iOS simulator after upgrading to Xcode 8.

      I was able to get past this by

      Click on the Capabilities tab (shown in your screen shot above),

      Click on Keychain Sharing

      Enable Keychain Sharing

      Deploy and run.

      Hope that helps,

      Dan van Leeuwen

      (3) 
  2. Abhishek Wajge

    Hi Daniel,

     

    I’m following your documents on Kapsel Development, Thanks for sharing it, Very useful.

     

    Currently stuck with below issue.

    I’ve created a Kapsel SAPUI5 offline app using WebIDE and HAT connectors and It’s working without any challenges, But now Login Screen is Standard with SAP Logo and text, I want to customize it and add Logo/Background Images and also hide few fields.

    Few questions here, Adding Context code as You mentioned in your Post under section Customizing the Logon Plugin’s UI is enough? And where exactly we need to add this code, It’s in LogonController.js file? I tried adding it in Firevent Function of LogonController.js but it always gives blank screen.

    Thanks for your help.

     

    Regards,

    Abhishek Wajge

     

     

    (0) 
    1. Daniel Van Leeuwen Post author

      Searching through the generated hybrid project in c:\users\user_name\saphybrid\project_name\hybrid\www\mobile.json there appears to be a context object which I assume you could specify the customization options to.

      There also appears to be a Logon Options section in the Web IDE under the Project Settings, Hybrid App Configuration section where it looks like you could hide fields, hide the copyright and logo and add your own background.

      I imagine that the above section controls what appears in that file.

      Regards,

      Dan van Leeuwen

       

       

       

      (0) 
  3. Pavel Lazhbanau

    Hi Colleagues,

    I’m trying to register on the HCPMS server using the Logon plugin. There are two versions of my application: without crosswalk plugin and with the crosswalk.

    So the version without installed crosswalk works perfect, and I’m connected to the HCPMS, but the same code with crosswalk plugin trigger an error:

     

    Logon plugin version 3.15.0

    Android 4.4.2

    Cordova 6.5.0

     

    have somebody faced with the issue?

     

    Best Regards,

    Pavel

    (0) 
  4. Vigil Jacob

    Hi Daniel,

    Thank you for the wonderful blog! In the overview, you had commented that the ability to customize the Logon plugin screens have been changed. We used the option provided in SP09 to skip the registration screens, and used the context parameters to set the necessary values. Is it still possible to achieve this in the latest versions?

    Also, what we are aiming is to bring our own manage passcode, or enter passcode screens instead of the standard Logon screens, such that we can pass, the new and old passcode as parameters to the sap.Logon.managePasscode call. Or if we can update the logonView.onShowScreen at runtime, after the Logon.init has already been called.

    The idea is to set the passcode, same as the user’s password, instead of skipping it, through which we can utilize the fingerprint authentication as well.

    Looking forward to hear from you.

    Cheers,
    Vigil George

    (0) 
    1. Daniel Van Leeuwen Post author

      I believe the recommended approach is to use the provided Logon screens especially now that they are more easily customizable.

      Regards,
      Dan van Leeuwen

      (0) 
  5. Luciano Gonzalez

    Hi, Great blog! Things are working great with Kapsel plugins for me.

    I am only struggling with Multi-User support on Android.

    What do you mean by “Android has multi user built in to the Android OS via Settings -> Users.”?

    Which is the way to accomplish it?

     

    Thank you!

    Lucho

    (0) 
      1. Luciano Gonzalez

        Thanks! That’s a good option, but my Android version does not allow that feature by default.

        Also, In my case, I am developing an app which needs to display different information according to the user that is logged in. In other words, I need the app to get the user id/name so that the underlying oData is able to filter and deliver the correct data (and not all of it).

        Do you know another workaround or have some other suggestion as regards this requirement?

         

        Thank you again for your time!

        Lucho

        (0) 
        1. Daniel Van Leeuwen Post author

          You should be able to get the user name from the context.  I believe the Logon sample in this section accesses it via applicationContext.registrationContext.user.  If you are using another authentication method such as SAML or OAuth in which case the Kapsel app does not process the user name, there is an example that requests the user name from the settings plugin.  Search for sap.Settings.getConfigProperty in https://blogs.sap.com/2017/02/22/appendix-f-using-oauth-with-kapsel/

          I would do some early prototyping.  One issue to consider is what happens if user1 has made changes but did not flush them before user2 starts using the app.  Does it matter if user2 sends user1s changes to the backend?  The autoFlush which attempts to flush any changes in the background might be helpful.  I believe it requires the latest PL for SP14.  It is documented at https://uacp2.hana.ondemand.com/doc/d9c75eebcfa840c8a4aa4b0e6a8136de/3.0.14/en-US/ed65d7f0acbd4ae2bb4bb59e0aa389ad.html

          Regards,

          Dan van Leeuwen

          (0) 
          1. Luciano Gonzalez

            Thanks! I managed to develop the requirement by getting the username the way you did and then passing it to the devapp to create a filter in the master controller. A bit messy that this logic resides in the application but a simple way to do it, though.

             

            Now struggling with the logout logic.

            Thank you so much Daniel for your dedicated answers. They were really helpful.

            Regards,

            Luciano

            (0) 
    1. Daniel Van Leeuwen Post author

      The previous method may still work but it is not recommended as you have to change the Kapsel plugin JavaScript files which can be a pain as those changes have to be redone with each update.

      One new feature in SP 15 is the ability for the Logon plugin to remember the user’s name and password for other forms of authentication such as SAML or OAuth or form based basic authentication.  This setting can be enabled with allowSavingFormCredentials.  There are more details including an example of it at https://blogs.sap.com/2016/11/10/sap-fiori-client-sp13/

       

      The AuthProxy plugin with the Logon plugin will automatically send the previously saved user name and password when using basic authentication.  In this case the user will login once the first time they open the app and then will not see a login screen again while their user name and password are valid.  There are more details on this at https://blogs.sap.com/2017/01/17/getting-started-with-kapsel-part-8-authproxysp13/

       

      Hope that helps,

      Dan van Leeuwen

      (0) 

Leave a Reply