handleChallenge() is not getting called after successful execution of isCustomResponse() - authentication

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);

Related

How to redirect from GraphQL middleware resolver on authentication fail?

Introduction:
I am using GraphQL Mesh as a gateway between my app and an API. I use Apollo Client as the GraphQL client. When a user wants to visit the first screen after hitting the log-in button, I do a query to load data from a CMS. This query has to go through the gateway. In the gateway I do an auth check to see if the user has a valid JTW access token, if not, I want to redirect back to the sign-in page. If the user has a token, he is let through.
The gateway is-auth.ts resolver:
const header = context.headers.authorization;
if (typeof header === "undefined") {
return new Error("Unauthorized: no access token found.");
} else {
const token = header.split(" ")[1];
if (token) {
try {
const user = jwt.verify(token, process.env.JWT_SECRET as string);
} catch (error) {
return new Error("Unauthorized: " + error);
}
} else {
return new Error("Unauthorized: no access token found.");
}
}
return next(root, args, context, info);
},
Problem: Right now, I am returning Errors in the authentication resolver of the gateway, hoping that I could pick them up in the error object that is sent to Apollo Client and then redirect off of that. Unfortunately, I don't get that option, since the Errors are thrown immediately, resulting in an error screen for the user (not what I want). I was hoping this would work in order to redirect to the sign-in from the client-side, but it does not work:
const { data, error } = await apolloClient(accessToken).query({
query: gql`
query {
...where my query is.
}
`,
});
if (error) {
return {
redirect: {
permanent: false,
destination: `/sign-in`,
},
};
}
Does anyone perhaps have a solution to this problem?
This is the GraphQL Mesh documentation on the auth resolver, for anyone that wants to see it: https://www.graphql-mesh.com/docs/transforms/resolvers-composition. Unfortunately, it doesn't say anything about redirects.
Kind regards.

Could not complete oAuth2.0 login

