This chapter from Programming Windows Identity Foundation takes a concrete approach to WIF programming, tackling many important problems and scenarios you might encounter when securing ASP.NET applications.

In this chapter:

More About Externalizing Authentication

Single Sign-on, Single Sign-out, and Sessions

Federation

Claims Processing at the RP

Summary

Now that most technicalities are out of the way, we can focus on intended usage of the product for addressing a wider range of scenarios.

This chapter resumes the architectural considerations that drove Part I of the book, “Windows Identity Foundation for Everybody,” by tackling more complex situations. I’ll assume you are now familiar with the flow described in Chapter 3, “WIF Processing Pipeline in ASP.NET.” I’ll give you concrete indications about how to customize the default behavior of Windows Identity Foundation (WIF) to obtain the desired effect for every given scenario.

Using claims-based identity in your application is, for the most part, the art of choosing who to outsource authentication to and providing just the right amount of information for influencing the process. This chapter will not exhaust all the possible ways you can customize WIF—far from it. However, it will equip you with the principles you need to confidently explore new scenarios on your own.

The first section, “More About Externalizing Authentication,” takes a deeper look at the entities to which you can outsource authentication for your application. I’ll go beyond the simplifications offered so far, introducing the idea of multiple provider types. A lot of the discussion will be at the architectural level, helping you with the design choices in your solutions. However, hardcore coders should not fear! The section also dives deep into the Security Token Service (STS) project template that comes with the WIF SDK. Although in real scenarios you’ll rarely need to create a custom STS, given that more often than not you’ll rely on off-the-shelf products such as Active Directory Federation Services 2.0 (ADFS 2.0), you’ll find it useful to see a concrete example of how the architectural considerations mentioned are reflected in code.

The “Single Sign-on, Single Sign-out, and Sessions” section explores techniques that reduce the need for users to explicitly enter their credentials when visiting affiliated Web sites and shows how to clean up multiple sessions at once. One specific case, sessions with sliding validity, is the occasion for a deeper look at how WIF handles sessions.

The “Federation” section dissects the pattern that is most widely used for handling access across multiple organizations. I’ll cover more in depth the use of STSes for processing claims, and we’ll tackle the problem of deciding who should authenticate the user when there are many identity providers (IPs) to choose from (something known as the home realm discovery problem). The solutions to those problems can be easily generalized to any situation in which the relying party (RP)—which was discussed in Chapter 3—needs to communicate options to the IP. I’ll demonstrate that with another example: the explicit request for a certain authentication level.

The “Claims Processing at the RP” section closes the chapter by describing how to use Windows Identity Foundation for preprocessing the claims received from the identity provider. I’ll briefly revisit the claims-based authorization flow—introduced in minimal terms in Chapter 2, “Core ASP.NET programming.” Then I’ll show you how to filter and enrich the IClaimsPrincipal before the application code gains access to it.

After you read this chapter, you’ll be able to make informed decisions about the identity management architecture of your solutions. You’ll know what it takes to implement such decisions in ASP.NET. You’ll have concrete experience using the WIF extensibility model for solving a range of classic identity management scenarios. That experience will help you to devise your own WIF-based solutions. Once again, I’ll give you practical code indications about the ASP.NET case, but the general principles introduced here can be applied more broadly, often to the WCF services case and even on non-Microsoft platforms.

More About Externalizing Authentication

Until now, I have described situations in which the application relies on only one external entity—what I defined as the identity provider, or IP. Although this is an accurate representation of a particular common scenario, the general case can be a bit more complicated. Not only might you have to accept identities from multiple identity providers, identity providers are not the only entities you can outsource authentication to!

So far, the role played by the entity within a transaction (the identity provider) has been conflated with the instrument used to perform the function (the STS). The purpose of this section is to help you better understand the separation between the two by providing more details about the nature of the identity provider, introducing a new role known as the federation provider, and studying how those high-level functions reflect on the implementation of the associated STS.

Identity Providers

Being an identity provider is a role, a job if you will. You know from Chapter 1, “Claims-Based Identity,” that an IP “knows about subjects.” In fact, all the thinking behind the idea of IP is just good service orientation applied to identity.

