How to: Add a Custom Token Handler

[Starting with the .NET Framework 4.5, Windows Identity Foundation (WIF) has been fully integrated into the .NET Framework. The version of WIF addressed by this topic, WIF 3.5, is deprecated and should only be used when developing against the .NET Framework 3.5 SP1 or the .NET Framework 4. For more information about WIF in the .NET Framework 4.5, also known as WIF 4.5, see the Windows Identity Foundation documentation in the .NET Framework 4.5 Development Guide.]

You can add a custom security token handler simply by creating a class that derives from SecurityTokenHandler or one of its descendents, and adding your derived class to the web.config or app.config file for your application. Note that if you want to write a custom security token handler to handle a well-known token type, such as SAML 2 or X.509, you must create a class that derives from the appropriate WIF class. For example, to handle SAML 2 tokens, you must derive from Saml2SecurityTokenHandler. This topic shows how to add a custom security token handler that provides custom username/password validation.

First, create a class that derives from UserNameSecurityTokenHandler and overrides CanValidateToken and ValidateToken.

using Microsoft.IdentityModel.SecurityTokenService;
using Microsoft.IdentityModel.Tokens;

namespace SimpleCustomSecurityTokenHandler
{
    public class CustomUserNamePasswordValidatorSecurityTokenHandler : UserNameSecurityTokenHandler
    {
/*
Important: You must override CanValidateToken and return true, or your token handler will not be used.
*/
        public override bool CanValidateToken
        {
            get { return true; }
        }

        public override ClaimsIdentityCollection ValidateToken(SecurityToken token)
        {
            if (token == null)
            {
                throw new ArgumentNullException();
            }
            UserNameSecurityToken UNtoken = token as UserNameSecurityToken;
            if (UNtoken == null)
            {
                throw new SecurityTokenException("Invalid token");
            }

/*
Validate UNtoken.UserName and UNtoken.Password here.
*/

            Claim claim = new Claim(System.IdentityModel.Claims.ClaimTypes.Name, UNtoken.UserName);
            IClaimsIdentity identity = new ClaimsIdentity(nameClaim,"Password");
            identity.Claims.Add(
                new Claim("https://schemas.microsoft.com/ws/2008/06/identity/claims/ClaimTypes.AuthenticationInstant",
                XmlConvert.ToString(DateTime.UtcNow, "yyyy-MM-ddTHH:mm:ss.fffZ"),
                "http://www.w3.org/2001/XMLSchema#dateTime")
            );
            identity.Claims.Add(
                new Claim("https://schemas.microsoft.com/ws/2008/06/identity/claims/ClaimTypes.AuthenticationMethod",
                "https://schemas.microsoft.com/ws/2008/06/identity/authenticationmethod/password")
            );
            return new ClaimsIdentityCollection(new IClaimsIdentity[] { identity });
        }
    }
}

Next, add the custom token handler to the service configuration by adding the following markup to the configuration section of the service’s app.config file.

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <!-- Registers the microsoft.IdentityModel configuration section -->
    <section name="microsoft.identityModel" type="Microsoft.IdentityModel.Configuration.MicrosoftIdentityModelSection, Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
  </configSections>
  <microsoft.identityModel>
    <service>
      <securityTokenHandlers>
        <remove type="Microsoft.IdentityModel.Tokens.WindowsUserNameSecurityTokenHandler, Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
        <add type="SimpleCustomSecurityTokenHandler.CustomUserNamePasswordValidatorSecurityTokenHandler, SimpleCustomSecurityTokenHandler"/>
      </securityTokenHandlers>
    </service>
  </microsoft.identityModel>
</configuration>

Note that not only do we add our custom security token handler, CustomUserNamePasswordValidatorSecurityTokenHandler, we also remove the default username/password security token handler, WindowsUserNameSecurityTokenHandler. This is because WIF’s token handler collection will, by default, contain only one token handler for each token type. There is a one-to-one relationship between token handlers and token types, so you should not write a custom token handler to handle more than one token type. A token type might be represented by one or more token identifier strings, so you can write a custom token handler to handle one token type and more than one token identifier.

If your custom security token handler overrides ReadToken, it must also override CanReadToken. Likewise, if it overrides WriteToken, it must also override CanWriteToken.