Category Archives: Windows Identity Foundation

Automatically generate Federation Metadata for your ASP.NET application

After having to generate Federation Metadata for the umpteenst time I decided it was time to deal with it once and for all. A tool like Thinktecture’s WS-Federation Generation Wizard is nice, but even that gets old after a while. Especially if you need to sign the metadata and the certificate is only on the hosting server. Fortunately, creating Federation Metadata for a passive endpoint is fairly easy, as the code below demonstrates. The code reads the current configuration from the system.identityModel section in web.config, so all you need to do is ensure it reflects your host configuration for a particular deployment (dev, test, acceptance, production). Of course you can manage that in Visual Studio with build management and config transforms.

private const string Saml10TokenTypeIdentifier = “urn:oasis:names:tc:SAML:1.0:assertion”;
private const string WsFederation12ProtocolIdentifier = “http://docs.oasis-open.org/wsfed/federation/200706″;

public string GenerateMetadata()
{
    return CreateMetadataString(CreateEntityDescriptor());
}

private EntityDescriptor CreateEntityDescriptor()
{
    var entityDescriptor = new EntityDescriptor();
    var applicationServiceDescriptor = new ApplicationServiceDescriptor();
    var config = FederatedAuthentication.FederationConfiguration;
    entityDescriptor.EntityId = new EntityId(config.WsFederationConfiguration.Realm);
    if (config.ServiceCertificate != null)
    {
        entityDescriptor.SigningCredentials = new X509SigningCredentials(config.ServiceCertificate);
        applicationServiceDescriptor.Keys.Add(GetSingingKey(config.ServiceCertificate));
    }
    applicationServiceDescriptor.ProtocolsSupported.Add(new Uri(WsFederation12ProtocolIdentifier));
    applicationServiceDescriptor.PassiveRequestorEndpoints.Add(new EndpointReference(config.WsFederationConfiguration.Realm));
    applicationServiceDescriptor.TargetScopes.Add(new EndpointReference(config.WsFederationConfiguration.Realm));
    entityDescriptor.RoleDescriptors.Add(applicationServiceDescriptor);
    return entityDescriptor;
}

private static string CreateMetadataString(EntityDescriptor entityDescriptor)
{
    var serializer = new MetadataSerializer();
    var stream = new MemoryStream();
    serializer.WriteMetadata(stream, entityDescriptor);
    stream.Seek(0L, SeekOrigin.Begin);
    return Encoding.UTF8.GetString(stream.ToArray());
}

private KeyDescriptor GetSingingKey(X509Certificate2 certificate)
{
    var x509Token = new X509SecurityToken(certificate);
    var x509RawDataKeyIdentifierClause = x509Token.CreateKeyIdentifierClause();
    var securityKeyIdentifierClause = new SecurityKeyIdentifierClause[]
                                            {
                                                x509RawDataKeyIdentifierClause
                                            };
    return new KeyDescriptor(new SecurityKeyIdentifier(securityKeyIdentifierClause))
    {
        Use = KeyType.Signing
    };
}

Fix: ClaimsAuthorizationManager.CheckAccess throws HttpException