The standard example of a concrete identity provider is one built on top of a directory, just as ADFS 2.0 is built on top of Active Directory. In this scenario, there’s an entity that is capable of authenticating users and making assertions about them, and all you are doing is making that capability reusable to a wider audience by slapping a standard façade (the STS) in front of it. The use of standards when exposing the STS is simply a way of maximizing the audience and increasing reusability. Here’s an example: Although a SharePoint instance on an intranet can take advantage of Active Directory authentication capabilities directly via Kerberos, that is not the case for a SharePoint instance living outside the corporate boundaries and hosted by a different company. Exposing the authentication capabilities of Active Directory via ADFS 2.0 makes it possible to reuse identities with the SharePoint instance in the second scenario, removing the platform and location constraints. WIF is just machinery that enables your application to take advantage of the same mechanism. It is worthwhile to point out that SharePoint 2010 is, in fact, based on WIF.

Another advantage of wrapping the actual authentication behind a standard interface is that you are now isolated from its implementation details. The IP could be a façade for a directory, a membership provider–based site, or an entirely custom solution on an arbitrary platform; as long as its STS exposes the authentication functionality through standards, applications can use it without ties or dependencies outside of the established contract. Who cares if the connection string to the membership database changes, or even if there is a membership database in the first place? All you need to know is the address of the STS metadata.

Those characteristics of the IP role tell you quite a lot about what to expect regarding the structure of the STS exposed by one IP.

NOTE

In literature, you’ll often find that one STS used by one IP can be defined as an “IP-STS.” In a short, you’ll see how this can sometimes be useful for disambiguating the function the STS offers.

In the WS-Federation Sign-in flow, described in Chapter 3, you saw that the details of how the STS authenticates the request for security tokens is a private matter between the STS and the user. Now you know that such a system has to be something that allows the STS to look up user information from some store—so that it can be extracted and packaged in the form of claims. Notable examples are the ones in which the STS leverages the same authentication methods of the resource it is wrapping. If the IP is a façade for Active Directory and the user is on the intranet, the STS might very well be hosted on one ASPX page that is configured in Internet Information Services (IIS) to leverage Windows native authentication. If the source is a membership database, the STS site will be protected via a membership provider, and so on. The claim value’s retrieval logic in the STS will use whatever moniker the authentication scheme offers for looking up claim values, but the authentication will often be performed by the infrastructure hosting the STS rather than the STS code itself.

Nothing prevents one IP from exposing more than one STS endpoint to accommodate multiple consumption models. For example, the same IP might be listening for Kerberos authenticated requests from the intranet and X.509 secured calls on an endpoint available on the Internet; the IP might expose further endpoints, both for browser-based requestors via WS-Federation and SAMLP or for active requestors via WS-Trust; and so on. This process offers another insight into how one IP is structured: authentication and claims issuance logic should communicate but remain separate so that multiple STS endpoints scenarios are handled with little or no duplication. As you’ll see later in the section, the WIF STS programming model is consistent with that consideration.

An IP will actively manage the list of the RPs it is willing to issue a token for. This is not only a matter of ensuring that claims are transmitted exclusively to intended recipients, but also a practical necessity. Especially in the passive case, in which token requests are usually simple, the IP decides what list of claims will be included in a token according to the RP the token is being issued for. (“Passive case” is mainly another way to say that you use a browser. You’ll know everything about it after reading Chapter 5, “WIF and WCF.”) Such a list is established when the RP is provisioned in the IP’s allow list. Just like WIF enables one application to establish a trust relationship with an IP by consuming its metadata via the Federation Utility Wizard, IP software such as ADFS 2.0 includes wizards that can consume the application metadata and automatically provision the RP entry in its allow list.

NOTE

In computer science as in other disciplines, an allow list is a list of entities that are approved to do something or to be recipients of some action. For example, if your company network has an allow list of Web sites, that means you can browse only on those sites and no other. Conversely, having a blacklist of Web sites means that you can browse everywhere but on those. An IP normally maintains an allow list of RPs it is willing to issue a token for: any request for a recipient not in the allow list is refused. The ADFS 2.0 UI describes that as Relying Party Trust. I am not very fond of that use of “trust,” which in this context has a special meaning (believing that the claims issued by a given IP about a subject are true), but your mileage may vary.

