Blast from the Past: A Delegation Scenario using WS-Federation and WS-Trust

A couple of weeks ago, I wanted to outline some of the different flavors and protocols available for delegation scenarios using a federated identity. One of the protocols on my list was WS-Federation and WS-Trust. Yes, I know, all the cool kids are doing OpenID Connect these days, but some of us are working for enterprises that bought into the whole federation-thing rather early and while still on-premise. For those environments ADFS is most likely the Identity Provider. And if the relying parties are .NET-based apps, the protocol of choice for identity federation is WS-Federation.

Of course, I did want to use the latest and greatest as much as possible, so I checked out the new OWIN/Katana gear for WS-Federation. And sure enough, getting identity federation to work using ADFS as the Identity Provider was a breeze. However, delegating the federated identity to a backend WCF service: not so much…

The theory here is that, firstly, the WCF service is also registered as a relying party in ADFS; secondly, that the web application is allowed to delegate identities to that relying party; and thirdly, that the web application can use the ADFS-issued user token to send back to ADFS as part of the request for a delegation token. Now the issue I encountered is that the token, as persisted by the OWIN middleware, does not have the same format as is expected by the time the delegation call is being made. More specifically, the token is persisted as a string, whereas the delegation code is expecting a SecurityToken.

I’ve tried to work this out in just about every way I could think of. This was not exactly made easier by the utter lack of online resources when it comes to WS-Federation (especially in its .NET 4.5 and OWIN incarnations). Still, I did not get this to work using the OWIN middleware. So I defaulted back to the ‘classic’ way of making this work, configuring the initial federation with ADFS through the web.config for both the front end MVC application and the backend WCF service that the web app is calling into. And as said, the online resources on WS-Federation in .NET 4.5 are limited, so I figured I’d share my sample on Github.

There’s a lot of moving parts to this sample, and principles to grasp if you want to fully understand the code. Luckily, all of that is pretty much covered in this guide. The ADFS part of it is pretty accurate as it is, and even though it is aimed at ADFS 2.0, it’s easily transferable to ADFS 3.0. As far as the code goes, the principles remain the same but the implementation is based on WIF on .NET 4.0. So you’ll have to do some digging through my sample to match it to the way it is described in the guide. Just see it as a learning opportunity ;).

I will reveal one difference: the guide assumes that the account running the web application is domain-joined so the web app can authenticate itself to ADFS using Windows Authentication when it makes the call to get the delegation token. To simplify the setup, I chose to authenticate to ADFS using a username and password so that I wouldn’t have to set up Kerberos. To make the username-based binding work, I used Dominick Baier’s UserNameWSTrustBinding. This was available in WIF on 4.0 but did not make it into 4.5, so Dominick added it to his Thinktecture.IdentityModel NuGet package.

Oh, and don’t expect the sample to be production ready. In fact, it won’t even work out of the box when you run it. You will have to configure several URL’s to match you environment. And as said, you’ll have to configure ADFS using the tutorial I mentioned.

Of course, I haven’t totally given up on the OWIN route, nor am I finished outlining the different delegation flavors. So stay tuned, because there’s more to come on coding identity federation and delegation!

Advertisement

ACS Now Supports Federated Signout

For all of us who gave identity federation a try, federated signout has probably been a theme of some controversy. If your application supports it, you might have had to explain to users why logging out of your application also means that they are logged out to all other applications that happen to use the same Identity Provider. But if your application does not support it, you might have had a discussion or two about why the logoff functionality “does not work” – meaning that a user that is logged off can log back in to your application with re-authenticating to the Identity Provider.

