woensdag 31 juli 2013

Call WCF service with ADFS token

How can we configure a WCF client to call an ADFS-secured WCF service? In this blog I'll show you how to do it with code only, no xml-configuration needed.

The are only two steps to take:

1. Get a securityToken from ADFS
2. Create a WCF channel to the WCF service, using the securityToken.



Create the WCF client

To get started, let's create a console application that will be the client:

internal class Program
{
   private static void Main()
   {
      var stsEndpoint =
            "https://myAdfsServer/adfs/services/trust/13/certificatemixed";
      var clientCertificateThumbprint = "[put the thumbprint here]";
      var svcEndpoint = "https://myDomain/myService.svc";

      var token = GetToken(stsEndpoint, svcEndpoint);

      var channel = CreateChannel<IMyService>(token,
                                              svcEndpoint,
                                              clientCertificateThumbprint);

      channel.Foo();

   }
}


How to get a secured token from ADFS?

The GetToken() method looks like this:

private static SecurityToken GetToken(string stsEndpoint,
                                      string svcEndpoint,
                                      string thumbprint)
{
   var binding = new
       CertificateWSTrustBinding(SecurityMode.TransportWithMessageCredential);
   
   var factory = new WSTrustChannelFactory(bindingstsEndpoint)
                            {
                               TrustVersion = TrustVersion.WSTrust13
                            };

    factory.Credentials.ClientCertificate.SetCertificate(
                                                StoreLocation.LocalMachine,
                                                StoreName.My,
                                                X509FindType.FindByThumbprint,
                                                thumbprint);

   var rst = new RequestSecurityToken

                      {
                         RequestType = WSTrust13Constants.RequestTypes.Issue,
                         AppliesTo = new EndpointAddress(svcEndpoint),
                         KeyType = WSTrust13Constants.KeyTypes.Symmetric
                      };

   var channel = factory.CreateChannel();


   var token = channel.Issue(rst);


   return token;

}

Please note that I'm using a client certificate, and not Username/Password to authenticate.


With the token, the secured channel can be created, like this:

private static T CreateChannel<T>(SecurityToken token, string svcEndpoint) where T : class
{
   var binding = new WS2007FederationHttpBinding(
                   WSFederationHttpSecurityMode.TransportWithMessageCredential
                                                                      );
   binding.Security.Message.EstablishSecurityContext = true;
   binding.Security.Message.IssuedKeyType = SecurityKeyType.SymmetricKey;

   var factory = new ChannelFactory<T>(binding, svcEndpoint);
   factory.ConfigureChannelFactory();

   // Turn Cardspace off
   factory.Credentials.SupportInteractive = false;

   var channel = factory.CreateChannelWithIssuedToken(token);
            
   return channel;
}