shamim
shamim

Reputation: 6778

mvc5 global authentication check Why not append return url

I want to use authentication for every action method in the application, so i add it as a global filter in FilterConfig.cs i dont use any [Authorize] attribute on controller level or any action level.If i put Authentication on controller and action level i get return url but when i put authorization on global level i dont get any return url

In my MVC project

web.config settings is bellow

<authentication mode="Forms">
  <forms name="DemoApplication" loginUrl="~/Account/Login" timeout="2880" protection="All" />
</authentication>

FilterConfig syntax is bellow

public static void RegisterGlobalFilters(GlobalFilterCollection filters)
        {
            filters.Add(new HandleErrorAttribute());
            filters.Add(new AuthorizationFilterAttribute());
        }

AuthorizationFilterAttribute inherit AuthorizeAttribute class and override bellow method

protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
        {

            if (!HttpContext.Current.User.Identity.IsAuthenticated)
                filterContext.Result = new RedirectResult("~/Account/Login");

        }

above code ensure me 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 why not append return url

AccountController Login syntax is bellow

[AllowAnonymous]


public ViewResult Login(string returnUrl)
    {
        //return View();
        //So that the user can be referred back to where they were when they click logon
        if (string.IsNullOrEmpty(returnUrl) && Request.UrlReferrer != null)
            returnUrl = Server.UrlEncode(Request.UrlReferrer.PathAndQuery);

        if (Url.IsLocalUrl(returnUrl) && !string.IsNullOrEmpty(returnUrl))
        {
            ViewBag.ReturnURL = returnUrl;
        }
        return View();
    }

Note: here returnUrl always null

[AcceptVerbs(HttpVerbs.Post)]
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings",
    Justification = "Needs to take same parameter type as Controller.Redirect()")]
public ActionResult Login(LoginViewModel model, string returnUrl)
{
    if (ModelState.IsValid)
    {
        authProvider.SignIn(model.UserName, false);

        if (!String.IsNullOrEmpty(returnUrl))
        {
            if (!String.IsNullOrEmpty(returnUrl.Replace("Account", "")))
            {
                return Redirect(returnUrl);
            }
        }
        //BuildUserSession(userName);
        return RedirectToAction("Index", "Home");
    }
    else
    {
        return View();
    }
} 

Bellow is my view portion

@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.Email, new { @class = "col-md-2 control-label" })
                    <div class="col-md-10">
                        @Html.TextBoxFor(m => m.Email, new { @class = "form-control" })
                        @Html.ValidationMessageFor(m => m.Email, "", new { @class = "text-danger" })
                    </div>
                </div>
                <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>

            }

Upvotes: 0

Views: 356

Answers (2)

NightOwl888
NightOwl888

Reputation: 56909

There are several issues with your approach:

  1. In HandleUnauthorizedRequest, you don't need to check whether the user is authenticated. That check is already done in AuthorizeCore.
  2. You are redirecting directly to the login page and not allowing Forms Authentication to append the redirect URL. The proper way to do that is to return a HTTP 401 not authorized response, not redirect to a URL.
  3. The default AuthorizeAttribute already does both of these things for you. There is no reason for a custom authorization filter in this case.

So, if you change your code as follows, it will work like you expect.

public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
    filters.Add(new HandleErrorAttribute());
    filters.Add(new AuthorizeAttribute());
}

Upvotes: 1

sanjeev
sanjeev

Reputation: 1407

Make sure you have included returnUrl in your view page.

@using (Html.BeginForm("Login", "Account", new { returnUrl = ViewBag.ReturnUrl }, FormMethod.Post))
{
}

Upvotes: 0

Related Questions