Geeks With Blogs

@sundriedcoder
  • sundriedcoder Headed to gym for a run, good god it's gray today! about 1272 days ago
  • sundriedcoder Getting ready to start work on a @heroku worker process, looking forward of taking of advantage of the new functionality in Celadon Cedar about 1274 days ago
  • sundriedcoder @craigkerstiens just saw your review for the TrackerBot app (Pivotal) in the iPad app store, that meant I had to buy it about 1290 days ago
  • sundriedcoder There's definitely a "cloud" pun in that somewhere about 1300 days ago
  • sundriedcoder Just pushed a Click Once deployment to AWS S3 from 35,000 feet. I love the future :) about 1300 days ago
  • sundriedcoder Those GNC sales people no how to push their product, felt like I was at a car dealership this morning! about 1303 days ago

Kevin Rohling Building great s0ftware, 1 line at a time.

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:

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

 

And now here is the secured version:

   1:  <?xml version="1.0" encoding="utf-16"?>
   2:  <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
   3:    <s:Header>
   4:      <Action s:mustUnderstand="1" xmlns="http://schemas.microsoft.com/ws/2005/05/addressing/none">http://tempuri.org/IService1/HelloWorld</Action>
   5:      <Security xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
   6:        <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">
   7:          <o:Username>open</o:Username>
   8:          <o:Password o:Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">sesame</o:Password>
   9:        </o:UsernameToken>
  10:      </Security>
  11:    </s:Header>
  12:    <s:Body>
  13:      <HelloWorld xmlns="http://tempuri.org/" />
  14:    </s:Body>
  15:  </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:

   1:  public class UserNameSecurityTokenHeader : MessageHeader
   2:      {
   3:          private const string NameValue = "Security";
   4:          private const string UserNameParameterName = "userName";
   5:          private const string PasswordParameterName = "password";
   6:   
   7:          private UserNameSecurityTokenHeader() { }
   8:   
   9:          public string UserName
  10:          {
  11:              get;
  12:              private set;
  13:          }
  14:   
  15:          private string Password
  16:          {
  17:              get;
  18:              set;
  19:          }
  20:   
  21:          protected override void OnWriteHeaderContents(System.Xml.XmlDictionaryWriter writer, MessageVersion messageVersion)
  22:          {
  23:              writer.WriteStartElement(Constants.Oasis.WsseNamespacePrefix, 
  24:                                       Constants.UserNameToken.UserNameTokenElementName, 
  25:                                       Constants.Oasis.WsseNamespace);
  26:   
  27:              writer.WriteAttributeString(Constants.Oasis.WsseUtilityNamespacePrefix, 
  28:                                          Constants.UserNameToken.IdAttributeName, 
  29:                                          Constants.Oasis.WsseUtilityNamespace, 
  30:                                          Guid.NewGuid().ToString());
  31:   
  32:              writer.WriteElementString(Constants.Oasis.WsseNamespacePrefix, 
  33:                                        Constants.UserNameToken.UserNameElementName, 
  34:                                        Constants.Oasis.WsseNamespace, 
  35:                                        this.UserName);
  36:   
  37:              /***Write Password***/
  38:              writer.WriteStartElement(Constants.Oasis.WsseNamespacePrefix,
  39:                                           Constants.UserNameToken.PasswordElementName,
  40:                                           Constants.Oasis.WsseNamespace);
  41:   
  42:              writer.WriteAttributeString(Constants.Oasis.WsseNamespacePrefix,
  43:                                          Constants.UserNameToken.TypeAttributeName,
  44:                                          null,
  45:                                          Constants.Oasis.WsseUserNameTokenNamespace);
  46:   
  47:              writer.WriteString(this.Password);
  48:              writer.WriteEndElement();
  49:              /***End-Write Password***/
  50:   
  51:   
  52:              writer.WriteEndElement();
  53:          }
  54:   
  55:          public override string Name
  56:          {
  57:              get { return NameValue; }
  58:          }
  59:   
  60:          public override string Namespace
  61:          {
  62:              get { return Constants.Oasis.WsseNamespace; }
  63:          }
  64:   
  65:          internal static UserNameSecurityTokenHeader FromUserNamePassword(string userName, string password)
  66:          {
  67:              UserNameSecurityTokenHeader header = new UserNameSecurityTokenHeader();
  68:              header.UserName = userName;
  69:              header.Password = password;
  70:   
  71:              return header;
  72:          }
  73:      }

 

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.

 

   1:      public class BasicHttpSecurityBinding : BasicHttpBinding
   2:      {
   3:          private BasicHttpSecurityBindingElement bindingElement;
   4:   
   5:          public BasicHttpSecurityBinding()
   6:          {
   7:              this.bindingElement = new BasicHttpSecurityBindingElement();
   8:          }
   9:   
  10:          public override BindingElementCollection CreateBindingElements()
  11:          {
  12:              BindingElementCollection bindingElements = base.CreateBindingElements();
  13:              bindingElements.Insert(bindingElements.Count - 1, bindingElement);
  14:   
  15:              return bindingElements;
  16:          }
  17:      }

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.

   1:  //Setup the security context
   2:  UserIdentity identity = new UserIdentity("open", "sesame", true, DateTime.Now.AddMinutes(60), new List<string>());
   3:  SecurityContext.CurrentPrincipal = new UserPrincipal(identity);
   4:              
   5:  //Create the proxy
   6:  string uri = "https://localhost/SecureSilverlight/Service1.svc";
   7:  Service1Client client = ProxyFactory.CreateProxy<Service1Client, IService1>(uri);
   8:   
   9:  //Make service call
  10:  client.HelloWorldCompleted += new EventHandler<HelloWorldCompletedEventArgs>(client_HelloWorldCompleted);
  11:  client.HelloWorldAsync();
