Yossi Dahan [BizTalk]

Google
 

Tuesday, December 16, 2008

Never thought Url would be case sensitive

 

Working on my Geneva Framework based STS scenario I’ve stumbled into a very weird and annoying case where by if the user typed a Url in the wrong case (compared to the case of the V-Dir) the browser would enter a circular redirect between the STS and the RP.

 

I’ve started a forum thread, which you can find here, that got an answered by Peter Kron from MS through which I’ve learnt that the path portion of a cookie is case sensitive; you can find this in this RFC spec as well (read 3.3.3) -

…the old  and new Domain attribute values compare equal, using a case-insensitive string-compare; and, the old and new Path attribute values string-compare equal (case-sensitive). …

I don’t know if that’s just me, but I find this really surprising as, as a web user, I was never “trained” to tread urls as case sensitive, but it appears that, according to the spec, any personalisation stored for a particular path might be lost if I enter the wrong url?

In the STS scenario case this would mean potentially me having to login again, although I have already logged in on the STS.

 

Peter suggest to store the cookie against the domain, which is not case sensitive, and is good enough for me (for now?), but I don’t know if that’s realistic for all scenarios…..

Labels: , ,

Thursday, December 04, 2008

Geneva-based passive STS and .net 2.0 web applications

Over the last few weeks I’ve been working on implementing a Geneva Framework based STS that supports both active and passive scenarios.

This is progressing very well and I already have a fairly solid PoC running for both scenarios.

Generally, to make any web site participate in the federated identity “dance” all it takes is some configuration on the application’s web.config (separate post coming shortly), but up until today I have only done so for web applications developed as a .net 3.5 project.

Today I have created a bog standard .net 2.0 web application project (on VS 2008, though, not that it should matter) and applied exactly the same configuration as I did on the other web sites.

when I ran this, not so surprisingly, everything just worked. isn’t it great?!

 

Of course – considering that everything is implemented as HttpModules – this should not be very surprising, but still.

It is important to remember, though, that .net 3.5, as well as the Geneva Framework, must be installed on the machine running the web site for this to work of course.

Labels: ,

Tuesday, November 25, 2008

Configuring the Geneva Framework based STS to work with custom UserNamePasswordValidator

It took me a little while (and quite a bit of help from others on this thread) to get to a relatively simple implementation, so I thought I’d summarise the steps I’ve taken –

At the risk of sounding the obvious I would definitely recommend making sure the overall STS scenario works well using windows authentication before changing it to support custom authentication.

 

Once that’s done change the STS’ bindings’ clientCredentialType to UserName and the establishSecurityContext to false.

      <ws2007HttpBinding>

        <binding name="UserNameAuthentication">

          <security mode="Message">

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

          </security>

        </binding>

      </ws2007HttpBinding>

The equivalent changes need to be made on the clinet’s binding going to the STS. these may not be obvious at first glance – on the client you will have the endpoint representing the RP and using ws2007FederationHttpBinding; inside this binding’s configuration you will find the issuer element, which is somewhat similar to an endpoint; this represents the STS’ endpoint and as such has a binding (ws2007HttpBinding) and a binding configuration; it is in that binding’s configuration that you need to change the credential type. setting it on the wrong bindings, as I did initially would send you back a couple of hours :-)

Next, as we’re using username authentication for the STS, a service certificate must be used so that the credentials can be encrypted. This is done through configuration of a service behaviour on the STS service as such:

    <behaviors>

      <serviceBehaviors>

        <behavior name="STSBehaviour">

          <serviceCredentials>

            <serviceCertificate findValue="STS" storeLocation="LocalMachine" storeName="My" x509FindType="FindBySubjectName"/>

          </serviceCredentials>

          <serviceMetadata httpGetEnabled="true"/>

          <serviceDebug includeExceptionDetailInFaults="true"/> <!—use for debug only -->

        </behavior>

      </serviceBehaviors>

(don’t forget to wire the behaviour to the service...)

As the test certificate I’m using are not valid, I needed to disable validation on the client side; I could not find a way to do this through configuration, as at the client there isn’t an endpoint as such for the STS service, just the issuer element in the ws2007FederationHttpBinding, so I’ve done this in the client code (this is a temporary measure for development only!) –

            proxy.ClientCredentials.ServiceCertificate.Authentication.CertificateValidationMode = X509CertificateValidationMode.None;

            proxy.ClientCredentials.ServiceCertificate.Authentication.RevocationMode = X509RevocationMode.NoCheck;

 

The current version of the Geneva Framework, unlike Zermatt before it, does not support the userNameAuthentication element in the serviceCredetial service behaviour. (to be accurate you can kind of force it to do so, but that’s planned to be blocked in the near future, so for all intents and purposes you should not include this element, see more information in the thread mentioned above)

