kwv
kwv

Reputation: 85

LTPA Authentication with Worklight

I'm trying to migrate away from AdapterAuthentication to use LTPA deferring the userRegistry to the Websphere container.

I’ve followed the Getting Started configuration here, Stack Overflow here. I believe option 2 is the right approach from this documentation.

I’ve established a stand-alone Liberty profile based worklight instance (using 6.2). I’ve modified the authenticationConfig.xml to use LTPA:

<customSecurityTest name="LTPASecurityTest">
  <test realm="wl_directUpdateRealm" step="1" />
  <test realm="WASLTPARealm" isInternalUserID="true"  />
</customSecurityTest>
<realm name="WASLTPARealm" loginModule="WASLTPAModule">
  <className>
  com.worklight.core.auth.ext.WebSphereFormBasedAuthenticator</className>
  <parameter name="login-page" value="conf/login.html" />
  <parameter name="error-page" value="conf/loginError.html" />
</realm>
<loginModule name="WASLTPAModule">
  <className>
  com.worklight.core.auth.ext.WebSphereLoginModule</className>
</loginModule>

I've confirmed (and rebuilt) the .war to have login.html and loginError.html at both the root level as well as under conf/ directory. (the documentation here "These HTML files must be added to the root directory in the Worklight Server WAR file" really needs to tell users how to do this in Worklight Studio)

I’ve modified the adapters to use the LTPA Realm:

<procedure name="profile" securityTest="LTPASecurityTest"> </procedure> 

I’ve modified the Liberty Profile’s server.xml per the documentation to add appSecurity (the screenshot only depicts how to do this from the Websphere Console), and to bind to the ldapRegistry

   <feature>appSecurity-2.0</feature>
   <feature>ldapRegistry-3.0</feature>

However, from server logs, when a client launches the app, 40+ instances of the stack trace are reported. From the client side it appears as if the initial calls to connect to Worklight are being rejected. I assume it is because they don’t have a LTPA token.

My expectation is, once the server determines that the user is requesting a secure resource, a challenge would be issued. It appears that rather than issuing the challenge, a WorkLightAuthenticationException is being thrown.

Do I need to add additional static-resources ? Are there additional configuration changes? Login.html is never returned to the end user.

console.log:

Browser console.log

    [ERROR   ] FWLSE0059E: Login into realm 'WASLTPAModule' failed. null. [project postal]
com.worklight.server.auth.api.WorkLightAuthenticationException
[ERROR   ] FWLSE0117E: Error code: 4, error description: AUTHENTICATION_ERROR, error message: An error occurred while performing authentication using loginModule WASLTPAModule, User Identity {wl_directUpdateRealm=null, SubscribeServlet=null, wl_authenticityRealm=null, AdapterAuthRealm=null, wl_remoteDisableRealm=null, wl_antiXSRFRealm=(name:q0a052t5g02f833ocv0o48e4sv, loginModule:WLAntiXSRFLoginModule), wl_deviceAutoProvisioningRealm=null, wl_deviceNoProvisioningRealm=null, myserver=(name:df680b07-3057-4339-8d94-96a050ff99ed, loginModule:WeakDummy), WASLTPARealm=null, wl_anonymousUserRealm=(name:df680b07-3057-4339-8d94-96a050ff99ed, loginModule:WeakDummy)}. [project XYZ] [project XYZ]
[ERROR   ] SRVE0777E: Exception thrown by application class 'com.worklight.core.auth.impl.AuthenticationContext.checkAuthentication:548'
com.worklight.server.auth.api.WorkLightAuthenticationException
        at com.worklight.core.auth.impl.AuthenticationContext.checkAuthentication(AuthenticationContext.java:548)
        at com.worklight.core.auth.impl.AuthenticationContext.login(AuthenticationContext.java:701)
        at com.worklight.core.auth.impl.AuthenticationServiceBean.login(AuthenticationServiceBean.java:120)
        at com.worklight.gadgets.serving.handler.LoginOnDemandHandler.doPost(LoginOnDemandHandler.java:68)
        at com.worklight.gadgets.serving.GadgetAPIServlet.doGetOrPost(GadgetAPIServlet.java:144)
        at com.worklight.gadgets.serving.GadgetAPIServlet.doPost(GadgetAPIServlet.java:107)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:595)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:668)
        at com.ibm.ws.webcontainer.servlet.ServletWrapper.service(ServletWrapper.java:1240)
        at [internal classes]
        at com.worklight.core.auth.impl.AuthenticationFilter$1.execute(AuthenticationFilter.java:204)
        at com.worklight.core.auth.impl.AuthenticationServiceBean.accessResource(AuthenticationServiceBean.java:76)
        at com.worklight.core.auth.impl.AuthenticationFilter.doFilter(AuthenticationFilter.java:208)
        at com.ibm.ws.webcontainer.filter.FilterInstanceWrapper.doFilter(FilterInstanceWrapper.java:194)
        at [internal classes]

