Durable Issued Token Provider

The DurableIssuedTokenProvider sample demonstrates how to implement a custom client issued token provider.

Discussion

A token provider in Windows Communication Foundation (WCF) is used to supply credentials to the security infrastructure. The token provider in general examines the target and issues appropriate credentials so that the security infrastructure can secure the message. WCF ships with a CardSpace token provider. Custom token providers are useful in the following cases:

  • If you have a credential store that the built-in token provider cannot operate with.

  • If you want to provide your own custom mechanism for transforming the credentials from the point when the user provides the details to when the WCF client uses the credentials.

  • If you are building a custom token.

This sample shows how to build a custom token provider that caches tokens issued by a Security Token Service (STS).

To summarize, this sample demonstrates the following:

  • How a client can be configured with a custom token provider.

  • How issued tokens can be cached and provided to the WCF client.

  • How the server is authenticated by the client using the server's X.509 certificate.

This sample consists of a client console program (Client.exe), a security token service console program (Securitytokenservice.exe) and a service console program (Service.exe). The service implements a contract that defines a request-reply communication pattern. The contract is defined by the ICalculator interface, which exposes math operations (add, subtract, multiply, and divide). The client gets a security token from the Security Token Service (STS) and makes synchronous requests to the service for a given math operation and the service replies with the result. Client activity is visible in the console window.

Note

The set-up procedure and build instructions for this sample are located at the end of this topic.

This sample exposes the ICalculator contract using the <wsHttpBinding>. The configuration of this binding on the client is shown in the following code.

<bindings>
  <wsFederationHttpBinding>
    <binding name="ServiceFed">
      <security mode="Message">
        <message issuedKeyType="SymmetricKey"
                 issuedTokenType="http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV1.1">
          <issuer address="http://localhost:8000/sts/windows"
                  binding="wsHttpBinding" />
        </message>
      </security>
    </binding>
  </wsFederationHttpBinding>
</bindings>

On the security element of wsFederationHttpBinding, the mode value configures which security mode should be used. In this sample, messages security is being used, which is why the message element of wsFederationHttpBinding is specified inside the security element of wsFederationHttpBinding. The issuer element of wsFederationHttpBinding inside the message element of wsFederationHttpBinding specifies the address and binding for the Security Token Service that issues a security token to the client so that the client can authenticate to the Calculator service.

The configuration of this binding on the service is shown in the following code.

<bindings>
  <wsFederationHttpBinding>
    <binding name="ServiceFed">
      <security mode="Message">
        <message issuedKeyType="SymmetricKey"
                 issuedTokenType="http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV1.1">
          <issuerMetadata address="http://localhost:8000/sts/mex">
            <identity>
              <certificateReference storeLocation="CurrentUser"
                                    storeName="TrustedPeople"
                                    x509FindType="FindBySubjectDistinguishedName"
                                    findValue="CN=STS" />
            </identity>
          </issuerMetadata>
        </message>
      </security>
    </binding>
  </wsFederationHttpBinding>
</bindings>

On the security element of wsFederationHttpBinding, the mode value configures which security mode should be used. In this sample, messages security is being used, which is why the message element of wsFederationHttpBinding is specified inside the security element of wsFederationHttpBinding. The issuerMetadata element of wsFederationHttpBinding inside the message element of wsFederationHttpBinding specifies the address and identity for an endpoint that can be used to retrieve metadata for the Security Token Service.

The behavior for the service is shown in the following code.

<behavior name="ServiceBehavior">
  <serviceDebug includeExceptionDetailInFaults="true" />
  <serviceMetadata httpGetEnabled="true" />
  <serviceCredentials>
    <issuedTokenAuthentication>
      <knownCertificates>
        <add storeLocation="LocalMachine"
              storeName="TrustedPeople"
              x509FindType="FindBySubjectDistinguishedName"
              findValue="CN=STS" />
      </knownCertificates>
    </issuedTokenAuthentication>
    <serviceCertificate storeLocation="LocalMachine"
                        storeName="My"
                        x509FindType="FindBySubjectDistinguishedName"
                        findValue="CN=localhost" />
  </serviceCredentials>
</behavior>

The issuedTokenAuthentication element inside the serviceCredentials element allows the service to specify constraints on the tokens it allows clients to present during authentication. This configuration specifies that tokens signed by a certificate whose Subject Name is CN=STS are accepted by the service.

The Security Token Service exposes a single endpoint using the standard wsHttpBinding. The Security Token Service responds to request from clients for tokens and, provided the client authenticates using a Windows account, issues a token that contains the client's user name as a claim in the issued token. As part of creating the token, the Security Token Service signs the token using the private key associated with the CN=STS certificate. In addition, it creates a symmetric key and encrypts it using the public key associated with the CN=localhost certificate. In returning the token to the client, the Security Token Service also returns the symmetric key. The client presents the issued token to the Calculator service, and proves that it knows the symmetric key by signing the message with that key.

Custom Client Credentials and Token Provider

The following steps show how to develop a custom token provider that caches issued tokens and integrate it with WCF: security.

