Search
Close this search box.

Implementing UserName Password & WS-Security with Silverlight

Download Sample Code

The Problem

Chances are that if you’ve done much Silverlight development you’ve worked with a few web services. In most cases, they are the primary channel for getting data in and out of your application.  Fortunately, the Silverlight runtime has a scaled down implementation of the client-side WCF framework that allows us to perform most simple web service operations fairly easy. However, it is important to note that when I say the WCF stack is scaled down, I mean it is really scaled down. Case in point, the only communication protocol we get in Silverlight is Basic Http.

It is true that Basic Http works just fine for many web services but one place where I’ve found it to be a major impediment is when using UserName/Password Message Security with WCF. This is because WCF uses the UsernameToken defined by WS-Security to pass your UserName and Password in message headers which requires functionality not present in our BasicHttp binding. Fortunately, WCF is an incredibly extensible framework, and this is true even of the mini-version we’re given in Silverlight. In this article I’ll show how we can plug into this extensibility and implement the missing pieces that allow us to send WS-Security UsernameTokens from our Silverlight web service proxy.

The Message Headers we need

Let’s start by taking a look at what our standard unsecured message looks like:

     <?xml version="1.0" encoding="utf-16"?>
     <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
       <s:Header>
         <Action s:mustUnderstand="1" xmlns="http://schemas.microsoft.com/ws/2005/05/addressing/none">http://tempuri.org/IService1/HelloWorld</Action>
       </s:Header>
       <s:Body>
         <HelloWorld xmlns="http://tempuri.org/" />
       </s:Body>
     </s:Envelope>

And now here is the secured version:

     <?xml version="1.0" encoding="utf-16"?>
     <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
       <s:Header>
         <Action s:mustUnderstand="1" xmlns="http://schemas.microsoft.com/ws/2005/05/addressing/none">http://tempuri.org/IService1/HelloWorld</Action>
         <Security xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
           <o:UsernameToken u:Id="8fb0111b-dda8-4073-a86a-dd48e3a79704" xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
             <o:Username>open</o:Username>
             <o:Password o:Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">sesame</o:Password>
           </o:UsernameToken>
        </Security>
      </s:Header>
      <s:Body>
        <HelloWorld xmlns="http://tempuri.org/" />
      </s:Body>
    </s:Envelope>


Notice the additional Security block that contains our UsernameToken.  You can also see that our UserName (open) and Password (sesame) are sent via clear text.  For this reason WCF requires that all Endpoints using UserNamePassword for Message Security run on a secure transport like Https.

Using a Custom Channel to add the Message Headers

Now that we’ve seen the differences between an unsecured message and a message with a WS UsernameToken our goal is fairly simple: To insert the token with our username and password into each of our messages before they are sent.  This can be achieved a number of ways but the cleanest approach I’ve found is to build a custom Channel that intercepts the message on it’s way down the Channel Stack and inserts the appropriate header.  If you’re unfamiliar with Channels and how they get stacked by WCF at runtime just think of the process like a series of postal workers each doing his bit of work before handing the message off to the next worker.  The last postal worker in the stack is always the one who does the delivery.  In WCF terms he’s known as a Transport Channel.

Let’s take a look at the meat of our UserNamePasswordSecurityChannel, then we’ll look at how we insert it into the ChannelStack:

public class UserNamePasswordSecurityChannel : ChannelBase, IRequestChannel {
  public IAsyncResult BeginRequest(Message message, TimeSpan timeout,
                                   AsyncCallback callback, object state) {
    if (SecurityContext.CurrentPrincipal != null) {
      UserIdentity identity =
          SecurityContext.CurrentPrincipal.Identity as UserIdentity;

      if (identity != null) {
        MessageHeader header = UserNameSecurityTokenHeader.FromUserNamePassword(
            identity.UserName, identity.Password);

        message.Headers.Add(header);
      }
    }

    return this.innerChannel.BeginRequest(message, timeout, callback, state);
  }

Notice that we’ve descended from ChannelBase and implemented IRequestChannel.  The BeginRequest method, which is part of the IRequestChannel interface, will be called for every message that we send and is our opportunity to inspect and modify it before it gets sent across the wire.  For this implementation I’ve created a static SecurityContext class which serves as a static storage location for the user’s credentials.  When the BeginRequest method is called these credentials are inspected and if the IIdentity is of type UserIdentity (a custom class) the credentials are extracted and used to build the UserNameToken security header.  This header is then added to the message and the message is then passed down into the Channel Stack where it will eventually be delivered by the Transport Channel.

Here is how the SecurityContext is set up:

UserIdentity identity = new UserIdentity("open", "sesame", true, DateTime.Now.AddMinutes(60), new List<string>());
SecurityContext.CurrentPrincipal = new UserPrincipal(identity);

And now to implement our Message Header we simply descend from the MessageHeader class and override the OnWriteHeaderContents method:

public class UserNameSecurityTokenHeader : MessageHeader {
  private const string NameValue = "Security";
  private const string UserNameParameterName = "userName";
  private const string PasswordParameterName = "password";