This is a conceptual problem that, to my mind, is not quite solved yet. And it may prove impossible to solve as long as we don’t rethink the concepts of logging in and out of web applications. For example, if I use my Microsoft Account (formerly known as LiveID) to logon to some random web application and then log off again, I might be surprised that I am also logged out of the Windows Azure Management Portal, my Office365 environment, and my MDSN Subscriber pages. That’s not what I want if I click ‘Logoff’ at randombobswebshop.com. On the other hand, if I click logoff and browse away, and then return after a while, I am probably surprised to see that I am logged back in again without re-submitting my credentials. Now I’m a technical guy, and I will probably have noticed my browser flickering due to the redirects to the Microsoft Logon Page and back, but my manager – or my girlfriend for that matter – may not be as perceptive as me.

As said, this problem may very well turn out not to have a technical solution. It is, however, an interesting topic for more philosophically inspired moments. But no matter where you stand on this matter, if you used Azure Access Control Service you did not really have a choice whether to implement federated signout or not, simply because ACS did not support it. Attempting to send the correct messages to ACS simply resulted in a static page that made the omission clear.

Recently, however, I discovered that ACS has been updated to support federated signout. The update apparently happened back in December 2012, so it took me quite a while to stumble upon this new feature, but here is how to use it from an ASP.NET application that acts as a Relying Party and uses WIF:

var config = FederatedAuthentication.FederationConfiguration.WsFederationConfiguration;
WSFederationAuthenticationModule.FederatedSignOut(new Uri(config.Issuer), new Uri(config.Reply));

Simple, right? So now we cannot hide behind the limited signout functionality in ACS anymore when we have to choose whether or not to support federated signout. So I guess that leaves no alternative than to really start thinking about the experience we want our users to get used to.

Different Ways To Verify ACS Token Signing Certificate

Today, I had an interesting discussion with a colleague on the possible attack vectors on the communication between a Relying Party application and ACS (Azure Access Control Service, also known as Windows Azure Active Directory Access Control). The discussion focussed on the extend to which the RP can be sure that he is in fact communicating with the STS (Secure Token Service, which in this scenario is ACS). The discussion was a bit blurred by some other issues that arose so we didn’t really get to the heart of the matter, but we were both left with a nagging concern that communication was not as secure as we would want it to be.

Now it’s never a good idea to keep a factual disagreement undecided, and when it comes to security there’s all the more reason to figure out what’s really going on. So, to set the background: we have an RP that lets its users authenticate with ACS (which in turn uses LiveID as Identity Provider). We consciously did not configure the tokens to be encrypted, so we only have them signed by ACS. This feature cannot be disabled (or I did not find the button for it) and that makes good sense, too. Since the communication between the RP and the STS all flows through the client (see this post for an extensive description), we want to make sure that the client is not able to tamper with the SAML token that it gets from the STS to pass on to the RP. There’s no viable scenario in which you would want to enable clients fiddling with the tokens, so not being able to disable token signing is a good thing as far as I’m concerned.

OK, so token signing is obligatory. The way this actually happens is – broadly – as follows if you don’t customize your ACS namespace: ACS generates a X.509 certificate that is used for signing the tokens. Upon signing a token, it uses this certificate’s private key to do the signing, and then embeds its own public key in the RequestSecurityTokenResponse that flows from ACS to the client and then to the RP. The RP then extracts this public key from the reponse, and uses it to validate the signed SAML token that is also part of this message.

So the question becomes: what is keeping a client from creating its own X.509 certificate on the fly, create its own SAML token and sign it with the private key of the roque certificate, and embed the certificate’s public key in the response? How will the RP, upon extracting the public key, detect that this is not the public key from the STS? Of course, the normal way of doing this would be to place the public key of the legitimate signing certificate in the Trusted Person Certificate Store on the client’s host (assuming it’s Windows). But that’s not how it is configured if you just use the Identity and Access Tool to register your app as an RP at your ACS namespace. In fact, the relevant part of your web.config will look like this:

  <system.identityModel>
    <identityConfiguration>
      <audienceUris>
        <add value="[your URI]" />
      </audienceUris>
      <issuerNameRegistry type="System.IdentityModel.Tokens.ConfigurationBasedIssuerNameRegistry, System.IdentityModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
        <trustedIssuers>
          <add thumbprint="[a certificate thumbprint]" name="[a friendly name]" />
        </trustedIssuers>
      </issuerNameRegistry>
      <certificateValidation certificateValidationMode="None" />
    </identityConfiguration>
  </system.identityModel>

