20 steps to get together Windows Authentication, Silverlight and WCF service

For one of my projects I had to query WCF web service for some data and display it in the Silverlight powered client. The web service should return data relevant to the identity of the person, querying the service with the Silverlight client. The choice of Silverlight over other technologies has been made to meet requirements for rich interface, consisting of the flexible grid with grouping, sorting and filtering. Pretty much the same grid as the one you may be looking at every day while using Microsoft Outlook. Since the application should be used inside the corporate intranet,  one of the requirements was the usage of integrated Windows authentication,

I started experimenting with WCF service project template in Visual Studio. The only method of the new web service was supposed to return login name of the current user, but ServiceSecurityContext.Current.WindowsIdentity.Name was not available at all (the context was null).

Googling for the solution brought advices about using the ASP.NET services with Form Authentication, which effectively renders usage of the Active Directory useless.

To make long story short here are the steps:

  1. Create Silverlight Project create_silverlight_project_from_template
  2. Choose to have the Visual Studio create a web site to host the Silverlight application select_web_site_for_silverlight_app
  3. Select the created web site and add Silverlight enabled WCF service to it. add_silverlight_enabled_wcf_web_service
  4. Open web site project properties and on Web tab, select Create Virtual Directory create_virtual_directory_for_web_site
  5. Start IIS management console and edit newly created directory’s security settings. You have to disable anonymous access and make sure you have Windows authentication selected disable_anonymous_access
  6. Open ASP.NET tab and edit Authorization configuration – Deny access for anonymous users for all requests deny_anonymous_users_in_asp_net
  7. Restart IIS  - sorry, no picture here :)
  8. Now time to implement DemoService.svc
    using System.ServiceModel;  
    using System.ServiceModel.Activation;
    
    namespace RouslanComDemoApp.Web  
    {
        [ServiceContract(Namespace = "")]
        [AspNetCompatibilityRequirements(RequirementsMode =
            AspNetCompatibilityRequirementsMode.Allowed)]
        public class DemoService
        {
            [OperationContract]
            public string GetWindowsUsername()
            {
                return ServiceSecurityContext.Current.WindowsIdentity.Name;
            }
        }
    }
    
  9. Open Web.config and locate section <system.ServiceModel>. Replace the entire section with the following directives:
    <system.serviceModel>  
        <bindings>
            <basicHttpBinding>
                <binding name="winAuthBasicHttpBinding">
                    <security mode="TransportCredentialOnly">
                        <transport clientCredentialType="Windows"/>
                    </security>
                </binding>
            </basicHttpBinding>
        </bindings>
        <behaviors>
            <serviceBehaviors>
                <behavior name="RouslanComDemoApp.Web.DemoServiceBehavior">
                    <serviceMetadata httpGetEnabled="true" />
                    <serviceDebug includeExceptionDetailInFaults="false" />
                </behavior>
            </serviceBehaviors>
        </behaviors>
        <serviceHostingEnvironment aspNetCompatibilityEnabled="true" />
        <services>
            <service behaviorConfiguration="RouslanComDemoApp.Web.DemoServiceBehavior"
                 name="RouslanComDemoApp.Web.DemoService">
                <endpoint address="" binding="basicHttpBinding"
                             bindingConfiguration="winAuthBasicHttpBinding"
                             contract="RouslanComDemoApp.Web.DemoService" />
            </service>
        </services>
    </system.serviceModel>  
    
  10. Make sure that project builds and try to open DemoService.svc in browser browse_to_test_web_service
  11. Now we can get back to the Silverlight client app, that should consume the service we have just created. So lets add the service reference to the Silverlight project. add_service_reference_to_silverlight_app use the Discover button to use the service in our solution add_service_reference_to_silverlight_app_2
  12. Now open Page.xaml and replace the contents with the code snippet below
    using System.Windows;  
    using RouslanComDemoApp.DemoServiceReference;
    
    namespace RouslanComDemoApp  
    {
        public partial class Page
        {
            public Page()
            {
                InitializeComponent();
            }
    
            private void QueryServiceButton_Click(object sender, RoutedEventArgs e)
            {
                DemoServiceClient ws = new DemoServiceClient();
                ws.GetWindowsUsernameCompleted += ws_GetWindowsUsernameCompleted;
                ws.GetWindowsUsernameAsync();
            }
    
            void ws_GetWindowsUsernameCompleted(object sender, GetWindowsUsernameCompletedEventArgs e)
            {
                UserNamePlaceHolder.Text = e.Result;
            }
        }
    }
    //please note, the code above does not use error checking for clarity
    
  13. Build and run the application.
  14. You should now get nothing but the empty blank IE screen with little yellow warning sign in bottom left corner, which is there to say,  that the Silverlight could not be loaded. could_not_download_the_silverlight_application
  15. To fix this problem you have to tell IIS about Silverlight mime type. It does not know that files with .xap extension should be served with application/x-silverlight-app content type header. add_mime_type_to_iss
  16. Now if you run the application once again, you will be presented with one more obstacle. This time friendly and informative Visual Studio exception window tells you something about missing access policy. cross-domain_policy_is_missing So let’s have one in place. Create file clientaccesspolicy.xml and put in there the following XML. Please note this is a sample policy and should not be used as is in production environment!
    <?xml version="1.0" encoding="utf-8" ?>  
    <access-policy>  
        <cross-domain-access>
            <policy>
                <allow-from>
                    <domain uri="*"/>
                </allow-from>
                <grant-to>
                    <resource include-subpaths="true" path="/"/>
                </grant-to>
            </policy>
        </cross-domain-access>
    </access-policy>  
    
  17. Place the XML file in to web server root folder and start the client application again.
  18. Now the application should start and you should be able to see the cute minimalistic design of our client app. client_app_ready_to_roll
  19. Press the Query Service button and wait for breakpoint to trigger (here I assume you have set it up in advance) debugger_quickwatch_of_webservice_method
  20. Well done, we have working client consuming WCF web service using Windows Integrated Authentication! result_of_web_service_call

PS: In case you happen to host your web service in a web site located inside your "My Documents" folder, then you should update NTFS permissions for web site folder so authenticated users could access it.
allow_authenticated_users_to_access_folder_in_my_documents

Please leave feedback! Thank you.