  private UserNameSecurityTokenHeader() {}

  public string UserName { get; private set; }

  private string Password { get; set; }

  protected override void OnWriteHeaderContents(
      System.Xml.XmlDictionaryWriter writer, MessageVersion messageVersion) {
    writer.WriteStartElement(Constants.Oasis.WsseNamespacePrefix,
                             Constants.UserNameToken.UserNameTokenElementName,
                             Constants.Oasis.WsseNamespace);

    writer.WriteAttributeString(Constants.Oasis.WsseUtilityNamespacePrefix,
                                Constants.UserNameToken.IdAttributeName,
                                Constants.Oasis.WsseUtilityNamespace,
                                Guid.NewGuid().ToString());

    writer.WriteElementString(Constants.Oasis.WsseNamespacePrefix,
                              Constants.UserNameToken.UserNameElementName,
                              Constants.Oasis.WsseNamespace, this.UserName);

    /***Write Password***/
    writer.WriteStartElement(Constants.Oasis.WsseNamespacePrefix,
                             Constants.UserNameToken.PasswordElementName,
                             Constants.Oasis.WsseNamespace);

    writer.WriteAttributeString(Constants.Oasis.WsseNamespacePrefix,
                                Constants.UserNameToken.TypeAttributeName, null,
                                Constants.Oasis.WsseUserNameTokenNamespace);

    writer.WriteString(this.Password);
    writer.WriteEndElement();
    /***End-Write Password***/

    writer.WriteEndElement();
  }

  public override string Name {
    get { return NameValue; }
  }

  public override string Namespace {
    get { return Constants.Oasis.WsseNamespace; }
  }

