Calling a WCF service from a Metro application – adding security

[Updated for Win8/Dev11 Beta]

This is an extension to my earlier post - where I did a walkthrough of calling a WCF service hosted locally on the same machine from a C# Metro client application. In this post, I’ll take you through how to add security.

As you will remember, I had a Picture REST service with webHttpBinding and a ViewCount SOAP service.

Below is a refresher on how to add security to the binding and configuring the respective service hosts with these bindings.

Code Snippet

  1. // Add service endpoint & webHttp behavior
  2. WebHttpBinding webBinding = new WebHttpBinding(WebHttpSecurityMode.TransportCredentialOnly);
  3. webBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Windows;
  4. pictureServiceHost.AddServiceEndpoint(typeof(IPictureService), webBinding, String.Empty)
  5.     .EndpointBehaviors.Add(new WebHttpBehavior() { HelpEnabled = true });

Code Snippet

  1. // Add service endpoints
  2. BasicHttpBinding basicBinding = new BasicHttpBinding(BasicHttpSecurityMode.TransportCredentialOnly);
  3. basicBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Windows;
  4. viewCountServiceHost.AddServiceEndpoint(typeof(IViewCount), basicBinding, String.Empty);

I am going to use my domain credentials and so I have configured my bindings with SecurityMode of TransportCredentialOnly and HttpClientCredentialType of Windows.

Once you have made the above changes to the service, you need to start up the service host and export metadata for the SOAP service so that we can add service reference to it. When you have completed the Add Service Reference (ASR) action from the metro client application, you will see the following now modified GetBindingForEndpoint method generated on the client (in Reference.cs file, if you do ‘Show all files’ on the added service reference) which as you can see is already configured for security.

Code Snippet

  1. private static System.ServiceModel.Channels.Binding GetBindingForEndpoint(EndpointConfiguration endpointConfiguration) {
  2.             if ((endpointConfiguration == EndpointConfiguration.BasicHttpBinding_IViewCount)) {
  3.                 System.ServiceModel.BasicHttpBinding result = new System.ServiceModel.BasicHttpBinding();
  4.                 result.AllowCookies = true;
  5.                 result.Security.Mode = System.ServiceModel.BasicHttpSecurityMode.TransportCredentialOnly;
  6.                 result.Security.Transport.ClientCredentialType = System.ServiceModel.HttpClientCredentialType.Windows;
  7.                 return result;
  8.             }
  9.             throw new System.InvalidOperationException(string.Format("Could not find endpoint with name \'{0}\'.", endpointConfiguration));
  10.         }

If you are doing this from a non-metro client then equivalents of lines 5 and 6 get generated in xml configuration but since there is no xml styled configuration for metro application, the WCF ASR experience is intelligent enough to generate the equivalent code. 

The above code configuration will enable the proxy to call the SOAP web service providing default domain credentials and you do not have to write any additional piece of code other than instantiating the proxy as we were already doing.

Code Snippet

  1. // Call the SOAP web service to get details - e.g. view count
  2. PhotoService.ViewCountClient proxy = new PhotoService.ViewCountClient();

That takes care of the SOAP service.

Now for the REST service, we need to configure our HttpClient, which we were using to access the service, to pass the credentials. Here is how you do it:

Code Snippet

  1. HttpClientHandler handler = new HttpClientHandler();
  2. handler.UseDefaultCredentials = true;
  3. HttpClient client = new HttpClient(handler);

You need to instantiate an HttpClientHandler object, configure it to use the default credentials and provide it to the HttpClient so that HttpClient knows that when it gets back an HTTP status code 401 from the service with request for credentials, it needs to pass the default domain credentials.

And finally, before running the metro client, don’t forget to enable the “Default Windows Credentials” capability which will allow the metro client to retrieve the required domain credentials:

I have modified my REST and SOAP service methods to print the domain credential name passed to the service (using OperationContext.Current.ServiceSecurityContext.WindowsIdentity.Name) so you know that the credentials are getting passed correctly!

Sample code attached:

Thank you!