How am I supposed to use ReturnUrl = ViewBag.ReturnUrl in MVC 4
Asked Answered
C

3

44

I'm working on 'ASP.NET MVC 4' application. I'm using/learning SimpleMembershipProvider and try to stick to the default logic created by VS2012 with the Internet template (if I'm not mistaken, the one with 'SimpleMembershipProvider' out of the box).

I'm stuck at the AccountController where I just can't figure put how exactly I can use this method:

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

From what I understand the whole idea is to get redirected to the location from where you've decided to log in (exactly what I want to accomplish). I took a look at how it's used in the view :

@using (Html.BeginForm(new { ReturnUrl = ViewBag.ReturnUrl }))

Look for a place where actually ViewBag.ReturnUrl is set with some value and I only got this method here:

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

and I'm getting pretty confused about how exactly I'm supposed to get the location/url. I set some breakpoints and I have never seen returnUrl to be something different from null which in this scenario seems pretty logical to me since it doesn't get value anywhere (unless I miss something of course).

So I really can't figure out how this work. I post the above just to show that I tried to do my homework, I investigate as much as I could but I didn't found an answer so I ask here. Could you provide explanation/example on how this actually work?

Cochrane answered 21/11, 2013 at 14:29 Comment(0)
T
67

When using forms authentication and the user is not authenticated or authorized the ASP.NET security pipeline will redirect to the login page and pass as a parameter in the query string the returnUrl equal to the page that redirected to the login page. The login action grabs the value of this parameter and puts it in the ViewBag so it can be passed to the View.

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

The View then stores this value in the form as shown by this line of code in the View.

@using (Html.BeginForm(new { ReturnUrl = ViewBag.ReturnUrl }))

The reason it is stored in the View is so that when the user does a Submit after entering their user name and password, the controller action that handles the post back will have access to this value.

    [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);
    }

If the model state is valid and they are authenticated by calling the WebSecurity.Login method then it calls the method RedirectToLocal with the value of returnUrl which came from the View, which originally came form the login action that created the View.

The returnUrl value will be null if the user is not redirected to the login page as is the case when they just click on the login link at the top of the page in the default layout. In this case the user will be redirected to the home page after successful login. The whole purpose of the returnUrl is to automatically send the user back to the page they were trying to access before they were authenticated/authorized.

Thenar answered 22/11, 2013 at 16:35 Comment(3)
Well, I have this question - does returnUrl reliable enough to base conditional check on it. What I want is to implement specific logic if the redirect is from the AdminController which in this case is returnUrl = /admin string. Even though I think I start to understand how things work I'm still in doubt if this variable is secure enough or it can easily be changed?Cochrane
It is not secure and can be easily changed since it is in the query string. Anyone can change this value in their web browser.Thenar
this didn't work for me. However you can always do: HttpContext.Request.Params["ReturnUrl"];Gloriole
P
13

That's because the default ASP.NET MVC template is using Forms authentication, and controllers are decorated with [Authorize] attribute:

<authentication mode="Forms">
  <forms loginUrl="~/Account/Login" timeout="2880" />
</authentication>

[Authorize]
public class AccountController : Controller
{
    //...
}

That means that if the user is not authenticated it will be redirected to the logon page defined in the LoginUrl attribute of the forms element.

During the redirection, FormsAuthentication which is an HttpModule will append the url which was requested in the query string automatically.

So if you navigate to /Account/Login, you wont get anything in the query string since it is decorated with [AllowAnonymous] attribute. But if you navigate to /Account/Manage you'll notice that the returnUrl in the query string becomes /Account/Manage (/Account/Login?ReturnUrl=%2fAccount%2fManage)

So you are not setting the returnUrl, the framework does it for you, you just use it in the AccountController to know where to redirect the user after he is authenticated.

Pluto answered 21/11, 2013 at 15:38 Comment(2)
Can be simulated by making the ActionResult Manage() fonction return this : return RedirectToAction("Login", "Connexion", new {returnURL = "/Account/Manage"});. This will end up being the same behavior, IN CASE the framework doesn't do this for you.Amphibolous
BTW this answer explain better than the accepted one. Thanks to you I now understand.Amphibolous
M
4

When an unauthenticated user tries to get into a section of your application which requires authentication, then returnUrl comes into the picture. The Url requested by the unauthenticated user is basically stored in returnUrl.

You can go through the PluralSight tutorial: Building Applications with ASP.NET MVC 4

Mast answered 21/11, 2013 at 15:25 Comment(1)
Thanks for sharing a video. For now I need a quick solution, first I thought that the template generates just a scaffold and I need to change it with something like this - new { ReturnUrl = ViewContext.HttpContext.Request.UrlReferrer.PathAndQuery }) but after your post I made a few more tests and it seems that everything is actually build in. I think that the answer is in the [Authorize] attribute and it's implementation but it would be nice to get explanation from someone that has good understanding.Cochrane

© 2022 - 2024 — McMap. All rights reserved.