PartialView Render Forms timeout - asp.net-mvc

I'm fetching a partial view via $.ajax() and in the situation where you set idle for 30 minutes and then try to fetch that partial view, the forms authentication has timed out and instead of getting my partial view returned to me, I'm getting the login page render into my .
Any suggestions on how to deal with a situation like this?
Thank you.
$(function () {
$("#addContact").click(function () {
$.get('/Contacts/Add', function (data) {
$("#content").html(data); <--gets login page as data
});
});
});

Does your Add Action have any non-Ajax consumers? If not, I'd suggest removing the [Authorize] attribute from the action, which would remove the timeout-redirect problem. (If you have your entire controller decorated with [Authorize], you'd need to remove the controller-level attribute and adorn all of your other actions. Annoying, I know).
For extra security, you could then do something like this to prevent non-Ajax calls from calling your Add action.
public ActionResult Add()
{
if (Request.IsAjaxRequest())
return View("Error");
return View();
}
If, on the other hand, your Add action needs to support Ajax and normal calls, one way you can address this issue is to create a new Attribute class that inherits from and overrides AuthorizeAttribute. Check out the source for guidance: http://aspnet.codeplex.com/SourceControl/changeset/view/23011#266447
You should be able to do the trick by overriding the AuthorizeCore method, like so
public class AjaxAuthorizeAttribute: AuthorizeAttribute
{
protected override bool AuthorizeCore(System.Web.HttpContextBase httpContext)
{
if (httpContext.Request.IsAjaxRequest())
return true;
return base.AuthorizeCore(httpContext);
}
}
Now you can use [AjaxAuthorize] on your controller and/or action.
To be clear, what you're doing here is giving the user an extension on their timeout if they initiate a call via Ajax. Once they refresh the page, or navigate away, they would be prompted to log back in, as normal.
Hope that helps. Let me know if you run into any issues.

Prior to making the Ajax call, can you make another one to an unauthorized controller to ensure that the user is authenticated? If they are, continue as normal, otherwise you can just show a login lightbox so you don't leave the page and maintain the user experience.

Another solution would be to add some script to your login page to check if it's being rendered within a pop-up. If it is you can use location.href() to redirect the whole page to the login page.

It depends a little on if you're ok with changing the length of time users will be logged in for. If you are, you can change your config file to something like the following...
<authentication mode="Forms">
<forms loginUrl="Login.aspx" timeout="512640" />
</authentication>
This will keep users logged in for one year. If changing the amount of time users are logged in is not an option, you would need to handle the ajax response and redirect users to a login form again.

Related

how to avoid using TempData

I'm new to asp.net mvc, so please bear with me.
I'm using TempData when I redirect to another Action and I don't want to "dirty" the URL with information. For example:
[AllowAnonymous]
public ActionResult ConfirmationEmailSent()
{
if (TempData["Username"] != null)
{
ViewBag.Username = TempData["Username"];
return View("ConfirmationEmailSent");
}
return View("Error");
}
So far so good, the user gets a simple and innocent looking web page with some message containing his username. But if the user hits the 'refresh' button then he gets the "Error" view, because TempDate is unavailable.
I would like to have the ability to redirect to action with information not presented in the querystring and also that if the user hits the refresh button then he gets just the same page.
Any ideas how to do this? (without session)
Thank you.
What you need is a way to have information persist across more than one request in a way that is associated with the browser. QueryString and Session are your two best options.
If the view is the result of a form post, you could make it a hidden input, but the user will get a prompt when they refresh (do you want to resubmit form?), but this is not good, as you should be doing a Post/Redirect/Get (PRG).
Erick
It sounds like cookies would do what you want. Then just delete them when you're done (or don't set an expiration date on them, in which case the browser will delete them for you when the session completes).

Enforcing a choice prior to viewing MVC and Web Forms pages

