There seems to be a lot of confusion on the web about how to secure the backend WCF service of your Silverlight application, with a username and a password. In Silverlight 2, things were a bit tricky. The generated proxies of Visual Studio didn’t have a ClientCredentials property. So to send any custom username / password information to your service, you had to manipulate some headers. Another option was to let the browser do the authentication for you, in combination with asp.net and forms authentication, but that would leave your site open for CSRF attacks. This was the most convenient option in Silverlight 2. You can find a good tutorial about combining ASP.NET’s forms authentication with Silverlight here.

Luckily, in Silverlight 3 we have a third option and this is in my opinion the best way to secure WCF services with a username / password for Silverlight. I’m going to cover this option in this blog post.

The WCF Service

We’re going to use a simple WCF service, because the point of interest is of course the security part. This is the service interface:

   1: using System;

   2: using System.Collections.Generic;

   3: using System.Linq;

   4: using System.Runtime.Serialization;

   5: using System.ServiceModel;

   6: using System.Text;

   7:  

   8:  

   9: [ServiceContract]

  10: public interface IHelloSayer

  11: {

  12:     [OperationContract]

  13:     string SayHello();

  14: }

  15:  

 

And this is the implementing class:

   1: using System;

   2: using System.Collections.Generic;

   3: using System.Linq;

   4: using System.Runtime.Serialization;

   5: using System.ServiceModel;

   6: using System.Text;

   7: using System.Security.Permissions;

   8: using System.Security;

   9:  

  10:  

  11: public class HelloService : IHelloSayer

  12: {

  13:     

  14:     public string SayHello()

  15:     {

  16:         //block unauthenticated users, can't use the [principalpermission] attribute for this

  17:         if (!OperationContext.Current.ServiceSecurityContext.PrimaryIdentity.IsAuthenticated)

  18:         {

  19:             throw new SecurityException();

  20:         }

  21:         return "Hello: " + OperationContext.Current.ServiceSecurityContext.PrimaryIdentity.Name;

  22:     }

  23:     

  24: }

Note the part to block unauthenticated users, I can’t use the [PrincipalPermission] attribute for this because I haven’t configured any role based security and therefore the calling thread’s current principal is not set. If you have a lot of methods, you could of course write your own behavior for this.

WCF Configuration

What’s more important, is the configuration file that is going to enable the security for my service. Well, here it is:

   1: <system.serviceModel>

   2:     <services>

   3:         <service behaviorConfiguration="ServiceBehavior" name="HelloService">

   4:     <host>

   5:       <baseAddresses>

   6:         <add baseAddress="https://localhost"/>

   7:       </baseAddresses>

   8:     </host>

   9:         <endpoint address="" binding="basicHttpBinding" bindingConfiguration="test" contract="IHelloSayer">

  10:             <identity>

  11:                 <dns value="localhost"/>

  12:             </identity>

  13:         </endpoint>

  14:             <endpoint address="mex" binding="mexHttpsBinding" contract="IMetadataExchange"/>

  15:         </service>

  16:     </services>

  17: <bindings>

  18:   <basicHttpBinding>

  19:     <binding name="test">

  20:       <security mode="TransportWithMessageCredential">

  21:         <message clientCredentialType="UserName"/>

  22:       </security>

  23:     </binding>

  24:   </basicHttpBinding>

  25: </bindings>

  26:     <behaviors>

  27:         <serviceBehaviors>

  28:             <behavior name="ServiceBehavior">

  29:                 <serviceMetadata/>

  30:                 <serviceDebug includeExceptionDetailInFaults="false"/>

  31:                 <serviceCredentials>

  32:                     <userNameAuthentication userNamePasswordValidationMode="Custom"

  33:                        customUserNamePasswordValidatorType="MyValidator, AuthenticationLib"/>

  34:                     </serviceCredentials>

  35:             </behavior>

  36:         </serviceBehaviors>

  37:     </behaviors>

  38: </system.serviceModel>

 