As this snippet shows, the certificateValidationMode is None, meaning that you can put anything you like in whichever Certificate Store on the client host, but your app will not hit that store to validate anything. Of course, we can always switch the certificateValidationMode to PeerTrust or PeerOrChainTrust and add the public key of the signing certificate to the Trusted Person Certificate Store. But if that would be the only way to be sure that the token does indeed come from the STS, it would for example mean that, first of all, the default configuration as done by the Identity and Access Tool would leave you severely unprotected, and second of all it would mean that there’s no way to get any level of issuer assurance in, for example, an Azure Websites scenario.

Luckily, it turns out that things are not as bad as they seem, and the key to this is the <trustedIssuers> element in the snippet above. As you can see, this element contains a collection of certificate thumbprints. The one element that we have in our web.config in this case is placed there by the Identity and Access Tool when I first set up my application to use ACS as STS, and it is the thumbprint for the certificate that ACS uses to sign my tokens. So now, when the RP receives a RequestSecurityTokenResponse from the client and extracts the public part of the certificate from that message, it first validates the certificate by comparing its thumbprint to the one in the web.config. If the client forged the response with his own certificate, the thumbprint in the web.config will not match with the certificate used, and authentication does not occur. The class responsible for this is the ConfigurationBasedIssuerNameRegistry, which holds a registry of all valid issuers based on the items in the <trustedIssuers> element. This class is referenced in the <issuerNameRegistry> element as the class to use for this purpose.

Of course, there’s always a theoretical possibility that a second certificate does resolve to the same thumbprint. After all, the output space for thumbprints is limited and in fact smaller than the output space for certificate keys. But the change that an attacker is able to willingly create a certificate that results in the same thumbprint as the ACS certificate is extremely small. But hey, if the risk is still too big for your comfort, you can always take the Trusted Persons Certificate Store route on top of all this :).

UPDATE: Today the discussion with my colleague continued for a bit. He made the valid point that the above behavior (i.e. authentication fails if the thumbprints don’t match) is not intended as a security feature. Instead, what happens here is that the WIF framework wants to determine the name of the issuer based on the incoming public key. It does this by calling into the ConfigurationBasedIssuerNameRegistry, which will return an empty string if the incoming public key does not resolve to a known thumbprint. Further down the line, WIF throws an exception if the issuer name is null or empty. This means that the availability of a matching thumbprint is not really what is evaluated here; it’s just that it happens to function as the “bridge” between the public key and the issuer name. This becomes a real concern given the possibility to provide your own implementation of the IssuerNameRegistry instead of going with the default configuration-based variant. Now if that custom implementation happens to ignore the thumbprint while resolving the certificate into an issuer name, the RP can no longer be sure that the incoming token actually came from the STS.

So, to conclude: if there’s really no possibility to do proper certificate validation, you can be sure of untampered communication with the STS, but only as a side effect of resolving the certificate to a name through the thumbprint. If at all possible, it’s a recommended practice to set the certificateValidationMode to at least PeerTrust and to place the token signing certificate’s public key in the Trusted Persons Certificate Store.

Use ClaimsAuthenticationManager To Make Sense Of Federated Identites

OK, so you figured you don’t want the burden of managing your users’ identities anymore. You can very well live without the responsibility of keeping their credentials save, and you have chosen to let the smart boys and girls that manage LiveID handle all that for you; you’re just using some STS (for example: Azure ACS) to let your users login with their LiveID. Good for you! Identity Federation is the way of the future, so why not start using it right now?

