A Crash Course in Windows Identity Foundation
Add federated and claims-based security to .NET Framework apps
December 2, 2009
RELATED: "Generate SAML Tokens Using Windows Identity Foundation" and "Securing Workflow Services with Windows Identity Foundation, Part 1"
Claims-based and federated security scenarios are gaining wider popularity as platform tools evolve to simplify the developer experience in this space. Adopting a claims-based and federated security model is beneficial to WCF and ASP.NET applications for many reasons. Claims-based security decouples applications from the mode of authentication so that they aren't impacted if credential requirements change or if new types are supported; it also provides a more flexible and more granular artifact for authorization. And most importantly, claims-based security supports federated security models. The primary value proposition of a federated security model is so that applications can grant access to users who authenticate to another domain. This reduces IT overhead associated with provisioning and de-provisioning users, reduces the potential for errors from managing duplicate accounts across applications and domains, and makes it possible to safely establish trust relationships between applications, departments, or even between corporate entities.
Assuming you understand the value proposition, this article will briefly introduce federated security concepts followed by a whirlwind tour of WIF features for WCF and ASP.NET. I'll walk through the participants and flow of communication for both active and passive federation while explaining the basic requirements to set up each scenario with WIF. The discussions will also include a very high-level explanation building an active or passive security token service (STS) with WIF.
Federated Security 101
Let's start with a brief introduction to federated security. Figure 1 shows what a federated security scenario looks like and the participants in the scenario, from a very high level.
Figure 1: Federation participants and communication flow
The subject is the party that will be granted access to an application. This is usually the end user interacting with a client application or a web application via the browser.
The requestor is the agent (the client application or browser) requesting the security token that will describe the subject.
The relying party (RP) can be a web application (ASP.NET) or service (WCF) that will authorize access to features and functionality based on the claims presented in a security token. This token must be from a trusted token issuer.
The identity provider (IdP) is responsible for authenticating the subject. This implies that it has a credential store in its domain (e.g., Active Directory or a custom credentials database).
The STS is responsible for issuing a security token for the subject. If the STS is located at the IdP, it will likely issue a security token that carries identity claims for the Subject. The STS may also issue other claims relevant to authorization at the RP.
The security token format can vary per scenario; however, Security Assertion Markup Language (SAML) is a very popular token format for federation. SAML is an interoperable, XML-based security token format that carries SAML assertions (claims) in a secure manner in that the issuing STS digitally signs the token and usually encrypts it for the RP.
A trust relationship must exist between the RP and the STS. The STS won't issue tokens for any RP it doesn't have knowledge of. The RP won't accept tokens from an STS it doesn't trust. The RP indicates its required claims; the requestor calls the STS requesting a security token for the Subject passing the RP's requirements and credentials for the subject; the STS (at the IdP) authenticates the subject, gathers identity and other relevant claims, and issues a security token carrying those claims; the requestor passes that token to the RP for authorization; the RP verifies that the token is from a trusted issuer, then authorizes access based on the claims within.
This flow of communication is based on the WS-Trust protocol (see docs.oasis-open.org/ws-sx/ws-trust/200512/ws-trust-1.3-os.html). The requestor passes a Request for Security Token (RST) to the STS according to this specification, and the STS replies with a Request for Security Token Response (RSTR). WS-Federation (see the WS-Trust 1.3 OASIS Standard documentation) supplies two profiles for federation with WS-Trust: the Active Requestor Profile (the rich client and web service scenario) and the Passive Requestor Profile (the browser and web application scenario). With the help of WIF applications built with the .NET Framework, you can implement scenarios based on these protocols.
Windows Identity Foundation
WIF is the new identity model framework for the .NET Framework. It provides the features necessary to build claims-based applications and services, for supporting active and passive federated security scenarios, and for building custom STS implementations as needed, and it has built-in support for information cards (for scenarios that involve Windows CardSpace). With WIF you write a lot less code to implement claims-based and federated security scenarios. Though not an exhaustive list, here are some examples of things that you can do with WIF:
Replace classic WCF security behaviors with a new, more flexible mechanism for authenticating and authorizing calls. You can do this even if you are not working with federated security.
Implement a claims-based security model for WCF services that integrates well with classic .NET Framework role-based security techniques.
Likewise, implement claims-based ASP.NET applications that can leverage classic role-based security techniques including existing ASP.NET login controls.
Support passive federation in your ASP.NET applications.
Implement a custom STS to meet requirements not satisfied by an existing STS platform such as Active Directory Federation Services (ADFS). Building a fully functional custom STS is a lot of work and thus should be a last resort.
Issue managed information cards from the STS, to support identity selection from CardSpace.
Support information card selection from ASP.NET applications.
Within each of these topics, of course, there are implementation details that can vary. Thus, they will be worthy of discussion in future installments to this column. For now, back to the crash course in WIF!
Active Federation with WIF
Active federation involves a client application (requestor), a web service (the RP), and an STS for token issuance (this could be part of the IdP). Figure 2 illustrates a simple active federation scenario involving a single STS. In this case, the WCF service at the RP exposes a federated endpoint; the STS exposes a WS-Trust endpoint; the client uses a WCF proxy to authenticate to the STS endpoint and request a security token, then passes the token with the first call to the RP to establish a secure session for the user.
Figure 2: Active federation with WCF and WIF
WIF Configuration for the WCF Service
To set up the WCF service, you expose a federated endpoint and configure the service to use WIF. Figure 3 shows a possible configuration for the RPService from Figure 2.
The federated endpoint uses WS2007FederationHttpBinding, which is based on WS-Trust 1.3 (the latest version of the standard). The binding configuration specifies the following:
which token format is expected, in this case SAML 1.1.
which claims are required; in this case, a simple name claim is indicated.
where the trusted issuer's metadata exchange endpoint is located, so that the client can generate a federated proxy.
In addition to the federated endpoint, the service is configured for WIF with the behavior. This behavior installs a FederatedServiceCredentials and IdentityModelServiceAuthorizationManager to replace the default ServiceCredentials and ServiceAuthorizationManager. These WIF components (among others) supply a new mechanism for processing incoming security tokens and authorizing calls to the service. They rely on a special configuration section, , to initialize the WIF runtime. Figure 4 shows the fundamental settings you should provide for a WCF service.
The in Figure 4 lists a single trusted certificate indicated by its thumbprint. The certificate matching the thumbprint must be stored in the LocalMachineTrustedPeople store. The result is that security tokens will be trusted only if they are signed by the matching private key for this certificate, which in this scenario means that only RP-STS (from Figure 2) is trusted. The section by default expects you to provide at least one accepted audience Uniform Resource Identifier (URI). SAML tokens usually include an audience constraint that indicates for whom the token is issued. If this behavior is not disabled, the incoming token must include a matching URI from this list to be valid. The section indicates the certificate to be used to decrypt incoming tokens, assuming they are encrypted. These are the basic settings that you must provide to initialize WIF for the RP service. There are some other identity model settings that are quite useful to the RP, and I'll explore them in future columns, as the scenario warrants.
At this point, you have a WCF service ready for federation. The client will generate a WCF proxy from the service metadata based on this configuration and must provide the appropriate ClientCredentials to authenticate to the STS. WIF is not required at the client to implement active federation—although WIF does include some useful components for specific client implementations.
Now that the service is ready for federation, how will you authorize access to operations and underlying features? If the service is only concerned that the user is authenticated at the STS, establishing that the security token came from a trusted issuer is sufficient. Most likely you'll expect the STS to issue a token carrying some claims useful for authorization, such as roles or permissions. WIF attaches an authenticated security principal to each request thread—in this case, a ClaimsPrincipal type, which is a wrapper for the collection of claims from the issued security token. Like any other security principal type that implements IPrincipal, ClaimsPrincipal exposes an IsInRole() method that can be used to implement access control.
if (!Thread.CurrentPrincipal.IsInRole("Admin")) throw new SecurityException("Access is denied.");
You can use the identity model configuration to indicate which claim type represents a "role" for IsInRole() calls—or you can write code to check that a specific set of claims are present in the ClaimsPrincipal instance. The former is a cleaner model and works as long as only one claim type is used for access checks.
Implementing an Active STS with WIF
Since I'm using a custom STS for the scenario in Figure 2, I'll briefly describe what has to be done to implement a custom STS. Keep in mind, however, that in this short discussion I'm skipping a lot of detail for brevity. You can implement a very basic STS as follows:
Subclass the SecurityTokenService type in WIF and provide an override for GetScope() and GetOutputClaimsIdentity().
Supply a WCF endpoint for the STS using one of the WS-Trust contracts implemented by the base STS type.
Figure 5 shows a simple STS implementation. The GetScope() override is responsible for validating that the request is for a trusted RP. If so, you will supply the certificate to be used for signing the token (in this case, the STS private key) and the certificate to be used to encrypt the token (the STS should know the RP certificate from the trust relationship).
public class RPSTS : SecurityTokenService{ public RPSTS(SecurityTokenServiceConfiguration config) : base( config ) {} protected override Scope GetScope(IClaimsPrincipal principal, RequestSecurityToken request) {Scope scope = new Scope(request.AppliesTo.Uri.AbsoluteUri); scope.EncryptingCredentials = this.GetCredentialsForAppliesTo(request.AppliesTo); scope.SigningCredentials = new X509SigningCredentials(CertificateUtil.GetCertificate(StoreName.My,StoreLocation.LocalMachine, "CN=RPSTS"));return scope; }protected override IClaimsIdentity GetOutputClaimsIdentity(IClaimsPrincipal principal,RequestSecurityToken request, Scope scope) { IClaimsIdentity claimsIdentity = new ClaimsIdentity(); claimsIdentity.Claims.AddRange(CredentialStore.GetClaimsForUser(principal.Identity.Name));return claimsIdentity; } // other supporting methods}
The override for GetOutputClaimsIdentity() is given access to a security principal based on IClaimsPrincipal. This type contains the claims granted the authenticated user, in this case based on the user's Windows credentials. Before the token is issued, you'll usually convert these claims into a meaningful set of claims for the RP, by initializing a new ClaimsIdentity instance. For this example, the credential store returns relevant claims per authenticated user. You should take a look at the code sample for additional details, but this sums up the core aspects of a simple STS implementation.
The service model configuration in Figure 6 shows how the sample STS from Figure 2 exposes a WS-Trust 1.3 endpoint using the synchronous version of the contract: IWSTrust13SyncContract. The binding is based on WS2007HttpBinding defaults, which means that the STS requires a Windows credential to authenticate calls. For this particular example, the default identity model configuration will suffice—for example, no need to override how incoming credentials are processed.
Passive Federation with WIF
Passive federation involves a browser (requestor), a web application (the RP), and a passive STS for token issuance. Figure 7 illustrates a simple passive federation scenario whereby the RP is an ASP.NET application that supports passive federation, and the custom STS is also passive—meaning that it is browser based as opposed to a WCF service implementation. When users browse to the RP, they are redirected to authenticate to the STS, then redirected back to the RP once a token has been issued. At this point, a secure session is established and a session cookie generated.
Figure 7: Passive federation with ASP.NET and WIF
Passive federation still involves WS-Trust messaging in the form of an RST and RSTR, the latter of which includes the issued security token. The differences lies in that they're sent as query strings and form parameters rather than as Simple Object Access Protocol (SOAP) messages. In the next sections, I'll review how the ASP.NET application is configured and review the passive STS implementation—again only from a very high level.
Enabling Passive Federation for ASP.NET
WIF supplies two HTTP modules to support passive federation: WSFederationAuthenticationModule (FAM) and SessionAuthenticationModule. The FAM handles redirecting to the STS from the RP and handles processing the RSTR to extract the security token and install a ClaimsPrincipal for the request thread. The SessionAuthenticationModule manages the authentication session: generating the session security token, which contains the ClaimsPrincipal; writing it to cookie; managing the lifetime of the session cookie; and rehydrating the ClaimsPrincipal from the cookie when it is present. To enable these two modules, add the code in Figure 8 to your section (for Microsoft IIS 6.x) and to your section (for IIS 7):
For the FAM to handle requests, you must set the authentication mode to None and deny anonymous users access to the site:
In addition, the FAM is initialized by the identity model configuration. You should at least supply the following settings: a list of trusted certificate issuers, the allowed audience URIs, the required claim types for authorization, and configuration for passive federation. The first three work as discussed for active federation, whereas the passive federation configuration is specific to ASP.NET scenarios. Figure 9 shows an example of the identity model configuration for a passive site.
In the section you must enable passive redirect to achieve passive federation for your ASP.NET application. This tells the FAM to handle process incoming requests to the site and to redirect to the STS for all unauthenticated calls. The issuer setting indicates the STS to redirect to. The realm setting actually tells the issuer (as part of the RST) for whom the token will be issued, and this should match one of the acceptable audience URI. When the RSTR is returned to the application, the FAM processes this response and constructs a ClaimsPrincipal for the request thread.
Once again, the ClaimsPrincipal is useful for access control. In the case of ASP.NET, the IsInRole() check is not only useful for dynamic checks, but this method is also encapsulated in most of the login controls. Feasibly, if you can select a particular claim type as the "role" claim type (this is configurable), you can continue to implement role-based security with your favorite techniques except that the role check becomes a claims check.
Implementing a Passive STS with WIF
A passive STS can be implemented in two ways with WIF: using the FederatedPassiveTokenService control (STS control) or using the FAM with the help of some additional custom code. For simplicity I will focus on the control implementation here. The architecture of this implementation looks as shown in Figure 10 (assuming that you're using forms authentication to authenticate users prior to issuing tokens).
Figure 10: A passive STS implemented with the FederatedPassiveTokenService control
When the FAM redirects from the RP to the STS, an RST is passed along with the request. In this case the FormsAuthenticationModule will redirect to the login page passing the original request in the ReturnUrl query string parameter. In the post back of the login page, the user is authenticated (using the ASP.NET provider model or your own custom code), then redirected to the ReturnUrl, which is the original RST and presumably targets a page that contains the STS control.
The STS control processes incoming RST requests before the page is loaded (during PreRender). The control is associated with a custom STS implementation (like that from Figure 5). This STS implementation programmatically invokes the FAM to produce a ClaimsPrincipal for the authenticated user after which the same overrides for GetScope() and GetOutputClaimsIdentity() are called, the latter of which is once again responsible for producing the actual claims to be issued and included in the security token. The control then redirects back to the RP (the RST indicates the URL where the reply should be sent), passing the RSTR in a form POST parameter. At this point, the FAM does its magic to process the RSTR at the RP and install a ClaimsPrincipal for the request thread for authorization.
The beauty of using this control to implement a passive STS is that very little code is required—only that of the custom STS, as discussed earlier. Of course, the downside is that you cannot customize much of the passive federation behavior beyond a few simple overrides before and after the RST is processed. I'll explore some deeper scenarios that require customization in future columns.
More to Come
I hope you've enjoyed this whirlwind tour of federated security and WIF, in which I've set the foundation for future installments of this new column. If you're looking for a deeper introduction to the WIF platform, you should download the WIF whitepaper from MSDN (I'll provide the latest link in the readme for the code download—see blurb below, since this is likely to change), which describes additional WIF features. I also spent some time elaborating on the value of claims-based and federated security in my Claims-Based WPF white paper, which you can find at claimsbasedwpf.codeplex.com.
Read more about:
MicrosoftAbout the Author
You May Also Like