How am I supposed to use ReturnUrl = ViewBag.ReturnUrl in MVC 4
Categories:
Mastering ReturnUrl in ASP.NET MVC 4 with SimpleMembership

Understand and effectively implement the ReturnUrl parameter in ASP.NET MVC 4 applications, particularly when integrating with SimpleMembership for secure user redirection after authentication.
The ReturnUrl
parameter is a fundamental concept in web application security and user experience, especially within authentication flows. In ASP.NET MVC 4, when using SimpleMembership, it plays a crucial role in redirecting users back to their intended destination after they've successfully logged in. This article will demystify how ReturnUrl
works, how to properly utilize ViewBag.ReturnUrl
, and best practices to ensure secure and seamless user navigation.
Understanding the ReturnUrl Mechanism
When an unauthenticated user attempts to access a protected resource, the application typically redirects them to a login page. To ensure a smooth user experience, after successful authentication, the user should be sent back to the page they originally tried to access, rather than just the homepage. This is where ReturnUrl
comes into play. The URL of the protected resource is captured and passed as a query string parameter to the login page. After login, this ReturnUrl
is then used to redirect the user.
sequenceDiagram actor User participant Browser participant WebApp participant LoginController participant AccountController User->>Browser: Requests protected resource (/Admin/Dashboard) Browser->>WebApp: GET /Admin/Dashboard WebApp->>WebApp: Checks authentication (User not logged in) WebApp->>Browser: Redirects to /Account/Login?ReturnUrl=%2FAdmin%2FDashboard Browser->>LoginController: GET /Account/Login?ReturnUrl=%2FAdmin%2FDashboard LoginController->>Browser: Renders Login View (stores ReturnUrl in ViewBag) User->>Browser: Enters credentials & submits form Browser->>AccountController: POST /Account/Login (with credentials & ReturnUrl) AccountController->>AccountController: Validates credentials alt Authentication Successful AccountController->>WebApp: Logs in user AccountController->>Browser: Redirects to ReturnUrl (/Admin/Dashboard) Browser->>WebApp: GET /Admin/Dashboard (now authenticated) WebApp->>Browser: Serves /Admin/Dashboard else Authentication Failed AccountController->>Browser: Renders Login View (with error) end
Sequence Diagram of ReturnUrl Flow in MVC 4
Implementing ReturnUrl with ViewBag in MVC 4
In ASP.NET MVC 4, the Login
action in the AccountController
(often generated by default templates) is designed to handle the ReturnUrl
parameter. When the login page is requested, the ReturnUrl
from the query string is typically stored in ViewBag.ReturnUrl
. This allows the login form to include ReturnUrl
as a hidden field, ensuring it's passed back to the Login
POST action. After successful authentication, the Login
POST action retrieves this ReturnUrl
and performs the redirection.
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);
}
private ActionResult RedirectToLocal(string returnUrl)
{
if (Url.IsLocalUrl(returnUrl))
{
return Redirect(returnUrl);
}
else
{
return RedirectToAction("Index", "Home");
}
}
AccountController Login Actions Handling ReturnUrl
@using (Html.BeginForm("Login", "Account", new { ReturnUrl = ViewBag.ReturnUrl }, FormMethod.Post, new { @class = "form-horizontal", role = "form" }))
{
@Html.AntiForgeryToken()
<h4>Use a local account to log in.</h4>
<hr />
@Html.ValidationSummary(true, "", new { @class = "text-danger" })
<div class="form-group">
@Html.LabelFor(m => m.UserName, new { @class = "col-md-2 control-label" })
<div class="col-md-10">
@Html.TextBoxFor(m => m.UserName, new { @class = "form-control" })
@Html.ValidationMessageFor(m => m.UserName, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(m => m.Password, new { @class = "col-md-2 control-label" })
<div class="col-md-10">
@Html.PasswordFor(m => m.Password, new { @class = "form-control" })
@Html.ValidationMessageFor(m => m.Password, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<div class="checkbox">
@Html.CheckBoxFor(m => m.RememberMe)
@Html.LabelFor(m => m.RememberMe)
</div>
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Log in" class="btn btn-default" />
</div>
</div>
}
Login View (Login.cshtml) passing ReturnUrl via Html.BeginForm
RedirectToLocal
helper method is crucial for security. It uses Url.IsLocalUrl(returnUrl)
to prevent open redirection vulnerabilities, ensuring users are only redirected to pages within your application's domain.Common Pitfalls and Best Practices
While ReturnUrl
is straightforward, there are common issues and best practices to consider for robust implementation.
1. Always Validate ReturnUrl
As shown in the RedirectToLocal
method, always use Url.IsLocalUrl()
to validate the ReturnUrl
. This prevents malicious users from redirecting legitimate users to external phishing sites after login.
2. Handle Null or Empty ReturnUrl
If ReturnUrl
is null or empty, ensure your application has a default redirect target, typically the application's home page or a user dashboard. The RedirectToLocal
method handles this by redirecting to Index
of Home
controller.
3. Consider TempData
for Complex Scenarios
For more complex scenarios, such as multi-step authentication or if you need to preserve ReturnUrl
across multiple redirects before the final login, TempData
can be a more robust option than ViewBag
or hidden fields, as it persists data for one subsequent request.
4. Encode ReturnUrl
in Query Strings
When constructing URLs manually or passing ReturnUrl
between different parts of your application, ensure it is properly URL-encoded to prevent issues with special characters and potential injection attacks.
ReturnUrl
can expose your application to open redirection attacks, a serious security vulnerability. Always use Url.IsLocalUrl()
or a similar validation mechanism.