To develop a custom token provider

  1. Write a custom token provider.

    The sample implements a custom token provider that returns a security token retrieved from a cache.

    To perform this task, the custom token provider derives the SecurityTokenProvider class and overrides the GetTokenCore method. This method tries to get a token from the cache, or if a token cannot be found in the cache, retrieves a token from the underlying provider and then caches that token. In both cases the method returns a SecurityToken.

    protected override SecurityToken GetTokenCore(TimeSpan timeout)
    {
      GenericXmlSecurityToken token;
      if (!this.cache.TryGetToken(target, issuer, out token))
      {
        token = (GenericXmlSecurityToken) this.innerTokenProvider.GetToken(timeout);
        this.cache.AddToken(token, target, issuer);
      }
      return token;
    }
    
  2. Write custom security token manager.

    The SecurityTokenManager is used to create a SecurityTokenProvider for a specific SecurityTokenRequirement that is passed to it in the CreateSecurityTokenProvider method. Security token manager is also used to create token authenticators and token serializers, but those are not covered by this sample. In this sample, the custom security token manager inherits from the ClientCredentialsSecurityTokenManager class and overrides the CreateSecurityTokenProvider method to return the custom token provider when the passed token requirements indicate that an issued token is requested.

    class DurableIssuedTokenClientCredentialsTokenManager :
     ClientCredentialsSecurityTokenManager
    {
      IssuedTokenCache cache;
    
      public DurableIssuedTokenClientCredentialsTokenManager ( DurableIssuedTokenClientCredentials creds ): base(creds)
      {
        this.cache = creds.IssuedTokenCache;
      }
    
      public override SecurityTokenProvider CreateSecurityTokenProvider ( SecurityTokenRequirement tokenRequirement )
      {
        if (IsIssuedSecurityTokenRequirement(tokenRequirement))
        {
          return new DurableIssuedSecurityTokenProvider ((IssuedSecurityTokenProvider)base.CreateSecurityTokenProvider( tokenRequirement), this.cache);
        }
        else
        {
          return base.CreateSecurityTokenProvider(tokenRequirement);
        }
      }
    }
    
  3. Write a custom client credential.

    A client credentials class is used to represent the credentials that are configured for the client proxy and creates the security token manager that is used to obtain token authenticators, token providers and token serializers.

    public class DurableIssuedTokenClientCredentials : ClientCredentials
    {
      IssuedTokenCache cache;
    
      public DurableIssuedTokenClientCredentials() : base()
      {
      }
    
      DurableIssuedTokenClientCredentials ( DurableIssuedTokenClientCredentials other) : base(other)
      {
        this.cache = other.cache;
      }
    
      public IssuedTokenCache IssuedTokenCache
      {
        get
        {
          return this.cache;
        }
        set
        {
          this.cache = value;
        }
      }
    
      protected override ClientCredentials CloneCore()
      {
        return new DurableIssuedTokenClientCredentials(this);
      }
    
      public override SecurityTokenManager CreateSecurityTokenManager()
      {
        return new DurableIssuedTokenClientCredentialsTokenManager ((DurableIssuedTokenClientCredentials)this.Clone());
      }
    }
    
  4. Implement the token cache. The sample implementation uses an abstract base class through which consumers of a given token cache interact with the cache.

    public abstract class IssuedTokenCache
    {
      public abstract void AddToken ( GenericXmlSecurityToken token, EndpointAddress target, EndpointAddress issuer);
      public abstract bool TryGetToken(EndpointAddress target, EndpointAddress issuer, out GenericXmlSecurityToken cachedToken);
    }
    // Configure the client to use the custom client credential.
    

    For the client to use the custom client credential, the sample deletes the default client credential class and supplies the new client credential class.

    clientFactory.Endpoint.Behaviors.Remove<ClientCredentials>();
    DurableIssuedTokenClientCredentials durableCreds = new DurableIssuedTokenClientCredentials();
    durableCreds.IssuedTokenCache = cache;
    durableCreds.ServiceCertificate.Authentication.CertificateValidationMode = X509CertificateValidationMode.PeerOrChainTrust;
    clientFactory.Endpoint.Behaviors.Add(durableCreds);
    

Running the sample

See the following instructions to run the sample. When you run the sample, the request for the security token is shown in the Security Token Service console window. The operation requests and responses are displayed in the client and service console windows. Press ENTER in any of the console windows to shut down the application.

The Setup.cmd Batch File

The Setup.cmd batch file included with this sample allows you to configure the server and security token service with relevant certificates to run a self-hosted application. The batch file creates two certificates both in the CurrentUser/TrustedPeople certificate store. The first certificate has a subject name of CN=STS and is used by the Security Token Service to sign the security tokens that it issues to the client. The second certificate has a subject name of CN=localhost and is used by the Security Token Service to encrypt a secret so that the service can decrypt it.

To set up, build, and run the sample

  1. Run the Setup.cmd file to create the required certificates.

  2. To build the solution, follow the instructions in Building the Windows Communication Foundation Samples. Ensure that all the projects in the solution are built (Shared, RSTRSTR, Service, SecurityTokenService, and Client).

  3. Ensure that Service.exe and SecurityTokenService.exe are both running with administrator privileges.

  4. Run Client.exe.

To clean up after the sample

Run Cleanup.cmd in the samples folder once you have finished running the sample.