Securing WCF Services Using the Windows Identity Foundation (WIF) - Real World .NET, C#, and Silverlight: Indispensible Experiences from 15 MVPs (2012)

Real World .NET, C#, and Silverlight: Indispensible Experiences from 15 MVPs (2012)

Chapter 10

Securing WCF Services Using the Windows Identity Foundation (WIF)

by Dominick Baier

If you are a software security geek like me, the world of distributed applications is one of the most exciting places to be. You can encounter a multitude of client types, network and authentication protocols, credential types, and requirements. In other words, you have just the complexity you need to feel like a real expert — or a little lost.

Although, in theory, the Windows Communication Foundation (WCF) has all the features you need to build even the most complex distributed systems, as always, complexity is the biggest enemy of security. That's the reason why Microsoft gave WCF security (and .NET security, in general — but more on that later) a refresh that enables you to build these systems with better abstraction layers and less error-prone code. This refresh is called the Windows Identity Foundation (WIF), and this chapter examines how to use this technology with WCF Simple Object Access Protocol (SOAP) and Representational State Transfer (REST) services.

note

The sample code used in this chapter, as well as the Thinktecture.IdentityModel library, is part of the code available for download on this book's companion website (www.wrox.com). Parts of the code are based on the movie database service described in Chapter 9.

Identity in .NET Applications

Since the first release of the .NET Framework, Microsoft wanted to give developers a standard and unified way to represent identity and access control in applications.

This section provides a brief history of the approaches Microsoft took, and how WCF changed the game. It concludes with a description of the concepts that WIF adds, and, more important, why WIF is the new (and preferred) way to model identity in your applications.

note

Especially when it comes to concepts such as claims, tokens, and federation, this chapter cannot provide a full introduction. Look at the free guide to identity and access control from Microsoft's Patterns & Practices group at http://tinyurl.com/claimsguide. For a more WIF API-centric book, check out Programming Windows Identity Foundation by Vittorio Bertocci (Redmond, Washington: Microsoft Press, 2010).

Identity in the Base Class Library

The System.Security.Principal.IPrincipal interface provides a standard way to do role-based access checks and, in turn, wrap an instance of the IIdentity interface that holds the username and information about the authentication method. In addition, there is a per-thread storage “slot” to store that principal on Thread.CurrentPrincipal. Listing 10.1 shows this interface.

Listing 10.1: IPrincipal and IIdentity

interface IPrincipal

{

IIdentity Identity { get; }

bool IsInRole(string roleName);

}

interface IIdentity

{

bool IsAuthenticated { get; }

string AuthenticationType { get; }

string Name { get; }

}

This enables writing standard plumbing to query authentication and authorization information for the current user. (Because the principal is stored on a thread-static property, it enables scenarios in which multiple concurrent clients use the application — such as WCF or ASP.NET.) Examples of such standard plumbing would be Base Class Library (BCL) classes such as PrincipalPermission, or ASP.NET's URL Authorization.

These interfaces are deliberately minimal to provide common ground, and are meant for customization to adapt to different authentication types and application scenarios. The framework itself includes a number of implementations for such common scenarios as the following:

· WindowsPrincipal/WindowsIdentity — Represents a Windows user and groups

· GenericPrincipal/GenericIdentity — Represents a generic user (for example, backed by a custom user database)

· FormsIdentity — Represents an ASP.NET Forms Authentication user and information in the corresponding cookie

Other common credential types such as an X.509 certificate, unfortunately, don't have an IPrincipal representation.

On the other hand, because so many different implementations exist (and each comes with its own optimization for the concrete authentication type to make it more useful), it was difficult to write applications that should support multiple authentication and credential types. This could be especially challenging for independent software vendors (ISVs) who must write generic software without a priori knowledge of the security and authentication system of their customers.

Another consequence of the IPrincipal interface design is the focus on role-based security for authorization. This is not a bad thing because roles are extremely useful for coarse-grained authorization. But when you want to do more fine-grained security decisions (or maybe even just personalization), you must come up with your own custom implementations (which not always helps the greater good).

Identity in WCF

WCF was designed a few years later than the BCL. While retaining some backward compatibility with the original IPrincipal idea, WCF also featured a brand new security system to cater for the vast amount of scenarios it was built for.

WCF security was built around the notion of security tokens and claims. The WCF team created a completely new object model centered on the ServiceSecurityContext type for that purpose.

Security Tokens and Claims

WCF introduced important security concepts into the .NET Framework. One is the notion of a security token. A token is the outcome of an authentication process and describes a credential. WCF comes with a number of implementations of security tokens (all derived from the abstract base class called SecurityToken), such as for Kerberos, usernames and passwords, X.509 certificates, and Security Assertion Markup Language (SAML). One interesting aspect of security tokens is that they can be serialized and transferred between services (even across traditional security boundaries). This is important when it comes to security token services, SAML, and federation.

In a nutshell, a claim is a statement about an entity, typically a user in your system. A claim consists of three pieces of information: the type of the statement, the statement itself, and the issuer of that statement. When translated into the role mindset, this is a claim, such as “dominick is a domain administrator (says the domain controller).” But claims go further than roles because they enable more than just simple yes/no decisions, such as, “dominick's e-mail address is dominick.baier@thinktecture.com (says our directory service).” Claims can be hydrated from the contents of a security token, or you have application local logic that adds claims to the client's security context.

