UPDATE: I updated the application attachment for this posting. Before it would only let you toggle a claims provider for a zone if that zone was using SAML claims. In retrospect that limitation didn't make a lot of sense, so now it lets you toggle a claims provider for any zone that is using any type of claims – Windows, FBA or SAML.
As the consciousness of using claims providers begins to spread, more questions continue to come up around usage scenarios. One of the scenarios I’ve heard mentioned a few times is how can I configure my custom claims provider to only be used for a certain web application or two, instead of all of them? Originally I was hoping that we could solve this rather simply, and just scope the feature for the claim provider to web application instead of farm. Well, guess what – that won’t work. So here’s lesson #1 – a feature for a custom claims provider must always be scoped to the Farm level. If you configure the scope of your feature to be anything different, you will get a "The method or operation is not implemented" error.
So the if the feature is scoped to the farm level, how do we configure the provider only to be used on select web applications and/or zones? Turns out the answer to that is much more complicated.
To begin with, it’s important to understand two very important properties on a claims provider: IsEnabled and IsUsedByDefault. Out of the box, when you install a new claims provider feature, the claim provider has IsEnabled and IsUsedByDefault set to true. That means that your claim provider will be used everywhere SAML claims are enabled on a zone. So in order to even be able to configure a claim provider to be used only on select zones, we first need to change the IsUsedByDefault property to false. As long as IsEnabled equals true, and IsUsedByDefault equals false, we can configure use of the claims provider on a per zone basis.
So, obviously the next question is how can I set the IsUsedByDefault property to false? Well there are a couple of options here. One option is to use your feature receiver that you create for your custom claims provider and configure it in there. In the override for the FeatureActivated event (which you should already have coded anyways), you can use the SPClaimProviderManager to get a reference to your provider and set the IsUsedByDefault property to false. Here’s an example that I used in one of my providers:
SPClaimProviderManager cpm = SPClaimProviderManager.Local;
foreach (SPClaimProviderDefinition cp in cpm.ClaimProviders)
if (cp.ClaimProviderType == typeof(SqlClaimsProvider.SqlClaims))
cp.IsUsedByDefault = false;
In this code where it uses typeof(SqlClaimsProvider.SqlClaims), you would substitute the assembly and class name of your provider, like typeof(MyProvider.MyClaims). You can also wrap the code above in a custom application. This is in fact what I’ve done, and what I’ll also be using to demonstrate how the rest of this works throughout this posting. Here’s a screenshot of my Claims Provider Activation application, and you can see in the left side of application is a list of my registered custom claims providers (there is only one), and checkboxes below it where I can change the IsEnabled and IsUsedByDefault properties on it:
So as you can see, you just select your custom claims provider, check the boxes as desired for IsEnabled and IsUsedByDefault, then click the Update button.
Now, once your claims provider is configured to be IsEnabled but not IsUsedByDefault, you can configure where it will be used. Once your claims provider is configured this way, it will only be used when the SPIisSettings for a zone contain the name of the claims provider in the ClaimsProviders property. Getting to this information is a little more difficult if you’re trying to do it generically, as I did with the Claims Provider Activation application. The first step was to enumerate all of the web applications, which is done easily enough with the SPService class:
//get the content service
SPWebService ws = SPWebService.ContentService;
//get each web app
foreach (SPWebApplication wa in ws.WebApplications)
//enumerate each zone in each web application
Enumerating each zone in a web application is a little less common operation – here’s the code for doing that:
foreach (SPAlternateUrl aam in wa.AlternateUrls)
//look at each zone to see if it’s using claims
To determine whether a zone is using claims, I start by getting the SPIisSettings for the zone; as I mentioned above, I’m going to need this anyway in order to add or remove my claim provider from the ClaimsProviders property. Here’s how I get those settings:
SPIisSettings curConfig = wa.GetIisSettingsWithFallback(aam.UrlZone);
In my curConfig variable now I can look at the ClaimsAuthenticationProviders property. If a zone is configured to use claims, then the ClaimsAuthenticationProviders property will not be null, and the ClaimsAuthenticationProviders.Count() property will be greater than zero. So I enumerate all of the providers and if I find that one, I add the zone to my list of zones and show it as enabled. Otherwise I can’t use my custom claims provider with that zone so I add it to the list of zones but show it disabled. So that’s the basics of how I generate the list of web applications and zones, and determine which zones can use a custom claims provider.
With that list then, for any particular zone my claims provider will be used if the claim provider name is in the ClaimsProviders property of the SPIisSettings. This particular property is actually an IEnumerable of type string, so it’s really a little awkward to work with because it doesn’t contain an overloaded method to Add or Remove or RemoveAt items. In order to get past that, I took the property and converted it to a List<string> so I could add or remove my claim provider name, and then I assign the List<string> back to the ClaimsProviders property. Here’s what that code looks like:
List<string> providers = theZoneIisSettings.ClaimsProviders.ToList();
//NOTE: myClaimProvider is type SPClaimProviderDefinition
//plug the new values back into the ClaimsProviders IEnumerable
theZoneIisSettings.ClaimsProviders = providers;
//you must get the web application that contains the zone and call its
//update method in order to save your changes
Here’s what this looks like in the Claims Provider Activation Application:
In this case the menu says “Enable Basketball Teams” because my Basketball Teams provider is not enabled for that zone. If it were, then the menu would say “Disable Basketball Teams”. You’ll also notice that the zones above it, in the SharePoint – Anon Profile Test web application are disabled. That’s because those zones are not configured to use SAML claims.
So that’s how this all works. It’s somewhat complicated, but also has lots of flexibility for configuration. I’ve attached the Claims Provider Activation application to this posting so you can download and use it. My disclaimer is that I’ve only tested it in my environment on my single server farm, so I will freely invoke the developer’s defense mantra of “but it works on my machine!” However, if you try it and find it isn’t working for your particular scenario you can leave a comment and I will try when I have free time to take a look at it.