November 2009

Volume 24 Number 11

Claims-Based Apps - Claims-Based Authorization with WIF

By Michele Leroux | November 2009

Over the past few years, federated security models and claims-based access control have become increasingly popular. In a federated security model, authentication can be performed by a Security Token Service (STS), and the STS can issue security tokens carrying claims that assert the identity of the authenticated user and the user’s access rights. Federation allows users to authenticate in their own domain while being granted access to applications and services that belong to another domain—provided the domains have an established trust relationship. This approach removes the need to provision and manage duplicate accounts for a single user, and enables single sign-on (SSO) scenarios. Claims-based access is central to a federated security model whereby applications and services authorize access to features and functionality based on claims from issuers (the STS) in trusted domains. Claims can contain information about the user, roles or permissions, and this makes for a very flexible authorization model. Together, federated security and claims-based access enable a range of integration scenarios across applications, departments and partners in a wider ecosystem.

 Platform tools in this area have also come a long way. Windows Identity Foundation (WIF) is a rich identity model framework designed for building claims-based applications and services and for supporting active and passive federated security scenarios. With WIF, you can enable passive federation for any ASP.NET application, and integrate a claims-based authorization model into your ASP.NET applications and WCF services without breaking a sweat. Furthermore, WIF provides the plumbing to build custom STS implementations, and includes features and controls to support authentication scenarios that involve managed information cards and identity selectors such as Windows CardSpace. 

WIF significantly reduces the code required to implement rich application scenarios that involve federated and claims-based security. In this two-part article, I’ll focus on the framework’s core functionality for enabling passive federation in ASP.NET applications and for supporting claims-based security models in both WCF and ASP.NET. I’ll focus on WCF in this article and ASP.NET in a later one.

Why Federated and Claims-Based Security?

The benefits of federated and claims-based security can be seen in the context of a few distinct goals:

  • Decoupling the authentication mechanism from applications and services.
  • Replacing roles with claims as a more flexible, granular artifact for authorization.
  • Reducing IT pain related to provisioning and deprovisioning users.
  • Granting trusted domains, including possibly external federated partners, access to application features and functionality.

If even one of these goals rings true for your application scenario, adopting a claims-based model that can immediately or eventually involve federated security is incredibly useful.

When you design your applications and services, the authentication and authorization model is part of this design. For example, an intranet application typically expects users to authenticate to a particular domain with their Windows credentials, while an Internet application typically uses a custom credential store such as Microsoft SQL Server. Applications can also require certificates or Smart Card authentication, or support multiple credential types so that different groups of users can use the appropriate type. If your application will only (and always) expect users to authenticate with a single credential type, your job is easy. More often than not, however, the credential types supported by an application can evolve to support alternative modes of authentication or additional modes that accommodate a different set of users.

For example, an application might support internal users behind the firewall within a domain while also supporting external users over the Internet. When the security model for an application is decoupled from the mode of authentication—as it can be with a claims-based model—there is very little, if any, impact to the application when you introduce new modes of authentication.

In a similar vein, applications are more flexible if authorization is not tied to a fixed set of roles. If your application will always rely on a specific set of roles to authorize access, and if those roles will always carry the same meaning in terms of access rights to features and functionality, you’re again in good shape. But the meaning of roles often varies across departments that use an application and thus require customization. That might mean evaluating roles differently depending on the user’s domain, or allowing custom roles to be created to control access rights. WIF makes adopting a claims-based security model easy so you can decouple roles (if applicable) from the authorization mechanism. This way, logical roles can be mapped to a more granular set of claims, and the application authorizes access based on those claims. If modified or new roles warrant a different set of claims to be issued, the application isn’t affected.

Of course, claims can be much more than just roles or permissions. One of the added benefits of working with a claims-based model is that a claim can carry information about an authenticated user, such as e-mail address, full name, birth date and so on. Claims can also be used to verify information, for example, without sharing a user’s actual age or birth date (information that many users don’t want to be public knowledge). A claim could indicate whether a user is at least the age required to perform an action (a Boolean claim indicating IsOver21 or IsOver13), or verify that a user belongs to a particular department without sharing a list of all departments the user belongs to.

