7

I've implemented a custom role provider, and configured it in my web.config file like this:

<roleManager enabled="true" defaultProvider="TDRoleProvider" cacheRolesInCookie="true">
  <providers>
    <clear/>
    <add name="TDRoleProvider" type="TDRoleProvider"/>
  </providers>
</roleManager>

I've overridden the GetRolesForUser function in my custom role provider, and I've stepped into it, and it works just fine - loads up 60 roles for the user I'm testing with. However, I've noticed that the GetRolesForUser gets called on every request that calls User.IsInRole. In other apps I've written, it only calls it once, then caches the result in the cookie. For some reason, the caching is not working for this app. Any ideas as to why?

Jack
  • 5,680
  • 10
  • 49
  • 74
Brandon Montgomery
  • 6,924
  • 3
  • 48
  • 71

3 Answers3

3

I was having the same problem. In my case the issue was that I was setting Context.User to GenericPrincipal and not RolePrincipal. So instead of:

this.Context.User = new GenericPrincipal(customIdentity, roles);

this fixed for me:

            HttpCookie roleCookie = this.Context.Request.Cookies[Roles.CookieName];
            if (IsValidAuthCookie(roleCookie))
            {
                this.Context.User = new RolePrincipal(customIdentity, roleCookie.Value);
            }
            else
            {
                this.Context.User = new RolePrincipal(customIdentity);
                var x = this.Context.User.IsInRole("Visitor"); // do this to cache the results in the cookie
            }

The IsValidAuthCookie method checks for null and empty:

    private static bool IsValidAuthCookie(HttpCookie authCookie)
    {
        return authCookie != null && !String.IsNullOrEmpty(authCookie.Value);
    }

UPDATE: After upgrading to MVC5 .NET 4.5 roleManager stopped working (not saving roles in cookie) so had to save it myself:

        HttpCookie roleCookie = filterContext.HttpContext.Request.Cookies[Roles.CookieName];
        if (IsValidAuthCookie(roleCookie))
        {
            filterContext.Principal = new RolePrincipal(customIdentity, roleCookie.Value);
            RolePrincipal rp = (RolePrincipal)filterContext.Principal;
            if (!rp.IsRoleListCached) // check if roles loaded properly (if loads old cookie from another user for example, roles won't be loaded/cached).
            {
                // roles not loaded. Delete and save new
                Roles.DeleteCookie();
                rp.IsInRole("Visitor"); // load Roles
                SaveRoleCookie(rp, filterContext);
            }

        }
        else
        {
            filterContext.Principal = new RolePrincipal(customIdentity);
            filterContext.Principal.IsInRole("Visitor"); // do this to cache the results in the cookie.
            SaveRoleCookie(filterContext.Principal as RolePrincipal, filterContext);
        }

Save the roleCookie

    private void SaveRoleCookie(RolePrincipal rp, AuthenticationContext filterContext)
    {
        string s = rp.ToEncryptedTicket();
        const int MAX_COOKIE_LENGTH = 4096;
        if (string.IsNullOrEmpty(s) || s.Length > MAX_COOKIE_LENGTH)
        {
            Roles.DeleteCookie();
        }
        else
        {
            HttpCookie cookie = new HttpCookie(Roles.CookieName, s);
            cookie.HttpOnly = true;
            cookie.Path = Roles.CookiePath;
            cookie.Domain = Roles.Domain;
            if (Roles.CreatePersistentCookie)
                cookie.Expires = rp.ExpireDate;
            cookie.Secure = Roles.CookieRequireSSL;
            filterContext.HttpContext.Response.Cookies.Add(cookie);
        }
    }

Place this code on AuthenticationFilter and register it globally. See here.

Community
  • 1
  • 1
Joao Leme
  • 9,598
  • 3
  • 33
  • 47
  • I've been searching for hours on this issue and this fixed it! Thank you so much for posting this! Can you answer one question, what is the reference for method IsValidAuthCookie or what class is the code written in that implements that method? – Ben Gripka Apr 01 '15 at 20:02
  • Thanks this answer helped a lot. This is he only solution that addresses caching no longer working in MVC 5 when you implement you rown RoleProvider. I put this in my own Authorization attribute instead. – Lee May 25 '17 at 12:16
3

https://web.archive.org/web/20101123220352/http://connect.microsoft.com/VisualStudio/feedback/details/104688/rolemanager-cacherolesincookie-option-does-not-work

"The issue of when to cache (or not cache) in RolePrincipal went through a number of design iterations, and we finally settled on only caching for the method exposed by the IPrincipal interface (i.e. IsInRole). "

howcheng
  • 2,211
  • 2
  • 17
  • 24
Greg
  • 16,540
  • 9
  • 51
  • 97
1

Same is true for me. It keeps calling GetRolesForUser()

kheya
  • 7,546
  • 20
  • 77
  • 109