Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Where and How to "cache" ASP.NET Role Data

Design Choices

Presume two ASP.NET MVC authorization/role design stories. Both are intended to minimize hits against a middle-tier or data store:

  • After fetching “MyRoleProvider” info from a database, it is subsequently stored in the AuthTicket user-data which in turn means that the auth-cookie holds role-info. Furthermore, in this story, storing and retrieving this role-info is done by a distinct implementation, entirely outside "MyRoleProvider."

Or...

  • After fetching “MyRoleProvider” info from a database, it is subsequently stored in session and accessed from there. More precisely, the class “MyRoleProvider” itself will internally access (and update) session when possible.

Again, both the above stories aim to minimize the latency of hitting an external store by "caching" role info. Both work fine. The question is, are both design-story choices equally valid? If not, why?

The Issue

There appears to be no "best practice" stated anywhere, which says "your (custom) RoleProvider should always know where role-info is found, whether in session, cache, the database, etc."

Also, there appears to be no (solid) guidance anywhere that explains what kinds of things you should not store in "userData" in the authCookie. What little documentation and guidance there is suggests only two things:

  1. 'userData' can store additional user information (a vague, broad statement). I've seen only one code example online, which involved storing additional 3rd party identity/authentication info.
  2. Do not put too much data in 'userData', because it might make the cookie become too big to be transferred (some browsers limit cookie size).

That's it. From the above guidance, nobody is being expressly told "it is a best practice to limit the userData only to authentication info." And I certainly have not seen a document which says "it is a bad idea to put authorization or role-info into 'userData' because..."

Deduce a Best Practice?

My own answer about the two design choices being equal is “no.” I want to present my argument, and learn from you if:

  1. You agree.
  2. You disagree, because I'm making needless fuss.
  3. You disagree; although my argument is valid, there are even better arguments of why my thinking ultimately is not right.

Hopefully a best-practice concept will emerge as an answer. Now for my argument(s)...

My Argument(s)

I believe the second design-story should be taken, because it represents better modularity: role information is handled completely inside “MyRoleProvider.” The first option needlessly intermingles authentication (extracting identify from a cookie) and authorization concerns. Indeed, all the built-in ASP.NET security mechanisms keep these two topics separate; base ASP.NET functionality never stores role-info in the authcookie. If anyone wants to confirm this, try reflecting these classes: FormsAuthenticationModule, FormsAuthentication, IPrincipal with RolePrincipal implementaion, and IIdentity with FormsIdentity implementation.

The ASP.NET RolePrincipal deserves a particular highlight. It permits that role-info is indeed stored in a cookie, but it is a second cookie separate from the auth-cookie. Furthermore, this peculiar idiom still involves consultation with the RoleProvider. See the example section of this MSDN documentation.

Investigating RolePrincipal further, let's look at RolePrincipal.IsInRole(). Although any such IPrincipal derived class combines identity and role information programmatically, the internal implementation still maintains the RoleProvider as the only source of role information (note the reference to Roles.RoleProviders...):

    public bool IsInRole(string role)
    {
        if (this._Identity != null)
        {
            if (!this._Identity.IsAuthenticated || role == null)
            {
                return false;
            }
            else
            {
                role = role.Trim();
                if (!this.IsRoleListCached)
                {
                    this._Roles.Clear();
                    string[] rolesForUser = Roles.Providers[this._ProviderName].GetRolesForUser(this.Identity.Name);
                    string[] strArrays = rolesForUser;
                    for (int i = 0; i < (int)strArrays.Length; i++)
                    {
                        string str = strArrays[i];
                        if (this._Roles[str] == null)
                        {
                            this._Roles.Add(str, string.Empty);
                        }
                    }
                    this._IsRoleListCached = true;
                    this._CachedListChanged = true;
                }
                return this._Roles[role] != null;
            }
        }
        else
        {
            throw new ProviderException(SR.GetString("Role_Principal_not_fully_constructed"));
        }
    }

This kind of ASP.NET “one-stop” deep-assumption of where role information is found, is why role info should be consolidated into the RoleProvider. In short I claim:

  1. If you do store role info in a cookie, the reason why ASP.NET does not have a built-in capability to combine that into the auth-cookie, is precisely because ASP.NET maintains strongly a separation of concerns between the different providers.
  2. Any RoleProvider should be aware of where role-info might be found, whether in a cookie, session, or otherwise.

Conclusion:

Do not put role-info into the auth-cookie, and ensure your (custom) RoleProvider knows all the places where role info is found, whether the database, a web service, session, cache, or a cookie.

Agree? Disagree? Thoughts?

like image 979
Brent Arias Avatar asked Oct 18 '12 19:10

Brent Arias


2 Answers

There are a LOT of different concerns and design choices that go into deciding where to actually cache information whether it be cookie, session or some other provider.

Of course, the first question is whether you should even cache it at all. Sure there are concerns with regards to constantly accessing the database for fairly static information. However, there are equal concerns of being able to lock someone out immediately.

Only if an immediate response isn't needed does caching become viable. Because of this there can be no best practice with regards to caching authorization.. and certainly no best practices on where to cache it if your application doesn't need that level of security.

Presuming the above isn't an issue then there are trade offs with location.

Cookie
If you put it in a cookie, then it can be captured and subsequently replayed. Also it slightly increases your internet traffic because the cookie is transferred on every request.

Session
If you put it in session, then you either limit yourself to a single web server OR you must have session state stored in a location accessible by the entire web farm. If you take the latter route, then it's entirely likely you are storing session in a database somewhere thereby completely trashing your reason for having it in session to begin with.

Memcache or other
This is similar to storing the roles in session; however it's typically stored in memory on a separate server and is generally fast. The drawback is that for a load balanced web farm this can add yet another point of failure... unless it is properly clustered. At which point you've increased project development, deployment and maintenance costs....


Ultimately, there isn't a best practice. Only a bunch of trade-offs that are very situation dependent. Quite frankly for this type of data, I wouldn't cache it at all. It's usually relatively small and queries for it are generally quite fast. I might consider it if we were talking tens of thousands of users, but at that level there are many other operational considerations that would ultimately make the choice here apparent.

like image 184
NotMe Avatar answered Oct 08 '22 19:10

NotMe


It seems to me that you're asking an absurd question. Nobody in their right mind would store role data in the authentication cookie. ASP.NET doesn't do it that way precisely because they provide the RolePrincipal that will store it in an encrypted separate cookie.

The whole issue of cookie is kind of pointless anyhow, since this is an implementation detail of the RolePrincipal. The app should not be cognizant of where the role data is stored.

So my question to you is... Why are we even having this discussion? You're asking if it's best practice to do something contrary to how it's done by the system, and then arguing why you shouldn't do it in this way nobody would ever use.

Also, your comment about the RoleProvider having to deal with the cookie is not true. RoleProvider does not create the RolePrincipal, this is done inside the framework. The choice of whether to use cookies is not in the RoleProvider, but rather provided by the RoleManagerModule framework class, and the way IPrincipals are instantiated by the framework.

The example in the RolePrincipal has nothing to do with the RoleProvider. In fact, the RoleManagerModule controls this functionality. RoleManagerModule is an HttpModule that is installed into IIS and runs as part of the pipeline. It doesn't even know about the RoleProvider, and basically what this means is that the Roles are populated from the cookie at a very low level, and when `RoleProvider gets around to running, if it finds the roles already there, it just does nothing.

like image 4
Erik Funkenbusch Avatar answered Oct 08 '22 18:10

Erik Funkenbusch