Calling a Claims-Aware WCF Service From a SharePoint 2010 Claims Site
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 https://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 (https://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 =
new EndpointAddress("https://az1.foo.com/CustomersWCF_WebRole/Customers.svc");
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.
cust.ChannelFactory.ConfigureChannelFactory<AzureData.ICustomers>();
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
SPChannelFactoryOperations.ConfigureCredentials<AzureData.ICustomers>(cust.ChannelFactory, Microsoft.SharePoint.SPServiceAuthenticationMode.Claims);
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 =
SPChannelFactoryOperations.CreateChannelActingAsLoggedOnUser
<AzureData.ICustomers>(cust.ChannelFactory, svcEndPt);
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();
//enumerate results
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).
Calling a Claims Aware WCF Service from a SharePoint 2010 Claims Site.docx
Comments
Anonymous
January 01, 2003
The comment has been removedAnonymous
January 01, 2003
Hey Jim, I will probably have something to download at some point, but I'm doing some other work to make it much simpler and less complicated than it currently is. Keep your eyes peeled to this space. :-)Anonymous
January 01, 2003
thanksAnonymous
September 21, 2010
Great post. I've had no success with this, any chance of getting your code as a download?Anonymous
October 07, 2010
I've been trying to connect to WCF OR Web Service , which inturn uses WCF without much luck. I was unable to connect using the Project 2010 SDK samples. Hopefully you can have your code ready soon. I will be lurking here for the next few days / weeks.Anonymous
October 30, 2012
Hey steve, I've been following your posts for quite a while now and they have been very helpful. Like I created a Custom STS and have successfully integrated that with SharePoint.. Thank you. I am wondering if you could help me with my question.. My question is: I have integrated an asp.net web app with the custom STS and whenever I write it's url its successfully redirected to the STS login page and is being authenticated against my authentication mechanism when i enter the credentials the token is returned and I am allowed to view the website.. Now what i want is once i have been authenticated i want to be able to by pass the sts and directly interact with a claims aware wcf service which holds all the authorization details. Any kind of help suggestion would be highly appreciated. Thank you.Anonymous
June 11, 2014
Hi Steve, I enjoy your blog very much. I recently converted a 2010 Farm to use Claims Based Authentication. We have lots of Silverlight controls calling custom WCF services that in turn call the Search Query Service. Unfortunately, our WCF services are not Claims Aware and use Basic HTTP binding with metadata exchange (MEX) endpoints Ref: WCF Services in SharePoint Foundation 2010http://msdn.microsoft.com/en-us/library/ff521586(v=office.14).aspx. So these calls now don't have the user's credentials. I would love to get the credentials passed so that security trimming of the results still occurs. I would appreciate your thoughts.Anonymous
September 18, 2014
The comment has been removedAnonymous
February 28, 2015
This is part 3 of a 5 part series on the CASI (Claims, Azure and SharePoint Integration) Kit.
·Anonymous
February 28, 2015
This is part 3 of a 5 part series on the CASI (Claims, Azure and SharePoint Integration) Kit.
·Anonymous
March 01, 2015
Okay, this will hopefully be the longest titled post I ever write, but I wanted to make sure it coveredAnonymous
March 01, 2015
Okay, this will hopefully be the longest titled post I ever write, but I wanted to make sure it covered