IBM Worklight : WL.Client.getUserName Fails to retrieve userIdentity immediately after authentication - ibm-mobilefirst

I have done adapter based authentication and there is no problem in authentication and it works fine. I have faced some issues in getting the active users useridentity.The code may explain you a bit more
adapterAuthRealmChallengeHandler.handleChallenge = function(response){
var authRequired = response.responseJSON.authRequired;
if (authRequired == true){
if (response.responseJSON.errorMessage)
alert(response.responseJSON.errorMessage);
} else if (authRequired == false){
adapterAuthRealmChallengeHandler.submitSuccess();
setTimeout(function(){pageTransitionCall();},10000); //this code only works
pageTransitionCall(); //This throws null error in console
}
};
function pageTransitionCall(){
console.log(WL.Client.getUserName("AdapterAuthRealm"));
}
As you can see i was trying to get the active userName of the realm. The WL.Client.getUserName("AdapterAuthRealm") only works after some time interval only and i am not sure about the time interval. By adapter code is as below
function submitAuthentication(username, password,userCred){
if (username==="worklight" && password === "worklight"){
WL.Logger.info("if");
var userIdentity = {
userId: userCred,
displayName: userCred,
attributes: {
foo: "bar"
},
loginName : userCred,
userName : userCred
};
WL.Server.setActiveUser("AdapterAuthRealm", userIdentity);
WL.Logger.info(JSON.stringify(userIdentity));
return {
authRequired: false
};
}
else
{
WL.Logger.info("else");
return onAuthRequired(null, "Invalid login credentials");
}
}
My doubt is why does the client cant retrieve the activeuser. And i am sure that my code is correct and active user is set and i can see in the server log.After the setactvieruser is set only i have return false in the adpter and why cant the client retrieve the user at instant and why it needs delay to retrieve. i have verified in both Worklight V6.0 and also Worklight V6.1.i have created the Ipad environment.

The info that contains logged in userId (basically any userIdentity data) is not returned immediately after adapter authentication but only when an original request succeeds. Consider this
You're making request#1 to the server (let's say invoke procedure)
You're getting response with authRequired:true
You're submitting auth data
You're getting authRequred:false
You're calling submitSuccess()
WL framework automatically re-invokes request#1
You're getting response for request#1
userIdentity data will be returned in step7 and not in step4. Basically once you start authentication flow you're considered out of the original invocation context. You need to finish the flow and tell WL framework that auth has completed. Once you do - WL framework will reinvoke the original request. WL server add userIdentity data to the response and WL client will update userName, displayName etc properties.
In case you need user data before that, e.g. right away once auth is complete, you can add custom properties to your submitAuthentication function response, e.g.
WL.Server.setActiveUser("AdapterAuthRealm", userIdentity);
return {
authRequired: false,
loginName: userIdentity.loginName
};
this will make sure that loginName will be returned to your handleChallenge function. you can retrieve it there and do whatever you want with it.

Related

How to provide own login page if windows authentication get failed?