It does present you with a challenge, though, that you didn’t have in the olden days (not in its current form, at least). Suddenly, your user has an identity that, out-of-the-box, doesn’t mean all that much to your application. Let’s take the case of a simple webshop. If you enable users to get in using their LiveID, how are you gonna trace whether they are new or recurring customers? Did they already register a shippng address, payment details, and that sort of stuff? Back in the days of Forms Authentication, this info was usually gathered when the user registered himself with the application; now it’s a separate step that a logged in user may or may not have taken already.

So, the problem is clear: how do I keep track of users whose identities are external to my application? Of course there are more than one pieces to this puzzel: I have to worry about gathering the additional application-specific information, and I have to store some token to associate identities with this extra information. And then there is the issue of deciding whether a user that has just logged in is a new or a recurring user. This post presents a possible solution to this last problem, and shows how the WIF framework let’s you handle this quite nicely.

First off, let’s reformulate the problem in a more ‘claimsy’ type of language. In claims-world, everything there is to know about an identity is expressed in claims. For example: what we used to call a ‘role’ is now a claim of type ‘role’. An email address is now a claim of type ’emailaddress’. So we might say that being a properly registered customer for our webshop (where ‘properly registered’ means: having provided the application with your shipping details and having received a customerId in return), means to have a claim of type ‘customerId’. So now we can distinguish between logged in users of our webshop: those that have a claim ‘customerId’ are all set; those that don’t still need to register some details before they can place an order.

The problem now is: how do we set this extra claim at runtime if a recurring user logs in? As said, WIF has an extensibility point that let’s you handle these types of scenario’s. First of all, this requires you to provide your implementation of the ClaimsAuthenticationManager-class. This basically involves implementing the Authenticate-method as follows:

        [SecuritySafeCritical]
        public override ClaimsPrincipal Authenticate(string resourceName, ClaimsPrincipal incomingPrincipal)
        {
            if (incomingPrincipal.Identity != null && incomingPrincipal.Identity.IsAuthenticated)
            {
                var identity = incomingPrincipal.Identity as ClaimsIdentity;
                int? customerId = null;
                // TODO: add logic that evaluates the incoming principal and retrieves customerId if available
                if (customerId != null)
                {
                    identity.AddClaim(new Claim(ClaimTypes.UserData, customer.Id.ToString()));
                }
            }

            return base.Authenticate(resourceName, incomingPrincipal);
        }

As a sidenote: notice the SecuritySafeCritical attribute above this method? That’s required because the method AddClaim() on ClaimsIdentity is marked as SecurityCritical so that SecurityTransparent code cannot call into it. (I actually feel that CAS may be dying a silent death these days, for several reasons, but that’s another discussion.)

So, now you’ve created your own ClaimsAuthenticationManager. All that’s left to do now is letting your app know that you want to use it. That’s done through the web.config:

  <system.identityModel>
    <identityConfiguration>
      <!-- All sorts of config stuff -->
      <claimsAuthenticationManager type="[Your fully qualified type]" />
    </identityConfiguration>
  </system.identityModel>

Now, your custom ClaimsAuthenticationManager is called when a user is authenticated against the STS of your choice and returns to your application with a token. You can now handle the login event and add application-specific claims to the incoming identity. Of course, these claims can also come from an external attribute store or whatever source you can think of.

One final note: if you are going to stuff loads and loads of claims in here, it may be a good idea to store the actual claims data of the server instead of in a cookie that travels to the client; some browsers may take issue with the fact that you feel storing an image blob in a cookie is appropriate :). To do that, you have to edit the global.asax and handle the Application_Start event. Paste the following code in there:

            FederatedAuthentication.FederationConfigurationCreated += (s, args) =>
            {
                FederatedAuthentication.SessionAuthenticationModule.IsReferenceMode = true;
            };

What this does is tell the SessionAuthenticationModule to store the actual claims data in the session, and pass the reference to that session object to the client through a cookie. Note that this requires either a single server deployment, or a form of session management where sessions are shared between servers.

OK, happing federating everyone! Please leave a comment if you have any follow-up questions.