While preparing a demo about Claims Based Authorization in .NET 4.5, I ran into an exception that initially had me dumbstruck. I created an ASP.NET MVC 4 application from the Internet template. Then I used the Identity and Access Tools for Visual Studio (downloadable from http://bit.ly/IDATVS2012) so users login through an external Identity Provider (for the demo LocalSTS). So far so good. Next I created a custom ClaimsAuthorizationManager class with a custom CheckAccess method to demonstrate how you can invoke this from code, essentially separating security checks from business logic. After hooking it up in web.config I run the code and after a wait I got an HttpException with the message Unable to connect to SQL Server database on a call to Principal.IsInRole, as you can see in the image below.

This had me puzzled to say the least. Why would a simple role check go to a database, especially because these are already in the Claims collection? First I tried commenting it out and just do a direct claim check… no dice. Why? The claims collection is empty. Huh? I have a page that lists the claims and they look fine. A closer look at the principal in the AuthorizationContext revealed it was actually of type System.Web.Security.RolePrincipal. That still inherits from ClaimsPrincipal, but it isn’t a normal ClaimsPrincipal, explaining the behavior. But now the big question: where did it come from? This took me a while to figure out with the help of a forum post (this one), ILSpy, and this blogpost by Phil Haack. As it turns out, the ASP.NET MVC 4 Internet template includes the WebMatrix.WebData DLL. This DLL, among other things contains classes used with the SimpleMembership API introduced by WebMatrix. SimpleMembership, which uses a database, is automatically enabled because the DLL uses PreApplicationStart method to initialize itself (as explained in Phil Haack’s blogpost). As soon as you place the WebMatrix.WebData DLL in your bin folder, it automatically registers itself. The result is that Thread.CurrentPrincipal yields a System.Web.Security.RolePrincipal instead of the ClaimsPrincipal associated with the user.

You could obviously remove the WebMatrix.WebData DLL, but there might be other functionality in there you want to use. A better option is to add the following key in web.config appSettings:

<addkey="enableSimpleMembership"value="false" />

Dealing with Token Timeout in Windows Identity Foundation

When a Security Token Service (STS) creates a token, that token has an absolute expiration. Usually this is about 60 minutes, after which the Relying Party (RP) has to send the user back to the STS to acquire a new token. When using Windows Identity Foundation (WIF) in ASP.NET (so for passive federation), this is the default behavior, because the SessionAuthenticationModule stores the token in the FedAuth cookie and checks that token on each request.

What’s the problem?

I hear you thinking “Cool, WIF takes care of all that for me”, and that is cool. But there’s also a nasty side effect, one which all web developers have encountered in a different context: sessions. If a user logs in, starts filling out some form, and then gets interrupted by a phone call, chances are that the session expires. This can lead to problems when the user submits the form, because the user has to log in again. The original posted data gets lost in the process, much to the frustration of the user. Because sessions use sliding expiration the problem is minor. But with absolute expiration, expiration can wreak havoc even if the user posts a form just a minute after the form was presented to the user.

Solving the problem

There are several ways to solve the problem outlined above:

  1. Don’t use the SessionAuthenticationModule.
  2. Modify the stored token to implement sliding expiration.
  3. Force reacquiring a token in the background, so you can control when this happens.

Ditching the SessionAuthenticationModule

Not using the SessionAuthenticationModule appears to be simple. Just remove it from web.config and you’re done. However, if you remove it, you have to ensure a user stays logged in after receiving the token, and you need to keep track of the received claims. This means you need to use a cookie or session data to keep track of the user. Basically you would be recreating what the SessionAuthenticationModule does for you for free.

Making token expiration sliding

This is actually easier than ditching the SessionAuthenticationModule. All you have to do is handle the SessionSecurityTokenReceived event and modify the ValidTo property of the token, as shown in this MSDN Forums post.
Tip: You can hookup the event in the Application_Start event in global.asx using FederatedAuthentication.SessionAuthenticationModule to point to the module.

Force reacquiring a token

The above methods work fine, but have some drawbacks. One of these is the fact that Single Sign-On (SSO) no longer works properly if a user spends too long in a single application. This is because the login session with the Identity Provider will expire at some point. Another issue arises if you want to use delegation or impersonation when you call a web service. WS-Trust 1.4 supports delegation (ActAs) and impersonation (OnBehalfOf). Even though WIF officially implements WS-Trust 1.3, WIF does support these constructs. When calling web services from a web application, using delegation is a recommended practice, because it greatly improves the security. The reason is that in order for the web application to make the web service call on your behalf, it needs to acquire a token from the STS, based on the original token. This means a malicious user would need that token in order to make the web service call. Breaking into the web application is not enough.

So, how can you ensure you get a new token once in a while? Although the implementation is somewhat more difficult, the principle is simple: logout of the RP. That triggers the RP to re-authenticate the user by redirecting to the STS. As long as the user is still known in the STS, a new token is transparently given out. You can do this in roughly two ways: in an invisible iframe that does this process under the covers or in the main request stream. The latter is more visible to the user and requires you to bring the user back to where he was before the logout was forced, which is slightly more complicated than a simple redirect. The iframe solution is more elegant, because it does not interrupt the main working process. Basically all you have to do is point the iframe to a handler that logs the user out if necessary, triggering the re-authentication process when necessary and coming back to the handler to produce an empty HTML page. The handler to do this is shown in the code below.

using System;
using System.Configuration;
using System.Globalization;
using System.IdentityModel.Tokens;
using System.Threading;
using System.Web;
using Microsoft.IdentityModel.Claims;
using Microsoft.IdentityModel.Web;
namespace WebApp
{
    /// 
    /// HTTP Handler triggering token renewal when this is necessary.
    /// 
    public class TokenRenewalHandler : IHttpHandler
    {
        /// 
        /// The default number of minutes before the token expires when renewal should be triggerd.
        /// 
        private const int DefaultTokenRenewalWindow = 20;

        /// 
        /// Value container for the TokenRenewalWindow
        /// 
        private int m_TokenRenewalWindow = 0;

        /// 
        /// Number of minutes before the token expires when renewal should be triggerd.
        /// 
        /// Should be less or equal to the session timeout.
        protected int TokenRenewalWindow
        {
            get
            {
                if (m_TokenRenewalWindow == 0)
                {
                    int securityTokenRenewalWindow;
                    if (!int.TryParse(
                           ConfigurationManager.AppSettings["SecurityTokenRenewalWindow"],
                           NumberStyles.Integer, CultureInfo.InvariantCulture,
                           out securityTokenRenewalWindow))
                    {
                        securityTokenRenewalWindow = DefaultTokenRenewalWindow;
                    }
                    m_TokenRenewalWindow = securityTokenRenewalWindow;
                }
                return m_TokenRenewalWindow;
            }
        }

        /// 
        /// Handles the HTTP reqyest and forces a signout to renew the token if necessary.
        /// 
        public void ProcessRequest(HttpContext context)
        {
            SecurityToken token = null;
            IClaimsPrincipal principal = Thread.CurrentPrincipal as IClaimsPrincipal;
            if (principal != null && principal.Identities.Count > 0)
            {
                token = principal.Identities[0].BootstrapToken;
                if (token != null)
                {
                    DateTime tokenExpirationTime = DateTime.Now.ToUniversalTime().AddMinutes(TokenRenewalWindow);
                    if (token.ValidTo.ToUniversalTime().CompareTo(tokenExpirationTime) < 0)
                    {
                        if (FederatedAuthentication.WSFederationAuthenticationModule != null)
                        {
                            // Force sign-out.
                            FederatedAuthentication.WSFederationAuthenticationModule.SignOut(false);
                        }
                        // Redirect to this handler to force a new sign-in request to the STS
                        context.Response.Redirect(context.Request.RawUrl, true);
                    }
                }
            }
            // Return an empty HTML page
            context.Response.ContentType = “text/html”;
            context.Response.Write(“”);
        }

        public bool IsReusable
        {
            get { return false; } }
    }
}

Common Windows Identity Foundation misconceptions

I am on crusade to get Windows Identity Foundation (WIF) adopted by the Microsoft .NET community at large. Why? Because maintaining a user store within an application as is propagated by ASP.NET Membership is just plain stupid these days. Yes, I may be a little harsh with that judgment, but sparing the rod spoils the child. Applications should no longer be islands, but should be working together. And if applications such as Spotify and Flickr can (re)use a user’s identity from Facebook, Twitter, LinkedIn, and so on, why can’t yours?

“A thousand mile journey begins with one step” – Lao Tze

In the past couple of years I’ve been speaking about WIF on many occasions, both at conference and with individual developers. Across the board I can say that WIF is largely misunderstood. Hence my first step is to address some of the misconceptions surrounding WIF, and more in general the concepts underlying WIF.

Misconception 1: WIF is Microsoft-only and not interoperable.

WIF actually implements the WS-Federation standard. Microsoft is an active participant in the standards commonly known as the WS-* specifications, a host of web services specifications for security, transactions, and reliable messaging. The WS-Federation standard is implemented by many other platforms, and WIF can interoperate with these just fine.

Misconception 2: WIF can only be used to secure web applications, not web services.

The WS-Federation protocol defines two profiles: Active and Passive. Passive federation is for browser based applications, because browsers don’t support the full cryptographic stack required for WS-Federation to work. Active federation is used for web services and can be used with clients that do support the needed cryptographic capabilities. I’ll get back to what this all means in another post.

Misconception 3: WIF can only be used to secure web services, not web applications.

See misconception #2.

Misconception 4: WIF is only for cloud (Azure) applications.

WIF works with any application written in .NET 3.5 and up. You can host that application anywhere you like, in the cloud or in your own data center. In fact, there is nothing that prevents you from creating applications with WIF for use in just the local network and for internal use only.

Misconception 5: You can’t do role-based security with WIF.

Quite the opposite is true. You can still do role-based security if you want to, but you can do much more. The underlying protocol is much more flexible, and you can implement security checks in your applications based on the information you get about a use any way you like.

Misconception 6: WIF only adds complexity.

It is indeed true that properly connecting a WIF enabled application to a security token service can be a challenge. You need to get the protocol settings to match and need certificates for encryption and signing. However, inside your applications WIF is just as easy as role-based security as you are used to. If you want to do more elaborate things, things obviously get more complex, but this is true for any type of security.

Misconception 7: To use WIF in an existing application I need to re-architect the whole application.

WIF extends the IIdentity and IPrincipal interfaces. This means that your existing application will keep working if you migrate to WIF to get authenticate and authorize the user. The only thing you need to be aware of is the fact that because you don’t have a local user directory anymore, you can’t do things for which you require information about another user. This means you may have to provide a different way to deal with such scenarios. If you use ASP.NET Profiles for this kind of information, a custom provider may be all you need.

Who is running my Workflow Service?

Workflow Services in .NET 4 allow you to do long running processes. But when you do that, there’s an interesting question: when a workflow has been suspended, under which user is the workflow running when it is active again. To answer this question I created a simple workflow that writes the user in the current thread to a log. On the initial call, the user making the call was logged (in this case I used Windows Identity Foundation to authenticate, but this should be the same for all types of authentication). After a Delay of a minute that user was gone, and instead the user in the current thread was unauthenticated. This means that any code you call from the workflow can’t rely on Thread.CurrentPrincipal to get the proper authorizations. You have to save the user, and somehow reinstate principal so it runs under the original context. Alternatively you can use some form of delegation.

Solving: Could not establish trust relationship for the SSL/TLS secure channel …

Working with Windows Identity Foundation can be quite a minefield. Solve one issue, and the next creeps up. Because it’s all these little tweaks to make it work, I often find myself thinking “How did I solve that last time?” One of those issues is the following exception:
Could not establish trust relationship for the SSL/TLS secure channel with authority ‘somesite.runningunder.ssl’
There are two reasons why you can run into this exception, each discussed below.

The certificate isn’t trusted and/or the URL doesn’t correspond with the URL in the certificate.

If this is the case, you get certificate warnings when you browse to the service WSDL with a browser. The best way to solve the former is to have your (development) environment work with certificates it trusts. This means setting up a Certificate Authority (Active Directory Certificate Services), placing the root CA certificate in the Trusted Root Certificates of the machine your clients (and services) run on, issuing the needed certificates from the CA, and placing these where they are needed. Alternatively, you can just add a
single line of code to your client so it ignores certificate issues before you do any service call:

System.Net.ServicePointManager.ServerCertificateValidationCallback =
    ((sender, certificate, chain, sslPolicyErrors) => true);

WARNING! ONLY USE THE ABOVE CODE FOR DEVELOPMENT PURPOSES. IT IS NOT SECURE.
If after you’ve done the above you still get an exception, the above code is likely not even being hit. That means you (also) have the problem below.

You’ve setup identity trust in your client, and the certificate reference is incorrect.

This often happens when you copied some configuration from somewhere, and forgot to change the corresponding certificate reference. The red stuff in the client configuration below (which is much longer in a real configuration) is the culprit. It should contain the encoded certificate.

<system.serviceModel>
  <client>
    <endpoint address="https://YourServer/Service1.svc"
              binding="customBinding"
              bindingConfiguration="CustomBinding_IService1"
              contract="ServiceReference1.IService1"
              name="Service1Binding">
      <identity>
        <certificate encodedValue="MIIF5jCCBM6gAwIBAgIKYSt2tQA..."/>
      </identity>
    </endpoint>
  </client>
</system.serviceModel>

To solve this, you need to get the base64 encoded certificate string, and paste it in place of what’s in there now. To get it you can do the following:

  1. Browse to the endpoint with your browser.
  2. View the certificate information.
  3. Save the certificate to file.
  4. Open the certificate with notepad.
  5. Copy the encoded value between the —–BEGIN CERTIFICATE—– and —–END CERTIFICATE—– placeholders.

Solving: The provided URI scheme ‘https’ is invalid; expected ‘http’.Parameter name: via

I’ve been working with WCF for quite a while, and every so often I run into this exception:

ArgumentException: The provided URI scheme 'https' is invalid; expected 'http'.Parameter name: via

The problem is obvious. You’re trying to access a service under HTTPS, but it’s being called with HTTP. Under most bindings you can solve this by adding something like this to the client binding configuration:

<binding name="MyBinding"> 
  <security mode="Transport"> 
    <transport clientCredentialType="None" /> 
    <message clientCredentialType="None"
             negotiateServiceCredential="false"
             establishSecurityContext="false" />
  </security> 
</binding>

When you use a (custom) ws2007FederationHttp binding, for instance when working with Windows Identity Foundation, the above won’t work. In that case you need to look in the binding for the <httpTransport> element and replace it with <httpsTransport>.

Merging Claims Based Authorization and Application Authorization

It is typically a good idea to separate general authorizations of a user from application specific authorizations. This is why we invented Application Roles (Settings Administrator), which are separate from Organizational Roles (System Administrator).When using Application Roles, we can map these roles to Organizational Roles. In organizations using Active Directory, Organizational Roles are typically stored in AD. Application Roles can then be stored using Authorization Manager (AzMan) in XML or AD in Application Mode (separate from the organization AD).

Over the years I’ve built quite a few applications that use the above model, and it works well if you authorize with roles. But these days I do most of my work using things like Claims Based Authorization, so the question is “Does this translate to teh CBA world? And if so, how?”. The answer is that yes, it does translate (very well actually), at least in Windows Identity Foundation.

In the CBA world an application receives a token with claims about the user. Like with roles, this should typically be claims not specific to the application, unless the only source for the claim information lies within (or is only accessible to) the STS. This serves two purposes:

  1. The token is generally usable across applications, so the STS can deal with this more easily.
  2. Tokens are not stuffed with a lot of claims.

The latter is actually more important than you might think. Adding more claims means a bigger token, and there comes a point where the token is so large that for instance ASP.NET rejects the request, because it is bigger than the accepted request size (which you should only increase if really necessary).

Now, one of the great things about CBA is that it enables me to create business logic which checks the authorization based on meaningful values, rather than a role. On top of that, I wouldn’t want to have a hybrid security system for the claims stuff and the application specific stuff. Fortunately, In Windows Identity Foundation I can add claims to a claims identity, and these claims then behave the same as the claims acquired from the STS token. The only difference is that the issuer is set to LOCAL AUTHORITY, rather than the STS, which means these claims are really only usable locally in my app (or service). The code to add a local claim is easy:

IClaimsPrincipal claimsPrincipal = Page.User as IClaimsPrincipal;
IClaimsIdentity claimsIdentity = (IClaimsIdentity)claimsPrincipal.Identity;
claimsIdentity.Claims.Add(new Claim("http://MyApp/SomeAppClaim", "SomeValue"));

You can execute code like this when a session starts, and add all application specific claims for the user (identified by an STS claim) to the claims identity. The local claims then get the same lifetime as the claims originally from the token, so you only have to add them once. This way adding application specific claims is still separated from the functional code. Which was the benefit to start with.

Although the above code will definitly work, there is another option when using WCF, known as Claims Transformation. With Claims Transformation you can define policies that define ClaimSets to add to a user’s token. This model is much more flexible, as explained in the MSDN article Service Station: Authorization in WCF-Based Services (jumps straight to the Claims Tranformation section). That article is from the pre-WIF era, but you can do similar stuff with teh ClaimsAuthorizationManager in Microsoft.IdentityModel.

Aantekeningen SDN Event 13 December 2010

Op het SDN Event van 13 december heb ik twee presentaties gegeven. Hieronder kun je de aantekeningen downloaden die ik gemaakt heb op de tablet (voor wie er niet bij was: ik heb in plaats van slides mijn sessie gedaan met behulp van tekenen in OneNote).