The IP also keeps track of the certificate associated with the RP, both for ensuring that the RP has a strong endpoint identity (exposed via HTTPS) and for encrypting the token with the correct key if confidentiality is required.

Nonauditing STS

There are situations, especially in the area of e-government, in which the user would like to keep private the identity of the RP he is using. For example, a citizen might want to use a token issued by a government IP proving his age, but at the same time he would like to maintain his privacy about what kind of sites (for example, liquor merchants) he is using the token for.

Technically, the scenario is possible, although setting up such functionality would introduce some limitations. For example, not knowing the identity of the RP, the IP would not know the associated X.509 certificate and that would make it impossible to encrypt the issued token. Also, some protocols handle the scenario better than others. Although the WS-Federation specification allows for specifying which claims should be included in the requested token, most implementations expect the list of claims required by one RP to be established a priori, which is of course of no help if the identity of the RP is not known. Things can be a little easier with WS-Trust, as you’ll see in the next chapter.

In the business world, the most common scenario requires the IP to have a preexisting relationship with the RP before issuing tokens for it; therefore, off-the-shelf products such as ADFS 2.0 normally mandate it.

The scenario described so far—one application outsourcing authentication to one identity provider—is common, and none of the further details about IPs I gave here invalidate it. However, sometimes the planets do not align the way you’d like, and for some reason simple direct outsourcing to one IP does not solve the problem.

Federation Providers

Let’s consider for a moment the matter of handling multiple identity providers. Imagine being a developer for a financial institution. Let’s say you are writing a corporate banking application, which allows companies to handle the salary payment process for their workforce. This is clearly one case in which you need to trust multiple identity providers—namely, all the companies who access your financial institution for managing payments.

From what you have seen so far, you know only one way of handling the situation: adding multiple FederatedPassiveSignIn controls to your application entry page, each of them pointing to a different identity provider. Although the approach works, it can hardly be called a full externalization of identity management because provisioning and deprovisioning identity providers forces you to change the application code. Things get worse when you have one entire portfolio of applications to make available to a list of multiple identity providers—having to reapply the trick mentioned previously for every application rapidly becomes unsustainable as the number of apps and IPs goes up. This clearly indicates the need to factor out IP relationship management from the application responsibilities.

Another common issue you might encounter has to do with the ability of your application to understand claims as issued by one identity provider. Here is why:

