we are developing MVC3 application such that most of our action methods are called via ajax calls and return partialviews. we come across a situation where we need to identify if the action method is called from Form Authentication time out.
public ActionResult LogOn()
{
// I want to return View("LogOn"); if the call is coming from
// Form Authentication time out
return PartialView(Model);
}
here is my web.config looks like:
<authentication mode="Forms">
<forms loginUrl="~/Home/LogOn" timeout="20" />
</authentication>
Appreciate your input.
Your action will never be hit if the authentication cookie has timed out. The forms authentication module directly redirects to the logon page. One possibility for you to detect this happening from client scripting is to set a custom HTTP header in the controller action serving this logon page:
public ActionResult LogOn()
{
var model = ...
Response.AppendHeader("X-LOGON", "true");
return View(model);
}
and then when performing your AJAX request you could use the getResponseHeader method on the XHR object in order to verify if the X-LOGON header was set meaning that the server redirected to the logon page. In this case in your success AJAX handler instead of simply injecting the server response into the DOM or relying on the returned JSON you could show some alert message informing the user that his authentication session has timed out and he needs to login again. Another possibility is to automatically redirect him to the logon page using the window.location.href method:
$.ajax({
url: '/home/some_protected_action',
success: function (data, textStatus, XMLHttpRequest) {
if (XMLHttpRequest.getResponseHeader('X-LOGON') === 'true') {
// the LogOn page was displayed as a result of this request
// probably timeout => act accordingly
}
}
});
There is no way from the server to distinguish between the user loading the page normally versus performing a page refresh.
There are ways to tell the difference between a regular request and an AJAX request, but it doesn't sound like that's what you're asking for.
There is no easy way but if you apply Post-Redirect-Get, I am not sure you will have that problem.
Related
I am working on a website in ASP.NET MVC and working on LoginPage. On the LoginPage, we also have 'Forgot Password' link and clicking that link opens a Modal-popup (bootstrap) with content being returned as PartialView.
Problem I am facing is, when I click on 'Forgot Password' link on the page, Index method of Login controller gets called instead of ForgotPassword related method which results in LoginPage being returned in modal popup.
[AllowAnonymous]
[System.Web.Services.WebMethod()]
public ActionResult ForgotPassword()
{
return PartialView("_ForgotPassword");
}
It seems like some sort of authentication issue because if I login using our old Login Page (it's an .aspx page) and then try to manually go to a new ASP.NET MVC page, all the link with partialView on login Page works fine.
Anyone else had this issue? any pointer would be appreciated.
Thanks
Edit 1: Javascript used to call modal popup
function AttachPopup() {
$('.modal-popup').click(function (event) {
event.preventDefault();
event.stopImmediatePropagation();
var url = $(this).attr('data-content-url');
var modalId = $(this).attr('data-target-model');
var target = $(this).attr('data-target-content');
$.ajax({
url: url,
type: 'POST',
datatype: "json",
cache: false,
traditional: true,
success: function (data) {
$(target).empty();
$(target).append(data);
$(modalId).modal('show');
}
});
});
}
url comes correct but still index is called.
When the AllowAnonymous attribute, the onAuthorization method of AuthorizeAttribute simply ignores authorization and authentication checking. Even if you had a global authorization filter. It should still work. A user had a similar issue. Please check out this link
As mentioned, I was converting an aspx website to MVC. In Web.config, we had
<authorization>
<deny users="?" />
</authorization>
This was taking precedence over MVC [AllowAnonymous] attribute. Commenting out above code, fixed my problem.
Word of caution: When you do that, do check that your website is secure with [Authorize] attribute
OWIN beginner here. Please be patient...
I'm trying to build an OWIN authentication middleware which uses form posts to communicate with my external authentication provider. I've had some success with getting the authentication bits working. In other words, I can:
communicate with the remote provider through form post;
process the response returned by the remove provider
If everything is ok, I'm able to signal the default authentication provider
THis in turn gets picked up by the cookie middleware which ends up generating the authentication cookie
So far, so good. Now, what I'd like to know is how to handle a log off request. Currently, the controller will simply get the default authentication manager from the owin context and call its SingOut method. This does in fact end my current session (by removing the cookie), but it really does nothing to the existing "external" session.
So, here are my questions:
1. Is the authentication middleware also responsible for performing log off requests?
2. If that is the case, then can someone point me to some docs/examples of how it's done? I've found some links online which describe the logging in part, but haven't found anything about the log off process...
Thanks.
Luis
After some digging, I've managed to get everything working. I'll write a few tips that might help someone with similar problems in the future...
Regarding the first question, the answer is yes, you can delegate the logoff to the middleware. If you decide to do that, then your middleware handler should override the ApplyResponseGrantAsync method and check if there's a current revoke request. Here's some code that helps to illustrate the principle:
protected override async Task ApplyResponseGrantAsync() {
var revoke = Helper.LookupSignOut(Options.AuthenticationType,
Options.AuthenticationMode);
var shouldEndExternalSession = revoke != null;
if (!shouldEndExternalSession) {
return;
}
//more code here...
}
After checking if there's a revoke request, and if your external authentication provider is able to end the response through a redirect, then you can simply call the Response.Redirect method (don't forget to check for the existance of redirect - ex.: if you're using asp.net identity and MVC's automatically generated code, then the sign out will redirect you to the home page of your site).
In my scenario, things were a little more complicated because communication with my authentication provider was based of form posts (SAML2 messages with HTTP Post binding). I've started by trying to use Response.Write to inject the HTML with the autopostback form into the output buffer:
protected override async Task ApplyResponseGrantAsync() {
//previous code + setup removed
var htmlForm = BuildAndSubmitFormWithLogoutData(url,
Options.UrlInicioSessaoAutenticacaoGov);
Response.StatusCode = 200;
Response.ContentType = "text/html";
await Response.WriteAsync(htmlForm);
}
Unfortunately, it simply didn't work out. Not sure on why, but the browser insisted in redirecting the page to the URL defined by the Logoff's controller method (which was redirecting the page to its home page or '/'). I've even tried to remove the location HTTP header from within the ApplyResponseGrantAsync method, but it still ended up redirecting the user to the home page (instead of loading the predefined HTML I was writing).
I've ended up changing the redirect so that it gets handled by my middleware. Here's the final code I've ended up with in the ApplyResponseGrant method:
protected override async Task ApplyResponseGrantAsync() {
//previous code + setup removed
//setup urls for callbabk and correlation ids
var url = ...; //internal cb url that gets handled by this middleware
Response.Redirect(url);
}
This redirect forced me to change the InvokeAsync implementation so that it is now responsible for:
Checking for a new authentication session
Checking for the end of an existing authentication session (handle the logoff response from the external provider)
Checking if it should generate a new form post html message that ends the current session controlled by the external provider
Here's some pseudo code that tries to illustrate this:
public override async Task<bool> InvokeAsync() {
if (Options.InternalUrlForNewSession.HasValue &&
Options.InternalUrlForNewSession == Request.Path) {
return await HandleLoginReply(); /login response
}
if (Options.InternalUrlExternalSessionEnded.HasValue &&
Options.InternalUrlExternalSessionEnded == Request.Path) {
return await HandleLogoffReply();//logoff response
}
if (Options.InternalUrlForEndingSession.HasValue &&
Options.InternalUrlForEndingSession == Request.Path) {
return await HandleStartLogoutRequest(); //start logoff request
}
return false;
}
Yes, in the end, I've ended with an extra request, which IMO shouldn't be needed. Again, I might have missed something. If someone manages to get the ApplyResponseGrantAsync to return the auto submit post (instead of the redirect), please let me know how.
Thanks.
I have this SignalR / MVC web application I'm building.
I currently have login and logout functionnality using ajax calls.
I would like to have those transit via SignalR instead. Is that possible? I'm having a hard time understanding how the auth cookie is going to be handled.
Currently my controller action login does this
[HttpPost]
public ActionResult Login()
{
var nickname = Request.Params["nickname"];
var password = Request.Params["password"];
// [Some database stuff...]
FormsAuthentication.SetAuthCookie(agencyMember.Id.ToString(), true);
}
So to give this a try, I've setup my logout function in my hub:
public class Proto1 : Hub
{
[Authorize(RequireOutgoing = false)]
public string Logout() {
FormsAuthentication.SignOut();
return "ok";
}
This does'nt work, when I debug, I get a "Response is not available in this context." exception and the logout does not go through.
Am I going wrong about this?
Thanks!
What's your motivation for wanting to do login and logout with SignalR?
SignalR Hub method invocations do not necessarily correspond to distinct HTTP requests (they can be triggered by a WebSocket message for example). This means that it's not always possible to modify response headers or set cookies when you are inside of a Hub method.
I would suggest continuing to login and logout using Ajax calls.
When you do return Json(...) you are specifically telling MVC not to use a view, and to serve serialized JSON data. Your browser opens a download dialog because it doesn't know what to do with this data.
i got the above information from below link
How to return Json object from MVC controller to view
when i give this below code in some action result
return Json(new { Url = "sep/employee"}
whether it will redirect to specified action ? how it redirects to the URl ?
for this case why i cant use return RedirectToAction("sep/employee").
how return Json code redirects to action which specified in the Url.
ex:
public ActionResult Index()
{
................................
return Json(new { Url = "sep/employee"}
}
public ActionResult employee()
{
....................
}
what is the difference b/s redirectaction and return Json
You return the following line to an ajax success call
return Json(new { Url = "sep/employee"});
you then need to specify where to redirect to the new page
success: function(result) {
window.location.href=result.Url;
}
RedirectToAction simply returns 302 code to your browser with URL telling where the browser should redirect to. The browser immediately makes yet another call to the server to that URL obtained from redirection response.
RedirectToAction internals are:
Construct redirection url from parameters passed to RedirectToAction
Return new RedirectToRouteResult
In ExecuteResult of RedirectToRouteResult you can find the following line:
context.HttpContext.Response.Redirect(destinationUrl, endResponse: false);
which is merely returning 302 with redirection URL. More info - look at source code here.
Returning JSON data simply returns JSON serialized object to your browser. Is not meant to do any redirect. To consume such a result you will likely call the server using $.ajax:
$.ajax({
url: 'sep/employee',
type: 'POST'
success: function(result) {
// handle the result from the server, i.e. process returned JSON data
}
});
ExecuteResult of JsonResult just serializes passed CLR object to the response stream, which lands in your browser after response is fully received. Then you can handle such response in JavaScript code.
EDIT:
You of course can mimic 302 redirection by returning Json like
return Json(new { Url = "redirectionUrl"}
and at client side handle it like
$.ajax({
url: 'sep/employee',
type: 'POST'
success: function(result) {
// mimic the 302 redirection
windows.location.href = result.Url
}
});
although IMHO it should be avoided since you reinvent MVC infrastructure and enlarge your code base.
whether it will redirect to specified action ? how it redirects to the URl ?
I assume you mean to ask, "will it redirect to specified action? how will it redirect the the URI?"
To answer your question: How it redirects to the URL?
In your example it will redirect to the URL, if you made the HTTP request as AJAX and that you will explicitly handle the resulting HTTP response to actually redirect to the URL that you received. So your view should have something like this:
$.ajax({
url: '{your controller}/index',
type: 'GET'
success: function(url) {
// write code to redirect to the URL
// example:
// window.navigate(url)
}
});
If your view does not have anything that, then it will not redirect. You did not post your view, so I am assuming it just a normal page load.
Your next question, what is the difference b/s redirection and return Json?
If you really just want to redirect then do RedirectToAction from your controller, that is exactly what it is for. You can do the same effect using AJAX and JSON to be able to redirect, but AJAX and JSON serves a much wider purpose than just redirecting and in most cases (unless you have very good reasons) you probably will not want replace RedirectToAction with that approach.
I'm using the "Redirect After Post" (http://en.wikipedia.org/wiki/Post/Redirect/Get) pattern to solve the problems with refreshing that it solves, but I'm not seeing the URL change after the POST and subsequent GET.
Here is my setup:
I have a form with some pretty extensive client-side validation, then submit.
#using (Html.BeginForm("AddItem", "Order", FormMethod.Post, new { Id = "addItemForm" }))
{
// form stuff
}
Client-side validation:
$('#addToOrder').click(function () {
// do a bunch of validation stuff.
}
if (criteriaMet) {
$('#addItemForm').submit();
}
"AddItem" controller:
public class OrderController {
[HttpPost]
public ActionResult AddItem(long? orderId, long menuItemId)
{
if (oneConditionIsTrue)
{
return RedirectToRoute("NamedRoute1", new { RouteValueDictionary values });
}
else
{
return RedirectToRoute("NamedRoute2", new { RouteValueDictionary values });
}
}
public class NamedRouteController
{
public ActionResult NamedRouteAction
{
// do some stuff
if (mobile)
{
return View("MobileView", model);
}
else
{
return View("RegularView", model);
}
}
After redirecting from the POST action (AddItem), I can step things through the GET action to the return (either one). I would expect the URL in the browser after all of this to be http://mydomain.com/NamedRoute/NamedRouteAction but it's http://mydomain.com/Order/AddItem. Why is this? Shouldn't the RedirectToRoute change the URL?
What am I missing?
I suspect that the controller action is somehow invoked with an AJAX request. For example this could happen if you are using jQuery Mobile or something. Or maybe there's some other script that you have written doing this - it hijacks the form submission and sends an AJAX request instead. And since it is an AJAX request, you cannot possibly expect that the url in the client browser would ever change - that's the whole point of AJAX - stay on the same page.
This could be very easily verified by using a javascript debugging tool such as FireBug. Simply look at the Network tab and see if the POST request was an AJAX request. In the Net tab find the request and see if there's the following request header:
X-Requested-With: XMLHttpRequest
jQuery appends this HTTP header to all AJAX requests it sends.
So basically if you expect a redirect to happen after a POST request you shouldn't use AJAX to submit your form. Or to be more precise: the redirect happens on the server side (once again you will be able to see it in FireBug - the 302 status code) and then the XMLHttpRequest simply follows this redirect but the client browser will not change its current location.