Invoking Custom WCF Services in SharePoint with Claims
In SharePoint, if you host a custom WCF service in a claims-enabled application, the authentication via NTLM is actually quite tricky if you are attempting to invoke it from a console application, for example.
There are various articles and Stackoverflow entries on using System.ServiceModel.Description.ClientCredentials on either the ChannelFactory or the client instance, but all of these did not work in the sense that on the server side, SPContext.Current.Web.CurrentUser was null and ServiceSecurityContext.Current.IsAnonymous returned true.
It seems like it should be possible to invoke the service authenticating through NTLM as if the user were accessing it through the web site.
In fact, it is possible, but it involves some manual HTTP requests to get this to work without doing some Windows Identity Foundation programming and consequently setting up tons of infrastructure to get what seems like a relatively simple and straightforward scenario to work.
The first step is to actually manually retrieve the FedAuth token:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
/// <summary> /// Gets a claims based authentication token by logging in through the NTLM endpoint. /// </summary> /// <returns>The FedAuth token required to connect and authenticate the session.</returns> private string GetAuthToken() { string authToken = string.Empty; CredentialCache credentialCache = new CredentialCache(); credentialCache.Add(new Uri(_portalBaseUrl), "NTLM", new NetworkCredential(_username, _password, _domain)); HttpWebRequest request = WebRequest.Create(string.Format("{0}/_windows/default.aspx?ReturnUrl=%2f_layouts%2fAuthenticate.aspx%3fSource%3d%252F&Source=%2F ", _portalBaseUrl)) as HttpWebRequest; request.Credentials = credentialCache; request.AllowAutoRedirect = false; request.PreAuthenticate = true; // SharePoint doesn't like it if you don't include these (403 Forbidden)? request.UserAgent = "Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko"; request.Accept = "text/html, application/xhtml+xml, */*"; HttpWebResponse response = request.GetResponse() as HttpWebResponse; authToken = response.Headers["Set-Cookie"]; return authToken; } |
There are three keys here:
- The first is that AllowAutoRedirect must be false or you will get an error that you are getting too many redirects. What seems to happen is that the cookies are not set correctly when using auto redirect so the chain will continue until an exception is thrown. In Fiddler, you will see this as a long cycle of requests and redirects.
- The second is that the URL must be the NTLM authentication endpoint (/_windows…”) as any other URL will return a 302 and for that, you will need to set AllowAutoRedirect to true.
- The third is that it seems as if SharePoint really doesn’t like it when the user agent and accept headers are not included. I tried it later without these and it seemed to work, but I could not get it to work without them (403 unauthorized) initially.
Once you have the FedAuth token, you are able to basically impersonate the user. To do so, you will need to include a cookie in your HTTP header request:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
// Get the FedAuth cookie var authToken = GetAuthToken(); // Create the connection artifacts. EndpointAddress endpointAddress = new EndpointAddress(endpointUrl); BasicHttpBinding binding = new BasicHttpBinding(); ChannelFactory<ISomeService> channelFactory = new ChannelFactory<ISomeService>(binding, endpointAddress); // Initiate the client proxy using the connection and binding information. ISomeService client = channelFactory.CreateChannel(); using (new OperationContextScope((IContextChannel) client)) { // Set the authentication cookie on the outgoing WCF request. WebOperationContext.Current.OutgoingRequest.Headers.Add("Cookie", authToken); // YOUR API CALLS HERE } |
The key is to add the header on the outgoing request before making your service API calls.
With this, you should see that you are able to invoke SharePoint hosted custom WCF service calls in claims-based web applications with NTLM authentication.