Currently i am working on one POC with Identity server4 where i have to show my own login page if windows authentication get failed(in this case i just want to show my own login page and avoid browser login popup .
My question is where to inject my own login page in code? and how application will know windows authentication get failed?If you check below code, first request to AuthenticateAsync always return null and then it call Challenge from else block which ask browser to send Kerberos token
and we achieve SSO but now i want to show my own login page if SSO fail.
My scenario is exactly similar like this
Anyone know how to achieve this?
private async Task<IActionResult> ProcessWindowsLoginAsync(string returnUrl)
{
// see if windows auth has already been requested and succeeded.
var result = await HttpContext.AuthenticateAsync(_windowsAuthConfig.WindowsAuthenticationProviderName);
if (result?.Principal is WindowsPrincipal wp)
{
var props = new AuthenticationProperties
{
RedirectUri = Url.Action("Callback"),
Items =
{
{ "returnUrl", returnUrl},
{ "scheme", _windowsAuthConfig.WindowsAuthenticationProviderName}
}
};
var id = new ClaimsIdentity(_windowsAuthConfig.WindowsAuthenticationProviderName);
var claims = await _userStore.GetClaimsForWindowsLoginAsync(wp);
id.AddClaims(claims);
_logger.LogDebug("Signing in user with windows authentication.");
await HttpContext.SignInAsync(IdentityServerConstants.ExternalCookieAuthenticationScheme,new ClaimsPrincipal(id),props);
return Redirect(props.RedirectUri);
}
else
{
_logger.LogDebug("Re-triggered windows authentication using ChallengeResult.");
// Trigger windows auth
// since windows auth don't support the redirect uri,
// this URL is re-triggered when we call challenge
return Challenge(_windowsAuthConfig.WindowsAuthenticationSchemes);
}
}

Windows authentication fail with "401 Unauthorized"

I have a MVC client accessing a Web API protected by IDS4. They all run on my local machine and hosted by IIS. The app works fine when using local identity for authentication. But when I try to use Windows authentication, I keep getting "401 Unauthorized" error from the dev tool and the login box keeps coming back to the browser.
Here is the Windows Authentication IIS setting
and enabled providers
It's almost like that the user ID or password was wrong, but that's nearly impossible because that's the domain user ID and password I use for logging into the system all the time. Besides, according to my reading, Windows Authentication is supposed to be "automatic", which means I will be authenticated silently without a login box in the first place.
Update
I enabled the IIS request tracing and here is the result from the log:
As you can see from the trace log item #29, the authentication (with the user ID I typed in, "DOM\Jack.Backer") was successful. However, some authorization item (#48) failed after that. And here is the detail of the failed item:
What's interesting is that the ErrorCode says that the operation (whatever it is) completed successfully, but still I received a warning with a HttpStatus=401 and a HttpReason=Unauthorized. Apparently, this is what failed my Windows Authentication. But what is this authorization about and how do I fix it?
In case anyone interested - I finally figured this one out. It is because the code that I downloaded from IndentityServer4's quickstart site in late 2020 doesn't have some of the important pieces needed for Windows authentication. Here is what I had to add to the Challenge function of the ExternalController class
and here is the ProcessWindowsLoginAsync function
private async Task<IActionResult> ProcessWindowsLoginAsync(string returnUrl)
{
var result = await HttpContext.AuthenticateAsync(AccountOptions.WindowsAuthenticationSchemeName);
if (result?.Principal is WindowsPrincipal wp)
{
var props = new AuthenticationProperties()
{
RedirectUri = Url.Action(nameof(Callback)),
Items =
{
{ "returnUrl", returnUrl },
{ "scheme", AccountOptions.WindowsAuthenticationSchemeName },
}
};
var id = new ClaimsIdentity(AccountOptions.WindowsAuthenticationSchemeName);
id.AddClaim(new Claim(JwtClaimTypes.Subject, wp.Identity.Name));
id.AddClaim(new Claim(JwtClaimTypes.Name, wp.Identity.Name));
if (AccountOptions.IncludeWindowsGroups)
{
var wi = wp.Identity as WindowsIdentity;
var groups = wi.Groups.Translate(typeof(NTAccount));
var roles = groups.Select(x => new Claim(JwtClaimTypes.Role, x.Value));
id.AddClaims(roles);
}
await HttpContext.SignInAsync(IdentityConstants.ExternalScheme, new ClaimsPrincipal(id), props);
return Redirect(props.RedirectUri);
}
else
{
return Challenge(AccountOptions.WindowsAuthenticationSchemeName);
}
}
Now my windows authentication works with no issues.

Authentication Service throws Procedure invocation error post upgrade from MFP 6.3 to MFP 7.1

Authentication adapter throwing "Procedure invocation error" sometimes. Tried clearing cache and cookies but still the same. So we tried to login from different system for same user and it works. This is quite confusing as once we try with different ID in browser where issue occurred, it works and then it works with Member ID which has issue as well. Auth required is not coming in response when issue occurs.
we have tried to look into logs and found WorklightAuthenticationException from Authentication Adapter while trying security test procedure.
Authentication Adapter code:
var result = WL.Server.invokeHttp(input);
WL.Logger.info("Authentication service : " + JSON.stringify(result));
authResponse = prepareJSONResponse(result,channelId);
WL.Logger.info('Formatted response -> ' + JSON.stringify(authResponse));
if(result.isSuccessful == false){
WL.Logger.info("Error: " + result.errorMessage);
return onAuthRequired(null, "Error in connecting to server. Please try again later.");
}
if(typeof authResponse.errorMessage != 'undefined'){
WL.Logger.info("Error is defined" +authResponse.errorMessage);
return onAuthRequired(null, authResponse);
}
WL.Logger.info("Authentication service success: " + JSON.stringify(result));
WL.Logger.info("userIdentity Parameters: " + inputParams.CorpId);
var userIdentity = {
userId: inputParams.CorpId,
displayName: inputParams.CorpId,
attributes: {
foo: "bar"
}
};
WL.Logger.info("userIdentity::"+JSON.stringify(userIdentity));
WL.Server.setActiveUser("SingleStepAuthRealm", userIdentity);
return {
authRequired: false
};
It is happening due to the requests going from one node to another node. Handled it in Load balancer to send requests to specific node based on cookies and post that it works fine.
The description mentions about clearing cache and cookies and using browser.
Browser based environments are not supported in session independent mode. These work only in session dependent mode. As such, it is imperative that session based affinity be enabled to ensure the requests land in the same JVM for authentication state to be preserved.
More details can be found here : Session-independent mode

Check if user is already logged in or or not in MFP V8.0

I have written an adapter procedure in MFP V8.0. This procedure is secured bu a security check. I want to check that user is already logged-in before calling this adapter procedure:
Procedure is mapped to scope as below:
<procedure name="searchData" scope="restrictedResource"/>
Security Check is defined as below:
<securityCheckDefinition name="UserValidationSecurityCheck" class="com.sample.UserValidationSecurityCheck">
I have done the the Scope Element mapping the server also.
I have written below method which calls the adapter method:
function callAdapterProcedure(invocationData){
var procedureName = invocationData.procedure;
var successHandler = invocationData.successHandler;
var failureHandler = invocationData.failureHandler;
var parameters = invocationData.parameters;
var isuserLoggedIn = checkForLoggedInUser();
alert('is logged in' + isuserLoggedIn);
if(isuserLoggedIn){
var dataRequest = new WLResourceRequest(getAdapterPath(procedureName), WLResourceRequest.GET);
dataRequest.setQueryParameter("params", [JSON.stringify(parameters)]);
dataRequest.send().then(successHandler,failureHandler);
}else{
hideProgressBar();
showAlert(Messages.ALERT_SESSION_TIME_OUT);
logoutWithoutConfirmation();
openLogin();
}
}
Below is the implementation of checkForLoggedInUser() method:
function checkForLoggedInUser(){
var userAlreadyLoggedIn = undefined;//WL.Client.isUserAuthenticated(mrmGlobal.realms.authenticationRealm,null);
WLAuthorizationManager.obtainAccessToken("restrictedResource").then(
function (accessToken) {
alert("obtainAccessToken onSuccess");
userAlreadyLoggedIn = true;
},
function (response) {
alert("obtainAccessToken onFailure: " + JSON.stringify(response));
userAlreadyLoggedIn = false;
});
return userAlreadyLoggedIn;
}
I know that WLAuthorizationManager.obtainAccessToken sends the asynchronous call to the server that's why userAlreadyLoggedIn is always coming as undefined. Is there any way through which I can check that the user session is not timed out before making the adapter call? Basically I want to implement something like WL.Client.isUserAuthenticated (which was there in earlier versions).
--Update--
Plus I have observed one more thing that the handlers method of WLAuthorizationManager.obtainAccessToken are also not getting called.
From your code:
WLAuthorizationManager.obtainAccessToken("restrictedResource").then(
function (accessToken) {
alert("obtainAccessToken onSuccess");
userAlreadyLoggedIn = true;
},
function (response) {
alert("obtainAccessToken onFailure: " + JSON.stringify(response));
userAlreadyLoggedIn = false;
});
It is a common misconception to think that obtainAccessToken's onFailure means the user is not logged in. But that's not exactly how it works.
When you call obtainAccessToken, there are 3 possible outcomes:
Success: The user is logged in, and obtainAccessToken onSuccess is called (along with the challenge handler's success method).
Challenge: The user is not logged in, the security check sent a challenge to the client. This challenge will be received by your challenge handler. obtain will remain on hold until you answer the challenge. This is probably what happens in your case, this would not explain why none of the obtain's handlers are being called.
Failure: Something went wrong during the authentication. It could be that the server is down, networking issue, the scope does not exist, or the user is blocked, etc. In this case, obtainAccessToken's onFailure will be called.
There currently is no API to check if a scope is granted without triggering a challenge. I have opened an internal feature request, feel free to submit your own (https://www.ibm.com/developerworks/rfe ).
In the meantime you could add your own internal boolean flag, that you set to true whenever you login and false whenever you logout.

Cookie Authentication with ASP.NET 5 MVC 6 Web API

I totally understand if someone finds that my question is very basic or might not make a lot of sense all the way.
I am new to this and I am trying to use the latest .NET Framework 5 with MVC 6 in order to build a Web Api that could be used from an Angular JS client-side. This will allow me to create a website for it, as well as a mobile application by wrapping it with Phonegap. So please bear with me a bit.
What I am trying to achieve for the moment is to have a Web API controller that receives a login request and returns a result to the client based on Cookie Authentication (later the client should store this cookie and use it for communications with the server)
I added the following in the project.json
In the Startup.cs, I added under ConfigureServices:
// Add entity framework support
services.AddEntityFramework()
.AddSqlServer()
.AddDbContext<ApplicationDbContext>(options =>
{
options.UseSqlServer(Configuration["Data:DefaultConnection:ConnectionString"]);
});
// add ASP.NET Identity
services.AddIdentity<ApplicationUser, IdentityRole>(options => {
options.Password.RequireDigit = false;
options.Password.RequireLowercase = false;
options.Password.RequireUppercase = false;
options.Password.RequireNonLetterOrDigit = false;
options.Password.RequiredLength = 6;
})
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
In the Startup.cs, under Configure:
// Using the identity that technically should be calling the UseCookieAuthentication
app.UseIdentity();
Now, in the Controller method to login, I am able to find the user using its email address and the UserManager:
// Verify that the model is valid according to the validation rules in the model itself.
// If it isn't valid, return a 400 Bad Request with some JSON reviewing the errors
if (!ModelState.IsValid)
{
return HttpBadRequest(ModelState);
}
// Find the user in our database. If the user does not exist, then return a 400 Bad Request with a general error.
var user = await userManager.FindByEmailAsync(model.Email);
if (user == null)
{
ModelState.AddModelError("", INVALID_LOGIN_MESSAGE);
return HttpBadRequest(ModelState);
}
// If the user has not confirmed his/her email address, then return a 400 Bad Request with a request to activate the account.
if (!user.EmailConfirmed)
{
ModelState.AddModelError("Email", "Account not activated");
return HttpBadRequest(ModelState);
}
// Authenticate the user with the Sign-In Manager
var result = await signInManager.PasswordSignInAsync(user.UserName, model.Password, model.RememberMe, lockoutOnFailure: false);
// If the authentication failed, add the same error that we add when we can't find the user
// (so you can't tell the difference between a bad username and a bad password) and return a 400 Bad Request
if (!result.Succeeded)
{
ModelState.AddModelError("", INVALID_LOGIN_MESSAGE);
return new BadRequestObjectResult(ModelState);
}
return Ok();
The problem is happening at the line:
// Authenticate the user with the Sign-In Manager
var result = await signInManager.PasswordSignInAsync(user.UserName, model.Password, model.RememberMe, lockoutOnFailure: false);
it is throwing the following error:
Error: No authentication handler is configured to handle the scheme:
Microsoft.AspNet.Identity.Application
I am currently blocked and I searched googled for almost every possible token I could think of and tried multiple solution still in no vain. Any help is highly appreciated.
Regards,
Ok I finally figured it out after writing this whole question and I wanted to share the answer to avoid the hussle for someone else if they commit the same mistake I did!
The problem was that in the Configure in Startup.cs, I called "app.UseIdentity()" after calling "app.UseMVC()". The order should have been inversed. I donno if this is common knowledge or I should have read about it somewhere.