  internal static UserNameSecurityTokenHeader FromUserNamePassword(
      string userName, string password) {
    UserNameSecurityTokenHeader header = new UserNameSecurityTokenHeader();
    header.UserName = userName;
    header.Password = password;

    return header;
  }
}

Plugging Into the Binding

Now that we have a custom Channel that is able to insert our security headers we have to get it insert into the Channel Stack.  It is worth mentioning that this is where things start to get fairly hairy.  While I did mention earlier that WCF is incredibly extensible, and that this is a good thing, it comes at the price of complexity.  That said, before WCF will assemble our aforementioned Channel Stack it will first build up a stack of Channel Factories.  These factories will then be used to instantiate the Channels.  Here’s a look at our ChannelFactory:

public class UserNamePasswordSecurityChannelFactory
    : ChannelFactoryBase<IRequestChannel> {
  private IChannelFactory<IRequestChannel> innerChannelFactory;

  public UserNamePasswordSecurityChannelFactory(
      IChannelFactory<IRequestChannel> innerChannelFactory) {
    this.innerChannelFactory = innerChannelFactory;
  }

  protected override IRequestChannel OnCreateChannel(EndpointAddress to,
                                                     Uri via) {
    IRequestChannel innerChannel = innerChannelFactory.CreateChannel(to, via);
    UserNamePasswordSecurityChannel securityChannel =
        new UserNamePasswordSecurityChannel(this, innerChannel);

    return (IRequestChannel)securityChannel;
  }

Notice that we’re wrapping another Channel Factory and when OnCreateChannel is called we use that factory to create a Channel that our UserNamePasswordSecurityChannel will wrap.  We must have another channel to call because we’re not doing any actually delivery, so after we make our changes to the message we are delegating the work to the next Channel in the stack. 

To get the rest of the way we need a BindingElement and a Binding.  The BindingElement is used to create the ChannelFactory and the Binding is used to inject the BindingElement.

public class BasicHttpSecurityBindingElement : BindingElement {
  public override IChannelFactory<TChannel> BuildChannelFactory<TChannel>(
      BindingContext context) {
    if (context == null) {
      throw new ArgumentNullException(ContextParameterName);
    }
    if (!this.CanBuildChannelFactory<TChannel>(context)) {
      throw new InvalidOperationException(
          Resources.UnsupportedChannelTypeMessage);
    }

    UserNamePasswordSecurityChannelFactory factory =
        new UserNamePasswordSecurityChannelFactory(
            context.BuildInnerChannelFactory<IRequestChannel>());

    return (IChannelFactory<TChannel>)factory;
  }

Notice here that we’re given a BindingContext that we can use to create the Channel Factory that we will wrap.

public class BasicHttpSecurityBinding : BasicHttpBinding {
  private BasicHttpSecurityBindingElement bindingElement;

  public BasicHttpSecurityBinding() {
    this.bindingElement = new BasicHttpSecurityBindingElement();
  }

  public override BindingElementCollection CreateBindingElements() {
    BindingElementCollection bindingElements = base.CreateBindingElements();
    bindingElements.Insert(bindingElements.Count - 1, bindingElement);

    return bindingElements;
  }
}

What’s interesting about our BasicHttpSecurityBinding is that we are descending from BasicHttpBinding.  That is because we are still running under Basic Http, the only difference is that when CreateBindingElements() is called we will delegate that call to the base class, then discretely slip our custom binding element into the BindingElementCollection and then pass it on.  Later the runtime will use that collection of BindingElements to instantiate the ChannelFactories which will in turn be used to instantiate the Channels.  Simple enough?  Right?  🙂

Creating the Proxy

Much of what we’ve looked at so far resembles the extensibility mechanisms provided in the full WCF implementation.  However, here is where things deviate a bit and the reason is that we do not have the ability to create custom Bindings in our configuration file in Silverlight.  Instead, we are forced to create our proxy in code which is admittedly far from ideal.  However, this can be wrapped up into a nice factory that does all of the work for us.  In this example I’ve created a ProxyFactory class that will take the Proxy interface and the concrete type that were generated by the “Add Service Reference” operation and instantiate the proxy using a constructor that allows us to pass in our BasicHttpSecurityBinding as an argument.  Here is what the proxy instantiation looks like:

string uri = "https://localhost/SecureSilverlight/Service1.svc";
Service1Client client = ProxyFactory.CreateProxy<Service1Client, IService1>(uri);

The code inside of CreateProxy uses a bit of Reflection to pop off the right Constructor.  There may be a better way to do this.  I’d especially be interested to see if a Dependency Injection (DI) container like Unity could clean this up.

public static Proxy CreateProxy<Proxy, Contract>(string uri)
    where Proxy : ClientBase<Contract>, new()
    where Contract : class {
  if (string.IsNullOrEmpty(uri)) {
    throw new ArgumentNullException(UriParameterName);
  }

  EndpointAddress endpointAddress = new EndpointAddress(uri);
  BasicHttpSecurityBinding binding = new BasicHttpSecurityBinding();
  binding.Security.Mode = System.ServiceModel.BasicHttpSecurityMode.Transport;

  ConstructorInfo proxyConstructor =
      typeof(Proxy).GetConstructor(RequiredConstructorParameterTypes);
  object[] constructorParameters = new object[] { binding, endpointAddress };
  Proxy proxy = proxyConstructor.Invoke(constructorParameters) as Proxy;

  return proxy;
}

Making the Service Call

And finally, here is what the code to call our web service with UserName/Password Message Security looks like.  Note that once the proxy has been instantiated it can be used as if it were no different than the normal empty constructor instantiation, all of the details are hidden.

// Setup the security context
UserIdentity identity = new UserIdentity(
    "open", "sesame", true, DateTime.Now.AddMinutes(60), new List<string>());
SecurityContext.CurrentPrincipal = new UserPrincipal(identity);

// Create the proxy
string uri = "https://localhost/SecureSilverlight/Service1.svc";
Service1Client client =
    ProxyFactory.CreateProxy<Service1Client, IService1>(uri);

// Make service call
client.HelloWorldCompleted +=
    new EventHandler<HelloWorldCompletedEventArgs>(client_HelloWorldCompleted);
client.HelloWorldAsync();
This article is part of the GWB Archives. Original Author: Kevin Rohling

Related Posts