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:
After changing the password property of the ClientCredentials property of the proxy to the allowed password “vanbeek”, the message will look like this:
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.
9 comments
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.
willemm
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.
Alex van Beek
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.
willemm
Thanks, that’s a realy nice example…
Calabonga
Thanks Calabonga………
Alex van Beek
I Get the following error when i run my WCF service
Could not load type ‘MyValidator’ from assembly ‘BasicSSLWcf’.
Sid
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…..
Alex van Beek
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?
Michel Metselaar
Thank you!
This is real nice posts
Silverlight Hosting