noobed
noobed

Reputation: 1339

MVC session expires - continue where left

We have an internal ASP.NET MVC application that requires a logon. Log on works great and does what's expected. We have a session expiration of 5 minutes. After sitting on a single page for that period of time, the user has lost the session. If they attempt to refresh the current page or browse to another, they will get a log on page.

My question is (after reloggin in) where to tell MVCto redirect to their (refresh/browse) attempt instead of getting HOME controller page always ?

Upvotes: 3

Views: 2205

Answers (3)

David
David

Reputation: 219127

Generally this happens out-of-the-box with the AccountController in an ASP.NET MVC application. Take a look at the default Login action:

[AllowAnonymous]
public ActionResult Login(string returnUrl)
{
    ViewBag.ReturnUrl = returnUrl;
    return View();
}

[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult Login(LoginModel model, string returnUrl)
{
    if (ModelState.IsValid && WebSecurity.Login(model.UserName, model.Password, persistCookie: model.RememberMe))
    {
        return RedirectToLocal(returnUrl);
    }

    // If we got this far, something failed, redisplay form
    ModelState.AddModelError("", "The user name or password provided is incorrect.");
    return View(model);
}

// later...
private ActionResult RedirectToLocal(string returnUrl)
{
    if (Url.IsLocalUrl(returnUrl))
        return Redirect(returnUrl);
    return RedirectToAction("Index", "Home");
}

When the session expires and the user is redirected to the login page, the system should be supplying a returnUrl parameter to that redirect. If it's not in your application, what in your application is different from the default in this case?

Notice how the first Login action, which just renders the login page after the user is redirected to it, includes that value in the ViewBag. The default view for this page adds the value to the form element so it will be included in the login POST request. The second Login action, which receives that request, then directs the user to that supplied URL (or to a default of /Index/Home, as seen in the helper method).

Is your application maintaining this chain of events? If not, where does this chain of events break? In keeping with convention, you should be:

  1. Receiving the returnUrl from the session expiration redirect.
  2. Including the returnUrl in the login form.
  3. Redirecting the user to the returnUrl after a successful login.

Upvotes: 2

Julien Poulin
Julien Poulin

Reputation: 13025

This feature is integrated out of the box when you create a new ASP.Net MVC project.

In short, the requested URL is sent as a parameter to the Index action of the AccountController by the MVC framework and then becomes a parameter to the log in page URL:

[AllowAnonymous]
public ActionResult Login(string returnUrl)
{
  ViewBag.ReturnUrl = returnUrl;

  return View();
}

When the user posts his credentials and the log in is successful, the returnUrl is used to redirect the user to the originally requested page:

[HttpPost]
[AllowAnonymous]
public ActionResult Login(LoginModel model, string returnUrl)
{
  if (ModelState.IsValid && Membership.ValidateUser(model.UserName, model.Password))
  {

    return RedirectToLocal(returnUrl);
  }

  // If we got this far, something failed, redisplay form
  ModelState.AddModelError("LogInIncorrectError", Resources.LogInIncorrectError);

  return View(model);
}

private ActionResult RedirectToLocal(string returnUrl)
{
  if (Url.IsLocalUrl(returnUrl))
  {
    return Redirect(returnUrl);
  }

  return RedirectToAction("Index", "Home");
}

I suggest you create a new project and see how it behaves...

Note that this only works for HttpGet, if the user was submitting a form, it won't work.

Upvotes: 2

Darin Dimitrov
Darin Dimitrov

Reputation: 1039588

Normally this should happen automatically. When an anonymous user hits a protected resource (he is anonymous because his session expired), the FormsAuthentication module intercepts this requests and redirects to the loginUrl you registered in your web.config by appending a ReturnUrl query string parameter pointing to the protected resource.

So if for example you configured ~/Account/LogOn to be your logon url, and the anonymous user attempts to hit the ~/Foo/Bar protected resource he will be redirected to ~/Account/LogOn?ReturnUrl=%2FFoo%2FBar.

Then he will be presented with a login page where he will input his credentials and submit the form to the [HttpPost] LogOn action on the Account controller. The credentials will be validated and if valid, a new forms authentication cookie will be generated and the user redirected to the initially requested protected resource.

Here's the respective code from the LogOn action:

[HttpPost]
public ActionResult LogOn(LogOnModel model, string returnUrl)
{
    if (ModelState.IsValid)
    {
        if (Membership.ValidateUser(model.UserName, model.Password))
        {
            FormsAuthentication.SetAuthCookie(model.UserName, model.RememberMe);
            if (Url.IsLocalUrl(returnUrl) && returnUrl.Length > 1 && returnUrl.StartsWith("/")
                && !returnUrl.StartsWith("//") && !returnUrl.StartsWith("/\\"))
            {
                return Redirect(returnUrl);
            }
            else
            {
                return RedirectToAction("Index", "Home");
            }
        }
        else
        {
            ModelState.AddModelError("", "The user name or password provided is incorrect.");
        }
    }

    // If we got this far, something failed, redisplay form
    return View(model);
}

Notice how the user is redirected either to the default Home/Index or to the returnUrl parameter upon successful authentication.

Now of course all this story that I told here is true for the default ASP.NET MVC template created by Visual Studio. If for some reason you have modified this template this might explain the behavior you are observing.

In any case the thing to remember from my answer is that if you are using Forms Authentication, the module will pass as ReturnUrl query string parameter the initially requested protected resource to the configured logon page. So it's up to you to redirect the user to this page upon successful authentication.

Upvotes: 3

Related Questions