Configuring a Custom Claims Provider to be Used only on Select Zones in SharePoint 2010
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;
cpm.Update();
break;
}
}
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
if (EnableProvider)
providers.Add(myClaimProvider.ClaimProvider.Name);
else
providers.Remove(myClaimProvider.ClaimProvider.Name);
//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
theWebApp.Update();
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.
Comments
Anonymous
January 01, 2003
The comment has been removedAnonymous
January 01, 2003
thanksAnonymous
June 03, 2010
Very nice post as always. Thanks I think i have claims augumentation feature enabled at web application level scope and see no issues. I used ActivateOnDefault = "FALSE" and activated the feature only on web applications in need of custom claims.Anonymous
July 09, 2010
Hi, This is vishwas. I am very new to SharePoint 2010 Development. I am working with a team on building custom claims provider for SP 2010. Could you give me some guidance on creating one. Our SharePoint is all under Claims based authentication. We also have forms based and windows based authentication. We have ADFS as our trusted provider and the claims that ADFS sends are just uid and memberof [contains all the groups in a global LDAP Directory]. Hope I am clear on my question. Thanks VishwasAnonymous
August 30, 2010
I tried to downlad the example zip and i found it to be corruptAnonymous
October 23, 2010
This post is very helpful, but I also found the .zip file to be corrupt..pls re-create this...thanksAnonymous
April 28, 2011
Steve, you ROCK! Every time I need to dig into a question on Claims and SharePoint I end up finding the gem in one of your blog entries. You have saved us a LOT of time by positing this blog (and others I must add) and including your excellent Claims Provider Activation tool. A big thanks for all you do to educate the community on how SP and Claims works and providing real world answers to questions/problems. Without it I would have a harder time moving into the Claims authN and authZ world in our env. Thanks again.Anonymous
December 01, 2012
Steve, I was able to have my custom claim provider work for our custom STS, but it still seems to be hit in any zone. I've posted a question here, sharepoint.stackexchange.com/.../custom-claims-provider-in-specific-zone . I was wondering about your thoughts on this. Regards, NickAnonymous
November 13, 2013
Steve, thank you for the explanation of IsUsedByDefault. Very useful. In order to make the claims provider available in a Web app, I use the technique mentioned in your post "How to Override the Default Name Resolution and Claims Provider in SharePoint 2010" (April 28 2010). If this is used, is it still necessary to do what you describe here ?Anonymous
September 18, 2014
The comment has been removedAnonymous
March 02, 2015
As I was going round and round a few weeks ago trying to figure out why my custom claims provider was