The most of this should be familiar to WCF developers, so i will highlight only the options that have something to do with Silverlight and Security:

  • Line 9: I’m using the basicHttpBinding, this is of course because Silverlight only supports the basicHttpBinding. Note that I use a specific binding configuration named “test”.
  • Lines 18-24: This part specifies the “test” binding configuration. Note the security mode. Using Transport only would leave me no options for a custom username / password scenario; basic and digest both don’t play nice with WCF’s custom username / password mechanism. Ideal would be Message security, but the combination of Message security and basicHttpBinding doesn’t support a username / password scenario. That leaves us with one option that supports a WCF custom username / password scenario and can be used with a basicHttpBinding, namely TransportWithMessageCredential. This option means that the credentials are part of the SOAP message, but are stored in plain text. Because storing the credentials in plain text is unsecure, a secure connection is mandatory, for http this means SSL. So this option really is a combination of transport security (SSL) and credentials inside the message, hence the name TransportWithMessageCredential I guess :).
  • So far we have configured that we need the credentials to be a part of the message and that the transport of the message must be done using SSL. The only thing we still have to configure is what form of credentials the client must provide. That part is configured in line 21, the value “UserName” actually means “UserNameAndPassWord”, because a client can provide a username and a password.
  • Lines 31-34: This section inside a service behavior, specifies how WCF should handle incoming client credentials. Since our credentials take the form of a username / password combination, we use the “userNameAuthentication” element to configure that we want to authenticate the credentials our self. This is achieved by setting the “userNamePasswordValidationMode” to “Custom” and the “customUserNamePasswordValidatorType” to a type that can perform our custom validation. The format is {FullyQualifiedTypeName, AssemblyName}. Take a look at the “MyValidator” class:
   1: using System;

   2: using System.Collections.Generic;

   3: using System.Linq;

   4: using System.Web;

   5: using System.IdentityModel.Selectors;

   6: using System.Security;

   7: using System.IdentityModel.Tokens;

   8: using System.ServiceModel;

   9:  

  10: /// <summary>

  11: /// Custom username / password validator

  12: /// </summary>

  13: public class MyValidator : UserNamePasswordValidator

  14: {

  15:     public MyValidator()

  16:     {

  17:         

  18:     }

  19:  

  20:     public override void Validate(string userName, string password)

  21:     {

  22:         if (userName != "Alex" || password != "vanbeek")

  23:         {

  24:             throw new FaultException("Invalid username / password combination!");

  25:         }

  26:  

  27:     }

  28: }

This is a very simple example, but I might have used ADO.NET to check the credentials in a database or any other way to authenticate the credentials. A couple of rules for this class:

  • In order to be used as a custom username / password validator, it must be derived from the UserNamePasswordValidator class.
  • Perform authentication by overriding the Validate method.
  • If validation fails, throw a SecurityTokenException if you want a non informative message or a FaultException if you want a informative message. If validation succeeds, just do nothing.

 

Binary Message Encoding

More and more people are using the new Silverlight 3 binary message encoding and with good reason. So just for completeness, here is the binding part of the configuration file when using the new BinaryMessageEncoding for Silverlight 3:

   1: <customBinding>

   2:    <binding name="test">

   3:      <security authenticationMode="UserNameOverTransport"/>

   4:      <binaryMessageEncoding/>

   5:      <httpsTransport/>

   6:    </binding>

   7: </customBinding>

 

The Silverlight application

 Take a look at the code of the Silverlight application:

   1: using System;

   2: using System.Collections.Generic;

   3: using System.Linq;

   4: using System.Net;

   5: using System.Windows;

   6: using System.Windows.Controls;

   7: using System.Windows.Documents;

   8: using System.Windows.Input;

   9: using System.Windows.Media;

  10: using System.Windows.Media.Animation;

  11: using System.Windows.Shapes;

  12: using SecureSilverlightApp.security;

  13: using System.Net.Browser;

  14:  

  15: namespace SecureSilverlightApp

  16: {

  17:     public partial class MainPage : UserControl

  18:     {

  19:         public MainPage()

  20:         {

  21:             WebRequest.RegisterPrefix("https://", WebRequestCreator.ClientHttp);

  22:             InitializeComponent();

  23:             HelloSayerClient hsc = new HelloSayerClient();

  24:             hsc.ClientCredentials.UserName.UserName = "Alex";

  25:             hsc.ClientCredentials.UserName.Password = "test";

  26:             hsc.SayHelloCompleted += new EventHandler<SayHelloCompletedEventArgs>(hsc_SayHelloCompleted);

  27:             hsc.SayHelloAsync();

  28:         }

  29:  

  30:         void hsc_SayHelloCompleted(object sender, SayHelloCompletedEventArgs e)

  31:         {

  32:             if (e.Error != null)

  33:             {

  34:                 MessageBox.Show(e.Error.InnerException.Message);

  35:             }

  36:             else

  37:             {

  38:                 MessageBox.Show(e.Result);

  39:             }

  40:         }

  41:     }

  42: }

It’s a simple application that calls the service on startup. Important lines are:

  • Line 21: I’m using the new ClientHttpStack, to prevent the dreaded “NotFound” errors. This way, error messages from the service can flow through to my Silverlight application.
  • Lines 24-25: In Silverlight 3 the generated proxies support the new ClientCredentials property. You can use this to set the username and password. You have to set this only once and the credentials get send with every call you make on the proxy. The observative reader will notice that the provided password, is not allowed by my custom password validator. When running this application the message will be:

securityError

After changing the password property of the ClientCredentials property of the proxy to the allowed password “vanbeek”, the message will look like this:

message

 This shows that the client’s credentials flow neatly into the WCF service with every call. You can extend this example with declarative role based security by using WCF’s custom role based security mechanism.

