0
votes

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>

            }
2
Have you included the value for returnUrl in the view (as a route parameter or hidden input in the form)? - user3559349
Stephen Muecke, thanks for your reply.Please check my view on above,already add return on my view.But i don't get this.On browser url not contain the return portion then how i get this on my view. - shamim

2 Answers

1
votes

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());
}
0
votes

Make sure you have included returnUrl in your view page.

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