Authorize attribute doesn't work in MVC

1.5k views Asked by At

I have a User with a role of Member. I have this Login Action:

public virtual ActionResult Login(string returnUrl)
{
    if(User.Identity.IsAuthenticated)
    {
        if (IsValidReturnUrl(returnUrl))
            return Redirect(returnUrl);
        return Redirect(FormsAuthentication.DefaultUrl);
    }
    return View();
}

And I have this ActionMethod :

[Authorize(Roles="Member")]
public virtual ActionResult PostLostThing()
{
    var maingroups = _maingroups.SelectAll();
    var Provinces = _provinces.SelectAll();
    ViewBag.MainGroups = new SelectList(maingroups, "GroupId", "GroupName", maingroups.FirstOrDefault().GroupId);
    ViewBag.SubGroups = new SelectList(maingroups.FirstOrDefault().SubGroups, "id", "name");
    ViewBag.Provinces = new SelectList(Provinces, "Id", "Title", Provinces.FirstOrDefault().Id);
    ViewBag.Cities = new SelectList(Provinces.FirstOrDefault().Cities, "Id", "Name");

    return View();
}

When user is logged in and call view PostLostThing it redirects to Login Page, but when the Role of Authorize attribute is removed, it works very well. I have this SetAuthCookie method:

private void SetAuthCookie(string memberName, string roleofMember, bool presistantCookie)
{
    var timeout = presistantCookie ? FormsAuthentication.Timeout.TotalMinutes : 30;

    var now = DateTime.UtcNow.ToLocalTime();
    var expirationTimeSapne = TimeSpan.FromMinutes(timeout);

    var authTicket = new FormsAuthenticationTicket(
        1,
        memberName,
        now,
        now.Add(expirationTimeSapne),
        presistantCookie,
        roleofMember,
        FormsAuthentication.FormsCookiePath
        );

    var encryptedTicket = FormsAuthentication.Encrypt(authTicket);

    var authCookie = new HttpCookie(FormsAuthentication.FormsCookieName, encryptedTicket)
    {
        HttpOnly = true,
        Secure = FormsAuthentication.RequireSSL,
        Path = FormsAuthentication.FormsCookiePath
    };

    if (FormsAuthentication.CookieDomain != null)
    {
        authCookie.Domain = FormsAuthentication.CookieDomain;
    }

    if (presistantCookie)
        authCookie.Expires = DateTime.Now.AddMinutes(timeout);

    Response.Cookies.Add(authCookie);
}

What's the problem?

1

There are 1 answers

2
Tieson T. On BEST ANSWER

Since you're setting the auth cookie yourself, you need to implement the Application_AuthenticateRequest in the Global.asax.cs file. Otherwise, nothing beyond the user name is added to the Principal object.

Here's a sample implementation:

protected void Application_AuthenticateRequest(Object sender, EventArgs e)
{
    HttpCookie authCookie = Context.Request.Cookies[FormsAuthentication.FormsCookieName];
    if (authCookie != null)
    {
        FormsAuthenticationTicket authTicket = FormsAuthentication.Decrypt(authCookie.Value);

        string[] roles = null;

        GenericPrincipal userPrincipal = new GenericPrincipal(new GenericIdentity(authTicket.Name), roles);
        Context.User = userPrincipal;
    }
}

The overload you're using assumes that the value you've passed roleofMember is actually some serialized data. You then need to tell it how to handle deserializing that user data. Since you're just passing a single role name, you can amend the sample above to:

protected void Application_AuthenticateRequest(Object sender, EventArgs e)
{
    HttpCookie authCookie = Context.Request.Cookies[FormsAuthentication.FormsCookieName];
    if (authCookie != null)
    {
        FormsAuthenticationTicket authTicket = FormsAuthentication.Decrypt(authCookie.Value);

        string[] roles = new string [] { authTicket.UserData };

        GenericPrincipal userPrincipal = new GenericPrincipal(new GenericIdentity(authTicket.Name), roles);
        Context.User = userPrincipal;
    }
}