# Tuesday, 05 December 2006

This took me WAY longer to figure out that either a) it shoudl have or b) I had hoped it would.  I finally got it working last week though.  I now have my client calling the server via a Dual Contract using IssuedToken security (requires a SAML token), said token being obtained from an STS written by me, which takes a custom token from the client for authentication. 

On the plus side, I know a whole bunch more about the depths of the ServiceModel now than I did before. :-)

It turns out (as far as I can tell) that the binding I needed for client and server cannot be described in a .config file, and must be created in code.  That code looks like this on the server

 

public static Binding CreateServerBinding(Uri baseAddress, Uri issuerAddress)

{

 

    CustomBinding binding = new CustomBinding();

    IssuedSecurityTokenParameters issuedTokenParameters =

        new IssuedSecurityTokenParameters();

    issuedTokenParameters.IssuerAddress = new EndpointAddress(issuerAddress);

    issuedTokenParameters.IssuerBinding = CreateStsBinding();

    issuedTokenParameters.KeyType = SecurityKeyType.SymmetricKey;

    issuedTokenParameters.KeySize = 256;

    issuedTokenParameters.TokenType

        = "http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV1.1";

 

    SecurityBindingElement security

        = SecurityBindingElement.CreateIssuedTokenBindingElement(issuedTokenParameters);

    binding.Elements.Add(SecurityBindingElement.CreateSecureConversationBindingElement(security));

    binding.Elements.Add(new CompositeDuplexBindingElement());

    binding.Elements.Add(new OneWayBindingElement());

    binding.Elements.Add(new TextMessageEncodingBindingElement());

    binding.Elements.Add(new HttpTransportBindingElement());

 

    return binding;

}

and essentially the same on the client, with the addition of setting the clientBaseAddress on the CompositeDuplexBindingElement.

This works just fine, getting the token from the STS under the covers and then calling the Dual Contract server interface with the token. 

I ended up with a custom binding to the STS itself, mostly because I needed to pass more credentials than just user name and password.  So the STS binding gets created thusly:

 

public static Binding CreateStsBinding()

{

    Binding binding = null;

 

    SymmetricSecurityBindingElement messageSecurity

        = new SymmetricSecurityBindingElement();

 

    messageSecurity.EndpointSupportingTokenParameters.

     SignedEncrypted.Add(new VoyagerToken.VoyagerTokenParameters());

 

    X509SecurityTokenParameters x509ProtectionParameters

        = new X509SecurityTokenParameters( X509KeyIdentifierClauseType.Thumbprint);

    x509ProtectionParameters.InclusionMode = SecurityTokenInclusionMode.Never;

 

    messageSecurity.ProtectionTokenParameters = x509ProtectionParameters;

    HttpTransportBindingElement httpBinding = new HttpTransportBindingElement();

 

    binding = new CustomBinding(messageSecurity, httpBinding);

 

    return binding;         

}

Not as easy as I had hoped it would be, but it's working well now, so it's all good.  If you go the route of the custom token, it turns out there are all kinds of fun things you can do with token caching, etc.  It does require a fair amount of effort though, since there are 10-12 classes that you have to provide.