[ERROR   ] FWLSE0020E: Ajax request exception: Environment identity is null or not proven for realm WASLTPARealm [project XYZ]
[ERROR   ] FWLSE0117E: Error code: 1, error description: INTERNAL_ERROR, error message: FWLSE0069E: An internal error occurred during gadget request  [project XYZ]Environment identity is null or not proven for realm WASLTPARealm, User Identity Not available. [project XYZ]

edit

To answer the questions from David/Simon:

I am explicitly calling wl.client.connect as the app is loading (not using connectOnStartup) But as of now I'm not waiting for onSuccess/onFailure. Its on my backlog of things to remedy; but I suspect that this worked for AdapterAuth, it ought to work for LTPA.

I started with AdapterAuth (it works), switched to LDAPLoginModule (and it works); to externalize the LDAP endpoint, we're switching LTPA. Based on this; be forewarned that the client side code is a bit more complicated than I'd like.

Here's the client side challenge handler. Assume that the worklight factory code gets fired nearly immediately after the app starts; and is subsequently followed by the Adapter invocation, which triggers the challenge handler code.

...
angular.module(
        'XYZ',
        [ 'ionic', 'XYZ.controllers', 'XYZ.services', 'worklight' ])
.run(function($ionicPlatform) {
    $ionicPlatform.ready(function() {
        if (window.StatusBar) {
            window.StatusBar.styleDefault();
        }
    });
})
.constant("authenticationRealm" , "WASLTPARealm");
//  .constant("authenticationRealm" , "AdapterAuthRealm");
...



