Presume two ASP.NET MVC authorization/role design stories. Both are intended to minimize hits against a middle-tier or data store:
Or...
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?
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:
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..."
My own answer about the two design choices being equal is “no.” I want to present my argument, and learn from you if:
Hopefully a best-practice concept will emerge as an answer. Now for 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:
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?
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.
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 IPrincipal
s 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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With