Tuesday, 05 December 2006 15:47:33 (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [0]  | 
# Wednesday, 15 November 2006

I've come a long way since the last issue, and I think I have most of that stuff worked out.  Now the problem is that what I really need is to be able to use the Dual Http binding, in conjunction with an STS to provide the SAML tokens.  I have it working fine (with Dual binding) as long as I issue the token myself and attach it using ClientCredentials.

The server side binding looks like this

 

        <behaviors>

            <serviceBehaviors>

                <behavior name="tokenBehavior">

                    <serviceCredentials>

                        <clientCertificate>

                            <certificate findValue="CN=VoyagerClient" />

                            <authentication certificateValidationMode="PeerOrChainTrust"

                                revocationMode="NoCheck" />

                        </clientCertificate>

                        <serviceCertificate findValue="CN=VoyagerServer" />

                        <issuedTokenAuthentication allowUntrustedRsaIssuers="true">

                            <knownCertificates>

                                <add findValue="CN=VoyagerSecureTokenService" storeLocation="LocalMachine"

                                    storeName="My" x509FindType="FindBySubjectDistinguishedName" />

                            </knownCertificates>

                        </issuedTokenAuthentication>

                    </serviceCredentials>

                </behavior>

            </serviceBehaviors>

        </behaviors>

        <bindings>

            <wsDualHttpBinding>

                <binding name="tokenBinding" clientBaseAddress="http://localhost:8897/client">

                    <security>

                        <message clientCredentialType="IssuedToken" />

                    </security>

                </binding>

            </wsDualHttpBinding>

        </bindings>

the client side binding like so

 

        <behaviors>

            <endpointBehaviors>

                <behavior name="tokenBehavior">

                    <clientCredentials>

                        <clientCertificate findValue="CN=VoyagerClient" storeLocation="LocalMachine" />

                        <serviceCertificate>

                            <defaultCertificate findValue="CN=VoyagerServer" storeLocation="LocalMachine" />

                            <authentication certificateValidationMode="PeerOrChainTrust"

                                revocationMode="NoCheck" />

                        </serviceCertificate>

                    </clientCredentials>

                </behavior>

            </endpointBehaviors>

        </behaviors>

        <bindings>

            <wsDualHttpBinding>

                <binding name="tokenBinding" clientBaseAddress="http://localhost:8897/client">

                    <security>

                     <message clientCredentialType="IssuedToken"/>

                    </security>

                </binding>

            </wsDualHttpBinding>

        </bindings>

 On the client side, I'm using the SamlClientCredentials from the SamlTokenProvider sample, and attaching it to the outgoing endpoint.

 

SamlClientCredentials cred = new SamlClientCredentials();

cred.ClientCertificate.SetCertificate(StoreLocation.LocalMachine, StoreName.My, X509FindType.FindBySubjectDistinguishedName, "CN=VoyagerClient");

 

cred.ServiceCertificate.SetDefaultCertificate(StoreLocation.LocalMachine, StoreName.My, X509FindType.FindBySubjectDistinguishedName, "CN=VoyagerSecureTokenService");

            cred.ServiceCertificate.Authentication.CertificateValidationMode = System.ServiceModel.Security.

     X509CertificateValidationMode.PeerOrChainTrust;

cred.ServiceCertificate.Authentication.RevocationMode = X509RevocationMode.NoCheck;

 

IList<Claim> claims = new List<Claim>();

claims.Add(Claim.CreateNameClaim("Fred"));

ClaimSet claimset = new DefaultClaimSet(claims);

cred.Claims = claimset;

 

CallBack callback = new CallBack();

InstanceContext ic = new InstanceContext(callback);

HelloWorldClient client = new HelloWorldClient(ic);

client.ChannelFactory.Endpoint.Behaviors.Remove(typeof(ClientCredentials));

client.ChannelFactory.Endpoint.Behaviors.Add(cred);

This all works like I would expect, and I get properly authorized on the server side.  The only catch here is that since I'm signing the SAML token with a cert I created myself, I have to set the certificateValidationMode for the issuedTokenAuthorization bit on the server side, which sadly isn't supported through the config file, so I have to tweak the ServiceHost myself.

 

myServiceHost = new ServiceHost(typeof(HelloService), baseAddress);

 

myServiceHost.Credentials.IssuedTokenAuthentication.

CertificateValidationMode =

X509CertificateValidationMode.PeerOrChainTrust;

 

myServiceHost.Credentials.IssuedTokenAuthentication.RevocationMode = X509RevocationMode.NoCheck;

 

 

myServiceHost.Open();

Not too much trouble, but unfortunate.  Means things will be a little more interesting when it's hosted in IIS. 

So, that all works, but what I really need is for it to ask an STS for the correct token rather than creating one myself. 

I was hoping that putting this in my clientCredentials on the client side would do it

 

<issuedToken>

   <localIssuer address="http://localhost:9001/sts" binding="wsHttpBinding" bindingConfiguration="" />

</issuedToken>

but it doesn't seem to. I'm assuming that's behavior that's built into the wsFederationHttpBinding, and not in wsDualHttpBinding. I'm hoping that I won't have to make the call to the STS myself, but as a last resort, that'll do...
Wednesday, 15 November 2006 14:34:20 (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [0]  | 
# Monday, 06 November 2006

I've been having a lot of trouble with Rhapsody ever since installing IE 7.  It got significantly better after the official release of IE 7, but I was still having the Rhapsody client crash 5-6 times a day, which is very frustrating.  Frustrating enough that I was considering just giving it up. 

The redemption came in the form of their new online version of the player, which runs hosted inside a browser.  It's less fully featured than the offline client, but it's been totally stable, runs well inside Firefox 2.0, and generally hasn't given me any trouble at all.  For now, I'm willing to forego the extra features for a player which really works.  It's even supposed to work in Linux, which means I might finally upgrade my kitchen PC from W2K to Ubuntu.  We can only hope.

You can run the online player by going to http://www.rhapsody.com.  I've had a lot of trouble navigating Rhapsody related stuff, since it's spread across www.rhapsody.com, www.listen.com, and various bits and pieces of www.real.com.  None of them seem to have the same information or links, so it can be hard to find what you are looking for.  I'm willing to accept this as the price I pay for reasonably prices access to tons of music.

Monday, 06 November 2006 13:19:27 (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [0]  | 
# Friday, 03 November 2006

I've started working on building an STS for use in some new product scenarios, and using the wsFederationHttpBinding, and I totally get how that works if the user is supposed to send their username and password to the STS to be authorized. 

 

I'm talking to the STS with a binding like so

 

      <customBinding>

        <binding name="UsernameBinding">

          <security authenticationMode="UserNameForCertificate" requireDerivedKeys="true" messageProtectionOrder="SignBeforeEncryptAndEncryptSignature" requireSecurityContextCancellation="false" requireSignatureConfirmation="false">

            <secureConversationBootstrap/>

          </security>

          <httpTransport/>

        </binding>

      </customBinding>

and in the client (in this case a web app) I set the credentials thusly

 

ChannelFactory<IHelloWorldChannel> factory =

new ChannelFactory<IHelloWorldChannel>("clientendpoint");

factory.Credentials.UserName.UserName = "MyUser";

factory.Credentials.UserName.Password = "MyPassword";

 

 

IHelloWorldChannel helloWorldService = factory.CreateChannel();

 

string response = helloWorldService.HelloWorld("John Doe");

That works great.  I validate the users credentials with a custom UserNamePasswordValidator, and everyone is happy.  Works just like it's supposed to. 

What I'd also like to be able to support is self-issued CardSpace cards.  I envision it working like this

Which should mean (again, as I envision it working) that my STS is configured like

 

      <wsHttpBinding>

        <binding name="stsInfoCard">

          <security mode="Message">

            <message clientCredentialType="IssuedToken" establishSecurityContext ="false"/>

          </security>

        </binding>

      </wsHttpBinding>

The part I don't get is what's the equivalent of setting the username and password on the ChannelFactory for CardSpace cards?  It seems like there should be some way of presenting the CardSpace token to the STS and having the rest of it work.

Is this just not possible, or am I missing something?  Is there another way to make this work?  I can get the CardSpace token just fine as far as the web server, but I don't know where to go from there.

Friday, 03 November 2006 15:27:57 (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [0]  | 
# Wednesday, 01 November 2006

My ADAM woes are largely over, and now I'm on to implementing an STS and getting some federated trust scenarios working.  Wish me luck, and I'll soon have more to report...

Wednesday, 01 November 2006 11:14:24 (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [0]  | 

I'll be teaching C# at the CAPITAL Center (Portland/Beaverton) campus of OIT next term (CST 346P), so if you or someone you love is looking to learn more about C#, run don't walk to OIT's web site to sign up.

I'm in the midst of co-teaching the first part of the senior project class right now, and having quite a bit of fun at it.  It's very interesting to see how people approach their senior projects, and fun to talk about all aspects of software development in just 10 weeks. :-)

Wednesday, 01 November 2006 11:13:15 (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [0]  | 
# Tuesday, 17 October 2006

After some minor setbacks, I’m finally getting going with ADAM, and amazed at how well it works (once you figure out how it’s supposed to work, which is a non-trivial undertaking).  One thing that makes it harder is that most of the (still scant) documentation around ADAM is geared toward IT Pros, not developers, so there are plenty of places where you track down the documentation for that critical change you need to make to your directory, only to be told “start the command line tool…”.  Frustrating, but workable. 

Once the magic incantations are prized forth, it seems very fast.  Granted we’re not doing anything tricky at this point, but adding users, setting passwords, simple queries, all seem pretty snappy. 

On my previous problems with ADAM and AzMan integration, this is apparently caused by the fact that I had my users in one application partition in ADAM, and my AzMan store in another partition in the same ADAM instance.  The official word is that this is not supported, which probably makes sense.  It means that I’ll probably end up using a separate ADAM instance as the AzMan store, or fall back to the XML file, although that’s harder to distribute.  Only time will tell.

Tuesday, 17 October 2006 10:32:08 (Pacific Daylight Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  | 

Over the weekend I took my kids for a hike up the Deschutes river from where it meets the Columbia, just east of the Dalles.  The weather was pretty nice, although it was overcast most of the day.  At least the rain held off until the middle of the night.  Check out pictures and a brief description at portlandhikers.com

This was the longest hike I’ve ever done with my kids, and as we’ve progressed toward longer hikes over the summer, I’ve learned some valuable lessons about hiking with children and how to make the trip more enjoyable for everyone.  I got a lot of great tips from Extreme Kids: How to Connect With Your Children Through Today's Extreme (and Not So Extreme) Outdoor Sports.  It’s a very well written book, that starts with some general tips about going outdoors with children, and then has some sport-specific information in the second half.  The tips I’ve gotten the most out of so far:

  • dress them for the part.  Hiking-specific gear like hydration packs, boots, and trekking poles make them feel like they are participating in something special, and really help get them out on the trail.
  • talk up the hike.  Take some time to talk up the hike.  Make it sound hard, question their ability to handle such a difficult task (not too seriously) and make it into a challenge.  This has made a huge difference.  My kids both boogied right up Little Belknap Crater after I played up the difficulty of “scaling a volcano”. 
  • keep them fed.  Keeping their blood sugar up is vital.  I’ve started packing not just granola/Clif bars, but some smaller snacks to keep them sugared up.  Generally we avoid giving them sugar, so this one took me a while to warm up to, but on last weekend’s 7.8 miler, it made a big difference.  They were tired, but the never crashed.  The new Jelly Belly “Sports Beans” work great for this.  They are basically jelly beans with electrolytes in them (like Gatorade) that come in 100 calorie packs.  The kids love them, and the feel like they are getting away with something. :-)  We also tried some Clif Shot Bloks, which proved popular.  They come in packs of 6, and were easy to dole out at key milestones.

These tips (and more from the book) have made our time together outside much more enjoyable.  I’m already looking forward to next season (and maybe some snowshoeing over the winter).

Tuesday, 17 October 2006 10:18:17 (Pacific Daylight Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  | 
# Wednesday, 11 October 2006

I'm here in Redmond at the patterns and practices Summit, and yesterday Don Smith had some very interesting things to say about versioning of web services.  He was polling the audience for opinions and scenarios, and I started to realize that I hadn't thought very hard about the problem.  Typically in cases where I've wanted to version a web services, I've left the existing endpoint in place (supporting the old interface) and added a new endpoint which supports the new interface.  I still think that's a pretty good solution.  What I realized, though, was that there's no way to communicate the information about the new interface programmatically.  It still pretty much requires a phone call. 

Don suggested something like RSS (or actually RSS) to push information about new versions out to clients.  Another suggestion was to have people actively "subscribe" to your service.  That would not only handle the "push" notification, but you'd know how many people were actually using your old vs. new interface, etc. 

I started thinking about something like a WS-VersioningPolicy.  We already have standards like WS-SecurityPolicy, which carry metadata about how you should secure your use of a given service.  Why not use a similar formal, programmatic method for distributing information about versioning policy.  You could convey information like methods that are deprecated, where to find the new versioned endpoint (if that's your strategy), or define up front policies about how you will version your interface without switching to a new endpoint. 

That still doesn't solve the "push" problem.  Clients would have to not only consume the policy file at the time they start using the service, but presumably they'd have to check it from time to time.  That suggests human intervention at some level.  Hmmm. 

This is a hard problem, but one that Don's pretty passionate about solving, so check out his blog for future developments in this space.

Wednesday, 11 October 2006 09:12:47 (Pacific Daylight Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  | 
# Friday, 06 October 2006

I'd been having some trouble (read: beating my head against the wall) trying to get passwords set/changed for users in ADAM early this week.  Unfortunately, many of the available samples are geared toward IT rather than development users, so tend to be in script, etc.  I've been working from the excellent book "The .NET Developer's Guide to Directory Services Programming" by Joe Kaplan and Ryan Dunn.  Sadly, I couldn't get their password changing samples to work, although aside from that the information in the book has been invaluable. 

I finally found what I needed in an obscure section of the documentation after many a googling.  This article gave me the key info that made this work.  I haven't tried all the permutations, but I think the key piece I was missing was the right combination of AuthenticationTypes flags

 

            // Set authentication flags.

            // For non-secure connection, use LDAP port and

            //  ADS_USE_SIGNING |

            //  ADS_USE_SEALING |

            //  ADS_SECURE_AUTHENTICATION

            // For secure connection, use SSL port and

            //  ADS_USE_SSL | ADS_SECURE_AUTHENTICATION

            AuthTypes = AuthenticationTypes.Signing |

                AuthenticationTypes.Sealing |

                AuthenticationTypes.Secure;

 

            // Bind to user object using LDAP port.

            try

            {

                objUser = new DirectoryEntry(

                    strPath, null, null, AuthTypes);

                objUser.RefreshCache();

            }

            catch (Exception e)

            {

                Console.WriteLine("Error:   Bind failed.");

                Console.WriteLine("        {0}.", e.Message);

                return;

            }

This sample worked just fine, and I was finally able to set or change passwords for ADAM users.  Once that's done, authenticating that user is as easy as

            DirectoryEntry de = new DirectoryEntry("LDAP://localhost:50000/RootDSE",

                "test.user",

                "passwordSuppliedByUser",

                AuthenticationTypes.None);

            de.RefreshCache();

If the supplied password is correct, it succeeds, else you get an exception. 

Now I can get on with the interesting parts...

Friday, 06 October 2006 10:42:25 (Pacific Daylight Time, UTC-07:00)  #    Disclaimer  |  Comments [1]  |