// Provide the Worklight object as an Angular service.
angular.module('worklight', [])
.factory('worklight', ['$window', function($window) {
    //for unit testing, these aren't defined.  only attempt to connect if we're in a worklight container
    if (($window.WL)&& ($window.WL.Client)&&($window.WL.Client.connect)){
        $window.WL.Client.connect();
    }
    return $window.WL;
}])
.factory('worklightAuthenticator', ['worklight', 'authenticationRealm', function(worklight, authenticationRealm){

    var authenticator = {};

    /* public apis */
    authenticator.initLogin = function(){
        worklight.Client.login(authenticationRealm);
    };
    authenticator.setAuthRequiredCallback = function(callback){
        this.authRequiredCallback = callback;
    };
    authenticator.setLoginCompleteCallback = function(callback){
        this.loginCompleteCallback = callback;
    };
    authenticator.setLoginCompleteCallback = function(callback){
        this.loginCompleteCallback = callback;
    };
    // This will need to change as we get closer to USA
    authenticator.getFriendlyRealmName= function(){
        return (authenticationRealm !== "AdapterAuthRealm")? "Active Directory" : "local";
    };
    //Failing real abstraction going to assign functions based on what realm is defined
    authenticator.submitLogin = (authenticationRealm !== "AdapterAuthRealm") ? function(username, password) {
        var reqURL = '/j_security_check';
        var options = {};
        options.parameters = {
            j_username : username,
            j_password : password
        };
        options.headers = {};
        authenticator.realmChallengeHandler.submitLoginForm(reqURL, options, authenticator.realmChallengeHandler.handleChallenge);
    } : function(username, password) {
        var options = {
                parameters : [username, password],
                adapter : "Security",
                procedure : "submitAuthentication"
            };
        authenticator.realmChallengeHandler
            .submitAdapterAuthentication(options, {onSuccess: function(r){window.alert(r);}, onFailure: function(z){console.log(z);}});
    }

    this._realmChallengeHandler = worklight.Client.createChallengeHandler(authenticationRealm);

    this._submitLoginFormCallback = function(response){
        var isLoginFormResponse = this._isCustomResponse(response);
        if (isLoginFormResponse){
            authenticator.handleChallenge(response);
        } else {
            authenticator.realmChallengeHandler.submitSuccess();
            authenticator.loginCompleteCallback();
        }
    }
    //Failing real abstraction going to assign functions based on what realm is defined
    this._isCustomResponse = (authenticationRealm !== "AdapterAuthRealm") ? function(response) {
        console.log("Challenge Required...", response);
        var idx =response.responseText.indexOf("j_security_check") >= 0;
        return idx;
    } :  function(response) {
        console.log("Challenge Required...", response);
        if (!response || !response.responseJSON || response.responseText === null) {
            return false;
        }
        if (typeof (response.responseJSON.authRequired) !== 'undefined') {
            return true;
        } else {
            return false;
        }
    } ;

    //Failing real abstraction going to assign functions based on what realm is defined
    this._handleChallenge =  (authenticationRealm !== "AdapterAuthRealm") ? function(response) {
        console.log("Handle Challenge", response);
        var authRequired = response.responseText.indexOf("j_security_check") >= 0;
        if (authRequired === true) {
            authenticator.authRequiredCallback(response.responseText);
        } else if (authRequired === false) {
            console.log("Challenge Not Required");
            authenticator.realmChallengeHandler.submitSuccess();
            authenticator.loginCompleteCallback();
            return false;
        }
    } : function(response) {
        console.log("Handle Challenge", response);
        var authRequired = response.responseJSON.authRequired;
        if (authRequired === true) {
            authenticator.authRequiredCallback(response.responseJSON);
        } else if (authRequired === false) {
            console.log("Challenge Not Required");
            authenticator.realmChallengeHandler.submitSuccess();
            authenticator.loginCompleteCallback();
            return false;
        }
    };

    this._realmChallengeHandler.isCustomResponse = this._isCustomResponse;
    this._realmChallengeHandler.handleChallenge = this._handleChallenge;
    this._realmChallengeHandler.submitLoginFormCallback = this._submitLoginFormCallback;
    authenticator.realmChallengeHandler = this._realmChallengeHandler;
    return authenticator;
}])

;

Upvotes: 1

Views: 1310

Answers (1)

SimonK
SimonK

Reputation: 1

Are you declaring a ChallengeHandler in your client? Do you use connectOnStartup:true? WL.Client.connect? Can you share more of what exactly the client is doing?

This Web page https://pic.dhe.ibm.com/infocenter/wrklight/v6r1m0/index.jsp?topic=%2Fcom.ibm.worklight.deploy.doc%2Fadmin%2Fc_security_ltpa_overview.html describes the flow here, and the second step "Server asks user to login, since the resource is protected" is where you are getting the HTTP 401 in your console log. Can you show us what actually goes across the wire at this stage?

One thought, I notice that in the definition of your securitytest you have wl_directUpdateRealm marked as step 1, but no step marked for the WASLTPARealm. I'm not sure what will happen in this situation, but you might try marking the WASLTPARealm as one step after the direct update check:

<customSecurityTest name="LTPASecurityTest">
  <test realm="wl_directUpdateRealm" step="1" />
  <test realm="WASLTPARealm" isInternalUserID="true" step="2" />
</customSecurityTest>

or removing the steps altogether (so that everything happens in one step), or even not using Direct Update until you get the LTPA side working.

-simon

Upvotes: 0

Related Questions