How to avoid basic authentication when session has expired in JasperServer 5.5.0

Recently I had an issue with our clients in which they complained about showing a basic authentication popup window when the session is timeout and the user changes the values of the input controls in order to execute a report. I have decided to implement a solution in which when this event occurs the user […]

by Boyko Dimitrov

November 6, 2014

3 min read

Two Factor Authentication - How to avoid basic authentication when session has expired in JasperServer 5.5.0

Recently I had an issue with our clients in which they complained about showing a basic authentication popup window when the session is timeout and the user changes the values of the input controls in order to execute a report. I have decided to implement a solution in which when this event occurs the user will be redirected to the login page avoiding the basic authentication.

First I needed to find out where in the project is used basic authentication. The easiest way is to search for string “WWW-Authenticate” which according to the standard must be included in the HTTP request header. The result after the search was one – ForbiddenEntryPoint.java. The definition of the class is:

[java]
public class ForbiddenEntryPoint implements AuthenticationEntryPoint {
public final String SUPPRESS_BASIC_HEADER = “X-Suppress-Basic”;
public final String HTTP_WWW_AUTHENTICATE_HEADER = “WWW-Authenticate”;

public void commence(ServletRequest request, ServletResponse response, AuthenticationException authException)
throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;

if (!”true”.equalsIgnoreCase(httpRequest.getHeader(SUPPRESS_BASIC_HEADER))){
httpResponse.setHeader(HTTP_WWW_AUTHENTICATE_HEADER, “Basic realm=”Protected area””);
}

httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED, authException.getMessage());
}
}
[/java]

The basic authentication popup window will appear if in the header of the request the value of “X-Suppress-Basic” is NOT “true”, otherwise the response will be with status code 401(httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED, authException.getMessage());) which means the authorization has been denied. Now we have to find out where in the project is generated the request from the input controls of the report. After debugging a couple of hours with the help of the “Developer tools” in Chrome I found out that the file which I need is controls.datatransfer.js

First we need to add “X-Supress-Basic” with value “true” so we will modify the sendRequest function to look like this:

[java]
sendRequest : function(url, data, settings) {
var defaultSettings = {
url : url,
type : “GET”,
dataType : “json”,
cache : false,
headers : {‘X-Suppress-Basic’: ‘true’}
};
if (!_.isEmpty(data)) {
_.extend(defaultSettings, {
type : “POST”,
processData : false,
data : JSON.stringify(data),
contentType : “application/json”
});
}
return jQuery.ajax(_.defaults(settings || {}, defaultSettings)).
fail(commonErrorHandler);
}
[/java]

Only the red line is the modification.

Now we need to handle the response which will be with status code 401. The modification is in the commonErrorHandler function.

[java]
function commonErrorHandler(err) {
try {
try {
var errorObject = jQuery.parseJSON(err.responseText);
} catch (e) {}

if (errorObject && errorObject.error) {
_.each(errorObject.error, function (error) {
//TODO: try to avoid it
var viewModel = Controls.getViewModel();
var controlUri = error.inputControlUri.replace(“repo:”, “”);
var control = viewModel.find({uri:controlUri});
if (error.errorCode) {
control.set({error:error.errorCode});
} else if (error.defaultMessage) {
control.set({error:error.defaultMessage});
}
});
} else {
if (err.getResponseHeader(“LoginRequested”) || err.status == 401) {
document.location.href = ‘login.html’;
} else if (err.status == 500 || (err.getResponseHeader(“JasperServerError”)
&& !err.getResponseHeader(“SuppressError”))) {

var message = “”;
if (errorObject) {
message = Mustache.to_html(“<div><b>{{message}}</b></div><p>{{#parameters}}<div>{{.}}</div>{{/parameters}}</p>”, errorObject);
} else {
message = statusText;
}

dialogs.errorPopup.show(message);
}
}
} catch (e) {
// In this scenario security error is handled earlier, in errorHandler, so we can ignore exception here.
// Comment this because it will not work in IE, but can be uncommented for debug purpose.
// console.error(“Can’t parse server response: %s”, “controls.core”, err.responseText);
}
}
[/java]

Again the modification is red. So if we receive status code 401 the user will be redirected to the login page.

Leave your comments below

Java Developer at Dreamix