I have implemented Aspnet.security.openidconnect.server with .net core 2.1 app. Now I want to test my authorization and for that I am making postman request. If I change the grant type to client_credentials then it works but I want to test complete flow, so I select grant type to Authorzation code and it starts giving error "Could not complete oAuth2.0 login.
Here is the code:
services.AddAuthentication(OAuthValidationDefaults.AuthenticationScheme).AddOAuthValidation()
.AddOpenIdConnectServer(options =>
{
options.AuthorizationEndpointPath = new PathString(AuthorizePath);
// Enable the token endpoint.
options.TokenEndpointPath = new PathString(TokenPath);
options.ApplicationCanDisplayErrors = true;
options.AccessTokenLifetime = TimeSpan.FromMinutes(5);
#if DEBUG
options.AllowInsecureHttp = true;
#endif
options.Provider.OnValidateAuthorizationRequest = context =>
{
if (string.Equals(context.ClientId, Configuration["OpenIdServer:ClientId"], StringComparison.Ordinal))
{
context.Validate(context.RedirectUri);
}
return Task.CompletedTask;
};
// Implement OnValidateTokenRequest to support flows using the token endpoint.
options.Provider.OnValidateTokenRequest = context =>
{
// Reject token requests that don't use grant_type=password or grant_type=refresh_token.
if (!context.Request.IsClientCredentialsGrantType() && !context.Request.IsPasswordGrantType()
&& !context.Request.IsRefreshTokenGrantType())
{
context.Reject(
error: OpenIdConnectConstants.Errors.UnsupportedGrantType,
description: "Only grant_type=password and refresh_token " +
"requests are accepted by this server.");
return Task.CompletedTask;
}
if (string.IsNullOrEmpty(context.ClientId))
{
context.Skip();
return Task.CompletedTask;
}
if (string.Equals(context.ClientId, Configuration["OpenIdServer:ClientId"], StringComparison.Ordinal) &&
string.Equals(context.ClientSecret, Configuration["OpenIdServer:ClientSecret"], StringComparison.Ordinal))
{
context.Validate();
}
return Task.CompletedTask;
};
// Implement OnHandleTokenRequest to support token requests.
options.Provider.OnHandleTokenRequest = context =>
{
// Only handle grant_type=password token requests and let
// the OpenID Connect server handle the other grant types.
if (context.Request.IsClientCredentialsGrantType() || context.Request.IsPasswordGrantType())
{
//var identity = new ClaimsIdentity(context.Scheme.Name,
// OpenIdConnectConstants.Claims.Name,
// OpenIdConnectConstants.Claims.Role);
ClaimsIdentity identity = null;
if (context.Request.IsClientCredentialsGrantType())
{
identity = new ClaimsIdentity(new GenericIdentity(context.Request.ClientId, "Bearer"), context.Request.GetScopes().Select(x => new Claim("urn:oauth:scope", x)));
}
else if (context.Request.IsPasswordGrantType())
{
identity = new ClaimsIdentity(new GenericIdentity(context.Request.Username, "Bearer"), context.Request.GetScopes().Select(x => new Claim("urn:oauth:scope", x)));
}
// Add the mandatory subject/user identifier claim.
identity.AddClaim(OpenIdConnectConstants.Claims.Subject, Guid.NewGuid().ToString("n") + Guid.NewGuid().ToString("n"));
// By default, claims are not serialized in the access/identity tokens.
// Use the overload taking a "destinations" parameter to make sure
// your claims are correctly inserted in the appropriate tokens.
identity.AddClaim("urn:customclaim", "value",
OpenIdConnectConstants.Destinations.AccessToken,
OpenIdConnectConstants.Destinations.IdentityToken);
var ticket = new Microsoft.AspNetCore.Authentication.AuthenticationTicket(
new ClaimsPrincipal(identity),
new Microsoft.AspNetCore.Authentication.AuthenticationProperties(),
context.Scheme.Name);
// Call SetScopes with the list of scopes you want to grant
// (specify offline_access to issue a refresh token).
ticket.SetScopes(
OpenIdConnectConstants.Scopes.Profile,
OpenIdConnectConstants.Scopes.OfflineAccess);
context.Validate(ticket);
}
return Task.CompletedTask;
};
and here is the postman collection:
Now I am not sure that whether the issue is in my code or in postman collection? I think the callback url is creating some issue but I am not sure. Any help?
Update:
By visiing this page https://kevinchalet.com/2016/07/13/creating-your-own-openid-connect-server-with-asos-implementing-the-authorization-code-and-implicit-flows/ I have found the issue. I haven't handled authorization code flow in my code but I even don't want to. Is there any way I test my code with Resource owner password? I can't see this grant type in request form. In simple words I want postman to open login screen which is in Controller/Login/Index and I select my ssl Certificate and it generates a token for me?
hello i think that you have to add https://www.getpostman.com/oauth2/callback as the redirect_url in your server config, i don't think that your STS server will return tokens back to a non trusted url. that's why it works from your app but not from Postman

In OpenIdConnect getUser returns null, even after the receiving token

After being successfully authenticated by the Identity Server, a token is returned and the user is redirected to app.ts. However, when I call getUser() in app.ts it returns null. This code is working properly in localhost, but when I try it in production it doesn't work (returns null).
app.ts
constructor(private openIdConnect: OpenIdConnect, private httpClient: HttpClient) {
debugger;
let mgr = this.openIdConnect.userManager;
this.openIdConnect.userManager.getUser().then((user) => {
if (user) {
this.user = user;
console.log(" This.User: "+this.user);
console.log(" User: "+ user)
}else{
console.log(" logout ");
this.logout();
}
});
}
The Problem was the way I build the app to production.So earlier I used "au build --env prod " I change it to “au build --env”

IBM MobileFirst 7.1 - Calling adapters from their URL failing with global variables and setActiveUser()

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.

Error calling signout after gapi.auth.authorize

I'm using client side login for google+. The access token expires in 1 hour. Calling gapi.auth.signOut() does not log the user out after the token has expired. I'm trying to re-authorise the user if his token has expired by calling gapi.auth.authorize with client_id, scope and immediate = true parameters. After calling this method, gapi.auth.signOut() doesn't work. I am not able to understand why is it.
Here is the code:
var google = {
signOut: function() {
var token = gapi.auth.getToken();
if(!token) {
var params = {
'client_id': global.clientid,
'session_state': global.sessionState,
'response_type':'token'
}
gapi.auth.checkSessionState(params, function(state){
if(state == true) {
google.doSignOut();
} else {
google.silentAuthorize(function(data){
google.doSignOut();
});
}
});
} else {
google.doSignOut();
}
},
doSignOut: function() {
gapi.auth.signOut();
google.loggedin = false;
},
silentAuthorize: function(callback) {
var params = {};
params.client_id = global.clientid;
params.immediate = true;
params.scope = "https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/plus.me https://www.googleapis.com/auth/plus.login"
gapi.auth.authorize(params, callback);
}
}
calling google.signOut works fine while the token hasn't expired. But once the token has expired or I simple call google.silentAuthorize() with a callback, calling gapi.auth.signOut() starts throwing an error:
TypeError: Cannot read property 'clear' of null
Been trying to figure this out for 4 hours now, any help is highly appreciated!
I couldn't find anyway to renew token from front end, so I switched to hybrid method of using google auth. I now revive the session every time it is about to expire within php.