Sometimes you might have simple format issues. For example, the users you are interested in might come from another country and their IP might use claim URIs containing locale-specific terms your application does not understand. (An English application might need to know the name of the current user and expect it in an http://claims/name format, while an Italian IP might send the desired information in the http://claims/nome claim format.)

Sometimes the information will need some processing before being fed to your application. For example, an IP might offer a birth date claim, but your application might be forbidden from receiving personally identifiable information (PII). All you require here is a simple Boolean value indicating if the user is below or above a certain threshold age. Although the information is clearly available to the IP, it might not be offered as a claim.

Finally, you might need to integrate the claims received from the IP with further information that the IP does not know. For example, you might be an online book shop accepting users from a partner IP. The IP can provide you with name and shipping address claims, but it cannot provide you with the last 10 books the user bought from your store. That is data that belongs to you, and you have the responsibility of making it available in the form of claims if you want to offer to your developers a consistent way of consuming identity information.

What is needed here is a means of doing some preprocessing—some kind of intermediary that can massage the claims and make them more digestible for the application.

The standard solution to these issues is the introduction of a new role in identity transactions, which goes by the name of Federation Provider (FP).

A Federation Provider is a claims transformer; it is an entity that accepts tokens in input—kind of like an RP does—and issues tokens that are (usually) the result of some kind of processing of the input claims. An FP offers its token manipulation capabilities exactly like an IP, by exposing STS endpoints. The main difference is that, whereas one IP usually expects requests for security tokens secured by user credentials that will be used for looking up claims, the FP expects requests to be secured with an issued token that will be used as input for the claims transformation process. In the IP case, the issued token contains the claims describing the authenticated user; in the FP case, the issued token is the result of the processing applied to the token received in the request. Given the fact that an FP exposes one STS, applications can use it for externalizing authentication in exactly the same way as you have seen they do with IPs. WIF’s Federation Utility Wizard does not distinguish between IPs and FPs—all it needs is an STS and its metadata.

The reason that it’s known as the Federation Provider is that enabling federation is the primary purpose that led to the emergence of this role. In a nutshell, here’s how that works. Imagine company A is a manufacturer that has a number of line-of-business (LOB) applications for its own employees, including applications for supply management, inventory, and other usual stuff. Company B is a retailer that sells the products manufactured by A. To improve the efficiency of their collaboration, A and B decide to enter into a federation agreement: certain B employees will have access to certain A applications. Instead of having every A application add the B identity provider and having the B IP provision every application as a recognized RP, A exposes a Federation Provider.

The B IP will provision the A FP just like any other RP, associating to the relationship the list of claims that B decides to share with A about its users. All of the A applications that need to be accessible will enter into a trust relationship with the A FP, outsourcing their authentication management to its STS. Figure 4-1 shows the trust relationships and the sign-in flow.

FIGURE 4-1 The authentication flow in a federation relationship between two organizations

The flow goes as follows:

One employee of B navigates to one application in A.

The user is not authenticated because the application will accept only users presenting tokens issued by the A FP. The application redirects the user to the A FP.

Again, the user is not authenticated. The A FP will accept only users presenting tokens issued by the B IP. The application redirects the user to the B IP.

The user lands on the B IP, where authentication will take place according to the modes decided by B. The user gets a token from the B IP.

The user gets back to the A FP and presents the token from the B IP.

The A FP processes the token according to the application’s needs—some claims might be reissued verbatim as they were received from B; others might be somehow processed; still others might be produced and added anew. The A FP packages the results of the processing in the form of claims and issues the new token to the user.

The user gets back to the application and presents the token from the A FP; the application authenticates the call by examining the token from A FP.

The main advantage of using an FP in a federation scenario is obvious: you now have a single place where you can manage your relationship, defining its terms (such as which claims you should receive). The applications are decoupled from those details. Because the FP knows about both the incoming claims (because it is on point for handling the relationships) and the claims needed by the application (because it is part of the organization, it knows about which claim types are available and their semantics), applications can effectively trust it to handle authentication on their behalf even if the actual user credentials verification takes place elsewhere. The process can be iterated. For example, you can have an FP trusting another FP, which in turn trusts an IP, although that does not happen too often in practice.

The WIF STS Template

Outsourcing authentication to one external STS makes life much easier for the application developer, at the price of relinquishing control of a key system function to the STS itself. Although relinquishing control of the mechanics of authentication is sweet, as I’ve been pointing out through the entire book, the STS you choose better be good, or else. Here’s what I mean by “good” in this case:

An STS must be secure A compromised STS is an absolute catastrophe because it can abuse your application’s trust by misrepresenting the user privileges.

An STS must be available If the STS endpoint is down, as a consequence of peak traffic or any other reason, your application is unreachable: no token, no party.

An STS must be high-performing Every time a user begins a session with your application, the STS comes into play. Bad performance is extremely visible, can become a source of frustration for users, and even pile up to compromise the system’s availability.

An STS must be manageable If you own the STS, whether it used as an IP or FP, you’ll need to manage many aspects of its activities and life cycle, such as the logic used for retrieving claim values, provisioning of recognized RPs, establishment of trust relationships with the IP of federated partners, management of signing and encryption keys, auditing of the issuing activities, and management of multiple endpoints for different credential types and protocols. The list goes on and on.

In other words, running an STS is serious business: don’t let anybody convince you otherwise. An endpoint that understands WS-Federation, WS-Trust, or SAMLP requests and can issue a token accordingly technically fits the definition of “STS,” but protocol capabilities alone can’t help with any of the requirements just mentioned.

This is why in the vast majority of real-world scenarios it is wise to rely on off-the-shelf STS products, such as ADFS 2.0. Those products host STS endpoints and advanced management features that simplify both small and large maintenance operations that running an IP or an FP (or both) entails. Let’s take ADFS 2.0 as an example: ADFS 2.0 is a true Windows server role—tried, stressed, and tested just like any other Windows server feature.

The Windows Identity Foundation SDK makes the generation of an STS deceivingly simple by offering Microsoft Visual Studio templates for both ASP.NET Web sites and WCF services projects that implement a bare-bones STS endpoint (for WS-Federation and WS-Trust, respectively). The Generate New STS option in the Add STS Reference Wizard just instantiates one of those templates in the current solution. Those test STSes are an incredibly useful tool for testing applications, thanks to the near absence of infrastructure requirements (ADFS 2.0 requires a working Active Directory instance, SQL Server, Windows Server 2008 R2, and so on) and instantaneous creation. As somebody who had to write STSes from scratch with WCF in the past (a long and messy business), I am delighted by how easy it is to generate a test STS with WIF. For the same reason, such test STSes are consistently used in WIF samples and courseware. This book is no exception.

Why do I say “deceivingly simple”? Because of all the requirements I listed earlier. WIF can certainly be used to build an enterprise-class STS—it has been used for building ADFS 2.0 itself. However, between the STS template offered by the WIF SDK and ADFS 2.0, there are many, many man-years of design, enormous amounts of development and testing, tons of assumptions and default choices, brutal fuzzing, relentless stressing, and so on. The fact that the STS template gives you back a token does not mean it can be used as is in a real-life system. People regularly underestimate the effort required for building a viable STS, an error of judgment that can result in serious issues. That is why I always discourage the creation of custom STSes unless it’s absolutely necessary, and there’s not a lot of detailed guidance on that.

Now that I’ve got the disclaimer out of the way: this chapter will use a lot of custom STSes. Taking a peek inside an STS is a powerful educational tool that can help you understand scenarios end to end. Being able to put together test STSes can help you simulate complex setups before committing resources to them. Finally, you’ll likely encounter situations in which setting up a custom STS is the way to go—for example, if your user credentials are not stored in Active Directory. The guidance here is absolutely not enough for handling the task—that would involve teaching how to build secure, scalable, manageable, and performing services, which is well beyond the scope of this text—but it can be a starting point for understanding the token issuance model offered by WIF.

The rest of the section describes the STS template for ASP.NET offered by WIF SDK 4.0. As you read through this section, I suggest you go back to the simple example you created in Chapter 2 and put breakpoints on the parts of the STS project being discussed. Every time something is not too clear, try a test run in the debugger to get a better sense of what’s going on.

Structure of the STS ASP.NET Project Template

The ASP.NET Security Token Service Web Site template, as WIF SDK 4.0 names it, can be found in the C# Web sites templates list in Visual Studio. As mentioned, this is also the template that is used by the Add STS Reference Wizard for generating an STS project within an existing solution. Figure 4-2 shows the list of templates installed by the WIF SDK 4.0.

FIGURE 4-2 The templates installed by WIF SDK 4.0, with the template used for creating an ASP.NET STS highlighted

The STS Web site is typically created on the local IIS. Although it is possible to use the plain HTTP binding, in general the STS Web site will be created on an HTTPS endpoint.

NOTE

Using HTTP in this case is normally a really bad idea. Even if you encrypt the tokens you issue, and even if the RP can take steps for mitigating the risk of accepting stolen tokens, the reality is that using plain HTTP on browser-based scenarios makes you vulnerable to man-in-the-middle and other attacks. In Chapter 5, you’ll have a chance to dig deeper into the topic.

IIS vs. Visual Studio Built-in Web Server

Visual Studio allows you to develop Web sites without requiring the presence of IIS on your development machine. Visual Studio offers a built-in Web server, called the ASP.NET Development Server, which can be used to render pages directly from the file system.

Although you can get WIF to work on Web sites running on the ASP.NET Development Server, there are limitations (for example, the built-in Web server does not support HTTPS) and complications (for example, the dynamically assigned ports change the site URIs and thus force changes in the configuration). Because of this, it’s just simpler to use IIS.

Similar considerations led me to use Web site projects rather than Web application ones. Web application development starts on the file system and requires extra steps for hosting (and debugging) the application in IIS. Furthermore, at the time of this writing, Fedutil.exe is not a big friend of the dynamic ports system featured by ASP.NET Development Server. The Add STS Reference Wizard will not always work as expected when launched on a Web application project.

That is the structure of a minimal Web site protected via Forms authentication, containing the classic Login.aspx and Default.aspx pages. The web.config file is minimal, containing practically nothing specific to WIF apart from the reference to its assembly and a few values in the <appSettings>. The Web site is configured to use Forms Authentication. As you saw in the first example in Chapter 2, Login.aspx does not actually verify any credentials and represents just a pro-forma authentication page: the page will just create the authentication cookie and start a session regardless of the credentials entered in the UI.

The hands-on lab Web Sites and Identity (C:\IdentityTrainingKit2010\Labs\WebSitesAndIdentity\Source\Ex1-ClaimEnableASPNET) exercise 2, shows how to use an existing Membership store for authenticating calls to the STS, and how to source claim values from a Role provider.

All this emphasizes what I mentioned earlier about the separation between the STS functions and the authentication mechanism: here Forms authentication is the method of choice, but it is independent from what WIF does for implementing the token-issuing functionality. The authentication system could be easily substituted with Windows integrated authentication or whatever else, as long as it takes care of authenticating the user before giving access to Default.aspx.

NOTE

An obvious observation is that the STS template generates an IP-STS, something that authenticates users and issues tokens describing them. It is not hard to transform it into an R-STS: you can just run the Add STS Reference Wizard on the STS project itself, and that will be enough for excluding the current Forms authentication settings and externalize authentication to the second STS of your choosing. However, that would change only the way authentication is handled, not the way claims are generated: an R-STS transforms incoming claims, but the default template implementation does not do that. At the end of the section, I’ll discuss what you need to change for modifying the claim issuance criteria as well.

The Default.aspx page represents the STS endpoint, and it takes care of instantiating and executing the token-issuing logic in the context of an ASP.NET request. The page itself does not contain much. What we are interested in is the Page_PreRender handler in Default.aspx.cs:

This code is the STS counterpart of the WS-Federation processing logic that WIF provides for RPs, as studied in Chapter 3. Whereas the RP generates the request for a security token and validates it, the STS listens to those requests and issues tokens according to the WS-Federation protocol. Here’s a quick explanation of what the method does:

The handler inspects the request QueryString for the WS-Federation action parameter, wa. Let’s focus on the case in which wa is present and has the value wsignin1.0, which indicates a request for a token. (We’ll explore the sign-out case later in the chapter.)

The code creates a new SignInRequestMessage from the request—that is, a name-value collection that surfaces the various WS-Federation parameters as properties.

Do you have a non-empty IPrincipal? Is the current user authenticated? If it isn’t, an UnauthorizedAccessException is thrown and the user is redirected to the login page. If it is, the following must take place:

Get an instance of SecurityTokenService by retrieving an instance of a subclass, CustomSecurityTokenService. This class contains the core STS logic, as you’ll see in a moment.

The new STS instance, along with the incoming SignInRequestMessage and the user’s IPrincipal, is fed to FederatedPassiveSecurityTokenServiceOperations.ProcessSignInRequest, where it will be used for issuing the token and producing a suitable SignInResponseMessage.

Finally, FederatedPassiveSecurityTokenServiceOperations.ProcessSignInResponse writes the SignInResponseMessage in the response stream, which will be eventually forwarded to the RP and processed as you saw in Chapter 3.

There are a lot of classes with long names, but in the end the code shown earlier just feeds the authenticated user and the request to a custom SecurityTokenService class and sends back the result. The STS project features an App_Code folder, which contains all the classes the STS needs, including the CustomSecurityTokenService class; all you need to do is take a look at what happens there.

The Redirect Exception in the STS Template in Visual Studio 2010

At the time of this writing, the ASP.NET STS template exhibits a small issue with Visual Studio 2010. At the end of the Page_PreRender method, there is a catch clause that handles generic Exceptions and re-throws them after having added a message. Unfortunately, the code described earlier contains at least a redirect, which throws an exception. Normally, you would not see it, but the re-throw makes Visual Studio stop at the unhandled exception. There are various workarounds for this issue. You could catch ThreadAbortException and ignore it. You could just press F5 again, and the application will move forward without issues. You could comment that line in the template. You could start without debugging. I do not suggest disabling the Visual Studio default behavior of stopping at unhandled exceptions unless you know very well what you are doing.

STS Classes and Methods in App_Code

The Common.cs file is not very interesting; it’s just a bunch of constants. CertificateUtil.cs is not that remarkable either; it’s a helper class for retrieving X.509 certificates from the Windows stores, although there is an interesting piece of trivia for it. WIF uses that code, instead of the classic X509Certificate2Collection.Find because the latter does not call Reset on the certificates it opened.

CustomSecurityTokenServiceConfiguration, as the name implies, takes care of storing some key configuration settings for the STS: the name, the certificate that should be used for signing tokens, serializers for the various protocols, and so on. The most important setting it stores is the type of the custom SecurityTokenService itself.

Finally, we get to the very heart of the STS: the class in CustomSecurityToken.cs. The code generated by the template has the purpose of doing the bare minimum for obtaining a working STS; hence, I won’t analyze it too closely here, except for pointing out some notable behavior. Rather, I’ll use it as a base for telling you about the more general model that you have to follow when developing a custom STS in WIF. Note that the considerations about SecurityTokenService apply both to ASP.NET and WCF STSes.

SecurityTokenService In WIF, a custom STS is always a subclass of SecurityTokenService, and the ASP.NET template is no exception. The claims-issuance process is represented by a series of SecurityTokenService methods, which are invoked following a precise syntax that leads the form request validation to emit the token bits. Complete coverage of that sequence is beyond the scope of this book; however, here I’ll list the main methods you should know about:

ValidateRequest This method takes in a RequestSecurityToken and verifies that it is in a request that can be handled by the current implementation. For example, it checks that the required token type is known. SecurityTokenService provides an implementation of ValidateRequest. You should override it only if you are adding or subtracting from the default STS capabilities. There are also few things taking place in GetScope that could perhaps be done in ValidateRequest. I’ll point those out as we encounter them.

GetScopeGetScope is an abstract method in SecurityTokenService that must be overridden in any concrete implementation. It takes as input the IClaimsPrincipal of the caller and the current RequestSecurityToken.

The purpose of GetScope is to validate and establish some key parameters that will influence the token-issuance process. Those parameters are saved in one instance of Scope, which is returned by GetScope and will cascade through all the subsequent methods in the token-issuance sequence. Here are the main questions that GetScope answers:

Which certificate should be used for signing the issued token? Although a signing certificate has already been identified in the configuration class, GetScope should confirm that certificate (as done by the template implementation) or override it with custom criteria—for example, if something in the request influences which certificate should be used.

Is the intended token destination a recognized RP? As discussed earlier, normally an STS issues tokens only to the RP URIs that have been explicitly provisioned. If the incoming wtrealm (available in RequestSecurityToken via the property AppliesTo) does not correspond to a known RP, an InvalidRequestException should be thrown.

NOTE

The template implementation of GetScope performs the check against a hard-coded list. One could argue that a validation check would belong to the ValidateRequest method, but the item about encryption that follows shows how GetScope would need to query an RP settings database anyway.

If the AppliesTo value is valid, it is fed into the Scope object. It will be needed for the AudienceRestriction element of the issued token, which in turn will be validated by WIF against the <audienceURI> config element on the RP.

Should the issued token be encrypted? If yes, with which certificate? The STS configuration should specify whether the token should be encrypted. If it should be, the same store that was used for establishing whether the RP was valid should also carry information about which encryption certificate should be used. The template uses a value from config.

To which address should the token be returned? The template assumes that wtrealm—that is, the AppliesTo value—is both the identifier of the RP and its network-addressable URI. As a result, GetScope assigns the value of AppliesTo to the ReplyToAddress property of the Scope object.

Important

Although in many cases it is true that AppliesTo contains the network addressable endpoint of one RP, that does not always hold. Sometimes wtrealm will be a logical identifier for the application rather than a network address, and the actual address to which the token should be returned will be different. A way of handling this is by sending the actual address in the request via the wreply parameter, and then assigning it to Scope.ReplyToAddress (from RequestSecurityToken.ReplyTo). ReplyTo addresses should always be thoroughly validated because supporting wreply opens your STS up to redirect attacks.

NOTE

ADFS 2.0 does not handle wreply.

When the Scope is ready, a number of lower level token-issuance preparation steps take place. You can influence those if you want to, but I won’t go into further details here. After those steps are completed, it is finally time to work with claims.

GetOutputClaimsIdentity This method takes as input the IClaimsPrincipal of the caller, the RequestSecurityToken, and the Scope. It returns an IClaimsIdentity, which contains the claims that should be issued in the token for the caller. Note that at this point the IClaimsPrincipal of the caller is a representation of the IPrincipal obtained from the STS caller via Forms authentication. This should not be confused with the output IClaimsPrincipal created by the STS, which will be available at the RP after successful sign-in.

This is perhaps the least realistic of the implementations in the STS template. It returns two hard-coded claims, Name and Role, regardless of the targeted RP or the caller (the only concession being the value of the Name claim, extracted from the incoming principal):

In a more realistic setting, your GetOutputClaimsIdentity implementation would need to make some decisions about the outgoing IClaimsIdentity. These are the questions it will need to answer:

Given the current request, which claim types should be included? The list of claims that should be issued is often established per RP, at provisioning time. That is especially common for WS-Federation scenarios, and some products will go as far as implementing that tactic for the WS-Trust case as well.

NOTE

ADFS 2.0 uses that approach in every case. The list of claims to issue is always established on the basis of the RP for which the token is being issued.

Chances are that the list of claims to use will be available in the same store you used in GetScope for retrieving the RP URI and encryption certificate.

WS-Trust (and WS-Federation, via wreq or wreqptr parameters) supports requesting a specific list of claims for every request. Although that requires more work, which probably includes checking on an RP-bound list if the required claims are allowed for that given RP, there are many advantages to the approach. Apart from minimal disclosure and privacy considerations, possibly a bit out of scope here, one obvious advantage is that this can help keep the token size under control. A token representing a Windows identity can have many group claims. If for a given transaction the group claim is not required, being able to exclude it can dramatically shrink the resulting token.

If you want to support requests that specify the required claims, you’ll find that list in the RequestSecurityToken.Claims collection.

Given the current principal, which claim values should be assigned? Together with the request authentication method, this is the question that determines whether your STS is an IP-STS or an R-STS.

One IP-STS uses some claims of the incoming IClaimsPrincipal for looking up the caller in one or more attribute stores, from where the STS will retrieve the values to assign to the established claim types. That’s the direct descendent of using a user name for looking up attributes in a profile store; in fact, it can take place in exactly the same way if you have a user name claim. Of course, you are not limited to it—you can use any claim you like.

One R-STS processes the claims in the incoming IClaimsPrincipal in arbitrary ways, storing the results in other claims in the outgoing IClaimsIdentity. Note that the STS can also just copy some claims from the incoming token to the outgoing one without modification, and it can even add new claims in the same way the IP-STS does. I’ll show some examples of this later, during the federation and home-realm discovery discussions.

ADFS 2.0 offers a management UI, where administrators can specify how to source or transform claims. The mappings can be specified via a simple UI or via a SQL-like language that is especially well suited for claims issuance. In your own STS, you can embed the corresponding code directly in GetOutputClaimsIdentity, or you can develop a mechanism for driving its behavior from outside.

Metadata

You know about metadata from Chapter 3. If you need to change something in the metadata document of one RP, you can simply edit it. Perhaps that’s not the greatest fun you’ll have, but it is feasible.

Doing the same for one STS is out of the question because an STS metadata document must always be signed. The WIF SDK has one example showing how to use the WIF API for generating a metadata document programmatically. It’s not rocket science, just a lot of serialization. Generating the document has the advantage of keeping it automatically updated if you play your cards well and read things from the config. It also has another advantage of granting you better control of complicated situations, such as cases in which on the same Web site you expose both WS-Federation and WS-Trust endpoints.

Any dynamic content generation mechanism will do. My favorite is exposing a WCF service and hiding the .svc extension with some IIS URL rewriting.