Posted on Sunday, March 15, 2009 5:17 PM | Back to top


Comments on this post: Implementing UserName Password & WS-Security with Silverlight

# re: UserName Password & WS-Security with Silverlight
Requesting Gravatar...
Perfect, exactly what I needed! Works great, Thanks!
Left by mob on Apr 24, 2009 6:17 PM

# re: Implementing UserName Password & WS-Security with Silverlight
Requesting Gravatar...
Great! Thanks!
Left by Vasily on May 13, 2009 2:26 AM

# re: Implementing UserName Password & WS-Security with Silverlight
Requesting Gravatar...
Thanks for sharing this example with good explanation. When I am trying to run the example I am getting the following exception even if I added clientaccesspolicy.xml and crossdomain.xml files to the root of my services. Please suggest.


System.ServiceModel.CommunicationException was unhandled by user code
Message="An error occurred while trying to make a request to URI 'https://localhost/SecureSilverlight/Service1.svc'. This could be due to attempting to access a service in a cross-domain way without a proper cross-domain policy in place, or a policy that is unsuitable for SOAP services. You may need to contact the owner of the service to publish a cross-domain policy file and to ensure it allows SOAP-related HTTP headers to be sent. Please see the inner exception for more details."
StackTrace:
at System.ServiceModel.AsyncResult.End[TAsyncResult](IAsyncResult result)
at System.ServiceModel.Channels.ServiceChannel.SendAsyncResult.End(SendAsyncResult result)
at System.ServiceModel.Channels.ServiceChannel.EndCall(String action, Object[] outs, IAsyncResult result)
at System.ServiceModel.ClientBase`1.ChannelBase`1.EndInvoke(String methodName, Object[] args, IAsyncResult result)
at SecureSilverlight.MyService.Service1Client.Service1ClientChannel.EndHelloWorld(IAsyncResult result)
at SecureSilverlight.MyService.Service1Client.SecureSilverlight.MyService.IService1.EndHelloWorld(IAsyncResult result)
at SecureSilverlight.MyService.Service1Client.OnEndHelloWorld(IAsyncResult result)
at System.ServiceModel.ClientBase`1.OnAsyncCallCompleted(IAsyncResult result)
InnerException: System.Security.SecurityException
Message=""
StackTrace:
at System.Net.AsyncHelper.BeginOnUI(SendOrPostCallback beginMethod, Object state)
at System.Net.BrowserHttpWebRequest.EndGetResponse(IAsyncResult asyncResult)
at System.ServiceModel.Channels.HttpChannelFactory.HttpRequestChannel.HttpChannelAsyncRequest.CompleteGetResponse(IAsyncResult result)
InnerException: System.Security.SecurityException
Message="Security error."
StackTrace:
at System.Net.BrowserHttpWebRequest.InternalEndGetResponse(IAsyncResult asyncResult)
at System.Net.BrowserHttpWebRequest.<>c__DisplayClass5.<EndGetResponse>b__4(Object sendState)
at System.Net.AsyncHelper.<>c__DisplayClass2.<BeginOnUI>b__0(Object sendState)
InnerException:


Left by Venk on Jun 08, 2009 5:06 AM

# re: Implementing UserName Password & WS-Security with Silverlight
Requesting Gravatar...
Kevin,
Thanks for posting this, looks exactly like what we want. Are you actually using this code in production? Any problems you've encountered over the last few months?

Sam
Left by Samuel Jack on Jun 10, 2009 4:29 AM

# re: Implementing UserName Password & WS-Security with Silverlight
Requesting Gravatar...
Always good to hear from you Sam! Yes, I am using this in production with no problems to speak of. I have even adapted the client side library to work on Windows Mobile as there is no support for UserName/Password security there either. So far so good!
Left by Kevin Rohling on Jun 10, 2009 2:28 PM

# re: Implementing UserName Password & WS-Security with Silverlight
Requesting Gravatar...
Venk,
I'm sorry to hear you are having problems. This was a complex demo to put together and not exactly "download and run" style as I would have liked. First questions:
--Have you deployed the services to your local IIS? If so the next step would be to make sure your crossdomain and clientaccesspolicy are in place which you state you have done.
--Also, have you setup your machine to support HTTPS? The certificate you are using must be trusted by your local machine or Silverlight won't talk to it. A good way to test this is go to https://localhost if you see any certificate warnings you need to fix something.

Indeed, Silverlight + HTTPS is kind of a pain to setup. Hope this helps.
Left by Kevin Rohling on Jun 10, 2009 2:34 PM

# re: Implementing UserName Password & WS-Security with Silverlight
Requesting Gravatar...
I just noticed that TransportWithMessageCredential is already one of the options for the SecurityMode and that we have ClientCredentials and UserNameClientCredentials in there. I'm not sure if this is a half baked implementation (as many others in Silverlight) and I can't find anything about except for a post that they mention that this is something new in SL3....
The problem is that I can't find a way to set those credentials. In .NET we could do that using behaviors at the endpoint level, but a quick look through reflector shows that's not possible in Silverlight.

Have you looked at this?
Left by Miguel Madero on Dec 30, 2009 9:45 AM

# re: Implementing UserName Password & WS-Security with Silverlight
Requesting Gravatar...
Miguel,
You may have noticed this post was in reference to SL 2, I should really date these by specifying the version number int he title... In any case, I have not attempted using the ClientCredentials support in SL3 and would be interested in seeing what you uncover. In the meantime if I happen to com across more information i will surely post it here. Have a great new year!
Left by Kevin Rohling on Dec 30, 2009 11:27 PM

# re: Implementing UserName Password & WS-Security with Silverlight
Requesting Gravatar...
Kevin,
In Silverlight 3 the class is there, but I didn't have a way to set the credentials (or probably I didn't try hard enough). In SL4 it works fine, but it's a pain to work with it on dev machines and due to other requirements we ended up going with FormsAuthentication.
Left by Miguel Madero on Jan 10, 2010 2:48 AM

# re: Implementing UserName Password & WS-Security with Silverlight
Requesting Gravatar...
Thanks for this article. I tried to download the source code and it is not available for download. Could you email it to me? I am trying to implement this to see if it will help me with calling a JAVA web service on apache that requires a security message header. Thanks.
Left by Cameron on Jan 28, 2010 3:32 PM

# re: Implementing UserName Password & WS-Security with Silverlight
Requesting Gravatar...
Cameron, please give it another shot. My file server had some DNS issues but all is looking well now.
Left by Kevin Rohling on Jan 29, 2010 10:16 AM

# re: Implementing UserName Password & WS-Security with Silverlight
Requesting Gravatar...
Any idea what are the changes required to use ClientCredentials in SL3?
Left by Vamsi on Mar 13, 2010 10:43 AM

# re: Implementing UserName Password & WS-Security with Silverlight
Requesting Gravatar...
It doesn't work because MyUserNamePasswordValidator.Validate method never occurs. Do you know some solution how to resolve this problem? Thank you.
Left by Peter on May 28, 2010 4:52 AM

# re: Implementing UserName Password & WS-Security with Silverlight
Requesting Gravatar...
Hi
The way which you have given is good,
This code part where we need to write ,
please give me part where and all we need to write

ThankS
Chithirai
Left by chithiravelu on Oct 26, 2010 6:53 AM

# re: Implementing UserName Password & WS-Security with Silverlight
Requesting Gravatar...
Can you please post or send the code?
The download link does not provide the code any more.
Left by Where is the code? on Dec 07, 2010 5:39 PM

# re: Implementing UserName Password & WS-Security with Silverlight
Requesting Gravatar...
NO download :(
Left by steffen on Apr 07, 2011 8:02 AM

# re: Implementing UserName Password & WS-Security with Silverlight
Requesting Gravatar...
this shit doesnt download
Left by malik on Feb 24, 2012 12:00 PM

# re: Implementing UserName Password & WS-Security with Silverlight
Requesting Gravatar...
Download link does not work :(
Please fix it
Left by Nuker on Jan 04, 2013 2:44 PM

Your comment:
 (will show your gravatar)


Copyright © SundriedCoder | Powered by: GeeksWithBlogs.net | Join free