In order to implement authentication a customer SecurityTokenHandler needs to be added; to do so created a class that inherits from WindowsUserNameSecurityTokenHandler and overridden the ValidateToken method; again – several samples of such implementation exist on the thread but the idea is to validate the username and password (made available through the SecurityToken parameter to the method) in whatever way you wish and then, ideally, add some claims to the ClaimsIdentityCollection output; this should generally include the identity, authentication method and authentication instant, but you can add whatever you wish.

To wire the custom handler to the STS service a bit more configuration is required on the STS side -

  <microsoft.identityModel>

    <securityTokenHandlers>

      <remove type="Microsoft.IdentityModel.Tokens.WindowsUserNameSecurityTokenHandler, Microsoft.IdentityModel,Version=0.5.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>

      <add type="MyUserNameSecurityTokenHandler, MyUserNameSecurityTokenHandlerAsembly"/>

    </securityTokenHandlers>

  </microsoft.identityModel>

This replaces the built-in WindowsUserNameSecurityTokenHandelr with my class that inherits from WindowsUserNameSecurityTokenHandler and adds custom implementation.

Note: I needed to add the definition of this section as such –

<section name="microsoft.identityModel" type="Microsoft.IdentityModel.Configuration.MicrosoftIdentityModelSection, Microsoft.IdentityModel,Version=0.5.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>

I hope that makes sense….

Labels: ,

Thursday, November 06, 2008

From "Zermatt" to the "Geneva Framework" part II

A couple of days ago I've posted about the changes I've had to make to allow my custom STS to work with the updated Geneva framework. there's one more, quite crucial, change that I had to make, which I will try to describe next -