Although decoupling the authentication mechanism and specific roles from applications and services makes accommodating change easier, the claims-based model is also central to federated security scenarios, which make granting access to users belonging to any trusted domain much easier. Federation reduces IT overhead and some of the risks associated with identity management. It removes the need to maintain user credentials across multiple applications or domains, and this helps reduce risks when provisioning and deprovisioning accounts across domains—for example, forgetting to delete an account in multiple places. Password synchronization when multiple copies of an account aren’t managed also ceases to be a problem. Federation also facilitates SSO scenarios because users can log on to one application and be granted access to another (possibly across security domains) without having to authenticate again. Finally, adding new trust relationships between domains is also made easy with federated security platforms such as Active Directory Federation Server (ADFS) and WIF. Thus, extending an application to additional domains within a corporate entity, or even to external partner domains, is streamlined.

Active Federation with WIF

Active federation scenarios are based on the WS-Federation Active Requestor Profile (see the WS-Federation TC at oasis-open.org/committees/tc_home.php?wg_abbrev=wsfed) and the WS-Trust specification (see WS-Trust 1.3 at docs.oasis-open.org/ws-sx/ws-trust/200512/ws-trust-1.3-os.html). From a high level, WS-Trust describes a contract with four service operations: Issue, Validate, Renew and Cancel. Respectively, these operations are called by clients to request a security token, to validate a security token, to renew an expired security token and to cancel a security token that should no longer be used. Each operation processes messages in the form of a Request for Security Token (RST) and sends responses in the form of an RST Response (RSTR) following the WS-Trust specification. These WS-Trust features are implemented by an STS (or token issuer), an important participant in any federated security scenario.

A simple active federation scenario is illustrated in Figure 1. This scenario involves a Windows client application (the requestor), a WCF service (the relying party, or RP), and an STS belonging to the RP domain (RP-STS). As the figure shows, the client uses a WCF proxy to coordinate first authenticating to the RP-STS, then requesting a security token, and then calling the RP, passing the issued security token along with the request.


Figure 1 A Simple Active Federation Scenario

In this scenario, RP-STS is also the Identity Provider (IdP) for users authenticating to the RP domain. That means that the RP-STS is responsible for authenticating users, asserting the identity of those users, and issuing claims relevant to the RP for authorization. The RP verifies that the security token is issued by RP-STS and authorizes access based on the issued claims.

I’ve created a Todo List application to facilitate implementation discussions for this scenario. The accompanying code sample includes a WPF client, a WCF service, and an active STS implemented with WIF. To provide further context, the WCF service, TodoListService, implements the ITodoListService contract shown in Figure 2. The client calls the service by using a WCF proxy to get all the Todo items, and to add, update or delete items. The TodoListService relies on create, read, update and delete claims to authorize access to its operations.

Figure 2 ITodoListService Definition

[ServiceContract(Namespace="urn:TodoListApp/2009/06")]
public interface ITodoListService
{
    [OperationContract]
    List<TodoItem> GetItems();
    [OperationContract]
    string CreateItem(TodoItem item);
    [OperationContract]
    void UpdateItem(TodoItem item);
    [OperationContract]
    void DeleteItem(string id);
}

To implement this active federation scenario, you need to follow these four steps:

  1. Expose a federated security WCF endpoint for the TodoListService.
  2. Generate a WCF proxy for the client application and initialize the proxy with credentials to authenticate to the RP-STS.
  3. Enable WIF for the TodoListService to enable claims-based authorization.
  4. Place permission demands (IsInRole) or other authorization checks to control access to service operations or other functionality.

I’ll discuss these steps in the sections that follow.

Exposing Federated Endpoints

Claims-based WCF services typically expose federated endpoints that receive issued tokens such as those based on the SAML standard. WCF supplies two bindings to support federated security scenarios with WS-Trust. WSFederationHttpBinding is the original standard binding based on WS-Trust 2005 (an earlier version of the protocol), and WS2007FederationHttpBinding is the latest version of the binding (released with Microsoft .NET Framework 3.5) and supports WS-Trust 1.3, the approved standard. Typically, you should use WS2007FederationHttpBinding unless an interoperability requirement dictates the use of the earlier version. An STS based on ADFS version 2 or WIF can support either version of WS-Trust.

When you expose a federated endpoint for a service, you usually provide information about the expected security token format, the required and optional claim types and the trusted token issuer. Figure 3 shows the system.serviceModel listing for the TodoListService, which exposes a single federated endpoint over WS2007FederationHttpBinding.