I'm working on a system that needs to know a user's choice before they enter a site. Up till now the choice has been stored in a cookie and checked by JavaScript on the page load - if the cookie doesn't exist then a dialog is shown and the user makes the choice.
Although we'd normally expect the user to arrive at the homepage of the application, they can legally follow a URL to any page within the application, so the JavaScript to check the choice exists on every page.
This has caused problems (almost always fixed by clearing cookies) so we're switching to store the choice in the database. What we need is a neat way of making sure that all pages (MVC and Web Forms) check that the choice has been made and, if it hasn't, either display a dialog or redirect to a page where the choice can be made.
The main thing bothering me is that to cause a redirect using MVC, I need to return a RedirectResult, and this can only be done from an Action. I don't want every action to have code regarding this check - it seems like the kind of thing that should be possible from a base controller (in the same way a base page could cause a Response.Redirect.
Can anyone suggest a good way for all pages to perform a check on the database and then either cause a redirect or show a dialog?
The main thing bothering me is that to cause a redirect using MVC, I
need to return a RedirectResult, and this can only be done from an
Action.
Oh not at all. You could also redirect from a custom action filters.
For example you could write a custom IAuthorizationFilter that will check whether the user made the necessary choice and if not redirect to some given page. The check could be done against a cookie, database or wherever you decide to persist this information:
public class EnsureChoiceHasBeenMadeAttribute : FilterAttribute, IAuthorizationFilter
{
public void OnAuthorization(AuthorizationContext filterContext)
{
// get the current user
var user = filterContext.HttpContext.User;
if (user.Identity.IsAuthenticated && !UserMadeAChoice(user.Identity.Name))
{
// if the current user is authenticated and he didn't made a choice
// redirect him to some page without even attempting to execute
// the controller action that he requested
var values = new RouteValueDictionary(new
{
controller = "home",
action = "index"
});
filterContext.Result = new RedirectToRouteResult(values);
}
}
private bool UserMadeAChoice(string username)
{
throw new NotImplementedException();
}
}
Now you have different possibilities:
You decorate the controllers/actions that you want to perform this check with the [EnsureChoiceHasBeenMade] attribute
You register the action filter as a global action filter so that it applies to absolutely all actions
You write a custom filter provider in order to dynamically apply the action filter to some actions based on some dynamic values (you have access to the HttpContext).

Suppress browser authentication login dialog for a controller method that is using an [Authorize] action filter

I'm in the process of creating an Intranet site using Windows Authentication.
Maybe I'm not going about this the best way, but I'm trying to load partial views via calling a controller method that has an Authorize action filter wrapped around it, so that only authorized individuals are able to see that portion of the page. Say, for instance, I wanted to load administrator tools onto the page but only if the logged-in individual is an administrator.
So on the index.cshtml page I might have something like:
#Html.Action("LoadAdminTools","ControllerName")
The Controller would contain the code:
[Authorize(Roles="Admins")]
public ActionResult LoadAdminTools()
{
return PartialView("_AdminToolsPartialView");
}
And then the partial view containing the admin controls (or whatever) would render to the page - only if the logged-in user was part of the Admins role.
The 'problem' I'm having is that if the person logged-in is not authorized to load the partial view, the browser pops up the login dialog asking for the user's credentials. Closing the dialog without inputting any credentials causes the expected results - the partial view doesn't load while the rest of the page does. Cool, but annoying. Input the incorrect credentials and you get a 401 error - also as expected.
If it helps: In IIS, Anonymous Authentication is disabled, Windows Authentication is enabled.
"Automatic logon with current user name and password" is selected in Internet Options under "Security Settings - Local Intranet Zone."
My question is this: Is there a way to use the [Authorize] action filter to load a partial view (or to do anything, really) without the browser asking the user to log in? Just have it take the current logged-in credentials, check if they comply with the action filter, if they do, load the partial view, if not, then don't. If there isn't, is there simply a better way of going about what I want to accomplish here?
UPDATE
Beautiful. I read the solution to the question you posted, Mystere Man, created a new class inside the Controller's folder called IntranetAuthorizeAttribute.cs, threw in the code:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
public class IntranetAuthorizeAttribute : System.Web.Mvc.AuthorizeAttribute
{
protected override void HandleUnauthorizedRequest(System.Web.Mvc.AuthorizationContext filterContext)
{
if (filterContext.HttpContext.Request.IsAuthenticated)
{
filterContext.Result = new System.Web.Mvc.HttpStatusCodeResult(403);
}
else
{
base.HandleUnauthorizedRequest(filterContext);
}
}
}
Replaced the Authorize filter with my new IntranetAuthorize filter:
[IntranetAuthorize(Roles="Admins")]
public ActionResult LoadAdminTools()
{
return PartialView("_AdminToolsPartialView");
}
And now it loads the page just fine with no browser login dialog - with the partial view when it's an authorized user, and without the partial view when it is not an authorized user =)
Thank you!
Unfortunately, ASP.NET (and thus MVC) conflates Authorization and Authentication in may scenarios.
Check this question for a solution.
Why does AuthorizeAttribute redirect to the login page for authentication and authorization failures?

