I’ve been doing some work lately (that will be fodder for future posts) on more of the end-to-end integration story with SharePoint and other applications, and using claims authentication to flow identity across application boundaries. One of the specific points I’ve been working on lately is being able to make a call to a claims aware WCF endpoint from a SharePoint web part, and having that claims information flow over.
The first trick is getting your WCF service claims aware, and that’s really outside the scope of what I’m blogging about here today. Eric White did a great four-part blog posting on this process, which you can find at http://blogs.msdn.com/b/ericwhite/archive/2010/05/11/getting-started-building-a-wcf-web-service.aspx. Assuming you have all that set up, now you need to call into that from a SharePoint web part (in this example).
The tricky thing here is making this call from a SharePoint site that is secured with claims authentication. In this case I’m using a site that supports both Windows claims and SAML claims. What was curious at first is that I logged in as a Windows user, but when my web part made a call to the web service it would fail with this error: “SOAP security negotiation failed. See inner exception for more details.”, and when you look at the inner exception it said “The profile for the user is a temporary profile.” At first it struck me as fairly non-sensical – I was after all, logged in to the site using the same Windows credentials that I had logged onto my computer with. In thinking about it some more though, I realized it probably is accurate. I’m technically logged on as domain\speschka, but once I go into SharePoint I get transformed into a Windows claims user and I’m now 0#.w|domain\speschka. Okay, so how do we work around this? And pass our claims over to the WCF service?
Well, it turns out that there are a few pretty nice claims helper functions in Microsoft.SharePoint. One of these is the SPChannelFactoryOperations class. It has a helper function on it called CreateChannelActingAsLoggedOnUser that I used to connect to my WCF service (http://msdn.microsoft.com/en-us/library/ee579805.aspx). So from Visual Studio, I started out by adding a service reference to my WCF endpoint. In this case my service reference name was AzureData, and it points to another server running Customers.svc. So I start out by creating the endpoint address my WCF service is running at an instance of my service proxy:
//create the endpoint to connect to
EndpointAddress svcEndPt =
AzureData.CustomersClient cust = new AzureData.CustomersClient();
Now that I have that in place, I have to call a method on my proxy’s channel that will enable it to use a federated client identity. You can do that with this one simple line of code:
//configure the channel so we can call it with FederatedClientCredentials.
Steve's Updated Note: SharePoint actually also has a method for doing this, if you want to keep it all in the SharePoint family, so to speak. Instead of using the ConfigureChannelFactory method off the ChannelFactory class, you can instead use this method from the SharePoint API (I've found both work fine):
//configure the channel the SharePoint way
Now I can go ahead and create the channel using the claims credentials of the logged on user. That’s done with the CreateChannelActingAsLoggedOnUser method:
//create a channel to the WCF endpoint using the token and claims of the current user
AzureData.ICustomers claimsWCF =
In this case, I need to get an instance of the interface that the WCF endpoint implements – that’s why my variable is of type AzureData.ICustomers. When I call the CreateChannelActingAsLoggedOnUser method I need to tell it what kind of interface it’s creating the channel for, so again, I use the AzureData.ICustomers interface. Finally, to create the channel as the logged on user, I have to give it an existing channel reference and the endpoint that it is going to create the channel to. So I just pass in the ChannelFactory property of my proxy instance I created earlier, along with the endpoint address I defined up front. Once I’ve done this work, now I can call into my WCF endpoint:
//get our data
AzureData.Customer customers = claimsWCF.GetAllCustomers();
foreach (AzureData.Customer c in customers)
Debug.WriteLine("Name = " + c.Name + ", Email = " + c.Email +
“, City = “ + c.City);
Since claimsWCF was my instance of the interface, I can call whatever methods have been defined for it. With a debugger attached to both the web part and WCF service, I can see the call coming from SharePoint over to the service, and in the service I can enumerate all the claims that I got when I logged into SharePoint, including custom claims that I added with a custom claims augmentation component (my favorite basketball team claim from my series on writing a custom claims provider).