How to augment WIF features to streamline authorization for WCF services
Windows Identity Foundation (WIF) supplies the features to support claims-based identity for Windows Communication Foundation (WCF) services. Once WIF is enabled for a service, WIF components will process incoming security tokens and supply related authentication functionality. You provide configuration settings in the WIF configuration section to control the service certificate, the token handlers to be used and, in the case of issued tokens, which issuers are trusted and what the audience requirements are.
Ultimately, the WIF runtime will attach a ClaimsPrincipal to the request thread so that you can write code to inspect the user’s claims and authorize access based on those claims. You can interact with claims for the request by providing a custom ClaimsAuthenticationManager or ClaimsAuthorizationManager, writing code that checks claims surfaced by the ClaimsPrincipal, or utilizing the ClaimsPrincipalPermission or ClaimsPrincipalPermissionAttribute to make permission demands based on claims.
In this column, I'll review these features of WIF briefly; then I'll focus on how to enhance the authorization experience by taking it a step further. I’ll show you how to create a custom ServiceAuthorizationManager to install a custom ClaimsPrincipal type that can expose a friendlier object model for authorization checks.
WIF Components for WCF
In earlier installments to this column, I provided an overview of WIF components that play a role in service authentication and authorization. Figure 1 summarizes the flow of communication through these components. WIF security token handlers are responsible for authenticating incoming security tokens with each request and producing claims for each token. During token validation the ClaimsAuthenticationManager is called, yielding an opportunity to validate incoming claims and augment those claims for the request thread. Post authentication, the ClaimsAuthorizationManager is called to authorize access before they reach the service operation.
If the ClaimsAuthorizationManager doesn't deny access, service operations can use permission demands or write code within the operation to check claims for authorization purposes. For example, WIF provides the ClaimsPrincipalPermission and associated ClaimsPrincipalPermissionAttribute to support permission demands at the service operation.
The following summarizes what I would typically expect to do with these components in a WCF service application:
- Let the built-in WIF token handler authenticate incoming tokens and provide a custom token handler as needed for username and password authentication, for custom tokens, or to override default token authentication functionality. The token handler will produce claims available to the ClaimsAuthenticationManager.
- Supply a custom ClaimsAuthenticationManager to look at incoming claims and, if appropriate, transform those claims into something I can use for authorization at the service. For example, if I’m receiving tokens from an Identity Provider (IdP), it might be necessary to look up the user identity in my application database and gather their roles, permissions, and other attributes. If I’m receiving a token from my own resource STS that has already gathered these claims, I might not need to transform claims at this step. At this step I would also ensure that the ClaimsIdentity looks at the correct name and role claim types.
- Supply a custom ClaimsAuthorizationManager to perform access control checks. You can use this component for central access checks before calls reach the service operation—and you can also rig the component to process individual resource checks triggered from the ClaimsPrincipalPermission and ClaimsPrincipalPermissionAttribute. This is worth a little discussion (to follow).
- Check individual claims at the service operation boundary using an attribute like the ClaimsPrincipalPermissionAttribute or PrincipalPermissionAttribute—depending on your approach to authorization (centralized or per operation) and depending on the implementation of the ClaimsAuthorizationManager.
Now let's look at one possible scenario for utilizing these components for authorization, with the intent to later discuss an alternate approach that improves on this model.
Implementing an Authorization Model
For this discussion let’s assume that I want to implement an authorization model for the example Figure 1 shows. Users supply a username and password to authenticate against a custom credential store, which produces identity claims that include the roles a user belongs to. Additional claims are gathered from a permissions table indicating which secured resources a user has access to. The CustomClaimsAuthenticationManager will handle retrieving those claims from the database and producing a new ClaimsIdentity for the request thread.
The role claim type will be mapped to the Permission claim type so that permission demands will look at Permission claims to allow or deny access. Figure 2 illustrates an implementation of this component. The CustomClaimsAuthorizationManager will handle authorization centrally and will be engaged when the ClaimsPrincipalPermissionAttribute is applied to specific service operations.
Figure 3 illustrates the user roles and permissions granted each role for this scenario. In addition to this allocation of Role and Permission claims, a LicenseKey claim exists for all users. Following the example in Figure 1, this particular implementation will rely on the CustomUserNameSecurityTokenHandler to authenticate the user and produce Name, LicenseKey, and Role claims (Figure 4).
The Authenticate() method of the CustomClaimsAuthenticationManager will gather Permission claims based on the roles assigned during authentication (Figure 2). In this example, a new ClaimsIdentity is produced with all the user’s claims. This identity is added to a new ClaimsPrincipal.
Optionally, you can create a custom ClaimsIdentity type in your ClaimsAuthenticationManager implementation, but creating a custom ClaimsPrincipal type at this point is futile. If you want to use a custom ClaimsPrincipal type at runtime, you must use an alternate approach which I'll discuss later.
The first stop for authorization is the CustomClaimsAuthorizationManager. In the implementation that Figure 5 shows, the following happens:
- If the LicenseKey claim is not present, access is denied.
- If the user does not have the appropriate claim to access the service operation, access is denied.
Figure 5 shows a simplified view of what a policy engine might do to encapsulate rules for calling service operations. Most likely, the validation of action would confirm a match for the entire SOAP action Uri, or use some form of relative action value for a simpler disambiguation. A policy engine might also use another means to determine if the user has rights to call the operation based on their attributes, the time of day, the day of the week, and more. For simplicity, Figure 5 is hard coded to illustrate a simple policy.
As an alternative (or, in combination) to relying on central authorization checks by the ClaimAuthorizationManager, you can employ permission demands at the service operation and explicitly indicate the required claims to access the operation. In Figure 6, use of the PrincipalPermissionAttribute type and its associated PrincipalPermission type requires that a user have a particular permission to access the operation. The permission demand looks for a Permission claim as the role, because the ClaimsIdentity was configured to use the Permission claim type as the role claim type.
The ClaimsPrincipalPermission type is an implementation of IPermission for executing permission demands at runtime. ClaimsPrincipalPermission calls the configured ClaimsAuthorizationManager, passing the resource and action you supply. The ClaimsPrincipalPermissionAttribute type supplies a way to apply these permission demands declaratively. Using the same service operations as an example, you might do what Figure 7 shows. This requires the ClaimsAuthorizationManager to handle these resource and action combinations. The implementation in Figure 5 might then change to what Figure 8 shows.
Again, this implementation hard-codes a check for each type of resource and expects that there is a claim to satisfy that resource. But policy configuration can be much more dynamic than this in the internal policy engine. I’m also making an assumption that a single resource and action will be presented. In fact, you can stack ClaimsPrincipalPermissionAttribute or create a collection of ClaimsPrincipalPermission objects before you execute a permission demand. For example, both my application protected Customer and Order objects were accessed from a PrintReport() operation:
\\[ClaimsPrincipalPermission(SecurityAction.Demand, Operation = "Read", Resource = "Customer")\\]<br>\\[ClaimsPrincipalPermission(SecurityAction.Demand, Operation = "Read", Resource = "Order")\\]<br>public void PrintReport()
In theory, this will produce a multiple resource check; however, the default implementation of ClaimsPrincipalPermission will iterate through each resource and action (operation) pair and individually invoke CheckAccess(). Thus, a single resource and action claim is always presented. You could customize this behavior by calling the ClaimsAuthorizationManager from your own code and passing additional information—for example, from a custom ServiceAuthorizationManager. In that case, you should expect to handle multiple resource and action claims in the CheckAccess() call.
Implementing a Custom IClaimsPrincipal and IClaimsIdentity
The strategy discussed thus far is not a bad one. With the components supplied by WIF you can centralize your policy management in the ClaimsAuthorizationManager and choose from a few strategies, such as checking claims required to access each service operation or explicitly indicating the resource and action using permission demands and writing policy against those artifacts.
In either case, another way to enhance this experience is to create a custom ClaimsPrincipal and a custom ClaimsIdentity type to expose a friendlier object model for accessing specific claims or for checking claims at runtime. Figure 9 shows one possible listing for this implementation. The CustomClaimsIdentity can be created during authentication and provide quick access to application specific claims—in this case, LicenseKey, Role, and Permission claims—rather than having to fight through the cumbersome claims object model or rely on extension methods. The CustomClaimsPrincipal type is useful for returning the CustomClaimsIdentity for the Identity property. Multiple identities might be present and you want to be sure and default to your custom type. Better, you want to validate that your custom type is present; if not, the application might not be configured correctly. The possibilities for these custom components can be driven by your requirements.
Installing a Custom ClaimsPrincipal
Developers often think that they can attach a custom ClaimsPrincipal type to the request thread just by returning it from the Authenticate() method in their ClaimsAuthenticationManager implementation. After all, that implementation does expect you to return an instance of type IClaimsPrincipal:
public override IClaimsPrincipal Authenticate(string resourceName, IClaimsPrincipal incomingPrincipal)
If you create a custom IClaimsIdentity during Authenticate() and add this to the returned principal this is indeed available to the ClaimsAuthorizationManager and to the request thread thereafter. However, no matter what type of ClaimsPrincipal you return this custom identity instance will always be encapsulated in a plain-old ClaimsPrincipal type by the time you see it again. You can overcome this by implementing a custom version of the IdentityModelServiceAuthorizationManager, which Figure 10 shows.
WIF requires that the ServiceAuthorizationManager inherit IdentityModelServiceAuthorizationManager, so don’t try to create a custom ServiceAuthorizationManager and get away with it. In your custom implementation, you have to at least do the basics:
- Initialize the ServiceSecurityContext
- Validate that FederatedServiceCredentials are installed and that you have a valid action and to header
- Create your custom principal
- Assign that custom principal to the AuthorizationContext
- Invoke the configured ClaimsAuthorizationManager passing in the AuthorizationContext with your custom principal
Now you can safely count on your very own IClaimsPrincipal type to be installed to the request thread and to be available to your ClaimsAuthorizationManager. Not so hard but a little elusive!
Hopefully, this article gives you a good overview of the WIF components you can use to implement authorization features for your applications. You can use these components to drive calling in to your custom policy engine to evaluate authorization rules—just like we do in our custom client components for the Keystone product at BiTKOO. Of course, we do a few more things beyond what I've discussed here, but if you have any questions about this type of custom implementation for your own applications, you know who to ask. I look forward to hearing from you.
Michele Leroux Bustamante (email@example.com) is chief architect for IDesign, chief security architect for BiTKOO, a Microsoft Regional Director for San Diego, and a Microsoft MVP for Connected Systems. Her latest book is Learning WCF (O'Reilly), and she blogs at www.dasblonde.net.