Extend/Override jquery.ajax.done function to detect .net logon screen - asp.net-mvc-3

I'm using forms authentication on an asp.net MVC application and I'm using jquery and ajax to create a rich user interface. Some users tend to leave the page open for hours and their login session is expiring as expected. If the user click a link or reloads the page the login screen is returned as expected. If they click a button or trigger an ajax call the ajax call is returning the login html instead of the expected html or JASON result. Resulting in a mangled page with half a login screen.
As I've already coded a lot of ajax calls I thought I could extend or override the default .done event to add a check for the login screen before continuing with whatever event I've programmed.
For example i have this function :
function CallAjax() {
$.ajax({type: "GET", url: "foo" })
.done(function (data) { $('#result').val(data); });
}
is it possible to override the default implementation of .done so do a check without rewriting this function? My check would probably to see if the result is a html response if so search for "login" to see if its the login page.

I would recommend you to extend the ASP.NET Forms Authentication module as Phil Haack explained in this blog post so that it no longer redirects to the login page when requested with AJAX but returns a 401 HTTP status code instead. Then you could simply use the .fail callback and intercept the 401 status code. The .done handler will not be invoked in this case.

I tried Darin's suggestion but after hours of trying I just couldn't get it to work in my set up. However I stumbled across this rather simpler solution which worked for me first time:
just add this to the global.asax
protected void Application_EndRequest()
{
var context = new HttpContextWrapper(this.Context);
if (FormsAuthentication.IsEnabled &&
context.Response.StatusCode == 302 &&
context.Request.IsAjaxRequest())
{
context.Response.Clear();
context.Response.StatusCode = 401;
}
}
}
Taken from http://jvance.com/blog/2012/09/21/FixFormsAuthentication302.xhtml

Related

Creating database entries in Exist-DB with Ajax calls

