Setting up single sign-on with MobileFirst 7.0

You can set up single sign-on with MobileFirst® so users can share a session between a HCL and MobileFirst server.

Before you begin

Both HCL and MobileFirst servers must be configured to use the same user registry, LTPA keys, and be set with a specified domain for SSO. If you are using the WebSphere® Application Server, go to the WebSphere Application Server documentation.

For more information about system support requirements, see System requirements.

Procedure

  1. Run the following configEngine tasks. These configEngine tasks are available only on the HCL server. If the MobileFirst server is on WebSphere Application Server, run the following commands from the WebSphere Integrated Solutions Console. You can use steps B and C if MobileFirst is on the same instance as HCL.
    1. ConfigEngine.bat configure-single-signon -Ddomain=domain name -DWasRemoteHostName=hostname -DWasSoapPort=port -DWasPassword=password -Dinteroperable=true -DattributePropagation=true -DrequiresSSL=false
    2. Optional: ConfigEngine.bat export-ltpakeys-single-signon -DkeyFile=c:\ltpa.txt -DkeyPass=<testpass> -DdmgrFlag=false -DWasRemoteHostName=hostname -DWasSoapPort=port -DWasPassword=password
    3. Optional: ConfigEngine.bat import-ltpakeys-single-signon -DkeyFile=c:\ltpa_demo.txt -DkeyPass=<somepassword> -DdmgrFlag=false -DWasRemoteHostName=hostname -DWasSoapPort=port -DWasPassword=password
  2. To prepare the MobileFirst server, you must update the MobileFirst WAR to enable applications to authenticate with the user registry. Update authenticationConfig.xml in your MobileFirst project. IT is in MobileFirst Project/server/conf/authenticationConfig.xml. Find the <securityTests> element and add the mobile and web security tests.
    1. Find the <securityTests> element and add the mobile and web security tests from the following example.
      <mobileSecurityTest name="mobileTests"> 
          <testDeviceId provisioningType="none" /> 
          <testUser realm="WASLTPARealm" /> 
      </mobileSecurityTest> 
      <webSecurityTest name="WASLTPARealmTests"> 
          <testUser realm="WASLTPARealm"/> 
      </webSecurityTest> 
    2. Uncomment the security realm.
      <!-- For websphere --> 
      <realm name="WASLTPARealm" loginModule="WASLTPAModule">
         <className>com.worklight.core.auth.ext.WebSphereFormBasedAuthenticator
             </className> 
         <parameter name="login-page" value="/login.html"/> 
         <parameter name="error-page" value="/loginError.html"/> 
      </realm>
    3. Uncomment the login module for WebSphere.
      <!-- For websphere --> 
      <loginModule name="WASLTPAModule"> 
          <className>com.worklight.core.auth.ext.WebSphereLoginModule</className> 
      </loginModule>
  3. Modify the MobileFirst project WAR by adding two new HTML files to the WAR. The project WAR is in the /MobileFirst Project/bin directory. Copy this WAR to another location for editing.
    1. Create a login.html file with the following contents:
      <html> 
          <head> 
              <title>Login</title> 
          </head> 
          <body> 
              <form method="post" action="j_security_check"> 
                  <label for="j_username">User name:</label> 
                  <input type="text" id="j_username" name="j_username" /> 
                  <br /> 
                  <label for="j_password">Password:</label> 
                  <input type="password" id="j_password" name="j_password" /> 
                  <br /> 
                  <input type="submit" id="login" name="login" value="Log In" /> 
              </form> 
          </body> 
      </html>
    2. Create a loginError.html file with the following contents:
      <html> 
          <head></head> 
          <body> 
              Login Error 
          </body> 
      </html>
    3. Add the login.html and loginError.html files to the highest level directory of the WAR.
  4. Modify the MobileFirst project WAR that was updated in the previous step by editing the web.xml file in the WEB-INF directory.
    <login-config> 
        <auth-method>FORM</auth-method> 
        <form-login-config> 
            <form-login-page>/login.html</form-login-page> 
            <form-error-page>/loginError.html</form-error-page> 
        </form-login-config> 
    </login-config>
  5. Optional: Add a security constraint to protect the web resource by modifying web.xml in the updated MobileFirst project WAR:
    <security-constraint id="SecurityConstraint_1">
    	<web-resource-collection id="WebResourceCollection_1">
    		<web-resource-name>mobilefirst</web-resource-name>
    		<description>Protecting mobilefirst application</description>
    		<url-pattern>/*</url-pattern>
    		<http-method>GET</http-method>
    		<http-method>POST</http-method>
    	</web-resource-collection>
    	<auth-constraint id="AuthConstraint_1">
    		<description>MobileFirst applications</description>
    		<role-name>Administrator</role-name>
    	</auth-constraint>
    	<user-data-constraint id="UserDataConstraint_1">
    		<transport-guarantee>NONE</transport-guarantee>
    	</user-data-constraint>
    </security-constraint>
    
    <security-role id="SecurityRole_1">
    	<description>Only specific users</description>
    	<role-name>Administrator</role-name>
    </security-role>
  6. When the MobileFirst project WAR is updated, deploy it to the MobileFirst server.
  7. Restart the server where MobileFirst is installed. If you added the security constraint, map the group or user to the EAR file.
  8. Update the application-descriptor.xml file to add the security tests you configured. Open MobileFirst Project/apps/mobilefirst app/application-descriptor.xml in the design view and update it to have the correct realms and security tests.
  9. In the main application, add a security test that is called WASLTPARealmTests in the Common (optional) section.
  10. In Android phones and tablets > Details, add the Security test called mobileTests.
  11. After you create the server-side WAR, change the client side to allow authentication between the two servers. In a demonstration application, modify the HTML of MobileFirst Project/apps/mobilefirst app/common/index.html to include a log in form and JavaScript to handle the response. In a demonstration, index.html includes the code in the following example:
    <!DOCTYPE HTML>
    <html>
        	<head>
        		<meta charset="UTF-8">
        		<title>index</title>
        		<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=0">
        		<link rel="shortcut icon" href="images/favicon.png">
        		<link rel="apple-touch-icon" href="images/apple-touch-icon.png">
        		<link rel="stylesheet" href="css/main.css">
        		<script>window.$ = window.jQuery = WLJQ;</script>
        	</head>
        	<body id="content" style="display: none"> 
    		    <div id="AppBody">
    		        <div class="wrapper"> 
    		        </div>
    		    </div>
    		    <div id="AuthBody" style="display: none"> 
    		        <div id="loginForm"> 
    		            Username:<br/> 
    		            <input type="text" id="usernameInputField" autocorrect="off" autocapitalize="off" /><br /> 
    		            Password:<br/> 
    		            <input type="password" id="passwordInputField" autocorrect="off" autocapitalize="off"/><br/>		 
    		            <input type="button" id="loginButton" value="Login" /> 
    		            <input type="button" id="cancelButton" value="Cancel" /> 
    		        </div> 
    		    </div>
        		<!--application UI goes here-->
        		Hello Worklight
        		<script src="js/initOptions.js"></script>
        		<script src="js/main.js"></script>
        		<script src="js/messages.js"></script>
        		<script src="js/challengeResponse.js"></script>
        	</body>
    </html>
  12. Update the initialization options for MobileFirst to force the application to connect to the MobileFirst server on start by adding WL.Client.connect(); in MobileFirst Project/apps/mobilefirst app/common/js/main.js.
  13. Add JavaScript to handle the response from the MobileFirst and HCL Portal servers. In this SSO demonstration application, create a file that is called challengeResponse.js, in MobileFirst Project/apps/mobilefirst app/common/js. Add the following example to the contents of the file. Update the line goToPortalServer("http://server:port/wps/myportal"); to point to your HCL Portal server.
    /*
    *  Licensed Materials - Property of IBM
    *  5725-G92 (C) Copyright HCL Technologies Limited 2006, 2012, 2019. All Rights Reserved.
    *  US Government Users Restricted Rights - Use, duplication or
    *  disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
    */
    
    var sampleAppRealmChallengeHandler = WL.Client.createChallengeHandler("WASLTPARealm");
    var busyIndicator = new WL.BusyIndicator('content', {text: "Loading..."});
    
    sampleAppRealmChallengeHandler.isCustomResponse = function(response) {
        if (!response || response.responseText === null) {
            return false;
        }
        var indicatorIdx = response.responseText.search('j_security_check');
        
        if (indicatorIdx >= 0){
    		return true;
    	}  
    	return false;
    };
    
    sampleAppRealmChallengeHandler.handleChallenge = function(response) {
    	busyIndicator.show();
    	$('#AppBody').hide();
    	WL.EncryptedCache.open("wpsadmin", true, onReadOpen, onOpenError);
    };
    
    sampleAppRealmChallengeHandler.submitLoginFormCallback = function(response) {
        var isLoginFormResponse = sampleAppRealmChallengeHandler.isCustomResponse(response);
        if (isLoginFormResponse){
        	sampleAppRealmChallengeHandler.handleChallenge(response);
        } else {
        	$('#AppBody').show();
        	$('#AuthBody').hide();
        	sampleAppRealmChallengeHandler.submitSuccess();
        	WL.Client.getCookies().then(function (cookies) {
        		busyIndicator.hide();
        		goToPortalServer("http://server:port/wps/myportal");
        	});
        }
    };
    
    
    $('#loginButton').bind('click', function () {
    	busyIndicator.show();
    	WL.EncryptedCache.write("username", $('#usernameInputField').val(), onWriteSuccess, onWriteFailure);
    	function onWriteSuccess(status){
    		WL.EncryptedCache.write("password", $('#passwordInputField').val(), onWriteSuccess2, onWriteFailure);
    		function onWriteSuccess2(status2){
    			WL.EncryptedCache.close(onCloseCompleteHandler, onCloseFailureHandler);
    			
    		}
    	}
    	function onWriteFailure(status){
    		alert("Encrypted cache closed, writing failed");
    	}
    });
    
    
    function onCloseCompleteHandler(status){
    	var reqURL = '/j_security_check';
        var options = {};
        options.parameters = {
            j_username : $('#usernameInputField').val(),
            j_password : $('#passwordInputField').val()
        };
        options.headers = {};
        sampleAppRealmChallengeHandler.submitLoginForm(reqURL, options, sampleAppRealmChallengeHandler.submitLoginFormCallback);
    }
    
    
    function onCloseFailureHandler(status){
    	alert("close faiure");
    }
    
    
    $('#cancelButton').bind('click', function () {
    	sampleAppRealmChallengeHandler.submitFailure();
    	$('#AppBody').show();
    	$('#AuthBody').hide();
    });
    
    function onReadOpen(status){
    	WL.EncryptedCache.read("username", onDecryptReadSuccess, onDecryptReadFailure);
    	function onDecryptReadSuccess(value){
    		WL.EncryptedCache.read("password", onDecryptReadSuccess2, onDecryptReadFailure);
    		function onDecryptReadSuccess2(value2){
    			if (value && value2){
    				// submit 1 & 2
    				var reqURL = '/j_security_check';
    			    var options = {};
    			    options.parameters = {
    			        j_username : value,
    			        j_password : value2
    			    };
    			    options.headers = {};
    			    sampleAppRealmChallengeHandler.submitLoginForm(reqURL, options, sampleAppRealmChallengeHandler.submitLoginFormCallback);
    			} else {
    				// Didn't find any cached info, ask for login.
    				busyIndicator.hide();
    				$('#AuthBody').show();
    				$('#passwordInputField').val('');
    			}
    		}
    	}
    	function onDecryptReadFailure(status){
    		alert("Encrypted cache closed, reading failed");
    	}
    }
    
     function onOpenError(status){
    	switch(status){
    		case WL.EncryptedCache.ERROR_KEY_CREATION_IN_PROGRESS:
    			alert("ERROR: KEY CREATION IN PROGRESS");
    			break;
    		case WL.EncryptedCache.ERROR_LOCAL_STORAGE_NOT_SUPPORTED:
    			alert("ERROR: LOCAL STORAGE NOT SUPPORTED");
    			break;
    		case WL.EncryptedCache.ERROR_NO_EOC:
    			alert("ERROR: NO EOC");
    			break;
    		case WL.EncryptedCache.ERROR_COULD_NOT_GENERATE_KEY:
    			alert("ERROR: COULD NOT GENERATE KEY");
    			break;
    		case WL.EncryptedCache.ERROR_CREDENTIALS_MISMATCH:
    			alert("ERROR: CREDENTIALS MISMATCH");
    			break;
    		default:
    			alert("AN ERROR HAS OCCURED. STATUS :: " + status);
    	}
    }
  14. Create a Cordova Plugin that adds the token the WebView's cookie store so HCL Portal can receive the LtpaToken from an Android application.
    1. Create a Java file called CookieInjector.java in MobileFirst Project/apps/mobilefirst app/android/native/src/com/mobilefirst app. Add the following code to it. Replace mobilefirst app with your app name.
      package com.mobilefirst_app;
      
      import java.util.List;
      
      import org.apache.cordova.CallbackContext;
      import org.apache.cordova.CordovaArgs;
      import org.apache.cordova.CordovaPlugin;
      import org.apache.http.client.CookieStore;
      import org.apache.http.cookie.Cookie;
      import org.json.JSONException;
      
      import android.webkit.CookieManager;
      
      import com.worklight.common.Logger;
      import com.worklight.wlclient.HttpClientManager;
      
      public class CookieInjector extends CordovaPlugin {
      	Logger l = Logger.getInstance(CookieInjector.class.getName());
      
      	@Override
      	public boolean execute(String action, CordovaArgs args, CallbackContext callbackContext) throws JSONException {
      		if ("INJECT-COOKIES-TO-WEBVIEW".equals(action)){
      			l.debug("Started injecting cookies");
      
      			CookieStore cookieStore = HttpClientManager.getInstance().getHttpClient().getCookieStore();
      			List<Cookie> list = cookieStore.getCookies();
      			for (Cookie cookie : list){
      				String cookieName = cookie.getName();
      				l.debug("Found cookie :: " + cookieName);
      				if ("LtpaToken".equals(cookieName)){
      					l.debug("Found LtpaToken cookie");
      					CookieManager cookieManager = CookieManager.getInstance();
      					String cookieValue = cookie.getName() + "=" + cookie.getValue();
      					cookieManager.setCookie(cookie.getDomain(), cookieValue);
      					break;
      				}
      				l.debug("LtpaToken cookie not found");
      			}
      			l.debug("Done injecting cookies");
      			callbackContext.success();
      			return true;
      		}
      		return false;
      	}
      }
      
    2. Add the following code to MobileFirst Project/apps/mobilefirst app/android/native/res/xml/config.xml before the closing </widget> tag. Replace mobilefirst app with your app name.
      <feature name="CookieInjector">
          <param name="android-package" value="com.mobilefirst_app.CookieInjector" />
      </feature>
  15. Add the following function to MobileFirst Project/apps/mobilefirst app/android/apps/mobilefirst app/android/js/main.js.
    function goToPortalServer(url) {
    	cordova.exec(function () {
    		location.href = url;
    	}, function () {
    		alert('failure injecting cookies');
    	}, "CookieInjector", "INJECT-COOKIES-TO-WEBVIEW",[]);
    }
  16. Build your MobileFirst application for your MobileFirst server by right-clicking the MobileFirst application and selecting Run As > Build Settings and Deploy Target.
  17. Select Build the application to work with a different MobileFirst server.
  18. Add the information for your MobileFirst server to the Server and Context path fields.
  19. Build your application by right-clicking the MobileFirst application and selecting Run As > Build All Environments.
  20. Install the MobileFirst application to your MobileFirst server. Open the MobileFirst console at http://server:port/worklightconsole and upload the MobileFirst application by adding it to the Deploy application or adapter field. Your MobileFirst application file can be found in your Eclipse workspace in the bin folder. The MobileFirst application file has the .wlapp extension.