Figure 3 The Federated Endpoint Exposed by the TodoListService

<system.serviceModel>
  <services>
    <service name="TodoList.TodoListService" 
behaviorConfiguration="serviceBehavior">
      <endpoint address="" binding="ws2007FederationHttpBinding" bindingConfiguration="wsFed" contract="Contracts.ITodoListService" />
      <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
      <host>
        <baseAddresses>
          <add baseAddress="https://localhost:8000/TodoListService"/>
        </baseAddresses>
      </host>
    </service>
  </services>
  <bindings>
    <ws2007FederationHttpBinding>
      <binding name="wsFed">
        <security mode="Message" issuedTokenType=
“https://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-.1#SAMLV1.1" issuedKeyType="SymmetricKey" negotiateServiceCredential="true">
          <message>
            <claimTypeRequirements>
              <add claimType= 
“https://schemas.xmlsoap.org/ws/2005/05/identity/claims/name" isOptional="false"/>
              <add claimType= "urn:TodoListApp/2009/06/claims/permission" 
isOptional="false"/>
            </claimTypeRequirements>
            <issuerMetadata address="https://localhost:8010/rpsts/mex" />
          </message>
        </security>
      </binding>
    </ws2007FederationHttpBinding>
  </bindings>
  <behaviors>
    <serviceBehaviors>
      <behavior name="serviceBehavior">
        <serviceMetadata/>
      </behavior>
    </serviceBehaviors>
  </behaviors>
</system.serviceModel>

Federated security scenarios typically rely on SAML tokens, although this is not a strict requirement. For this scenario, SAML 1.1 tokens are used, as indicated by the issuedTokenType URI (docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.0.pdf). For an alternate token type, such as SAML 1.0 or SAML 2.0, use the URI for that standard.  Of course, the STS indicated in the federated binding configuration must support the token type you are requesting.

Other relevant settings in the message section include issuedKeyType and negotiateServiceCredential. The issuedKeyType setting indicates whether the proof key (see blogs.msdn.com/vbertocci/archive/2008/01/02/on-prooftokens.aspx) is symmetric (the default) or asymmetric (carries more overhead). Once again, this setting must be compatible with the STS. If negotiateServiceCredential is set to true, the client doesn’t need access to the RP public key a priori, but negotiation is not an interoperable protocol. If the client is not a WCF client, you should set negotiateServiceCredential to false. But don’t worry. If it is set to false, proxy generation with SvcUtil supplies the client with a base64 encoded copy of the RP’s public key.

The claim types supplied in the claimTypeRequirements section indicate the required and optional claim types that the service relies on for authorization. In this case, the service expects a name claim to identify the user and at least one permission claim—a custom claim type that indicates the user’s rights to create, read, update or delete Todo items. (These claim types are listed later in Figure 4.) The list of claim types is included in the service metadata so that clients are able to include this information in the RST. Frequently, the STS knows the claims it will issue for a particular RP, which means that the list doesn’t need to be exhaustive in the federation binding.

Figure 4 Claims Issued Per User for the Todo List Application Scenario

The trusted token issuer for this scenario is RP-STS, which happens to be implemented with WIF. RP-STS exposes a single WS-Trust endpoint at https://localhost:8010/rpsts, and its metadata exchange address is located at https://localhost:8010/rpsts/mex. In Figure 3, the issuer’s metadata address is supplied in the issuerMetadata section so that when the client generates the proxy, it can discover the available STS endpoints.

Suppose the STS were to expose multiple endpoints—for example, to authenticate intranet users with Windows credentials at https://localhost:8010/rpsts/internal and to authenticate Internet users with a username and password at https://localhost:8010/rpsts/external. The RP service can opt to specify a particular issuer endpoint associated with its federated endpoint configuration so that when clients generate a proxy, the configuration to communicate with the STS matches that endpoint instead of the first compatible endpoint. You accomplish this by supplying an address for both the issuerMetadata and issuer elements as follows:

<issuerMetadata address="https://localhost:8010/rpsts/mex" />
<issuer address="https://localhost:8010/rpsts/mex/external" />

The advantage of this approach is to simplify proxy generation for clients if there are multiple STS endpoints to choose from and the RP wants to influence which one is used. If the RP doesn’t care which endpoint the client authenticates to, it is best to supply only the issuerMetadata setting and let the client application determine the appropriate endpoint for authentication.