Both tokens and claims can be accessed in WCF via ServiceSecurityContext.Current (WCF's version of Thread.CurrentPrincipal).

This new system was more powerful and flexible compared to the standard .NET facilities — but this came with a price. First, programming against and extending the new system was more difficult and often required an intimate knowledge of the inner workings of WCF to succeed. But even worse, this new system broke backward compatibility to the BCL system in a lot of areas. (Thread.CurrentPrincipal is only available in certain situations in WCF.) When you were in a situation in which you needed to maintain both ASP.NET and WCF code, you often ended up duplicating security-related code to work against both security systems. Again, this could lead to errors and complexity.

Windows Identity Foundation

The purpose of the WIF library is to combine the power of the WCF security concepts with the simplicity and pervasiveness of the BCL IPrinpical approach.

This is achieved by introducing a class called Claim (and a corresponding ClaimCollection):

public class Claim

{

public virtual string ClaimType { get; }

public virtual string Value { get; }

public virtual string Issuer { get; }

// rest omitted

}

This enables packaging an arbitrary number of statements about the current user. To attach these statements to the current thread of execution in your application, claims-aware versions of IPrincipal and IIdentity have been created. They are called IClaimsPrincipal and IClaimsIdentity, and enable coupling a collection of claims with the current principal:

interface IClaimsPrincipal : IPrincipal

{

ClaimsIdentityCollection Identities { get; }

// rest omitted

}

interface IClaimsIdentity : IIdentity

{

ClaimCollection Claims { get; }

// rest omitted

}

Deriving from IPrincipal and IIdentity has the handy side effects that you can start using Thread.CurrentPrincipal again (regardless of ASP.NET or WCF), and that “legacy” code won't break when enabling WIF (because this code can simply see the standard BCL versions of the interfaces, which behave the same for backward-compatibility reasons). Also, because the IClaimsIdentity is generic and can hold arbitrary information, there is less (to no) need to provide custom implementations of the principal and identity.

Layering WIF on top of the WCF (and ASP.NET) security system has several benefits:

· Claims become a first-class citizen in every .NET application. Although WIF provides the plumbing to “claim-enable” arbitrary applications, you can find out-of-the-box support for WCF and ASP.NET.

· A number of new easy-to-use extensibility points are included. And, even more important, the same extensibility exists for ASP.NET and WCF, which means that you must write that code only once, and it works the same in both environments.

· The handling of credentials types and security tokens has been dramatically simplified (especially when compared to WCF's native programming model).

· By providing a single abstraction (the claims collection) over arbitrary authentication protocols and credential types, your code becomes agnostic to these low-level details. That effectively means that you can decouple your application logic from the low-level security details, which is huge.

In addition to replacing the principal/identity with a claims-aware version, WIF adds three other important concepts: security token handlers, claims transformation, and claims-based authorization. The following sections provide a brief description of the mechanisms. You use them later in the sample so that you can see them in action.

note

WIF is a single assembly called Microsoft.IdentityModel. When installed, it is serviced by Windows Update. You can also redistribute the assembly yourself if you like.

Security Token Handlers

Security token handlers are the glue between claims and security tokens. They have two purposes:

· Serialize and deserialize tokens (ReadToken and WriteToken)

· Turn a token into an IClaimsIdentity, and vice versa (CreateToken and ValidateToken)

When a token arrives in WCF, the first thing that WIF does is hand it over to the token handler for claims validation and claims extraction. WIF already ships with a number of token handlers to handle common token types, including the following:

· Kerberos service tickets

· Username/password

· X.509 certificates

· SAML 1.1 and 2.0

You can find them in the Microsoft.IdentityModel.Tokens namespace.

If you need to add support for a new token type, you have to write “only” a token handler for that specific token. WIF takes care of integrating that into the WCF runtime (more on that later). This might still not be a trivial task, but it is much easier compared to WCF's native extensibility points.

More common is that you might want to customize how an existing token handler handles a token. In this case, you can simply derive from that existing token-handler, and override an existing method, or inject your own logic. The whole token-handler design explicitly enables such customization. (Kudos to the WIF team!)

The SAML 1.1 Token Handler

A nice example of the customizability of the built-in token handlers is the handler for SAML 1.1 tokens. The CreateToken method is part of the SecurityTokenHandler base class and is used by WIF plumbing (or by yourself when you write your own WIF plumbing). The CreateToken method itself uses a pipeline of virtual methods that actually creates the security token. You are free to override every aspect of this pipeline. It looks as follows:

· CreateStatements — Creates the SAML subject, attribute, and authentication statements. This method calls out to the following:

o CreateSamlSubject — Looks for a name identifier claim and uses this to create the SAML subject. Additionally, if this claim has properties that describe the name format and qualifier, these values will be added to the subject. The last step is to set the proof key identifier and subject confirmation method (holder of key/bearer).

o CreateAttributeStatement — Creates the attribute statement based on the supplied claims.

o CreateAuthenticationStatementFromAuthenticationInformation — Creates the authentication statement based on the authentication information.

· CreateConditions — Sets the token lifetime and audience URIs restrictions.

· CreateAdvice — Creates the SAML advice. By default, no advice is created.

· CreateAssertion — Creates the SAML assertion based on the statements, the conditions, and the advice.

· GetSigningCredentials — Returns the credential used to sign the token.

· GetEncryptingCredentials — Returns the credential used to encrypt the token. If this method returns null, the token will not be encrypted.

Claims Transformation

An important concept of claims-based systems is transformation. By default, WIF turns incoming security tokens into claims (or an IClaimsPrincipal, to be exact). This is the job of the token handler.

The information inside the security token may or may not be directly usable by the application (typically not). So, for example, a Windows token contains the Windows account name and the security identifiers (SIDs) of the groups this account is a member of. An X.509 security token contains things such as public keys or serial numbers. This is typically not the information your application cares about. Rather, it cares about things such as authorization or personalization information such as a shopping cart limit, or trivial things such as the first name of the user.

This is where claims transformation comes in. The outcome of the security token handler's work is passed to a ClaimsAuthenticationManager. WIF passes the IClaimsPrincipal into the Authenticate method. In there, you can now do whatever modification to that principal you want to do (or even create a new one). You then return that modified principal back to WIF so that it can travel further toward application code.

Listing 10.2 shows an example of claims transformation.

Listing 10.2: Claims Transformation with ClaimsAuthenticationManager

class ClaimsTransformer : ClaimsAuthenticationManager

{

public override IClaimsPrincipal Authenticate(string resourceName,

IClaimsPrincipal incomingPrincipal)

{

// extract some unique identifier from the incoming token

var user = incomingPrincipal.Identities.Single().Name;

// build claims principal and return to WIF

return CreatePrincipal(user);

}

}

Claims-Based Authorization

Earlier, you learned that .NET's IPrincipal interface was designed for role-based security via the IsInRole method. This often led to code like what is shown in Listing 10.3.

Listing 10.3: Role-based Authorization with IsInRole

public void AddCustomer(Customer customer)

{

if (Thread.CurrentPrincipal.IsInRole("Sales"))

{

// add customer

}

}

This code has a fundamental flaw — it mixes business logic with (security) infrastructure, which is a clear violation of separation of concerns. Practically speaking, this can lead to two issues:

· Whenever the security requirements change (like another role should have access, too), you must recompile, retest, and reship the whole application (or business logic).

· These IsInRole calls (or PrincipalPermission/Attribute) are sprinkled all over your code base. Whenever changes occur, you must ensure that you don't miss a call while updating the application.

WIF advocates a slightly different approach that helps to keep both concerns separate. As opposed to embedding security-related information directly in your business code, you describe only what the current code tries to do (via a resource/operation pair), and hand it off to a separate component to make the security decision, as shown in Listing 10.4.

Listing 10.4: Claims-Based Authorization

// coarse grained and declarative

[ClaimsPrincipalPermission(SecurityAction.Demand,

Resource = "Customer", Operation = "Add")]

public void AddCustomer(Customer customer)

{

// fine grained and imperative

ClaimsPrincipalPermission.CheckAccess(

"AddCustomerInRegion",

Customer.Region);

// add customer

}

This triggers a call to the ClaimsAuthorizationManager. The CheckAccess method has a single argument of type AuthorizationContext. The context, in turn, holds the supplied resource and action, as well as the IClaimsPrincipal of the user. It is now the job of the authorization manager to check these values against some authorization policy, as shown in Listing 10.5.

Listing 10.5: ClaimsAuthorizationManager

public class AuthorizationManager : ClaimsAuthorizationManager

{

public override bool CheckAccess(AuthorizationContext context)

{

// extract required values from context

var action = context.Action.First().Value;

var resource = context.Resource.First().Value;

var client = context.Principal.Identities.First();

// evaluate against authorization policy

return CheckAuthorizationPolicy(action, resource, client);

}

}

An interesting detail about AuthorizationContext is that the resource and operation is modeled as a collection of claims. This enables describing these two “values” in an arbitrarily complex way — for example, “the user tries to print on a printer (but this printer is a color laser printer with 50 pages/minute on floor 3).”

In addition to invoking the authorization manager imperatively, WCF (or, rather, the WIF extensions to WCF) also calls the CheckAccess method for every incoming request. This time, the endpoint address and SOAP action (or HTTP verb for REST services) is passed in. This serves as a replacement for the “old” WCF ServiceAuthorizationManager.

This generic authorization extensibility also enables third parties to plug in authorization systems that operate at a higher abstraction layer. Think of graphical designers or digital simulation languages (DSLs).

Recapping the Building Blocks

You just learned about the three basic building blocks of WIF — token handlers, claims transformation, and authorization. Strictly speaking, these are three independent mechanisms — but typically, WIF combines them to a pipeline that plugs into a hosting platform such as WCF and ASP.NET:

· Turn an incoming security token into claims using a token handler.

· Transform those token-based claims into application claims.

· Do per-request (or explicit) authorization.

· Set IClaimsPrincipal on Thread.CurrentPrincipal.

The actual WIF pipeline is more complex, and a lot of extensibility points have not been mentioned. Look at the WIF SDK under “Building Relying Party Applications” for more information.

WCF and WIF

Now that you know why WIF exists, and what its fundamental building blocks are, start using it in a simple WCF service.

The following sections are based on the SimpleDemo sample from code available for download on this book's companion website (www.wrox.com). If you want to follow along, there is a “before” solution (which is plain WCF) and an “after” solution (which uses WIF and the extensibility points discussed here).

Prerequisites

The first thing you should do is download WIF and the SDK. Although WIF contains the library, the SDK gives you additional tools such as IntelliSense for the configuration file, as well as some wizards.

After downloading these packages, you can start using WIF by adding a reference to Microsoft.IdentityModel and System.IdentityModel (in addition to the typical WCF libraries like System.ServiceModel).

You must also register a new configuration section. Add the following to your app.config/web.config file:

<configSections>

<section name="microsoft.identityModel"

type="Microsoft.IdentityModel.Configuration.

MicrosoftIdentityModelSection, Microsoft.IdentityModel,

Version=3.5.0.0, Culture=neutral,

PublicKeyToken=31bf3856ad364e35"/>

</configSections>

Now you are ready to start configuring and enabling WIF in the WCF service.

Configuring and Enabling WIF

When you enable WIF in a WCF service, the following things change:

· WCF's native ServiceSecurityContext is disabled. All client identity information can now be consistently found on Thread.CurrentPrincipal (as an IClaimsPrincipal).

· All token processing now is done by WIF's internal pipeline and the security token handlers.

· As a consequence of the previous point, ClaimsAuthenticationManager and ClaimsAuthorizationManager are invoked as part of the request processing.

You enable WIF by calling FederatedServiceCredentials.ConfigureServiceHost and passing in your WCF service host. This is the easiest option for situations like self-hosting or for a service host factory.

You can also enable WIF using the WCF configuration. In this case, add the <federatedServiceHostConfiguration /> behavior to the service behavior section. You get this new behavior by registering the following behavior extension in the <system.serviceModel> configuration section:

<extensions>

<behaviorExtensions>

<add name="federatedServiceHostConfiguration"

type="Microsoft.IdentityModel.Configuration.

ConfigureServiceHostBehaviorExtensionElement,

Microsoft.IdentityModel, Version=3.5.0.0,

Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>

</behaviorExtensions>

</extensions>

WIF Internals: FederatedServiceCredentials.ConfigureServiceHost

WIF is built on top of public WCF extensibility points. When you do some WCF customization, it might be interesting to see how WIF manages to add its own processing pipeline and IClaimsPrincipal into the WCF runtime.

When you call ConfigureServiceHost (or use the behavior in configuration), the following things happen under the cover:

· WIF replaces the standard WCF ServiceCredential with its own FederatedServiceCredential. This replaces WCF's internal security token processing with the WIF pipeline.

o FederatedSecurityTokenManager replaces the standard SecurityTokenManager.

o The security token handler collection replaces the internal WCF security token provisioning, serialization, and authentication mechanisms.

· WIF also replaces the WCF internal claims generation by replacing the standard service authorization manager with the IdentityModelServiceAuthorizationManager.

o By setting the WCF PrincipalPermissionMode to Custom, this new service authorization manager is allowed to set Thread.CurrentPrincipal. This is how the claims coming from the security token handler make their way onto the thread static property, and into the WCF service code.

The following sections describe some typical WCF and WIF configuration settings for common scenarios.

Windows Authentication

Because Windows authentication is always the default in WCF, no special configuration is necessary for WIF as well. The token handler for Windows authentication produces three claim types: name (the Windows account name of the client); primarysid (the SID of the user); and groupsid (the SID of the groups the user is member of).

If you must translate SIDs to display names (such as group names), you can use the SecurityIdentifier and NTAccount classes from the BCL. The following code translates all groupsid claims to strings containing the Windows group names:

var id = Thread.CurrentPrincipal.Identity as IClaimsIdentity;

Console.WriteLine("\nWindows groups:");

id.Claims.Where(claim => claim.ClaimType ==

ClaimTypes.GroupSid).ToList().ForEach(c =>

{

var sid = new SecurityIdentifier(c.Value);

var acc = sid.Translate(typeof(NTAccount)) as NTAccount;

Console.WriteLine(acc);

});

Another specialty with Windows authentication is that you get a more specialized version of IClaimsPrincipal and IClaimsIdentity. They are called WindowsClaimsPrincipal and WindowsClaimsIdentity, and derive from the standard WindowsPrincipal and WindowsIdentity. The benefit is that you also get the Windows-centric functionality of the base classes like the Win32 token handle or the capability to impersonate the account.

WIF also includes a special service called the Claims to Windows Token Service (C2WTS). This service enables creating a WindowsIdentity without having to know the actual password of the account. This is useful in multihop scenarios in which you must access a Windows-secured resource (such as SQL Server) somewhere in the call chain. This is a highly privileged service (as you can imagine), and thus, is disabled by default. Consult the WIF documentation for more information.

Username/Password Authentication

WIF includes standard security token handlers for username- and password-based authentication when the credential validation is based either on Windows accounts or a membership provider.

Windows authentication is the standard for Username endpoints in WCF, but in case you need to do some custom validation and happen to have a membership provider, you must simply replace the standard token handler, as shown here:

<microsoft.identityModel>

<service>

<securityTokenHandlers>

<!-- use the membership provider to authentication UserName tokens -->

<remove type="Microsoft.IdentityModel.Tokens.

WindowsUserNameSecurityTokenHandler, Microsoft.IdentityModel,

Version=3.5.0.0, Culture=neutral,

PublicKeyToken=31BF3856AD364E35" />

<add type="Microsoft.IdentityModel.Tokens.

MembershipUserNameSecurityTokenHandler, Microsoft.IdentityModel,

Version=3.5.0.0, Culture=neutral,

PublicKeyToken=31BF3856AD364E35">

<userNameSecurityTokenHandlerRequirement

membershipProviderName="MembershipProviderName" />

</add>

</service>

</microsoft.identityModel>

When you already have an existing (nonmembership) password validation library, or want to do some customization, you can write your own (specialized) username security token handler. This is easier than it sounds and is mostly boilerplate code.

You basically start by deriving from UserNameSecurityTokenHandler and implementing the ValidateToken method. To be a “good” token handler, you must behave in a certain way (like checking configuration, checking for replay detection, or bootstrap tokens). You end up writing standard code where only the password validation logic is application-specific, as shown in Listing 10.6.

note

Listing 10.6: Generic UserName Security Token Handler

public class GenericUserNameSecurityTokenHandler : UserNameSecurityTokenHandler

{

public override ClaimsIdentityCollection ValidateToken(SecurityToken token)

{

if (token == null)

{

throw new ArgumentNullException("token");

}

if (base.Configuration == null)

{

throw new InvalidOperationException("No Configuration set");

}

UserNameSecurityToken unToken = token as UserNameSecurityToken;

if (unToken == null)

{

throw new ArgumentException("The SecurityToken is not

a UserNameSecurityToken", token);

}

// ValidateUserNameCredential is app specific logic

if (!ValidateUserNameCredential(unToken.UserName, unToken.Password))

{

throw new SecurityTokenValidationException(unToken.UserName);

}

var claims = new List<Claim>

{

new Claim(ClaimTypes.Name, unToken.UserName),

new Claim(ClaimTypes.AuthenticationMethod,

AuthenticationMethods.Password),

AuthenticationInstantClaim.Now

};

var identity = new ClaimsIdentity(claims);

if (base.Configuration.SaveBootstrapTokens)

{

if (this.RetainPassword)

{

identity.BootstrapToken = unToken;

}

else

{

identity.BootstrapToken = new UserNameSecurityToken(

unToken.UserName, null);

}

}

return new ClaimsIdentityCollection(new IClaimsIdentity[] { identity });

}

public override bool CanValidateToken

{

get

{

return true;

}

}

}

Code file [GenericUserNameSecurityTokenHandler.cs] available for download at Wrox.com.

You would then implement the ValidateUserNameCredential method and replace the built-in token handler with this custom one. Typically, username token handlers produce only a single username claim (besides the standard authentication method and instant claims).

You can also configure and add token handlers programmatically. The following code snippet adds the custom token handler and opens the service host:

ServiceHost host = new ServiceHost(typeof(ClaimsService));

// retrieves configuration

var wifConfiguration = new ServiceConfiguration();

// replace built-in username token handler

wifConfiguration.SecurityTokenHandlers.AddOrReplace(

new GenericUserNameSecurityTokenHandler();

// add WIF to service host and open

FederatedServiceCredentials.ConfigureServiceHost(host, wifConfiguration);

host.Open();

X.509 Certificate Authentication

In the case of X.509 client certificate authentication, the standard X509SecurityTokenHandler is used. This handler produces a number of standard claims such as the thumbprint, distinguished name, public key, or serial number.

Certificate validation is a two-step process in WIF. First, the token handler uses the standard WCF certificate validation mechanism. You have the choice between chain trust, peer trust, and no validation. You can specify the validation mode in the <microsoft.identityModel> configuration section (either at the global level, or the handler level). You can also write a custom validator by deriving from X509CertificateValidator.

After successful validation, the WIF token handler also uses the issuer name registry to determine if the issuer of that certificate is trusted in your service. WIF ships with one standard implementation of such a registry called the ConfigurationBasedIssuerNameRegistry. As the name implies, this enables adding trusted certificate authorities (CAs) by registering the certificate thumbprint through configuration. The value of the name attribute is then used by the token handler to set the Issuer property on the Claim class.

<microsoft.identityModel>

<service>

<!-- that's the default setting -->

<certificateValidation certificateValidationMode="ChainTrust" />

<!-- registers trusted CAs -->

<issuerNameRegistry type="Microsoft.IdentityModel.Tokens.

ConfigurationBasedIssuerNameRegistry, Microsoft.IdentityModel,

Version=3.5.0.0, Culture=neutral,

PublicKeyToken=31bf3856ad364e35">

<trustedIssuers>

<add thumbprint="xyz"

name="Internal CA" />

</trustedIssuers>

</issuerNameRegistry>

</service>

</microsoft.identityModel>

The combination of both facilities enables an easy implementation of the typical pattern where you first check the validity of an incoming certificate using chain validation, and then narrow down your trusted certificates by providing a registry list.

Because you can provide your own issuer name registry implementation, you have total freedom to determine what is “trusted” in your service. Listing 10.7 shows a sample issuer name registry that does no checking against a list, but simply returns the thumbprint of the issuer certificate. This is sometimes useful for testing — but don't use it in production environments.

note

Listing 10.7: Test Issuer Name Registry

public class TestIssuerNameRegistry : IssuerNameRegistry

{

public override string GetIssuerName(SecurityToken securityToken)

{

if (securityToken == null)

{

throw new ArgumentNullException("securityToken");

}

X509SecurityToken token = securityToken as X509SecurityToken;

if (token != null)

{

return token.Certificate.Thumbprint;

}

throw new SecurityTokenException(

securityToken.GetType().FullName);

}

}

Code file [TestIssuerNameRegistry.cs] available for download at Wrox.com.

SAML Token Authentication

WIF has built-in support for SAML 1.1 and 2.0 tokens. The token handlers decrypt and validate incoming SAML tokens, and turn the various statements into claims. But you must supply various configuration values to make this happen:

· The decryption key (private key) to decrypt incoming tokens

· A list of trusted token issuers

· A list of accepted audience URIs

This is typically done using the <microsoft.identityModel /> configuration section, but, as always, you can also supply the configuration via code. It's a little confusing that you can also configure some aspects using the standard WCF configuration, and ConfigureServiceHost has some intelligence to parse both configuration sections and merge them. However, in general, I would recommend moving everything into the WIF configuration section.

Listing 10.8 shows a sample WIF configuration for SAML tokens.

Listing 10.8: Sample WIF Configuration for SAML Tokens

<microsoft.identityModel>

<service>

<!-- list of accepted audience uris -->

<audienceUris>

<add value="https://server/service.svc" />

</audienceUris>

<!-- specifies how the issuer certificates get validated -->

<certificateValidation certificateValidationMode="ChainTrust" />

<!-- key used to decrypt incoming tokens -->

<serviceCertificate>

<certificateReference findValue="CN=Service"

x509FindType="FindBySubjectDistinguishedName"

storeLocation="LocalMachine"

storeName="My" />

</serviceCertificate>

<!-- registers trusted token issuers

similar to x.509 certificate authentication,

the certificate used to sign the token

gets passed into the registry -->

<issuerNameRegistry type="Microsoft.IdentityModel.Tokens.

ConfigurationBasedIssuerNameRegistry, ...">

<trustedIssuers>

<add thumbprint="xyz"

name="Internal Identity Provider" />

</trustedIssuers>

</issuerNameRegistry>

</service>

</microsoft.identityModel>

Sessions

WIF builds on top of the WCF session feature (or, more specifically, the WS-SecureConversation support). When WS-SecureConversation is turned on (that's the establishSecurityContext attribute on the binding configuration), the outcome of the claims transformation process on the first request gets cached on the server, and a unique identifier gets round-tripped using a SOAP header. On subsequent requests, this identifier is used to rehydrate the cached IClaimsPrincipal. The SAML security token handler and the ClaimsAuthenticationManager don't get called anymore. Because claims transformation is potentially expensive (for example, making round trips to data stores), sessions can improve performance.

But sessions also have downsides:

· Introducing state at the protocol level makes the client programming model more difficult. You must deal with things such as timeouts, faulted channels, and re-creating proxies.

· Sessions are, by default, incompatible with load balancing.

In the case in which you want to use WIF's session management in a load-balanced environment, you must put WIF into “cookie mode.” This means that the complete IClaimsPrincipal from ClaimsAuthenticationManager is serialized and round-tripped (as opposed to keeping the principal on the server side, and round-tripping an identifier). In this mode, the WCF client proxy sends the complete principal on each request, and the receiving node can rehydrate the principal from the SecureConversation header.

Unfortunately, the standard bindings don't expose that option directly — you need a custom binding for that. The “trick” here is to set requireSecurityContextCancellation to false — which is just a fancy way to say “serialize the session token (also known as the principal) into the message.” The following custom binding mimics the WS2007FederationHttpBinding in TransportWithMessageCredential security mode:

<customBinding>

<binding name="federation_cookie">

<security authenticationMode="SecureConversation"

messageSecurityVersion="WSSecurity11

WSTrust13

WSSecureConversation13

WSSecurityPolicy12

BasicSecurityProfile10"

requireSecurityContextCancellation="false">

<secureConversationBootstrap authenticationMode="IssuedTokenOverTransport"

messageSecurityVersion="WSSecurity11

WSTrust13

WSSecureConversation13

WSSecurityPolicy12

BasicSecurityProfile10">

<issuedTokenParameters>

<issuerMetadata address="https://…" />

</issuedTokenParameters>

</secureConversationBootstrap>

</security>

<textMessageEncoding />

<httpsTransport />

</binding>

</customBinding>

But that's not the complete story. When you round-trip the principal back to the user, it must be protected. Otherwise, the client could simply change the claims and resubmit the modified session token.

The standard WIF behavior is to use the DPAPI user key to protect the principal. This key cannot easily be shared between nodes in a cluster. Another more explicit (and practical) option is to use an RSA key. Most typically, you would use your SSL certificate, or the certificate used to decrypt incoming tokens, to protect the serialized principal.

Fortunately, WIF makes it easy to customize such low-level details. You must derive from SessionSecurityTokenHandler and modify the transforms. (Think of transforms as a mini pipeline for serializing the session token that contains the principal.) Listing 10.9 shows an example.

Listing 10.9: Session Security Token Handler for Load-Balanced Scenarios

public class LoadBalancedSessionSecurityTokenHandler : SessionSecurityTokenHandler

{

public LoadBalancedSessionSecurityTokenHandler(X509Certificate2

protectionCertificate)

: base(CreateRsaTransforms(protectionCertificate))

{ }

private static ReadOnlyCollection<CookieTransform> CreateRsaTransforms

(X509Certificate2 protectionCertificate)

{

var transforms = new List<CookieTransform>()

{

new DeflateCookieTransform(),

new RsaEncryptionCookieTransform

(protectionCertificate),

new RsaSignatureCookieTransform

(protectionCertificate),

};

return transforms.AsReadOnly();

}

}

You would then replace the built-in session token handler with this new one:

ServiceHost host = new ServiceHost(typeof(ClaimsService));

// retrieves configuration

var wifConfiguration = new ServiceConfiguration();

// replace built-in username token handler

wifConfiguration.SecurityTokenHandlers.AddOrReplace(

new LoadBalancedSessionSecurityTokenHandler

(wifConfiguration.ServiceCertificate);

// add WIF to service host and open

FederatedServiceCredentials.ConfigureServiceHost(host, wifConfiguration);

host.Open();

note

You can see that there is a lot of custom code and configuration necessary to get sessions up and running for nonsingle-server scenarios. Regardless of clustering, I personally prefer to turn off sessions altogether and do my own server-side caching in ClaimsAuthenticationManager. Using products such as AppFabric Caching or memcached, you can also do distributed caching across multiple cluster nodes.

Recapping Configuring and Enabling

WIF supports a lot of scenarios and configuration options out-of-the-box. Generally, you start by creating your normal WCF endpoints and binding configuration, and then you call ConfigureServiceHost before opening the WCF service host. Typically, the WIF runtime can take care of the rest, and your service code calls the IClaimsPrincipal on Thread.CurrentPrincipal.

In general, you should keep your WCF endpoint configuration as simple as possible. Use mixed-mode security (that's the TransportWithMessageCredential security mode) whenever possible because it can give you the best performance combined with the expressiveness of WS-Security tokens. Also, try to avoid sessions.

Furthermore, have a look at the following three blog entries to learn more about the WIF configuration system in general, as well as its extensibility:

· www.leastprivilege.com/WIFConfigurationNdashPart1ServiceConfiguration.aspx

· www.leastprivilege.com/WIFConfigurationNdashPart2SecurityTokenHandlerConfiguration.aspx

· www.leastprivilege.com/WIFConfigurationNdashPart3Extensibility.aspx

Transforming and Accessing Claims

Adding claims transformation to a WCF service is a matter of deriving from ClaimsAuthenticationManager and implementing the Authenticate method. (See the previous explanation of claims transformation.)

After that, you can register the authentication manager using configuration (or code):

<microsoft.identityModel>

<service>

<claimsAuthenticationManager type="MyAuthenticationManager, Assembly" />

</service>

</microsoft.identityModel>

Claims Transformation

I typically start by defining what the client identity should “look like” — in other words, on which claims the service or business logic can rely. I then make sure that the ClaimsAuthenticationManager can provide exactly these claims; otherwise, the request should fail.

This also involves removing claims that are not needed (or even better, creating a new principal with exactly the required claims that you have defined earlier).

Also, try to keep the claim set as minimal as possible — just enough claims so that the service can do its work. Remember that you also have the ClaimsAuthorizationManager abstraction that provides a lot of flexibility to couple authorization information with a principal.

The claims collection on IClaimsIdentity was explicitly designed to be LINQ-friendly. This enables you to use the query syntax or the extensions methods to query claims, and provides a lot of expressiveness.

I prefer the extensions methods because it is easy to clearly state your expectations. For example, the following query demands an e-mail claim to be present:

var email = id.Claims.First(c => c.ClaimType == ClaimTypes.Email);

You can even go one step further and demand that a claim exists only once in the claims collection. Being that explicit might reveal bugs in complex transformation scenarios in early stages:

var dob = id.Claims.Single(c => c.ClaimType == ClaimTypes.DateOfBirth);

You can also build convenience wrappers around the LINQ queries. When you have a look at the Thinktecture.IdentityModel library that is part of the code available for download from this book's website (www.wrox.com), you can see a couple of extension methods for IClaimsPrincipal andIClaimsIdentity that give you methods such as FindClaims, GetClaimValue, DemandClaim, and so on.

Accessing Claims

It's okay for low-level code like transformation or authorization (and sometimes façades) to work directly on the IClaimsPrincipal.

Business code should use an abstraction like a User class with properties that, in turn, queries the claims collection. This way, the technical details don't blur into your business logic, and you can also change the underlying implementation independently of the business code (and vice versa).

Also, keep in mind that you might want to inject principals in your code to make unit testing easier.

Authorization

You have already learned about the concept of claims-based authorization in WIF. You add an authorization manager either via code or configuration, as shown here:

<microsoft.identityModel>

<service>

<claimsAuthorizationManager type="MyAuthorizationManager, Assembly" />

</service>

</microsoft.identityModel>

From that point on, your authorization manager will be automatically called on a per-request basis. The authorization context will be populated with the SOAP action as the action, and the endpoint address as the resource. For REST services, it will be the HTTP verb as the action, and the resource URL as the resource. In all cases, these are represented as single-name claims.

This enables doing coarse-grained authorization based on the endpoint and operation the client tries to call. When you return false from the CheckAccess method, a SecurityException is triggered. WCF automatically translates security exceptions into an access-denied fault message. On the WCF client side, these types of fault messages are turned into a SecurityAccessDeniedException that the client can explicitly catch.

note

Those who know about the underlying native WCF ServiceAuthorizationManager, also know that this mechanism enables inspecting the contents of the incoming message during per-request authorization. This feature was rarely used and didn't make it in the WIF authorization abstraction. If you need this feature, have a look at the Thinktecture.IdentityModel library, which contains a sample of how to resurface message inspection.

For triggering the authorization manager from within your code, WIF has the ClaimsPrincipalPermission class (and a corresponding attribute as well). The attribute is similar to per-request authorization — the authorization manager is called automatically when the client invokes the operation. But you have control over the values for action and resource.

If you need more fine-grained control over resources and actions, you can use ClaimsPrincipalPermission.CheckAccess. This method wraps the resource and action into name claims, and passes them on as an authorization context to the registered authorization manager. In the case of a negative outcome, a SecurityException fires.

The built-in WIF API for authorization has two shortcomings:

· Because per-request authorization and calls to ClaimsPrincipalPermission both generate name claims, it is tedious to distinguish between them in your authorization manager code.

· CheckAccess always triggers a SecurityException when authorization fails. A way to branch code using a Boolean return value is missing.

You can work around both issues thanks to the WIF extensibility model.

The key to all customizations and domain-specific additions you want to make to authorization is grabbing the registered authorization manager and calling the CheckAccess method yourself. This enables you to create your own AuthorizationContext object, which, in turn, gives you full control over shape and form of your action and resource claims.

WIF puts an instance of the current configuration onto a message property called ServiceConfiguration. From there, you can reach into the ClaimsAuthorizationManager. Listing 10.10 shows an example.

note

Listing 10.10: Accessing WIF Configuration (Works in Both WCF and ASP.NET)

public static class IdentityModelConfiguration

{

/// <summary>

/// Gets the current WIF service configuration.

/// </summary>

/// <value>The service configuration.</value>

public static ServiceConfiguration ServiceConfiguration

{

get

{

if (OperationContext.Current == null)

{

// no WCF

return FederatedAuthentication.ServiceConfiguration;

}

// search message property

if (OperationContext.Current.IncomingMessageProperties

.ContainsKey("ServiceConfiguration"))

{

var configuration = OperationContext.Current

.IncomingMessageProperties["ServiceConfiguration"]

as ServiceConfiguration;

if (configuration != null)

{

return configuration;

}

}

// return configuration from configuration file

return new ServiceConfiguration();

}

}

}

Code file [IdentityModelConfiguration.cs] available for download at Wrox.com.

Invoking CheckAccess is now a matter of the following:

var authZ =

IdentityModelConfiguration.ServiceConfiguration.ClaimsAuthorizationManager;

if (authZ.CheckAccess(…))

{

// code

}

The Thinktecture.IdentityModel library contains code to further customize authorization based on this sample. This makes it easy to use claim types from namespaces that you control, thus making them easy to spot inside the authorization manager.

note

A well-hidden feature of WIF is that some parts of the configuration system can be extended. This also applies to claims authorization. It is possible to append an arbitrary XML fragment to the <claimsAuthorizationManager /> section. This way, you can declaratively pass in a hint to an authorization policy file, or add some other configuration entries. The WIF SDK has a sample that shows how to append an authorization policy that can be parsed at startup time (see Samples\Extensibility\Claims Based Authorization).

Tracing

Tracing in WCF is essential for troubleshooting. WIF adds a new trace source that you should absolutely enable during development and for testing.

You can register for the trace source using the following configuration snippet:

<system.diagnostics>

<source name="Microsoft.IdentityModel"

switchValue="Verbose" >

<listeners>

<add name="IdentityModelTraceListener" />

</listeners>

</source>

</sources>

<sharedListeners>

<add initializeData="wif.svclog"

type="System.Diagnostics.XmlWriterTraceListener, System,

Version=4.0.0.0,

Culture=neutral,

PublicKeyToken=b77a5c561934e089"

name="IdentityModelTraceListener"

traceOutputOptions="Timestamp">

</add>

</sharedListeners>

<trace autoflush="true" />

</system.diagnostics>

Possible Solutions for Securing the Movie Database SOAP Service

To see WIF in action, use the movies WCF service from Chapter 9 to add some security features. Start with an intranet-based scenario and later add federation.

The movie service has three types of clients:

· A website that uses the back-end services to provide read-only content to browser-based users.

· Internal users who must view, review, update, and create new content.

· At a time, certain movie genres will be outsourced to external content providers. These external providers can directly add content to the system using the WCF services.

The first two client types use Windows authentication to access the movie services. The external providers get access via a federation gateway using SAML tokens.

Internal Users

For enabling internal users, the course of events is as follows:

1. Add a service endpoint that does Windows authentication.

2. Enable and configure WIF.

3. Establish an identity via claims transformation.

4. Add authorization instrumentation and policy.

Adding Windows Authentication

The first step is to add a Windows authenticated endpoint to the movies service. Use mixed-mode security with a disabled session for that. That means you must first enable SSL. Exactly how this works depends on the hosting environment. You either have to bind SSL to an endpoint using IIS, or use the netsh command-line tool for self-hosting. In the case of self-hosting, you must also add a new base address to the WCF configuration:

<service name="MovieService" behaviorConfiguration="soapBehavior">

<host>

<baseAddresses>

<add baseAddress="net.tcp://localhost:7776"/>

<add baseAddress="http://localhost:8888"/>

<add baseAddress="https://localhost:8889"/>

</baseAddresses>

</host>

</service>

To add the actual endpoint, you must add code to the MoviesServiceFactory in the SetupMoviesServiceHost method:

var secureIntranetBinding = new WS2007HttpBinding(SecurityMode.

TransportWithMessageCredential);

secureIntranetBinding.Security.Message.ClientCredentialType =

MessageCredentialType.Windows;

secureIntranetBinding.Security.Message.EstablishSecurityContext = false;

host.AddServiceEndpoint(

typeof(IMovieService),

secureIntranetBinding,

"binary");

To see if the client is actually authenticated, add a bit of tracing code to MovieService in the ListMovies method:

public List<MovieData> ListMovies(PagedDataRequest request)

{

trace.TraceEvent(TraceEventType.Information, 0, "List Movies...");

if (Thread.CurrentPrincipal.Identity.IsAuthenticated)

{

var info = string.Format("{0} ({1})",

Thread.CurrentPrincipal.Identity.Name,

Thread.CurrentPrincipal.Identity.GetType().Name);

trace.TraceInformation(info);

}

else

{

trace.TraceInformation("Anonymous request");

}

// rest omitted

}

The next step is to change the Windows client to use the authenticated endpoint. This means that you must add the same binding to the client configuration, as well as change the endpoint address the client uses:

<client>

<endpoint name="default"

address="https://localhost:8889/binary"

binding="ws2007HttpBinding"

bindingConfiguration="IntranetSecure"

contract="MovieServiceContracts.IMovieService" />

</client>

<ws2007HttpBinding>

<binding name="IntranetSecure">

<security mode="TransportWithMessageCredential">

<message clientCredentialType="Windows"

establishSecurityContext="false" />

</security>

</binding>

</ws2007HttpBinding>

That's it! When you now run the client, the service can trace the client's Windows account name in the ListMovies call. But when you have a closer look at the Common Language Runtime (CLR) type of the client identity, you see WindowsIdentity.

To get claims, the last step is to enable WIF. Insert a call to FederatedServiceCredentials.ConfigureServiceHost(host) into the host (or the host factory for IIS hosting). When you rerun the client, the identity is now of type WindowsClaimsIdentity (which means WIF is doing its work).

Claims Transformation

The next step is to establish an identity for the client that the service can rely on. This is done in the claims authentication manager. For this scenario, the identity of the clients should have the claims shown in Table 10.1.

Table 10.1 Client Identity

Claim

Description

Name

Name of the client.

Company

Identifier for an external content provider. “Internal” refers to internal users.

Role

A user can have the following roles: Viewer, Reviewer, and Author.

Genre

Optionally, a user can be restricted to work only on specific genres (only relevant for reviewers and authors). Internal users have access to all genres by default.

In the case of Windows users, you expect certain groups in Active Directory (AD) to be in place. The claims transformation process then parses these group memberships and turns them into application roles.

As shown in Listing 10.11, the implementation of ClaimsAuthenticationManager distinguishes between internal users and external ones. You add the logic for external users in a later step.

Listing 10.11: ClaimsAuthenticationManager

public class ClaimsTransformer : ClaimsAuthenticationManager

{

public override IClaimsPrincipal Authenticate(string resourceName,

IClaimsPrincipal incomingPrincipal)

{

// internal user

var windowsUser = incomingPrincipal.Identity as WindowsClaimsIdentity;

if (windowsUser != null)

{

return CreatePrincipalForWindowsUser(windowsUser);

}

return base.Authenticate(resourceName, incomingPrincipal);

}

private IClaimsPrincipal

CreatePrincipalForWindowsUser(WindowsClaimsIdentity windowsUser)

{

var claims = new List<Claim>

{

new Claim(ClaimTypes.Name, windowsUser.Name),

new Claim(MovieClaimTypes.Company, "Internal")

};

using (var principal = new WindowsClaimsPrincipal(windowsUser))

{

// check Windows groups and turn them into roles

if (principal.IsInRole("Users"))

{

claims.Add(new Claim(ClaimTypes.Role, "Viewer"));

}

if (principal.IsInRole("MovieAuthors"))

{

claims.Add(new Claim(ClaimTypes.Role, "Author"));

}

if (principal.IsInRole("MovieReviewers"))

{

claims.Add(new Claim(ClaimTypes.Role, "Reviewer"));

}

}

// internal users have access to all genres

claims.Add(new Claim(MovieClaimTypes.Genre, "All"));

return ClaimsPrincipal.CreateFromIdentity(new ClaimsIdentity(claims));

}

}

Because all information is extracted directly from the Windows token and no database round trips are necessary here, there is no need for a caching layer.

Authorization

After you establish a security context and identity for the current user, you can annotate your code with authorization requirements.

At a coarse-grained level, you can either use the per-request authorization or add authorization attributes to the service façade — this gives you “is the user allowed to call this operation” semantics. Use the imperative approach described earlier when you need more detailed information before you call the authorization manager. Listing 10.12 shows an example.

Listing 10.12: Service Façade with Authorization Annotations

[ApplicationClaimPermission(SecurityAction.Demand,

Operation = "Add",

Resource = "Movie")]

public void AddMovie(MovieDetailsData movie)

{ … }

[ApplicationClaimPermission(SecurityAction.Demand,

Operation = "List",

Resource = "Movie")]

public List<MovieData> ListMovies(PagedDataRequest request)

{ … }

[ApplicationClaimPermission(SecurityAction.Demand,

Operation = "GetDetails",

Resource = "Movie")]

public MovieDetailsData GetMovie(string movieId)

{ … }

The access control for genres is an example for an authorization decision that needs more knowledge than that the user is invoking a certain operation. In this case, the ListMovies operation would probably use the genre's claims to construct a query. The GetMovie and AddMovie operations, in turn, would return an Access Denied message when users try to request or add a movie that they are not authorized for. Listing 10.13 shows how to do this.

Listing 10.13: Sample Imperative Authorization

[ApplicationClaimPermission(SecurityAction.Demand,

Operation = "GetDetails",

Resource = "Movie")]

public MovieDetailsData GetMovie(string movieId)

{

trace.TraceEvent(TraceEventType.Information, 0, "Get Movie...");

try

{

var movie = movieManager.GetMovie(movieId).MapAll();

// check if user is allowed

var genreClaim = new Claim(

Constants.ClaimTypes.Genre, movie.Genre);

if (ApplicationClaimPermission.CheckAccess(

Constants.Actions.GetDetails,

Constants.Resources.Genre, genreClaim))

{

return movie;

}

trace.TraceEvent(TraceEventType.Error, 401, string.Format(

"Authorization failed for user: {0} / genre: {1}",

Thread.CurrentPrincipal.Identity.Name,

movie.Genre));

throw new SecurityException();

}

catch (MovieNotFoundException mnfex)

{

trace.TraceEvent(TraceEventType.Error, 0, mnfex.Message);

throw new FaultException<NoSuchMovieFault>(

new NoSuchMovieFault { MovieId = movieId });

}

}

Listing 10.14 shows a simple implementation of an authorization manager that enforces the access policy.

Listing 10.14: Authorization Manager

public class AuthorizationManager : ClaimsAuthorizationManager

{

public override bool CheckAccess(AuthorizationContext context)

{

// distinguish between per-request authZ and app authZ

if (!IsApplicationAuthorization(context))

{

return base.CheckAccess(context);

}

// check which resource the client tries to access

var resource = context.Resource.First().Value;

switch (resource)

{

case Constants.Resources.Movies:

return AuthorizeMovieAccess(

context.Action, context.Principal);

case Constants.Resources.Genre:

var genre = context.Resource.First(c =>

c.ClaimType == Constants.ClaimTypes.Genre).Value;

return AuthorizeGenreAccess(genre, context.Principal);

}

return false;

}

private bool IsApplicationAuthorization(

AuthorizationContext context)

{

return (context.Action.FirstOrDefault(claim =>

claim.ClaimType == ApplicationClaimPermission.ActionType)

!= null);

}

private bool AuthorizeMovieAccess(

Collection<Claim> actions, IClaimsPrincipal principal)

{

var action = actions.First().Value;

switch (action)

{

case Constants.Actions.List:

return principal.IsInRole(Constants.Roles.Viewer);

case Constants.Actions.GetDetails:

return principal.IsInRole(Constants.Roles.Viewer);

case Constants.Actions.Add:

return principal.IsInRole(Constants.Roles.Author);

}

return false;

}

private bool AuthorizeGenreAccess(

string genre, IClaimsPrincipal principal)

{

if (principal.ClaimExists(Constants.ClaimTypes.Genre, "All"))

{

return true;

}

return principal.ClaimExists(

Constants.ClaimTypes.Genre, genre);

}

}

Adding an External Content Provider

Because WCF and WIF also support SAML tokens, it is easy to grant external users access to the movie service. This is typically accomplished by providing a federation gateway that the external parties can use to request a token for the services they want to access. In a Microsoft-oriented environment, the Active Directory Federation Services 2 (ADFS 2) product would be such a federation gateway.

The setup and configuration of ADFS 2 is out of scope of this book, and actually not relevant. But some interesting points affect the service design:

· External partners use the federation gateway to request a token for the service.

· The federation gateway knows who the external partner is and has rules for how to transform the incoming claims to claims that the service can process.

· The gateway can generate a token with the following claims:

o A claim containing the unique username of the external client

o A claim containing the company name (or identifier)

· The service transforms these incoming claims to the client identity the service code expects. This can, for example, involve querying a database to query the roles and to find out which genres the external user has access to.

This way, you can provision external users with minimal effort. As you'll see, you'll have to slightly modify only the claims transformation logic. All the service and authorization code can stay the same. Compared to “traditional” approaches, this is a huge simplification.

Adding the Service Endpoint for External Users

For accepting issued tokens, you must add a new endpoint in the service host factory. This endpoint advertises the address of the federation gateway in its metadata so that the external clients know how to request tokens for the service:

// endpoint for external users

var fedEndpoint = new

EndpointAddress("https://gateway/adfs/services/trust/mex");

var externalBinding = new WS2007FederationHttpBinding(

WSFederationHttpSecurityMode.TransportWithMessageCredential);

externalBinding.Security.Message.EstablishSecurityContext = false;

externalBinding.Security.Message.IssuerMetadataAddress = fedEndpoint;

host.AddServiceEndpoint(

typeof(IMovieService),

externalBinding,

"issuedtokens");

Next, you must configure WIF so that it can process the issued tokens. This involves the following:

· Registering the federation gateway as a trusted issuer of tokens. This is accomplished using the issuer name registry mechanism of WIF.

· Specifying a certificate for decrypting incoming tokens. For example, this could be the SSL certificate.

· Specifying allowed audience URIs. This is a value that must be coordinated between the federation gateway, the service, and the client. Think of it as a logical (sometimes also physical) name for the service.

Consider the following example:

<microsoft.identityModel>

<service>

<claimsAuthenticationManager type="Security.ClaimsTransformer,

SecurityPlumbing"/>

<claimsAuthorizationManager type="Security.AuthorizationManager,

SecurityPlumbing"/>

<!-- audience URI of movie service application -->

<audienceUris>

<add value="https://moviedb/" />

</audienceUris>

<!-- certificate for token decryption -->

<serviceCertificate>

<certificateReference storeLocation="LocalMachine"

storeName="My"

x509FindType="FindBySubjectDistinguishedName"

findValue="CN=Service" />

</serviceCertificate>

<!-- registers the federation gateway as a trusted token issuer -->

<issuerNameRegistry type="Microsoft.IdentityModel.Tokens.

ConfigurationBasedIssuerNameRegistry,

Microsoft.IdentityModel, Version=3.5.0.0,

Culture=neutral, PublicKeyToken=31bf3856ad364e35">

<trustedIssuers>

<add thumbprint="d1 c5 b1 25 97 d0 36 94 65 1c e2 64 fe 48 06

01 35 f7 bd db"

name="FederationGateway" />

</trustedIssuers>

</issuerNameRegistry>

<!-- only federation gateway allowed anyways, so no extra certificate

validation -->

<certificateValidation certificateValidationMode="None" />

</service>

</microsoft.identityModel>

Adjusting Claims Transformation

The only code modification you must make is in the claims authentication manager. Two things need to happen here:

· It is mandatory that external users have a username and a company claim.

· Role information and allowed genres for external users are stored in a database. This information must be turned into claims.

From a service and business logic point of view, there is no difference between internal and external users now.

The new authentication manager looks like this (with parts removed):

public class ClaimsTransformer : ClaimsAuthenticationManager

{

TraceSource trace = new TraceSource("mdb.Movies");

public override IClaimsPrincipal Authenticate(

string resourceName, IClaimsPrincipal incomingPrincipal)

{

trace.TraceInformation("Client: " + incomingPrincipal.Identity.Name);

// internal user

var windowsUser = incomingPrincipal.Identity as WindowsClaimsIdentity;

if (windowsUser != null)

{

trace.TraceInformation("internal user");

return CreatePrincipalForWindowsUser(windowsUser);

}

trace.TraceInformation("external user");

return CreatePrincipalForExternalUser(resourceName, incomingPrincipal);

}

private IClaimsPrincipal CreatePrincipalForExternalUser(

string resourceName, IClaimsPrincipal incomingPrincipal)

{

// make sure required claims exist

var name = incomingPrincipal.GetClaimValue(ClaimTypes.Name);

var company = incomingPrincipal.GetClaimValue

(Constants.ClaimTypes.Company);

var claims = new List<Claim>

{

new Claim(ClaimTypes.Name, name),

new Claim(Constants.ClaimTypes.Company, company)

};

GetRolesForUser(name, company).ForEach(

role => claims.Add(new Claim(ClaimTypes.Role, role)));

GetGenresForUser(name, company).ForEach(

genre => claims.Add(new Claim(Constants.ClaimTypes.Genre, genre)));

return ClaimsPrincipal.CreateFromIdentity(new ClaimsIdentity(claims));

}

}

The Client

Because of the design of the movie service client, all WCF specifics are encapsulated in the service agent library. This is where you add the code for token-based authentication.

The logic for setting up the communication channel to the service is as follows:

1. The client authenticates with its local identity provider and requests a token. This is done using Windows authentication.

2. The client then uses its identity token to request a service token from the federation gateway.

3. The service token is attached to the service channel factory, which, in turn, generates the client proxy.

WIF includes the necessary plumbing to request tokens from token services. It is implemented as a specialized WCF channel factory called WSTrustChannelFactory. For attaching tokens to service channels, WIF also includes extension methods that make this operation simple. The initialization could look as follows:

public class MovieClient : IMovieClient, IDisposable

{

public MovieClient()

{

// request token from idp (windows authentication)

var idpToken = GetIdPToken();

// request token from federation gateway (using idp token)

var serviceToken = GetServiceToken(idpToken);

// setting up channel factory

var movieServiceClientFactory = new

ChannelFactory<IMovieServiceChannel>("default");

movieServiceClientFactory.ConfigureChannelFactory();

movieServiceClientFactory.Credentials.SupportInteractive = false;

movieServiceClient = movieServiceClientFactory

.CreateChannelWithIssuedToken<IMovieServiceChannel>(serviceToken);

}

private SecurityToken GetIdPToken()

{

var factory = new WSTrustChannelFactory(

new WindowsWSTrustBinding(SecurityMode.TransportWithMessageCredential),

new EndpointAddress(idpUrl));

factory.TrustVersion = TrustVersion.WSTrust13;

var rst = new RequestSecurityToken

{

RequestType = RequestTypes.Issue,

AppliesTo = new EndpointAddress(fedGwUrl),

KeyType = KeyTypes.Symmetric

};

var channel = factory.CreateChannel();

return channel.Issue(rst);

}

private static SecurityToken GetServiceToken(SecurityToken idpToken)

{

var binding = new IssuedTokenWSTrustBinding();

binding.SecurityMode = SecurityMode.TransportWithMessageCredential;

var factory = new WSTrustChannelFactory(

binding,

fedGwUrl);

factory.TrustVersion = TrustVersion.WSTrust13;

factory.Credentials.SupportInteractive = false;

var rst = new RequestSecurityToken

{

RequestType = RequestTypes.Issue,

AppliesTo = new EndpointAddress(realm),

KeyType = KeyTypes.Symmetric

};

factory.ConfigureChannelFactory();

var channel = factory.CreateChannelWithIssuedToken(idpToken);

return channel.Issue(rst);

}

}

Assessing the Solution

You have just seen that you can add claims-based identity to an existing service without having to do major surgery on the architecture or code structure. This provides the benefit of having a more expressive representation of identity, as well as the decoupling of authorization from business logic flow.

As a “side effect,” it was easy adding support for external users and third-party authentication.

Possible Solutions for Securing the Movie Database REST Service

The (security) situation is a little different in the REST world (and I use this term in a fuzzy way) compared to SOAP. SOAP has WS-Security, which is a powerful (sometimes too powerful) protocol for authentication and message protection. On the other hand, REST has no standard mechanisms that go beyond standard HTTP-based authentication (for example, basic authentication — maybe Windows authentication for certain scenarios). Still, you can use WIF to move your REST services to the claims-based model.

In the spirit of the last section, start with securing the REST service using Windows authentication. Then move to custom authentication methods (such as SAML), which is a great opportunity to show you how to use the WIF core API to claims-enable service scenarios not supported out-of-the-box.

Internal Users

The good news is that WIF supports WCF REST services with HTTP authentication out-of-the-box. That means that you can reuse all the code and infrastructure you created earlier.

All you must do is enable Windows authentication on the binding and WebClient, and enable WIF in the web service host. From that point, your claims transformation code for Windows clients will be called, and the same authorization manager will be invoked. Job done!

note

If you don't like to share these components between your services, you don't have to, of course. In this case, you can use the <service /> element in the WIF configuration to create several named independent configuration settings. The call to ConfigureServiceHost has an overload that accepts the name of such a named configuration.

Token-Based Authentication

As stated earlier, there are no real standards around token-based authentication and REST services. But most authentication schemes out in the wild have one thing in common. The authentication information (from simple username/password to tokens) is typically transmitted using the HTTP authorization header.

The exact layout of that header (such as token format or encoding) is often simply defined by the service provider. Because there are no clear standards at the moment, WIF does not include direct support for these “homegrown” authentication handshakes, but the WIF API can be used to implement the support yourself. For the purpose of this example, add support for a SAML bearer token.

note

A number of emerging standards exist for token-based authentication for REST services. The most promising and complete seems to be OAuth 2, which is currently under development. As soon as the specifications are finalized, it is expected that WIF will also directly support these new protocols.

In a nutshell, following are the necessary steps to add your own SAML support to a WCF web service host:

1. Parse the incoming HTTP request, and extract the authorization header.

2. Extract the SAML token from the header, and validate it.

3. Turn the token into an IClaimsPrincipal, and set Thread.CurrentPrincipal.

4. Wrap this functionality in a WCF ServiceAuthorizationManager.

5. Wrap the authorization manager in a service host and factory.

This sounds like a lot of work, but luckily most of the heavy lifting is done by WIF. You need to provide only the header parsing code and the plumbing; WIF takes care of all token-related processing. Again, you can also share your claims transformation and authorization code.

A (shortened) version of such an ServiceAuthorizationManager could look like this:

public class FederatedWebServiceAuthorizationManager

: ServiceAuthorizationManager

{

protected override bool CheckAccessCore(OperationContext operationContext)

{

var properties = operationContext.ServiceSecurityContext

.AuthorizationContext.Properties;

var to = operationContext.IncomingMessageHeaders.To.AbsoluteUri;

// parse HTTP header and turn SAML token into IClaimsPrincipal

IClaimsIdentity identity;

if (TryGetIdentity(out identity))

{

// call claims transformation and set the IClaimsPrincipal

var principal = _configuration.ClaimsAuthenticationManager

.Authenticate(

to,

ClaimsPrincipal.CreateFromIdentity(identity));

properties["Principal"] = principal;

// call claims authorization manager

return CallClaimsAuthorization(principal, operationContext);

}

else

{

SetUnauthorizedResponse();

return false;

}

}

Turning the SAML token string into an IClaimsPrincipal is accomplished with the help of WIF's security token handlers:

private IClaimsIdentity GetSamlIdentityFromHeader(string header)

{

var token = header.Substring("SAML access_token=".Length);

var samlToken = _configuration.SecurityTokenHandlers.ReadToken(

new XmlTextReader(new StringReader(token)));

return _configuration.SecurityTokenHandlers.ValidateToken(samlToken).First();

}

On the client side, you can reuse the code for requesting tokens for the SOAP service. The only difference is that you must request a bearer token (see the movie sample client). This token then must be placed on the authorization header so that the service-side plumbing can pick it up.

Also, look at the samples called “REST” and “OData” in the Thinktecture.IdentityModel library.

Summary

This chapter was a whirlwind tour through several important security topics: WCF security, claims-based identity, and federation.

WCF is the foundation for everything here. It provides the runtime and messaging system, as well as the hooks to extract security information from incoming messages. WCF also has the federation functionality already built in, but it isn't accessible and easy to use all the time.

WIF sits on top of WCF and makes claims a first-class citizen via the IClaimsPrincipal abstraction. It furthermore streamlines the WCF security stack by providing more easy-to-use hooks in the form of security token handlers. And, finally, WIF adds the concepts of claims transformation and claims-based authorization.

As you have seen, when used in combination, this allows building logic on a nice abstraction layer so that even onboarding the external users could be accomplished with minimal code changes (and zero code changes to the actual business logic).

You should always use WIF when building distributed systems — federated or not. The claims-based model provides more expressiveness and better abstractions than the native WCF security system. It also makes you ready for whatever security scenarios you might have to implement in the future.

About the Author

Dominick Baier is an internationally recognized expert on security of .NET and Windows applications. He supports companies worldwide with design and implementation of security features in their software as a security consultant at thinktecture (www.thinktecture.com). As one of the few “Developer Security” Microsoft MVPs, he works directly with various security teams in Redmond, Washington. Offshoots of this cooperation are the books Developing More Secure Microsoft ASP.NET 2.0 Applications (Redmond, Washington: Microsoft Press, 2006) and Guide to Claims-based Identity & Access Control (Redmond, Washington: Microsoft Press, 2010). Baier also leads the security, WCF, and Azure curriculum at DevelopMentor (www.develop.com). You can find a wealth of security-related resources, as well as conference slide decks and tools/sample code, at his blog atwww.leastprivilege.com.