How to create a view that is not accessible directly but can only be redirected to?

I'm currently working on the user registration in my project. After the registration is done I wish to show some confirmation to the user. I decided to create another view. That's fine.
Now, if after the registration I just return the view like:
public class MyController : Controller
{
[AcceptVerbs (HttpVerbs.Post), ValidateAntiForgeryToken]
public ActionResult Registration (FormCollection form)
{
/* Some logic goes here */
return View ("ConfirmationView");
}
}
Everything is working as desired. No changed url in the title bar. But... If I click the refresh button, the browser will submit the data from the form again which I do not want.
Then I decided to create a separate action, but that means it will produce a new url in the address bar. I do not want the user to click refresh now because this view will not be able to sensibly display the confirmation information again. Is there any way to make an action not accessible directly? Or at least any way to determine whether it was called directly or by redirection? In the latter case I would just take the user away from that page to maybe the home page.
Any way to accomplish this?
So I found the solution myself.
One can use TempData to detect the repeated or external action calls.
public class MyController : Controller
{
[AcceptVerbs (HttpVerbs.Post), ValidateAntiForgeryToken]
public ActionResult Registration (FormCollection form)
{
/* Some logic goes here */
TempData["RedirectCall"] = true;
return RedirectToAction ("Confirmation");
}
[AcceptVerbs (HttpVerbs.Get)]
public ActionResult Confirmation ()
{
if (TempData["RedirectCall"] == null)
return RedirectToAction ("StartPage", "Home");
return View ();
}
}
Nice and simple. :)
One way to solve your problem is to attach a guid or similar type of "random" data to a user session, and check for a valid session when the page is requested. If there is none, you redirect to a page saying that this url is not available at the moment, and that the user will soon be redirected (and then redirect to home after 5 seconds or so using js).
Roughly it would work like this:
When the user is registered, a session cookie is created with for example a GUID. The GUID is also stored in a database table, in which you have one column for the UserID primary key and one for the GUID. You also create an authentication cookie, thus logging the user on to your site.
When all datacalls etc are done, the user has been successfully registered and so on, you redirect to the confirmation page.
When the confirmation page is loaded, the user is automatically logged on (because you created the authentication cookie in step 1). You can then check for a row in the UserID-GUID table corresponding to the logged on user.
a) If there is such a row, you delete the row, and display the confirmation page with all the information.
b) If there is no such row, you display the error message and redirect. As you deleted the row when you showed the message the first time, the user will not be able to access the confirmation page again.
Note: If you use this approach (or some other that makes the confirmation page available only once) you should make sure that it is clearly stated on the confirmation page that the user won't be able to access that page again.
if(Request.UrlReferrer == null)
{
return RedirectToAction("Index");
}

Redirect to Login Page on some condition of session check

In C# with MVC, i want to write a common utility or class in which if a particular conditoin fails need to redirect to login page.
For ex.: When the user logged in to the website, userid will be added to session. To Access the "ManageUsers" page, the user should be logged in as admin, else i need to redirect to Login page. i need to check this condition in some of the other similar pages also. i dont want to check either the user is admin or normal user while login. i need to check this in common class.
Any suggesstions?
Actually I think this is not particularly good behavior for an application. I think you ought to disable (or hide) any actions that a user is not able to perform. In the case where the user hand-enters a URL, or uses a bookmark from when they had the privilege, show an error message rather than redirecting to the login page.
Imagine you're a user who is logged into your application. You click on a user interface element and it looks like you've been logged out. You have no way of knowing that you weren't supposed to use it. Disabling/hiding the element prevents this scenario from occurring for most users. Redirecting to an error gives valuable feedback to the user as to why the action they took did not result in what they expected.
I use a custom attribute derived from AuthorizeAttribute to achieve this effect. If a user isn't logged in, it redirects to the login page. If they are logged in, but not sufficiently privileged, it displays a suitable error view.
This already exist in ASP.NET MVC with the Authorize Attribute:
[Authorize(Roles="Administrators")]
public AcitonResult ManageUsers() {
return View();
}
Or
[Authorize(Users="Admin,SomeUser")]
public AcitonResult ManageUsers() {
return View();
}
More infos:
http://www.asp.net/learn/mvc/tutorial-17-vb.aspx
[Authorize(Roles = "Admin")]
public ActionResult ManageUsersController()
{
...
}
In your web.config check:
...
<forms loginUrl="~/your_login_page" defaultUrl="~/">
...
Also you should setup both MembershipProvider and RoleProvider in your web.config

Resources