Keep in mind that if the service configuration omits the issuerMetadata element and supplies only the issuer address, the address should evaluate to the issuer’s logical URI (https://localhost:8010/rpsts/issuer), which may not necessarily map to a physical STS endpoint address. An equivalent configuration at the client will prompt the user to select a managed information card from the same issuer (via Windows CardSpace), and the card must also meet the criteria of the requested token format and claim types. For more information on active federation scenarios with Windows CardSpace, see wpfandcardspace.codeplex.com.

Client Proxy Generation

When you generate a proxy for a Windows client using SvcUtil or Add Service Reference, the metadata exchange address for the issuer is used to gather information about the endpoints exposed by the issuer. To reiterate, some possible scenarios are:

  • If the federation binding for the RP service endpoint supplies an issuer metadata address without a specific issuer address, the client configuration will include the first protocol-compatible STS endpoint with any other compatible endpoints commented for the client developer to optionally use.
  • If the federation binding for the RP service supplies an issuer metadata address and a specific issuer address, the client configuration will include that specific address (assuming it is protocol compatible).
  • If the federation binding for the RP service supplies only a metadata address, the client configuration will include only the metadata address as well, without a binding configuration for the issuer. This means an identity selector such as CardSpace will be triggered, as I mentioned earlier.

Assuming that the client generates a proxy for the TodoListService whose configuration is shown in Figure 3 and that the STS exposes a single endpoint, the client-side version of the WS2007FederationHttpBinding configuration will include the following issuer and issuerMetadata settings:

<issuer address="https://localhost:8010/rpsts" 
        binding="ws2007HttpBinding" 
        bindingConfiguration="https://localhost:8010/rpsts">
  <identity>
    <certificate encodedValue="[base64 encoded RP-STS certificate]" />
  </identity>
</issuer>
<issuerMetadata address="https://localhost:8010/rpsts/mex" />

Note that the issuer element specifies the issuer endpoint and the required binding configuration to communicate with that endpoint. In this case, the client authenticates to the STS with a user name and password using message security, as shown in the following WS2007HttpBinding configuration:

<ws2007HttpBinding>
    <binding name="https://localhost:8010/rpsts" >
        <security mode="Message">
            <message clientCredentialType="UserName" 
                     negotiateServiceCredential="false" 
                     algorithmSuite="Default" 
                     establishSecurityContext="false" />
        </security>
    </binding>
</ws2007HttpBinding>

The client endpoint associates the federation binding configuration with the RP endpoint:

<client>
  <endpoint address="https://localhost:8000/TodoListService" 
            binding="ws2007FederationHttpBinding" 
            bindingConfiguration="wsFed"
            contract="TodoList.ITodoListService" name="default">
    <identity>
      <certificate encodedValue="[base64 encoded RP certificate" />
    </identity>
  </endpoint>
</client>

With this configuration, the client proxy need only be initialized with a valid user name and password before calling the service:

TodoListServiceProxy _Proxy = new TodoListServiceProxy("default");

if (!ShowLogin()) return;

this._Proxy.ClientCredentials.UserName.UserName = this.Username;
this._Proxy.ClientCredentials.UserName.Password = this.Password;
this._TodoItems = this._Proxy.GetItems();

Token Issuance

The proxy first supplies credentials to authenticate to RP-STS, sending an RST that asks for a SAML 1.1 token, indicating that the RP requires at least one name and permission claim. The user is authenticated against the STS credential store, and the appropriate claims are issued for the authenticated user. The proxy then processes the RSTR that carries the issued token and passes that token to the RP to establish a secure session for the authenticated user.

For this example, the STS was built with WIF and authenticates users against a custom credential store, issuing claims for each user according to Figure 4.

Note that an STS based on ADFS version 2 authenticates users against the Windows domain and issues claims according to your ADFS configuration. A custom STS based on WIF can authenticate users against a credential store of your choosing, but you must roll your own code to manage the credential store and the relevant claims-mapping process.

 Identity Model Configuration

To enable claims-based authorization for your WCF services using WIF, you initialize the ServiceHost instance for federation. You can do this programmatically by calling the ConfigureServiceHost method exposed by the FederatedServiceCredentials type, as follows:

ServiceHost host = new ServiceHost(typeof(TodoList.TodoListService));
FederatedServiceCredentials.ConfigureServiceHost(host);
host.Open();

You can achieve the same result declaratively by using the behavior extension ConfigurationServiceHostBehaviorExtension:

<serviceBehaviors>
  <behavior name="fedBehavior" > 
    <federatedServiceHostConfiguration/>
    <serviceMetadata />
  </behavior>
</serviceBehaviors>

In either case, the ServiceHost is assigned an instance of the FederatedServiceCredentials type to drive claims-based authorization behavior for the service. This type can be initialized either programmatically or by the microsoft.identityModel configuration section for the service. Identity model settings are specific to WIF and supply settings for claims-based authorization in ASP.NET and WCF applications, most of which are summarized in Figure 5.

Figure 5 Summary of the Essential microsoft.identityModel Elements

For WCF services that use WIF, you no longer need to initialize the ServiceHost with typical WCF authentication and authorization behaviors. WIF supersedes this and provides a cleaner way to configure security in general. (WIF is useful beyond claims-based and federated scenarios). Figure 6 shows the identity model settings used for the TodoListService.

Figure 6 Identity Model Settings Frequently Supplied for WCF Services

<microsoft.identityModel>
  <service>
    <issuerNameRegistry type="Microsoft.IdentityModel.Tokens.
      ConfigurationBasedIssuerNameRegistry, Microsoft.IdentityModel, 
      Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35">
      <trustedIssuers>
        <add name="https://localhost:8010/rpsts" thumbprint=
"c3 95 cd 4a 74 09 a7 77 d4 e3 de 46 d7 08 49 86 76 1a 99 50"/>
      </trustedIssuers>
    </issuerNameRegistry>
    <serviceCertificate>
      <certificateReference findValue="CN=RP" storeLocation="LocalMachine" 
         storeName="My" x509FindType="FindBySubjectDistinguishedName"/>
    </serviceCertificate>
    <audienceUris mode="Always">
      <add value="https://localhost:8000/TodoService"/>
    </audienceUris>
    <certificateValidation certificateValidationMode="PeerTrust" />         
    <securityTokenHandlers>
      <remove type="Microsoft.IdentityModel.Tokens.Saml11.
         Saml11SecurityTokenHandler, Microsoft.IdentityModel, 
         Version=1.0.0.0, Culture=neutral, 
         PublicKeyToken=31bf3856ad364e35"/>
      <add type="Microsoft.IdentityModel.Tokens.Saml11.
         Saml11SecurityTokenHandler, Microsoft.IdentityModel, 
         Version=1.0.0.0, Culture=neutral, 
         PublicKeyToken=31bf3856ad364e35">
        <samlSecurityTokenRequirement >
          <roleClaimType 
            value="urn:TodoListApp/2009/06/claims/permission"/>
        </samlSecurityTokenRequirement>
      </add>
    </securityTokenHandlers>
    <claimsAuthorizationManager 
      type="TodoList.CustomClaimsAuthorizationManager, TodoList"/>
  </service>
</microsoft.identityModel>

The issuerNameRegistry setting is used to specify any trusted certificate issuers. If you use the ConfigurationBasedIssuerNameRegistry as shown in Figure 6, you must provide a list of trusted certificate issuers by specifying their thumbprints. At run time, the ConfigurationBasedIssuerNameRegistry checks X509 security tokens against this list and rejects those with thumbprints not found in the list. You can use the SimpleIssuerNameRegistry to allow any X509 or RSA token, but more likely you will supply a custom IssuerNameRegistry type to validate tokens using your own heuristics if the ConfigurationBasedIssuerNameRegistry doesn’t do the trick.

The configuration in Figure 6 rejects any tokens that are not signed by the RP-STS (using the certificate thumbprint for CN=RPSTS). The following configuration instead specifies a custom IssuerNameRegistry type, TrustedIssuerNameRegistry:

<issuerNameRegistry type="TodoListHost.TrustedIssuerNameRegistry, TodoListHost"/>

The TrustedIssuerNameRegistry implementation is used to achieve the same result—rejecting tokens not signed by CN=RPSTS by checking the subject name of the incoming token:

public class TrustedIssuerNameRegistry : IssuerNameRegistry
{
    public override string GetIssuerName(SecurityToken securityToken)
    {
        X509SecurityToken x509Token = securityToken as
            X509SecurityToken;
        if (x509Token != null)
        {
            if (String.Equals(x509Token.Certificate.SubjectName.Name,
                "CN=RPSTS"))
            {
                return x509Token.Certificate.SubjectName.Name;
            }
        }

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

The serviceCertificate setting in Figure 6 indicates the certificate to be used to decrypt incoming security tokens, assuming they are encrypted for the RP by the issuing STS. For the Todo List application, the RP-STS encrypts tokens using the public key for the RP, CN=RP.

Usually, the SAML token includes an audience URI element that evaluates to the RP, indicating who the token was issued for. You can explicitly refuse tokens that were not intended to be sent to the RP. By default, the audienceUris mode is set to Always, which means that you must supply at least one URI for validation against incoming tokens. In Figure 6, the configuration allows only SAML tokens that include an audience URI matching the TodoListService address. Although not generally recommended, you can set the audienceUris mode to Never to suppress evaluation of the audience restriction condition for an incoming SAML token:

<audienceUris mode="Never"/>

Be aware that when the client sends an RST to the STS, it usually includes an AppliesTo setting that indicates who the token should be issued to—the RP. The STS can use this information to populate the SAML token audience Uri.

The certificateValidation setting controls how incoming X509 tokens—those used for token signatures, for example—are validated. In Figure 6, certificateValidationMode is set to PeerTrust, which means that certificates are valid only if the associated certificate is found in the TrustedPeople store. This setting is more appropriate than PeerOrChainTrust (the default) for token issuer validation because it requires you to explicitly install the trusted certificate in the certificate store. PeerOrChainTrust indicates that signatures are also authorized if the root certificate authority (CA) is trusted, which on most machines includes a significant list of trusted CAs.

I’ll discuss a few of the other settings from Figure 5 and Figure 6 shortly. One other point to make about the subject of WIF initialization is that you can also programmatically initialize an instance of FederatedServiceCredentials and pass it to ConfigureServiceHost rather than initializing from the microsoft.identityModel section. The following code illustrates this:

ServiceHost host = new ServiceHost(typeof(TodoList.TodoListService));

ServiceConfiguration fedConfig = new ServiceConfiguration();
fedConfig.IssuerNameRegistry = new TrustedIssuerNameRegistry();
fedConfig.AudienceRestriction.AudienceMode = AudienceUriMode.Always;
fedConfig.AudienceRestriction.AllowedAudienceUris.Add(new 
Uri("https://localhost:8000/TodoListService"));
fedConfig.CertificateValidationMode = 
X509CertificateValidationMode.PeerTrust;
fedConfig.ServiceCertificate = CertificateUtil.GetCertificate(
StoreName.My, StoreLocation.LocalMachine, "CN=RP");

FederatedServiceCredentials fedCreds = 
new FederatedServiceCredentials(fedConfig);

FederatedServiceCredentials.ConfigureServiceHost(host,fedConfig);
host.Open();

Programmatic initialization is particularly useful for initializing the ServiceHost from database settings that apply to an entire server farm.

WIF Component Architecture

When you apply WIF behavior to a ServiceHost, several WIF components are initialized to facilitate claims-based authorization—many of them WCF extensions. Ultimately, this leads to a ClaimsPrincipal being attached to the request thread, and this supports claims-based authorization. Figure 7 captures the relationship between the core WIF components and the ServiceHost.


Figure 7 Core Components Installed with WIF

The FederatedServiceCredentials type replaces the default ServiceCredentials behavior, and the IdentityModelServiceAuthorizationManager (installed during initialization of FederatedServiceCredentials) replaces the default ServiceAuthorizationBehavior. FederatedServiceCredentials also constructs a FederatedSecurityTokenManager instance.  Collectively, these types drive authentication and authorization for each request, with the help of the ClaimsAuthenticationManager, the ClaimsAuthorizationManager, and the SecurityTokenHandler that applies to the specific request.

Figure 8 illustrates the flow of communication to these components that leads to constructing a security principal for the request thread—in this case, a ClaimsPrincipal type—and to opportunities to authorize access based on this security principal.


Figure 8 Components That Create and Can Authorize Against the ClaimsPrincipal

The FederatedSecurityTokenManager returns the appropriate token handler for the request—which in this case would be the Saml11SecurityTokenHandler—providing it with a reference to the ClaimsAuthenticationManager. The token handler constructs a ClaimsIdentity from the incoming token, creates the ClaimsPrincipal (via a wrapper class) and passes it to the ValidateToken method for the ClaimsAuthenticationManager. This yields an opportunity to modify or replace the ClaimsPrincipal that will be attached to the request thread. The default implementation merely returns the same ClaimsPrincipal provided:

public virtual IClaimsPrincipal Authenticate(string resourceName, IClaimsPrincipal incomingPrincipal)
{
    return incomingPrincipal;
}

You might consider providing a custom ClaimsAuthenticationManager to transform the incoming claims from the security token into something that the RP can use to authorize access. For this example, however, the SAML token carries the appropriate RP claims issued by RP-STS, and so the ClaimsPrincipal constructed from those claims works for authorization.

Next, the IdentityModelServiceAuthorizationManager, which references the ClaimsAuthorizationManager, calls its CheckAccess method, yielding an opportunity to customize how access is controlled. The default implementation does not restrict access:

public virtual bool CheckAccess(AuthorizationContext context)
{
    return true;
}

The AuthorizationContext parameter supplies access to the ClaimsPrincipal and its associated claims, a collection of actions relevant to the request (such as a URI indicating the service operation to be called) and information about the resource associated with the request (for example, the service URI), which can be useful to disambiguate calls to multiple services passing through the same authorization path. To implement centralized authorization, you can supply a custom ClaimsAuthorizationManager. I will describe an example when I discuss techniques for authorization.

Role-based security in the .NET Framework is founded on the premise that a security principal based on IPrincipal is attached to each thread, and this security principal wraps the identity of the authenticated user in an IIdentity implementation. Without WIF, WCF attaches a security principal to each request thread based on the system.serviceModel configuration for authentication and authorization. An IIdentity type is constructed based on the type of credentials presented for authentication. For example, a Windows credential will evaluate to a WindowsIdentity, an X.509 certificate to an X509Identity, and a UserName token to a GenericIdentity. The ServiceAuthorizationBehavior controls the type of IPrincipal wrapper for the identity. For example, with Windows authorization, a WindowsPrincipal is constructed; with the ASP.NET membership provider, a RoleProviderPrincipal. Otherwise, a custom authorization policy is used to construct an IPrincipal object of your choosing. The IPrincipal object exposes an IsInRole method that can be called directly or indirectly through permission demands to control access to features and functionality.

WIF extends this model by supplying ClaimsPrincipal and ClaimsIdentity types—based on IClaimsPrincipal and IClaimsIdentity—that ultimately derive from IPrincipal and IIdentity. All tokens are mapped to a ClaimsIdentity with WIF. When each incoming security token is validated, its associated SecurityTokenHandler type constructs a ClaimsIdentity, supplying it the appropriate claims. This ClaimsIdentity is wrapped in a ClaimsIdentityCollection (in the event a token produces multiple ClaimsIdentity instances), and this collection is wrapped in a ClaimsPrincipal and attached to the request thread. It is this ClaimsPrincipal that is the heart of WIF authorization for your WCF services.

Claims-Based Authorization

For WCF services, your approach to authorization will likely involve one of the following techniques:

  • Use the ClaimsPrincipal to perform dynamic IsInRole checks.
  • Use the PrincipalPermission type to perform dynamic permission demands.
  • Use the PrincipalPermissionAttribute to supply declarative permission demands at each operation.
  • Provide a custom ClaimsAuthorizationManager to centralize access checks in a single component.

The first three of these options ultimately rely on the IsInRole method exposed by the ClaimsPrincipal type. This doesn’t exactly mean you are doing role-based security; it just means that you select a role-claim type so that the correct claims are checked against the requested claims passed to IsInRole. The default role-claim type for WIF is schemas.microsoft.com/ws/2008/06/identity/claims/role. If the STS associated with the federation scenario issues this type of claim, you can optionally control access based on this claim type. For the Todo List application scenario, I mentioned that a custom permission claim type is used for authorization, so the identity model configuration must specify this as the role-claim type to facilitate IsInRole checks.

You provide the role claim type to the SecurityTokenHandler for the expected token type, in this case the Saml11SecurityTokenHandler. As Figure 6 illustrates, you can modify the default configuration for a SecurityTokenHandler by removing it and then adding the same one again, specifying the preferred property settings. SAML token handlers have a samlSecurityTokenRequirement section in which you can provide a setting for the name or role-claim type, along with other settings related to certificate validation and Windows tokens. For this scenario, I supplied a custom role claim type:

<samlSecurityTokenRequirement >
  <roleClaimType value= "urn:TodoListApp/2009/06/claims/permission"/>
</samlSecurityTokenRequirement>

What this means is that any time IsInRole is called for the ClaimsPrincipal, I check for a valid permission claim. One way to accomplish this is to explicitly call IsInRole before a section of code executes that requires a particular claim. You can access the current principal through the Thread.CurrentPrincipal property as follows:

if (!Thread.CurrentPrincipal.
IsInRole("urn:TodoListApp/2009/06/claims/permission/delete"))
  throw new SecurityException("Access is denied.");

Aside from explicit IsInRole checks at run time, you can also write classic role-based permission demands using the PrincipalPermission type. You initialize the type with the required role claim (the second constructor parameter), and when Demand is called, the IsInRole method of the current principal is called. An exception is thrown if the claim is not found:

PrincipalPermission p = new PrincipalPermission("", "urn:TodoListApp/2009/06/claims/permission/delete");
p.Demand();

You can also build a PermissionSet to collect several claims to check for:

PermissionSet ps = new PermissionSet(PermissionState.Unrestricted);
ps.AddPermission(new PrincipalPermission("", "urn:TodoListApp/2009/06/claims/permission/create"));
ps.AddPermission(new PrincipalPermission("", "urn:TodoListApp/2009/06/claims/permission/read"));
ps.Demand();

If access checks apply to the entire service operation, you can apply the PrincipalPermissionAttribute instead, which is a nice way to declaratively associate required claims to the operation being called. These attributes can also be stacked to check for multiple claims:

[PrincipalPermission(SecurityAction.Demand, Role = Constants.Permissions.Create)]
[PrincipalPermission(SecurityAction.Demand, Role = Constants.Permissions.Read)]
public string CreateItem(TodoItem item)

In some cases, you might find it useful to centralize authorization to a single component, which means you would provide a custom ClaimsAuthorizationManager to perform access checks. Figure 6 illustrates how to configure a custom ClaimsAuthorizationManager, and the implementation of this for the TodoListService is shown in Figure 9 (partially listed for brevity).

Figure 9 Custom ClaimsAuthorizationManager Implementation

class CustomClaimsAuthorizationManager : ClaimsAuthorizationManager
{
    public CustomClaimsAuthorizationManager()
    {
    }

    public override bool CheckAccess(AuthorizationContext context)
    {
        
        if (context.Resource.Where(x=> x.ClaimType == 
            System.IdentityModel.Claims.ClaimTypes.Name && x.Value == 
            "https://localhost:8000/TodoListService").Count() > 0)
        {
            if (context.Action.Where(x=> x.ClaimType == 
                System.IdentityModel.Claims.ClaimTypes.Name && x.Value == 
                Constants.Actions.GetItems).Count() > 0)
            {
                return
                    context.Principal.IsInRole(
                       Constants.Permissions.Read);
            }

        // other action checks for TodoListService
        }
        return false;
    }  
}

The ClaimsAuthorizationManager provides an override for CheckAccess that receives an AuthorizationContext parameter with reference to the resource (in this case, the service URI); a collection of actions (in this case, a single action indicating the service operation URI); and the ClaimsPrincipal, which is not yet attached to the request thread. You can check the resource if the component is shared across services, as this example does for illustration. Primarily, you will check the action against a list of service operation URIs and perform IsInRole checks according to the requirements of the operation.

Generally, I’m not a big fan of decoupling the authorization check from the protected operation or code block. It is much easier to maintain code that is declared at a location in context with the activity.

To Be Continued

At this point you should have a pretty good idea of how to set up an active federation scenario with WCF and WIF, including understanding federation bindings for WCF and proxy generation semantics; the token issuance process; configuring WIF at the service; and implementing various claims-based authorization techniques. In a follow-up article, I will move on to passive federation with ASP.NET and WIF.


Michele Leroux Bustamante is chief architect at IDesign, Microsoft regional director for San Diego and a Microsoft MVP for Connected Systems. Her latest book is “Learning WCF.” Reach her at mlb@idesign.net or visit idesign.net.