I have followed This Link
Which works great for me. I am having 3 adapters in all these three adapter I am using same security test realm for all procedures. After certain timeout which i provided adapter returns me login failed which is correct. But I am getting following error after timeout in application -
Console error - AUTHENTICATION_ERROR, error message: An error occurred while performing authentication using loginModule AdapterAuthLoginModule, User Identity Not available.
Application Error - handler.handleFailure is not a function
I am not able to get this above error which is in worklight.js
What causes this error? I want to call logout function after timeout. But I cant call because of above error.
adapter.xml
<wl:adapter xmlns:wl="http://www.worklight.com/integration" xmlns:http="http://www.worklight.com/integration/http" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" name="AuthenticationAdapter">
<displayName>AuthenticationAdapter</displayName>
<description>AuthenticationAdapter</description>
<connectivity>
<connectionPolicy xsi:type="http:HTTPConnectionPolicyType">
<protocol>http</protocol>
<domain>rss.cnn.com</domain>
<port>80</port>
</connectionPolicy>
<loadConstraints maxConcurrentConnectionsPerNode="2"/>
</connectivity>
<procedure name="submitAuthentication"/>
<procedure name="getSecretData" securityTest="AuthenticationAdapter-securityTest"/>
</wl:adapter>
adapterAuthRealmChallengeHandler.js
adapterAuthRealmChallengeHandler.isCustomResponse = function(response) {
if (!response || !response.responseJSON || response.responseText === null) {
return false;
}
if (typeof(response.responseJSON.authRequired) !== 'undefined'){
return true;
} else {
return false;
}
};
adapterAuthRealmChallengeHandler.handleChallenge = function(response){
var authRequired = response.responseJSON.authRequired;
if (authRequired == true){
navigator.notification.alert("Your session has timed out!");
logoutfunction(); //logout function call
} else if (authRequired == false){
if(CurrentSessionId == "" || CurrentSessionId == null){
logoutfunction(); //logout function call
}
else{
var invocationData = {
adapter : "API_Adapter",
procedure : "storeSession",
parameters : [userID],
compressResponse : true
};
WL.Client.invokeProcedure(invocationData, {
onSuccess : function Lsuccess(res){},
onFailure : function Lfaulure(res){},
timeout: timeout
});
}
adapterAuthRealmChallengeHandler.submitSuccess();
}
};
With help of Idan Adar i got solution
I put below function in adapterAuthRealmChallengeHandler.js
adapterAuthRealmChallengeHandler.handleFailure = function(response) {
adapterAuthRealmChallengeHandler.startChallengeHandling();
};
After calling above function And in adapterAuthRealmChallengeHandler.handleChallenge i have checked the flag value which I set after getting successful login.
Related
I am using Mobilefirst Platform 8.0 for my app development.
How can we use HTTP basic authentication method to authenticate the mobile client through datapower. Please help me out with challnegHandler sample code.
I tried the below code in challengeHandler but Datapower always returning 401.
var DataPowerChallengeHandler = function() {
var dataPowerChallengeHandler = WL.Client.createGatewayChallengeHandler("LtpaBasedSSO");
dataPowerChallengeHandler.canHandleResponse = function(response) {
if (!response || response.responseText === null) {
return false;
}
if (response.status=="401") {{
return true;
}
return false;
};
dataPowerChallengeHandler.handleChallenge = function(response) {
document.getElementById('result').style.display = 'none';
document.getElementById('auth').style.display = 'block';
};
dataPowerChallengeHandler.submitLoginFormCallback = function(response) {
var isLoginFormResponse = dataPowerChallengeHandler.canHandleResponse(response);
if (isLoginFormResponse) {
dataPowerChallengeHandler.handleChallenge(response);
} else {
document.getElementById('result').style.display = 'block';
document.getElementById('auth').style.display = 'none';
dataPowerChallengeHandler.submitSuccess();
}
};
document.getElementById("AuthSubmitButton").addEventListener("click", function() {
var username = document.getElementById('txtusername').value;
var password = document.getElementById('txtpassword').value;
var mystring = convertBase64(username+":"+password);
var headerString = "Basic "+ mystring;
WL.Client.addGlobalHeader("Authorization",headerString);
dataPowerChallengeHandler.submitSuccess();
});
document.getElementById("logout").addEventListener("click", function() {
WLAuthorizationManager.logout("LtpaBasedSSO").then(
function() {
WL.Logger.debug("logout onSuccess");
alert("Success logout");
},
function(response) {
WL.Logger.debug("logout onFailure: " + JSON.stringify(response));
});
});
document.getElementById('AuthCancelButton').addEventListener("click",function(){
document.getElementById('result').style.display = 'block';
document.getElementById('auth').style.display = 'none';
dataPowerChallengeHandler.cancel();
});
return dataPowerChallengeHandler;
};
Your gateway challenge handler code is improper and causing all these issues.
canHandleResponse method is used to determine whether it is a challenge thrown by datapower or not. Your client code is returning true whenever there is a 401 challenge. This will return true even though for non-datapower challenges which should be corrected.
and handleChallenge is explicitly getting called in submitLoginFormCallback method which is wrong. handleChallenge will get called by SDK only when canHandleResponse method returns true.
Kindly go through this tutorial for more information on how to use gateway challenge handler in your client application.
I have a very simple hybrid sample app which has 3 adapters.
submitAuthStep1(username, password)
submitAuthStep2(answer)
getSecretData()
Adapter 1 and 2 are using the "wl_unprotected" security test.
Adapter 3 is using "AuthRealm"
var userIdentity;
function onAuthRequired(headers, errorMessage){
WL.Logger.warn(" in OAuth Reuired...");
WL.Logger.debug(" in OAuth Reuired...");
errorMessage = errorMessage ? errorMessage : null;
WL.Logger.debug(" in OAuth Reuired errorMessage..."+errorMessage);
return {
authRequired: true,
authStep: 1,
errorMessage: errorMessage
};
}
function submitAuthStep1(username, password){
if (username === "wl" && password === "wl"){
WL.Logger.debug("Step 1 :: SUCCESS");
userIdentity = {
userId: username,
displayName: username,
attributes: {}
};
return {
authRequired: true,
authStep: 2,
question: "What is your pet's name?",
errorMessage : ""
};
}
else{
WL.Logger.debug("Step 1 :: FAILURE");
return onAuthRequired(null, "Invalid login credentials");
}
}
function submitAuthStep2(answer){
if (answer === "wl2"){
WL.Logger.debug("Step 2 :: SUCCESS");
WL.Server.setActiveUser("AuthRealm", userIdentity);
WL.Logger.debug("Authorized access granted");
return {
authRequired: false
};
}
else{
WL.Logger.debug("Step 2 :: FAILURE");
return onAuthRequired(null, "Wrong security question answer");
}
}
function getSecretData(){
/*return {
secretData: "A very very very very secret data"
};*/
WL.Logger.info(" Active User INfo "+JSON.stringify(WL.Server.getActiveUser("AuthRealm")));
WL.Logger.info(" .... User INfo "+ WL.Server.getClientRequest().getSession().getAttribute("AuthRealm"));
return userIdentity;
}
function onLogout(){
userIdentity = null;
WL.Server.setActiveUser("AuthRealm", userIdentity);
WL.Logger.debug("Logged out");
}
function signOut(){
userIdentity = null;
WL.Server.setActiveUser("AuthRealm", userIdentity);
WL.Logger.debug("Logged out");
}
When invoking this code with the hybrid application it works fine, when I try to test and invoke these adapters using eclipse (Call MobileFirst Adapter option) submitAuthStep1 works, then when I get to submitAuthStep2 my global variable 'userIdentity' is gone. I have also tried to invoke the adapters in sequence using their corresponding URL's in a chrome browser tab with the same result!
worklight.properties is using session dependence
mfp.session.independent=false
mfp.attrStore.type=httpsession
Why is this happening?
The "Call Adapter" feature of the MobileFirst Studio cannot be used to test authentication and security. The way it works, it gets direct access to the resource and skips all of the MobileFirst security framework. It is meant to test regular adapters.
Same thing if you try to get to the adapter directly from the browser.
You have no MobileFirst session, therefore you start fresh for every request. Global variables won't be carried on to the next request.
You can only test authentication and security features using an application.
I am developing an application using IBM MobileFirst Platform Foundation application 7.0, with AngularJS and Ionic. For the authentication I am using adapter-based authentication.
I have a single realm that protects the application, and all procedures. I've defined a Login Controller and a LoginChallangeHandler Service that handles authentication related activities:
I am basing it on the following: https://medium.com/#papasimons/worklight-authentication-done-right-with-angularjs-768aa933329c
When I login in the app, the authentication works fine by the following steps
Step1:
Calling ch.submitAdapterAuthentication(options, ch.submitLoginCallback);
Step2:
After successful Adapter invocation the following is called
ch.submitLoginCallback = function(response) {
console.log("submitLoginCallback.response ",response);
var isLoginFormResponse = ch.isCustomResponse(response);
console.log("submitLoginCallback.isLoginFormResponse ",isLoginFormResponse);
if (isLoginFormResponse){
console.log("submitLoginCallback.calling ");
ch.handleChallenge(response);
}
};
step3:
ch.isCustomResponse = function(response) is called which returns true or false.
ch.isCustomResponse = function(response){
console.log("isCustomResponse.responseJSON ",response);
//alert("response")
if (!response || !response.responseJSON || response.responseText === null) {
return false;
}
console.log("isCustomResponse.response.responseJSON ",response.responseJSON);
console.log(typeof(response.responseJSON.authRequired) !== 'undefined');
if (typeof(response.responseJSON.authRequired) !== 'undefined'){
//ch.handleChallenge(response);
return true;
} else {
return false;
}
};
Step4:
ch.handleChallenge = function(response) is called, in which we will handle the success or failure of usr authentication.
Problem is, when the authentication fails or server session logout or server session timeout, on server adapter side onAuthRequired method is called which returnsauthRequired=true.
function onAuthRequired(headers, errorMessage){
return {
authRequired: true,
errorMessage: errorMessage
};
}
At the client side, ch.isCustomResponse = function(response) is called, but the ch.handleChallenge = function(response) is not called after that. The execution is terminating at ch.isCustomResponse function and ch.handleChallenge is not called automatically. If I call manually like this
if (typeof(response.responseJSON.authRequired) !== 'undefined'){
//ch.handleChallenge(response);
return true;
} else {
return false;
}
at login time the ch.handleChallenge() is executed twice.
Can anyone please tell me how to solve this problem? How to call ch.handleChallenge() automatically when ch.isCustomResponse() returns either authRequired=true or false.
when the authentication fails or server session logout or server session timeout
First you need to take the user on Login page(where user give credentials and press login) or if you login by code then you need to call this
ch.submitAdapterAuthentication(options, ch.submitLoginCallback);
I have created angular service, where I'm registering challengeHandler this way:
azureChallengeHandler = WL.Client.createChallengeHandler(realm);
azureChallengeHandler.isCustomResponse = function (response) {
...
};
azureChallengeHandler.handleChallenge = function (response) {
...
};
So i'm logging in with this function:
WL.Client.login(realm, options)
And the first time it works ok, isCustomResponse gets called, returns "true", then handleChallenge gets called.
But after logging out with this function:
WL.Client.logout(realm, options)
When I try to login again, isCustomResponse gets called and still returns "true", but handleChallenge is not firing.
How can I fix that?
After calling WL.Client.reloadApp() or reloading app itself I can login again, but it's not a suitable solution.
UPDATE:
Here is adapter code:
function onAuthRequired(headers) {
return customLoginResponse(true, false, false);
}
function customLoginResponse(authRequired, azureTokenRequired, wrongTenant) {
return {
authRequired: authRequired,
azureTokenRequired: azureTokenRequired,
realm: 'AzureAuth',
wrongTenant: wrongTenant
};
}
function onLogout(){
WL.Server.setActiveUser("AzureAuth", null);
WL.Logger.debug("Logged out");
}
function submitLogout(uuid, orgId, ssogroup){
WL.Server.invokeProcedure({
adapter: "AzureTokenSqlAdapter",
procedure: "removeRefreshToken",
parameters: [uuid, orgId, ssogroup]
});
onLogout();
}
function submitLogin(uuid, orgId, ssogroup, code) {
var tokenObject = getTokens(code);
if (tokenObject.id_token) {
var jwtParsed = parseJWT(tokenObject.id_token);
var tenantId = jwtParsed.tid;
var invocationResult = WL.Server.invokeProcedure({
adapter: "AzureTokenSqlAdapter",
procedure: "checkTenant",
parameters: [orgId, tenantId]
});
if (!invocationResult.tenantRegistered) {
return customLoginResponse(true, true, true);
}
}
return authUser(tokenObject, uuid, orgId, ssogroup);
}
And here is the client code:
function azureAuthService($q, _, $state) {
var loginPromise;
azureChallengeHandler = WL.Client.createChallengeHandler(realm);
//first response after protected call
azureChallengeHandler.isCustomResponse = function (response) {
if (!response || !response.responseJSON || response.responseText === null) {
return false;
}
return response.responseJSON.realm == realm;
};
//when isCustomResponse returns true
azureChallengeHandler.handleChallenge = function (response) {
WL.Logger.debug("challenge handler -- handleChallenge");
var authRequired = response.responseJSON.authRequired;
var azureTokenRequired = response.responseJSON.azureTokenRequired;
var wrongTenant = response.responseJSON.wrongTenant;
if (wrongTenant) {
loginPromise.reject('wrong tenant');
} else if (authRequired && azureTokenRequired) {
fullLogin();
} else if (authRequired) {
fastLogin();
} else {
loginPromise.resolve();
}
};
azureChallengeHandler.handleFailure = function (error) {
console.log('failure');
console.log(error);
};
return {
init: init,
login: login,
logout: logout
};
function init(config) {
ssogroup = config.ssogroup;
orgId = config.orgId;
}
function login() {
loginPromise = $q.defer();
WL.Client.login(realm, {
onSuccess: function(info) {
loginPromise.resolve();
},
onFailure: function(error) {
loginPromise.reject();
}
});
return loginPromise.promise;
}
function logout() {
var logoutPromise = $q.defer();
var invocationData = {
adapter : 'AzureAuth',
procedure : 'submitLogout',
parameters : [device.uuid, orgId, ssogroup]
};
WL.Client.invokeProcedure(invocationData).then(function () {
WL.Client.logout(realm, {
onSuccess: function () {
logoutPromise.resolve();
},
onFailure: function () {
logoutPromise.reject();
}
});
}, function () {
logoutPromise.reject();
});
return logoutPromise.promise;
}
}
fastLogin and fullLogin is functions that perform some work and finally call
var options = {
parameters: [device.uuid, orgId, ssogroup, transitionAuthObject.requestToken],
adapter: "AzureAuth",
procedure: "submitLogin"
};
azureChallengeHandler.submitAdapterAuthentication(options);
Can't see your fullLogin() and fastLogin() methods so it's hard to say for sure. Make sure that you're calling challengeHandler's submitSuccess() or submitFailure() methods after successful/failed authentication. The authentication framework keeps a queue of requests/responses that require authentication. After successful/failed authentication you need to invoke submitSuccess/submitFailure on challenge handler in order for authentication framework to remove your requests from queue and process it. In case you're not doing so the request remains in the queue and once you're sending a new request that triggers authentication it is put into queue but not handled since there's another request already waiting for authentication.
LoginViewModel.js
function login(){
var username='worklight';
var password = 'worklight';
var invocationData = {
adapter : "AuthenticationAdapter",
procedure : "submitAuthentication",
parameters : [ username, password ]
};
adapterAuthRealmChallengeHandler.submitAdapterAuthentication(invocationData, {});
AdapterAuthRealmChallengeProcessor.js
var adapterAuthRealmChallengeHandler = WL.Client.createChallengeHandler("AdapterAuthRealm");
adapterAuthRealmChallengeHandler.isCustomResponse = function(response) {
if (!response || !response.responseJSON || response.responseText === null) {
return false;
}
if (typeof(response.responseJSON.authRequired) !== 'undefined'){
return true;
} else {
return false;
}
};
adapterAuthRealmChallengeHandler.handleChallenge = function(response){
var authRequired = response.responseJSON.authRequired;
if (authRequired == true){
window.location = "#login";
alert(response.responseJSON.errorMessage);
} else if (authRequired == false){
adapterAuthRealmChallengeHandler.submitSuccess();
window.location = "#Home";
}
};
AuthenticationAdapter-impl.js
function onAuthRequired(headers, errorMessage){
errorMessage = errorMessage ? errorMessage : null;
return {
authRequired: true,
errorMessage: errorMessage
};
}
function submitAuthentication(username, password){
var userIdentity = {
userId: username,
displayName: username,
attributes: {
foo: "bar"
}
};
WL.Server.setActiveUser("AdapterAuthRealm", userIdentity);
return {
authRequired: false
};
}
I am not able to set setActiveUser. How to set it and get it. Above wat I have to done for maintaining session. Is it right way to do it. Sometimes I got error Illegal State: Cannot change identity of an already logged in user in realm 'Adapter AuthRealm'. The application must logout first. If I tried to set it null first then it causes my application to enter an infinite loop of attempting to authenticate. Any idea why I would see this behavior?
Anything I am doing wrong?
please help mi out.
Check the SO answer for Worklight logout does not clear active user
Try reloading app onSuccess of Logout.
WL.Client.logout("AdapterAuthRealm",{
onSuccess: function(){ WL.Client.reloadApp(); },
onFailure: function(){ WL.Logger.debug("Error on logout");}
});
And you can check realm is authenticated or not by using wl client Java script API
WL.Client.isUserAuthenticated("AdapterAuthRealm");