37 thoughts on “Silverlight 3: Securing your WCF service with a custom username / password authentication mechanism

  1. Nice example on how to secure a silverlight app using custom username/password mechanism.

    However, what security does the binary message encoding provide? If I’m a rogue application I can still try to figure out the binary encoding and hack the application. In my opinion this is a case of security through obscurity.

  2. Hi Willem,

    The binary encoding itself doesn’t provide any security, just the encoding. In above example the security with the binaryEncoding comes from the
    element. This authenticationMode forces that the messages travel over a secure transport because the credentials are a unencrypted part of the messages, hence the
    element. A normal element isn’t allowed. So binaryEncoding itself, doesn’t provide security, but the other elements in the custom binding do.

  3. Hi Alex,

    I see and I’m glad Microsoft limited the use of that security setting to be used only with a https transport. This saves developers a lot of trouble and I’m pretty sure architects and reviewers will be pleased with this topo ;)

    In my opinion the binary message encoding provides other advantages that developers like. Mainly because it saves you precious time to get the request to the server and a response back to the silverlight client, because the messages send and received are a lot smaller.

  4. I Get the following error when i run my WCF service

    Could not load type ‘MyValidator’ from assembly ‘BasicSSLWcf’.

  5. Maybe you forgot to mark the MyValidator class as public? Also, try to put the MyValidator class in it’s own library project and see if that works…..

  6. Great article!
    I need to open an ASPX page from my Silverlight app in a new browser window. In the code behind page I have to check the username / password. Do you have any suggestions how to to share the username / password?

  7. When running your example from visual studio 2008, I get the following error messaage when I try to update my service reference:

    Could not find a base address that matches scheme https for the endpoint with binding CustomBinding. Registered base address schemes are [http].

    I also get this if I navigate to this service via the browser at:
    http://localhost:4061/Services/NewsService.svc/

    I get this if I try using the basicHttpBinding or the customBinding methods.

    Let me know if you have any ideas. Thanks!

  8. That’s because you need to configure IIS on your computer to support SSL. WCF requires a SSL connection when you use the specified security options, unless your IIS server supports this, you will get that excexption.

  9. Then you can’t use the example in the blog post. WCF requires the use of SSL with these security options, without SSL it wouldn’t be secure.

  10. Do you have the source code for this example ? Thanks ! Also, if I create a local certificate , it would be valid for WCF calls ?

  11. Sorry, no source code for this example (it’s been a while and i can’t find the solution anymore : s ). I guess a local certificate would work……

  12. Are the IsAutenthicated checks neccessary? It appears that the methods don’t even get called if the custom user validator faults.

  13. You’re right; in this case they aren’t necessary, but remember, the same service can be hosted multiple ways in WCF. For example, the service could also be exposed through TCP/IP on a whole different endpoint by a developer which doesn’t use the provided username / password validator. If this developer forgets to build in security, these checks make sure the service operations can’t be called.

  14. For the exception:
    ”Could not load type ‘MyValidator’ from assembly ‘BasicSSLWcf’.”

    , problem is the fully qualified name of the type. Namespace of the type is missing if it is in a seperate class lib.

    This works:

    customUserNamePasswordValidatorType=”ValidatorLib.MyValidator, ValidatorLib”

  15. Great aricle!

    What do I do with server side FaultException or SecurityTokenException?

    When I enter wrong credentials, server throws an unhandled excpetion, and SL client throws exception that i need. But what to do with the server side unhandled exception, where do I catch it?

  16. You don’t catch it :) WCF transforms the unhandled exception into a fault for Silverlight, it you catch the exception, this won’t happen.

  17. You Rock!!
    I was messing around with WCF/SSL/Creds forever. This did the trick. 10000 dials to set, this is a working combo.

    The code for checking if authentication has taken place in each method, can be omitted!

    Thank you!

  18. Hi Melle!,

    thanks for your kind words. I would’t omit the “if” check in each method call. If a developer decides to expose your service through another binding, which hasn’t got security enabled, these if checks prevent unauthorized users from calling those methods.

  19. Hi Alex,

    Instead of getting “Invalid username/password combination”, what i got is “The remote server returned an error: Not Found”.
    If i specify correct username/password, service will return expected result.

    FYI, i’m using .Net 4.0 & silverlight 4.

  20. Hi Alex.

    Instead of getting “Invalid username/password combination”, what i got is “The remote server returned an error: not found”.
    But when i specify correct username/password, the service will return expected result.

    I’m a newbie in this WCF things, any direction will be appreciated.
    FYI, i’m uses .Net 4 & silverlight 4 (visual studio 2010).

  21. Hi Bohebolo,
    Obviously the authentication works. I think you’re using the BrowserHTTP stack from Silverlight, all errors on the serverside will translate to a “Not Found”. Try using the ClientHTTP stack:

    WebRequest.RegisterPrefix(“https://”, WebRequestCreator.ClientHttp);

  22. This example will secure the backend from being accessed by anybody, but it will not encrypt messages to and from the backend. Silverlight still does not do encryption.

  23. Hi Alex, I’ve got an error:
    Could not find a base address that matches scheme https for the endpoint with binding CustomBinding. Registered base address schemes are [http].

    what can be wrong with config?