Search code examples
asp.net-mvcreturnurl

returnUrl is always null in mvc when using loginOrRegister view


I need some help with this problem. For some reason the returnUrl is always null and it will not return the user to the step where he/she were.

I'm creating a booking flow which consist of 3 steps right now.

  1. Travel step
  2. Hotel step
  3. Tpa step

On all these steps, the user can click on the login link or register link which takes them to the view LoginOrRegister.cshtml. (Have both the login and register options in one view)

My AccountController has a method which takes a returnUrl:

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

The problem is that the returnUrl is always null, and the Travel page will be loaded at all times. If the user went to the Tpa step and clicked logged in, the user will be redirected to the Travel page when logged in succesfully.

The LoginOrRegister view have 2 forms which can be posted. Both are set up like this one:

@using (Html.BeginForm("Login", "Account", new { ReturnUrl = Request.QueryString["ReturnUrl"] }, FormMethod.Post, new { @class = "form-horizontal", role = "form" }))
                {
@using (Html.BeginForm("Register", "Account", new { ReturnUrl = Request.QueryString["ReturnUrl"] }, FormMethod.Post, new { @class = "form-horizontal", role = "form" }))
                {

Have also tried with

ReturnUrl = ViewBag.ReturnUrl instead of the queryString.

I'm not 100% sure how this works, so do I miss something?

AccountController Login actionResult post method

[HttpPost]
    [AllowAnonymous]
    [ValidateAntiForgeryToken]
    public async Task<ActionResult> Login(UserViewModel model, string returnUrl)
    {
        if (!ModelState.IsValid)
        {
            return View(model);
        }

        // This doesn't count login failures towards account lockout
        // To enable password failures to trigger account lockout, change to shouldLockout: true
        var result = await SignInManager.PasswordSignInAsync(model.LoginModel.Email, model.LoginModel.Password, model.LoginModel.RememberMe, shouldLockout: false);
        switch (result)
        {
            case SignInStatus.Success:
                return RedirectToLocal(returnUrl);
            case SignInStatus.LockedOut:
                return View("Lockout");
            case SignInStatus.RequiresVerification:
                return RedirectToAction("SendCode", new { ReturnUrl = returnUrl, RememberMe = model.LoginModel.RememberMe });
            case SignInStatus.Failure:
            default:
                ModelState.AddModelError("", "Invalid login attempt.");
                return View(model);
        }
    }

Solution

  • You need to add a returnUrl parameter to the login and register links that you have:

    @Html.ActionLink("Login or Register", "LoginOrRegister", "Account", new { ReturnUrl = Request.RawUrl }, null)
    

    This is going to pass a current url (that corresponds to each one of your steps) as a query string parameter to LoginOrRegister action

    [HttpGet]
    public ActionResult LoginOrRegister(string returnUrl)
    {
       ViewBag.ReturnUrl = returnUrl;
       return this.View()
    }
    

    Then inside LoginOrRegister.cshtml you can use it like this:

    Html.BeginForm("Login", "Account", new { ReturnUrl = ViewBag.ReturnUrl }, FormMethod.Post, new { @class = "form-horizontal", role = "form" })