Getting a Token from PingFederate using WIF

| | Comments (1) | TrackBacks (1)
If you want to get a security token from the STS that is included in Ping Identity's flagship product, PingFederate, using a client coded in Windows Identity Foundation (WIF), you'll have to tweak a few knobs.  It isn't hard, but it does require that you know which dials to turn and how.  This can be quite time consuming if you have to (re)learn a bunch about WCF bindings.

To figure all this out, I started by setting up PingFederate using the guide (included with the product).  Then, I installed the .NET-based WS-Trust client sample.  This sample is a WSE-3-based console application that sends an RST, parses the RSTR, and prints the security token included in that message.  Once I had that working, I was able to use a proxy to grab the messages sent between it and PingFederate.  I used these as my baseline.

Creating a similar console app in WIF is easy thanks to the WSTrustChannelFactory (which is the replacement for WSTrustClient from the betas).  The trick was the binding and getting the protocol versions to match what was used in the WSE app.  Here's the entire console app in hopes that it helps:

using System;

using System.Net;

using System.ServiceModel;

using System.ServiceModel.Channels;

using System.ServiceModel.Security;

using System.Text;

using Microsoft.IdentityModel.Protocols.WSTrust;

using Microsoft.IdentityModel.Protocols.WSTrust.Bindings;

 

namespace ConsoleApplication1

{

    class Program

    {

        private const string userName = "john";

        private const string password = "Password1";

        private const string appliesTo = "http://www.valid.com";

        private const string stsAddress = "https://computer:9031/idp/sts.wst";

 

        static void Main()

        {

            IgnoreCertificateValidation();

 

            var rstr = RequestSecurityToken();

 

            PrintRequestSecurityTokenResponse(rstr);

 

            Console.ReadLine();

        }

 

        private static void IgnoreCertificateValidation()

        {

            ServicePointManager.ServerCertificateValidationCallback = (sender,

                cert, chain, errors) => true;

        }

 

        private static void PrintRequestSecurityTokenResponse(

            RequestSecurityTokenResponse rstr)

        {

            Console.WriteLine("Security token issued by PingFederate:");

            Console.WriteLine(rstr.RequestedSecurityToken.SecurityTokenXml

                .InnerXml);

        }

 

        private static RequestSecurityTokenResponse RequestSecurityToken()

        {           

            var factory = GetChannelFactory();

            var rst = new RequestSecurityToken(WSTrustFeb2005Constants

                .RequestTypes.Issue)

            {

                AppliesTo = new EndpointAddress(appliesTo)

            };

            RequestSecurityTokenResponse rstr;

            var channel = factory.CreateChannel() as WSTrustChannel;       

            var token = channel.Issue(rst, out rstr);

 

            return rstr;

        }

 

        private static WSTrustChannelFactory GetChannelFactory()

        {

            var binding = GetBinding();

            var endpoint = new EndpointAddress(stsAddress);

            var factory = new WSTrustChannelFactory(binding, endpoint)

            {

                TrustVersion = TrustVersion.WSTrustFeb2005

            };

 

            factory.Credentials.UserName.UserName = userName;

            factory.Credentials.UserName.Password = password;

 

            return factory;

        }

 

        private static Binding GetBinding()

        {

            var binding = new CustomBinding();

 

            binding.Elements.AddRange(new BindingElement[]

            {

                SecurityBindingElement

                    .CreateUserNameOverTransportBindingElement(),

                new TextMessageEncodingBindingElement(MessageVersion

                    .Soap11WSAddressingAugust2004, Encoding.UTF8),

                new HttpsTransportBindingElement(),

            });

 

            return binding;

        }

    }

}