While developing a self-contained exist-db app I ran into some issues with passing on the user's identity during an ajax call.
I have created a standard page that allows the user to login in with his or her standard exist-db account. (It's more or less based on this https://github.com/eXist-db/existdb-login ).
At some point, the users need to create their own entries and upload files, and this is done with an ajax script (I'm using Kartik's bootstrap uploader, but it's basically a standard ajax upload function):
$('#input-24').fileinput({
uploadUrl: "../modules/uploader.xql",
uploadAsync: true,
fileActionSettings : {
uploadExtraData() {
return {
'FileName': $(this).data('title'),
}
}
},
});
Now, of course, this will fail, since it will appear like a GUEST user is trying to create entries, which is an action that is allowed only for registered users.
Does exist-db allow a way to send in my login credentials at this point without having to resort to the standard HTTP login (which is problematic, since it means creating a cookie to memorize the password, which more or less renders the whole login suing exist's mechanisms useless), or is there any way to use the controller instead of an external script?
Thanks in advance!
I think you can add the user and password into the URI to trigger the Ajax client to do basic with. e.g. http://username:password#your-server/some/uri/modules/uploader.xql
For simple, basic authentication we do the following since the page itself is being served from xQuery.
step 1: we create in the page xQuery a variable containing the session info. This should be fine for you ... the result of your login would be an HTML page:
let $sessinfo := util:base64-encode(concat(request:get-parameter('user', ()), ":", request:get-parameter('pass', ())))
step 2: during the build of the result page in xQuery, we turn that into a javascript variable through a simple script command placed in <head> tag:
<script>
var sessinfo = "{$sessinfo}";
</script>
step 3: in Javascript loaded with the page (we use jQuery), after the page is ready we setup authentication for all AJAX requests:
jQuery.ajaxSetup({
headers: {
"Authorization": "Basic " + sessinfo
}
});
After that, any AJAX request made would send basic authentication header with user and password to other xQueries executed with jQuery.ajax
To have an eXistdb session work with XHR (Ajax requests) you need make sure that
login:set-user('my.login.domain', (), false())
is called in the controller before you forward it to your request handler. Otherwise all request will seem to originate from the 'guest' user.
If you want to use vanilla/native JavaScript requests e.g. fetch you also need to tell it to add the credentials to the request:
fetch(url, { credentials: 'same-origin', method: 'GET' })
By the way, the persistent login used in exidtb-login likely uses a cookie value to pick up the session variables stored on the server (JSESSIONID), but I need to check that.

This page isn't redirecting properly error with MVC3 + AJAX + Forms Authentication

EDIT: Removed non-relevant code/desc, since the issue was not just to do with the initial code there.
I have an MVC3 based application that uses a lot of Ajax calls (such as with jqGrid) and Forms Authentication. I also use the [Authorize] attribute on certain controller/actions that I call with Ajax in most cases. Every so often my application falls over with a 'This page isn't redirecting properly' or a 'this page has a redirect loop'.
I checked this out with fiddler. After logging in as a user and trying to access pages that require authentication, sometimes I get redirected to Account/LogOn which then goes into an infinite loop. This usually happens when I'm calling any controller/action with an Authorize attribute with an Ajax call. The application seems to send out a 302 redirect to Account/Logon. The Account/Logon call then seems to redirect to itself. And the textView on Fiddler shows the following.
<html><head><title>Object moved</title></head><body>
<h2>Object moved to here.</h2>
</body></html>
I have the following in my Global.asax file
protected void Application_EndRequest()
{
var context = new HttpContextWrapper(this.Context);
//if we are an ajax request and forms authentication caused a 302, then we actually need to do a 401
if (System.Web.Security.FormsAuthentication.IsEnabled && context.Response.StatusCode == 302 && context.Request.IsAjaxRequest())
{
context.Response.Clear();
context.Response.StatusCode = 401;
}
}
And this in my main Layout page
<script type="text/javascript">
$(document).ajaxError(function (xhr, props) {
if (props.status == 401) {
location.reload();
}
});
</script>
My web.config setting for Forms authentication has this
<authentication mode="Forms">
<forms loginUrl="~/Account/LogOn" timeout="2880"/>
</authentication>
The redirect starts every so often and it is not with the same controller/action either. Just seems quite random. I am not sure if the cookie is expiry for the user and causing this issue for some reason or if it is an issue with the application pool recycling. Any suggestions on how to get around this would be most appreciated. Been struggling with this the last few days now, so any help from the experts will be great. Thank You
decorate your action method with Authorize attribute to make it available to authenticated/logged in users, after that check user role.
I don't see a loop in the above code. But maybe in one of controller1 or controller2 you have a redirect back to this home/index action.
I would suggest to run fiddler while testing the site, when a redirect loop happens you can easily detect it there, and then it is easier to find out what's wrong with the code.
Please see the answer in the linked question. This was the resolution to my redirect loop problem. Thank you for all your inputs.
IIS Session timeout and Forms Authentication loop

CakePHP redirect method not redirecting

I am trying to redirect to a new page using CakePHP redirect method but for some reason the redirect is not working. The code I am using to redirect is
public function update_sticky_postition_in_db()
{
$this->autoRender = false;
... // Save info to database
$this->redirect(array('action' => 'tool'));
}
Where tool is the name of the view I am tring to redirect to. To try and speed the process of finding the problem I have checked few things and think I have found the cause of the problem. Basically I am trying to redirect to the view that is currently active which I think is part of the reason why it is not redirecting. I have read that it might have something to do with caching of the page but I am not sure how to solve that issue.
Also when using firebug I can see the redirect is sending a GET request but after that nothing is happening. Do I have to do something with the GET request or should Cake handle that for me. Also I have checked the URL of the GET and it is correct.
It is located within the controller with the correct name as I can view the original tool page.
Also the update_sticky_postition_in_db() method does not have a view (hence why the auto render is set to false), its intended purpose is to update a row in the database from an ajax call.
From your post it seems you're firing the update_sticky_postition_in_db() using ajax call, so that the redirection will not work.
You can do redirection using JavaScript within ajax success method.
In order to do that, you may send some json_encode() message from you above method and checking that status within ajax success method you can do a redirect using window.location.

Logging out from an ajax call

Nowadays we tend to split the amount of work made in just one page before this one renders, to speed things up, so, we normally load the basic and then upon DOM is ready, we call all parts to bring more data showing the normal animated loading gif ...
What is the normal procedure to, when requesting for such method, the user session already expired and he/she needs to login again?
Currently I have no such thing and the browser only shows endless the loading gif... I was wonder what would be the best approach to such trivial problem.
I thought about sending an error flag that, from javascript I could read and redirect to the login page
$.get( url, data, function(data) {
if(data.logout) document.location.href = '/LogOut';
...
});
but I get nothing from my controller soon the session expired...
P.S. My web application is made using ASP.NET MVC 3.
I would recommend you start with the following article in which Phil Haack explains how to properly handle 401 errors in ASP.NET MVC without automatically redirecting you to the LogOn page.
Then simply do your AJAX requests as normal:
$.get(url, data, function(data) {
// if we got here it means that the request succeeded
// => do whatever you have to do for this case
});
and then you could register a global AJAX event to handle the 401 case for all AJAX requests:
$(document).ajaxError(function (event, jqXHR, ajaxSettings, thrownError) {
if (jqXHR.status == 401) {
// the server returned 401 (Unauthorized) =>
// redirect to the LogOn page
window.location.href = 'put your logon url here';
}
});

Redirecting browser using AJAX

Is there a way to redirect a user to a page (login page in my case) when an AJAX query is made by the user to a server? The server is supposed to serve data only if the session is set or else the user should be redirected. I have tried sending a header("location:...") request but the browser handled it as a string (xmlhttp.response) rather than acting on it.
Not directly. Your callback function would have to handle it.
For example, if the server sends the text "LOGIN;/login.php;" then your onreadystatechange call back could have the snippet
if (xmlhttp.responseText.substring(0,6) == 'LOGIN;') {
window.location.href = xmlhttp.responseText.split(";")[1];
return;
}
If you're using a framework for the Ajax, this code could be in whichever callback gets the result of the Ajax call.
No. Not directly. You can return something special which should be handled as a redirect. But since the browser isn't looking to navigate and it won't.
In the callback function you can set the window.location to the new page if the session is not set.

Resources