How often will FillClaimsForEntity in my Custom Claims Provider be called?

This was the question I posed to a colleague this morning. His answer surprised me – “Every time you authenticate.”

Before we move on, let’s just call this foreshadowing.

Problem is, this statement was at odds with the behavior that I, and many of you, were seeing/have seen. It just didn’t seem to be true. Clearly, I was seeing that very nearly every single time a page load occurred, my custom claims provider was invoked. Looking at a Fiddler trace, it was also obvious that I wasn’t receiving a 401 on every page request, either. This was certainly different than a web application configured for Windows Classic authentication – where you can sometimes see a 401 on every page load.

First, a refresher on the oddities of a claims based web application.

When a web application is configured for claims based authentication, the IIS binding has Anonymous access enabled on it. This is because, unlike with Windows Classic, authentication is a SharePoint problem with claims (SAML or Windows). SharePoint 2010 manages this via a module (or two) within the IIS pipeline. The enablement of anonymous access generally goes unnoticed by most administrators, since its one of a myriad of settings that the product sets on the IIS binding for a web application. My personal experience is that when administrators do notice this, they generally freak out. It’s okay. The product did this on purpose; if it didn’t you wouldn’t have all that fancy claims-based authentication federating with your partner ADFS instances and delegating the pain of user management. This is a feature.

Refresher over.

So, why do I see a ton of 401’s when running under Windows Classic, but only one, at session start, under Windows Claims? And how does this influence the frequency with which my custom claims provider is going to get called?

image
401’s with Windows Classic

Under Windows Classic, IIS is in charge of authentication. And IIS says that if a request doesn’t include an Authorization header, it is challenging for credentials based on the configuration of the site. Therefore, you’ll see a lot more 401’s under Windows Classic. Now, a 401 is expensive. With NTLM authentication, every 401 means that an Active Directory Domain Controller was interrogated to validate the passed credentials.

When you’ve got a web application that is configured for Windows Claims only, and Windows Claims is the only authentication type specified for that web application, you execute a very unique code path within the authentication system. If you run a Fiddler trace you’ll notice that after the initial 401, a cookie, WSS_KeepSessionAuthenticated, is set on the browser. This cookie is an index into a token cache maintained by SharePoint, and is actually a chunk of code left over from SharePoint 2007 that is only invoked when a site is:

  1. Configured for Claims Based Authentication
  2. Only configured for Windows Claims (i.e. NTLM or Kerberos authentication)

Remember that integrated mode module that we talked about before? The one that handles authentication for claims based web applications? When this module sees the WSS_KeepSessionAuthenticated cookie, it checks the state of the cached token that the cookie value points to on the server, and in so doing executes a code path that iterates over all registered claim providers and, as a result, winds up calling FillClaimsForEntity on each one that returns true from SupportsEntityInformation (For more information, see the SPClaimProvider documentation on MSDN).

We’ll cache the token according to settings specified on the security token services configuration (accessible via PowerShell by calling Get-SPSecurityTokenServiceConfig). As a side benefit, we don’t wind up issuing a 401 on ever page request in this model, significantly reducing authentication overhead and domain controller impact, but we do wind up calling any registered claim providers a lot. Lesson: Optimize your claim providers – slow downs in authentication will equal a slow site.

Now, add a second authentication mechanism, and things suddenly change. Under Windows Claims, if a second authentication method is defined for the URL – say, Forms, or a Trusted Identity Provider – we stop issuing the WSS_KeepSessionAuthenticated cookie, and start issuing a FedAuth cookie instead. The FedAuth cookie is the the cookie we issue when you are using SAML Claims, FBA, or Windows Claims with multiple authentication providers. The details of authentication when a FedAuth cookie is used are significantly different; for instance, we’ll only call registered claim providers when an actual FedAuth cookie is issued, as that’s the time authentication really occurs; we’ll only re-execute registered claim providers when the cookie expires and an authentication is forced again.

All this talk of Windows Authentication and cookies might have some folks wondering – cookies can be a real pain, I went with Windows Authentication to avoid cookies and the pain they bring. Remember the death by a thousand cuts that was the integration  of Office 2007 and SharePoint FBA applications under SharePoint 2007 before Service Pack 2 (and a litany of other conditions)?

Using cookies to streamline the authentication process is actually okay with Windows Authentication, because the browser first authenticated with Windows Auth, and will respond seamlessly if it receives a 401 down the road. The fact that it’s not getting 401’s mean faster browsing for you, less chatter between the server and the domain controller infrastructure (in the case of NTLM authentication), etc. Should a 401 be issued, the client will know just what to do.

Remember earlier, when my colleague tried telling me that my custom claims provider will have its FillClaimsForEntity called on every authentication? Turns out, he was right – every time I authenticate, even if the authentication process is short circuited (ala WSS_KeepSessionAuthenticated and Windows Claims), my custom claims provider will be invoked. Sometimes, though, I authenticate a lot less than I thought I would!

I hope this helps those folks that are trying to understand the complexities behind custom claims providers and the logic behind their invocation.