If my understanding is correct (and unfortunately there's all the chances in the world that it is not, so if you know otherwise please do comment) the October Geneva SDK has tightened security a little bit around token validation.

I believe that the previous version of SDK, the RP simply made sure that a token was included with the request, and that this token was signed by a party whose certificate exists on the server (and is accessible); the RP did not check which certificate was used to sign the token.

 

As far as I can tell the Geneva Framework SDK now behaves differently - if you execute the same code and configuration you had before (baring necessary changes to allow the code to compile on the new version, but these are mostly name changes) you will get the following error from the RP:

 

"An unsecured or incorrectly secured fault was received from the other party. See
the inner FaultException for the fault code and detail."

 

Basically the Client gets a token from the STS and attaches it to the request but the RP does not recognise the issuer of the token; in order to instruct the RP to accept tokens signed by a particular STS you need to provide it with a list of issuers you accetps, this can be done using the following configuration for example -

 

<microsoft.identityModel>
  <issuerNameRegistry type="Microsoft.IdentityModel.Tokens.ConfigurationBasedIssuerNameRegistry, Microsoft.IdentityModel, Version=0.5.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35">
    <trustedIssuers>
      <add name="STS" thumbprint="7a0671d475673c1ab131ca1c0c804e4fbd385140"/>
    </trustedIssuers>
  </issuerNameRegistry>
</microsoft.identityModel>

 

This bit of configuration lists all the certificates that are acceptable as STS token signing.

 

It is interesting to note that this model is completely extensible - you can define your own registry IssuerNameRegistry type that would look and behave differently if you have other means of listing those; the same can also be done via code, which is the example provided with the SDK - you define a custom IssuerNameRegistryClass -

 

namespace ClaimsAwareWebService
{
    public class TrustedIssuerNameRegistry : IssuerNameRegistry
    {
        /// <summary>
        ///  Returns the issuer Name from the security token.
        /// </summary>
        /// <param name="securityToken">The security token that contains the STS's certificates.</param>
        /// <returns>The name of the issuer who signed the security token.</returns>
        public override string GetIssuerName( SecurityToken securityToken )
        {
            X509SecurityToken x509Token = securityToken as X509SecurityToken;
            if ( x509Token != null )
            {
                //Note: This piece of code is for illustrative purposes only. Validating certificates based on
                //subject name is not a good practice.  This code should not be used as is in production.
                if ( String.Equals( x509Token.Certificate.SubjectName.Name, "CN=STS" ) )
                {
                    return x509Token.Certificate.SubjectName.Name;
                }
            }

            throw new SecurityTokenException( "Untrusted issuer." );
        }
    }
}

 

and then when configuring the host for the RP service you provide this as a parameter -

 

FederatedServiceCredentials.ConfigureServiceHost(host, new TrustedIssuerNameRegistry());

 

And while I'm on the subject - as this has sent me going in circles - it appears that the framework is not happy with claim-less tokens, so if you're dumb enough (as I was) and end up not adding any claims (I was adding them base on the requested claims in the incoming request, which, at some point, was empty in my configuration) you will get a  error, which, after setting the ServiceDebugBehavior would read "A SamlAssertion requires at least one statement.  Ensure that you have added at least one SamlStatement to the SamlAssertion you are creating."

 

I can't decide about this one - does it not make sense to have a scenario in which you just want to get a signed token to indicate that an STS has authenticated the caller, but don't actually need any claims? not that it's a problem to find at least one claim to add (identity, authentication method are two easy examples), but speaking in principal I'm not yet convinced not having any specific claim should be an error.

Labels: ,

Tuesday, November 04, 2008

From "Zermatt" to the "Geneva Framework"

I have already mentioned that Zermatt has been renamed as the "Geneva Framework", which makes total sense.

At PDC Microsoft have released a new download for the "Geneva Framework", which I have downloaded today to check some of my code against;

While not at all an extensive list, here are the changes I had to do to my code to get it to work with the updated framework -

On the STS:

  • The SecureTokenService class, which is the base class for any STS implementation has moved to the main Microsoft.IdentityModel namespace (it formerly existed under it's own namespace - Microsoft.IdentityModel.Service)
  • The GetScope method of the SecureTokenService is now marked as abstract and so has to be implemented (I believe it previously was not abstract so a base implementation could have been used, either directly or indirectly through an overriding method;
  • ClaimsPrincipal no longer has a 'Current' property, you can get the claims principal from an IClaimsPrincipal instance using the CreateFromPrincipal method or from an IIdentity instance using the CreateFromIdentity method.
  • GetOutputSubjects renamed to GetOutputClaimsIdentity, the order of the parameters has changed a bit (but otherwise remained the same) and the return value is now IClaimsIdentity and not ClaimsIdentityCollection (which, again, makes perfect sense)
  • In the STS service configurationI have changed the bindings from wsHttpBinding to ws2007HttpBinding and the STS contract from IWSTrustFeb2005SyncContract to IWSTrust13SyncContract.

On the RP:

  • ExtensibleServiceCredentials, which is used to configure the RP's host to use the Geneva Framework is now called FederatedServiceCredentials
  • To get the list of Claims in the RP you no longer use something like "(IClaimsIdentity)ClaimsPrincipal.Current.Identity;" but instead check the CurrentPrincipal of the current thread - "IClaimsIdentity identity = Thread.CurrentPrincipal as IClaimsIdentity;"

Labels: ,

Sunday, November 02, 2008

Non-Optional Claims in the Geneva Framework

I'm currently doing some work with the Geneva Framework (formerly known as "Zermatt"), which I am very excited about;
With the SOA wave and now the coming Cloud wave, federated identity becomes a crucial component in the enterprise and it is great to see such a good story for it from Microsoft.

Using the "Zermatt" SDK (I now need to download the updated framework and align with it) I have succesfully, and quite simply, managed to create both an active STS scenario and a passive STS scenario, both sharing the same underlying STS code; this was a great experience and I hope to post some more details over the next few days.

I was, however, a little bit surprised by the behaviour of the framework around non-optional claims -

 

In my scenario the RP (=relaying party, the service the client actually want to call) indicates through its configuration that it requires a specific (custom) claims, which is not optional -

<security mode="Message">
  <message>
    <claimTypeRequirements>
      <add claimType="http://myCompany/claims/someClaim" isOptional="false"/>
      <add claimType=http://myCompany/claims/someOtherClaim isOptional="false"/>
    </claimTypeRequirements>
    <issuer address="http://localhost:6000/STS"/>
    <issuerMetadata address="http://localhost:6000/STS/mex"/>
  </message>
</security>

 

When the client adds a web reference to this service, it is correctly configured with the STS details and the required claims (not posted here, I will try and describe my scenario in detail in a separate post) and so when it calls the service, WCF ensures it first hits the STS requesting the claims as indicated in the config.

You would all probably know that when thinking about any aspect of security in WCF the story is very “tight”, in the sense that you could set up pretty much all the requirements in configuration should you wish to and you could trust that the service’s code will never get executed if these are not met; I believe this is a key design point for WCF - the implementer of the method should not need to worry about how authentication is implemented, nor should you need to change the code if you decide to change your authentication method.

Considering this I expected the STS to try and provide all the claims it can based on the request message and/or configuration for the RP, and then I would expect the channel on the RP side (using the "Geneva" Framework to reject any requests that arrived without all the non-optional claims BEFORE calling the service’s code.

When testing my scenario I deliberately set the STS code so that it does not provide the required and was surprised to find out this was not the case.
My service's method was called whether both claims existed or not; I did have, of course, full access to the claims in code and so it was fairly easy to validate the existence of the claims required, but this seemd a little misaligned with the WCF approach to all the other security aspects and quite wrong frankly.

I could not find much help online (this is still early days for the framework), and checking with a couple of people they all confirmed both my observation and my expectations; luckily for me, though, I was able to attend PDC and so I made sure to give a visit to the Identity folks' booth.

I'm happy to say that they as well have confirmed that the expectation is quite valid and indeed, they expect this behaviour to change before RTM; hopefully this will happen which would keep things nice and tidy.

Labels: , ,