More occult goodness for your programming pleasure! The Session Mode is a great feature of WIF which is not known as widely as it should be.
Sometimes you will be in situations in which it is advisable to limit the size of the cookie you send around. WIF already take steps for being parsimonious with the cookie size. By default, the cookie will contain just the layout defined by the SessionSecurityToken: more or less the minimal information required for being able to reconstruct the IClaimsPrincipal across requests (as opposed to a verbatim dump of the entire incoming bootstrap token, with its logorrheic XML syntax, key references & friends).
Let’s see if we can visualize the quantities at play here. If you take the FedAuth cookie generated from the default token issued from the default STS template implementation in the WIF SDK, the one with just name & role claims hardcoded in a SAML1.0 assertion, you get the following:
Slightly more than 2K. Not the nimblest thing you’ve ever seen, but not a cetacean cookie either. On the other hand we have just two claims here; what happens if we have more than those, or we have big claims such as base64’ed images or similar? Moreover: sometimes we do need to include the bootstrap token in the session, for example when we call a frontend which needs to invoke a backend acting as the caller. Let’s pick this last case: keeping the same token we used above, let’s save it in the session (by adding saveBootstrapTokens="true" to the microsoft.identityModel/service element on the RP) and see how big a cookie we get:
That’s bigger: 5K of DPAPI-packaged goodness. And it’s just 2 claims of ValueType string, you can imagine how the size could go up fast with number and size of the individual claims. On the other hand, if you need that info there is no (clean) way around it, you’ve got to ask for a token which will contain it. However that does not necessarily imply that your cookie has to grow as well. Since RTM, WIF provides you with a simple but effective way of keeping the token info in a server side cache, which means that the cookie can be as small as the reference to the cache entry that should be retrieved in the context of the current session.
WIF already had a cache for tokens, the abstract SecurityTokenCache and subclasses (such as the default implementation MruSecurityTokenCache), which is associated to the SessionSecurityTokenHandler via the config element add/sessionTokenRequirement/securityTokenCacheType (see the post about the config). The idea behind the token cache was (and is) reducing the request processing time, since retrieving the SessionSecurityToken from a cache is more efficient than reserializing it from a cookie every time.
From there, the move toward session mode is very natural. WIF RTM added a property to the SessionAuthenticationModule, IsSessionMode. When flipped to true, IsSessionMode has the effect of ensuring that the SessionSecurityToken remains in the cache for the whole duration of the session and generating a cookie which contains just a session identifier rather than the content of the session itself. The result is a very lightweight cookie! Let’s see what cookie we get in session mode:
ASP.NET_SessionId f1dy2w55eigyfj55fvbnpm55 FedAuth 77u/PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz48U2VjdXJ pdHlDb250ZXh0VG9rZW4gcDE6SWQ9Il8yMGEyZWVkMy1mYzFkLTQyYzEtYT JjYi1jMWJiNTEwYTA4MWItMjJGM0E5M0I2NDZDNjFEQTE0M0NBN0YxREJEO TAyNjQiIHhtbG5zOnAxPSJodHRwOi8vZG9jcy5vYXNpcy1vcGVuLm9yZy93c3 MvMjAwNC8wMS9vYXNpcy0yMDA0MDEtd3NzLXdzc2VjdXJpdHktdXRpbGl0 eS0xLjAueHNkIiB4bWxucz0iaHR0cDovL2RvY3Mub2FzaXMtb3Blbi5vcmcvd3 tc3gvd3Mtc2VjdXJlY29udmVyc2aW9uLzIwMDUxMiIPElkZW50aWZpZXIdXJu OnV1aWQ6NDE2ODIxMGMtOTYzMy00NjIwLTk5ZGItZDc0Y2YyOTY0NmVjP C9JZGVudGlmaWVyPjxJbnN0YW5jZT51cm46dXVpZDo1N2Y0Y2JmNi00MjIw LTQ2NzEtYWQxZC1lODNkNGFlYWI4YWI8L0luc3RhbmNlPjwvU2VjdXJpdHlD b250ZXh0VG9rZW4
Tiny! A meager ~650 bytes. Neat trick, eh? Now, that doesn’t mean that all of a sudden it is a good idea to use claims for moving videos around, it stil isn’t, but the session mode does decouple you from a lot of drawbacks associated to size.
The catch is that, you guessed it, the out of the box cache class is not NLB ready (hence won’t work too well on Windows Azure either). However fixing it is not hard, derive from SecurityTokenCache your own cache and use whatever NLB-friendly storage you like. I seem to recall that Hervey did give some details in his PDC talk.
But wait, I have shown you the advantages of the session mode but I neglected to tell you how to enable that on your application! It’s pretty easy: in your global.asax add
<%@ Import Namespace="Microsoft.IdentityModel.Web" %>
void WSFederationAuthenticationModule_SessionSecurityTokenCreated(object sender, Microsoft.IdentityModel.Web.SessionSecurityTokenCreatedEventArgs e)
FederatedAuthentication.SessionAuthenticationModule.IsSessionMode = true;
Yes, it would be nice to be able to flip this from configuration; unfortunately in the current version of WIF it can’t be done. Luckily doing it from global.asax is pretty easy as well.
That’s it, your cookies are now ready for the beach season! Unfortunately I can’t say the same about myself, but there are things that cannot be solved with Visual Studio ;-)
P.S. Many thanks to Daniel Wu for a very illuminating chat on this very topic.
"Sometimes you will be in situations in which it is advisable to limit the size of the cookie you send around."
You are always in a situation in which it is advisable to limit the size of cookies--they are not supposed to exceed 4kb. Some browsers will accept them, others will just ignore them. This can lead to a bug which is incredibly difficult to identify--certain users and/or users with specific browser/versions will be unable to authenticate because they will never save the cookie. Cookies containing a lot of SAML assertions or base64 encoded images simply won't store--no error message, no warnings.
I stumbled on this issue while implementing an authentication client for the CAS protocol. I was trying to stuff assertion & context information into a FormsAuthenticationTicket's UserData property. When the size of the UserData + the size of everything else in the FormsAuthenticationTicket (+serialization, crypto, encoding overhead) caused the FormsAuthenticationCookie to exceed 4kb, browsers were refusing to store the cookies.
The solution was to cache the assertion info on the server and store just a key in the UserData property in the cookie, similar to what ServerTokenCache seems to provide. This can lead to other issues, particularly in clustered and load-balanced configurations. Every node needs access to the same information on the server-side, which rules out the possibility of storing it in the web application's memory space --these configurations require persistent storage (database, file system, memcached, etc.).
In any case, the ServerTokenCache workaround should probably be mandatory and/or the WIF documentation should reflect the caveats.
here we can discuss, of course: you can never rule out (reasonable) tactical decisions. For example, some times you know in advance the scope of your app very well and you know you can take the cookie size hit, in which case not having to worry about where to save stuff server side could be a plus (especially in NLB environments, where storage may not be free). Therefore, I think that having the choice between passing session by value or by reference is a good thing; I do agree about the visibility of this feature in the documentation. Note that I would not exactly call it a "workaround", this has precisely the same dignity as the cookie by value method :-)
I agree about the tactical decisions point. The 'cookie by value' approach is fine in certain circumstances, but the approach breaks down if/when the server attempts to send a >4096 byte cookie to the client. The server can't safely assume that the cookie will come back--these users won't be able to remain authenticated between requests. I imagine this won't be a problem for most of clients, but it will be very difficult to troubleshoot for those that it does (systems sending user images in SAML assertions, for instance). You might consider calculating the serialized/encoded cookie size in bytes and throwing an exception on the server before sending a cookie > 4kb on machines configured to send the cookie by value. They should switch to 'cookie by reference'.
I misspoke on calling the ServerTokenCache a "workaround". I just mean that a byproduct of using the ServerTokenCache is that it would eliminate the risk of encountering these types of problems.
When can we expect to see a durable / web farm friendly SecurityTokenCache implementation for W.I.F.?
Maybe something based on Velocity (Windows Server App Fabric Cache)?
we used IsSessionMode=true to decrease our FedAuth (RP) cookies size from 4K to 700 bytes.
However, cookies created by our STS Server are still a problem.
In our case the STS Server generates many small cookies (MSISLoopDetectionCookie, MSISSignOut, MSISAuthenticated, MSISIPSelectionPersistent) and three big cookies: MSISAuth (2K), MSISAuth1 (2K) and MSISAuth2 (320 bytes).
Is there a way to reduce these cookies size as well?
Thanks in advance!
@Jimit: potentially, yes!
@Giacomo: unfortunately i am not very deep in ADFS: I'd suggest checking out the forums social.msdn.microsoft.com/.../threads
Thank you Vittorio,
I'll post my question on the forum.
Nice article...found it while reading your new book - which I wish we had 8 months ago :)
glad you liked the article and found the book useful :-)
8 months ago I was just about to start the push for finishing the last chapters. Sorry for not having gotten the book sooner!