ASecurity Token Services Primer
Building a Custom Active STS
Extending SecurityTokenService
Hosting and Configuring the STS
Security Token Handlers
Building a Custom Passive STS
FederatedPassiveTokenService Control
SessionAuthenticationModule
User Authentication
Claims Transformation
Wrap-Up
In my last article about the Geneva Framework I discussed a better way to build claims-based Windows Communication Foundation (WCF) services that rely on tokens issued by an STS. Here, I will build a custom STS with the Geneva Framework.
Before reading on, you should read the Geneva Framework white paper for developerswritten by Keith Brown and Sesha Mani and my last article, " Geneva Framework: A Better Approach for Building Claims-Based WCF Services."
Whether the STS is based on Geneva Server or built with the Geneva Framework, its primary role is to act as a security gateway to authenticate callers and issue security tokens carrying claims that describe the caller. You'll recall from the aforementioned articles that there are several scenarios supported by STS authentication:
Any of these scenarios can be based on passive federation (browser-based) or active federation (Windows client-based). Next, I will elaborate on these scenarios while describing how to build relevant logic into a custom STS built with the Geneva Framework.
Before diving into the implementation of an STS, let me first review some fundamentals. An STS used in active federation is an implementation of the WS-Federation Active Requestor Profile (see WS-Federation TC) and (primarily) the WS-Trust specification (see WS-Trust 1.3).
From a high level, WS-Trust describes a contract with four service operations: Issue, Validate, Renew, and Cancel. Respectively, these operations are called by clients to request a security token, to validate a security token, to renew an expired security token, or to cancel a security token that should no longer be used. Each operation is sent messages in the form of Request for Security Token (RST) and return messages in the form of RST Response (RSTR) according to the WS-Trust specification. For this article I am assuming that the issued token is a Security Assertion Markup Language (SAML) 1.1 or SAML 2.0 token.
Figure 1illustrates the core contents of the RST and RSTR for active token issuance. The RST message includes necessary information to request a security token including the type of token to issue (SAML for this discussion), the claims requested by the relying party (RP) to be included in the issued token, the information about the RP (AppliesTo) including the URL and usually the certificate identifying the RP, and optionally (not shown) key material to be used for the proof-of-possession key (proof key) returned with the RSTR.
If token issuance is successful, the RSTR will include the issued SAML token and the proof key (assuming the STS decides which proof key to use and thus must return it in the RSTR). The SAML token will include relevant claims for the authenticated party, is signed by the STS to protect the token from being tampered with, contains the proof key encrypted for the RP, and is itself encrypted for the RP so that only the intended recipient can process the token.
The client uses the proof key to sign the message to the RP. The RP must be able to decrypt the proof key inside the SAML token or reject the message. If the proof key inside the token matches the signature on the message, this proves that the call to the RP was sent by the party that requested the token.
Passive federation scenarios are based on a WS-Federation Passive Requestor Profile, which involves browser-based communication. The underlying messaging is still based on WS-Trust; however, the RST is broken into query string parameters on the STS URL, and the RSTR is typically posted to the RP as a form parameter. The passive STS and RP intercept these parameters with federation Web handlers. The passive STS may process WS-Trust requests directly or pass them to an underlying WS-Trust implementation. Figure 2illustrates how the RST and RSTR are handled in a passive federation scenario.
One relevant difference between the active and passive federation scenarios is the type of SAML token issued. Active federation usually relies on a SAML token that uses a subject confirmation of type "holder-of-key," which means that there is a proof key used as I have described to prove that the client sending the token to authenticate is the subject that requested the token (also known as ActAs behavior). Passive federation scenarios usually involve a SAML token with a subject confirmation of type "bearer" (it is also known as a bearer token). This type of token does not include a proof key and is sometimes called a keyless token. It relies on the transport to securely procure it from the STS and transmit it to the RP.
Figure 4illustrates the moving parts behind this implementation. Included are a custom SecurityTokenService implementation, the use of a ServiceHost extension (WSTrustServiceHost) to initialize the runtime for federation, configuration of one or more WS-Trust endpoints using a derivative of the WSTrustContract type, and other related configuration settings for the identity model runtime. In the sections that are to follow, I will review each one of these elements of a custom STS implementation based on the Geneva Framework.
The Geneva Framework provides the core functionality to build a custom STS through the SecurityTokenService type from the Microsoft.IdentityModel.SecurityTokenService namespace. This abstract class handles the heavy lifting of processing RST and RSTR messages and generating security tokens. A custom STS type inherits this class and provides (at a minimum) the following functionality:
When GetOutputClaimsIdentity is called, a ClaimsPrincipal is passed by the runtime with the authenticated caller's identity. This identity is typically used to determine the appropriate claims to grant the caller. In Figure 5the code generates a name claim and a hardcoded role claim for the caller, and it returns this in the form of a ClaimsIdentity. This ClaimsIdentity supplies claims to the runtime for the token to be issued.
If you are familiar with WCF, you know that one or more endpoints must be configured in order for clients to send messages to a service. In the case of an STS, the service contract to be used for each endpoint must be based on WS-Trust protocol, which includes four operations: Issue, Validate, Renew, and Cancel. In fact, there are two versions of WS-Trust protocol that could be implemented by an STS:
The STS should expose an endpoint based on the WS-Trust February 2005 for backward compatibility with preexisting clients. The service type that implements both contracts is the WSTrustServiceContract type found in the Microsoft.IdentityModel.Protocols.WSTrust namespace. This is the type that should be referenced in the configuration section for the STS.
As illustrated in the diagram in Figure 4, the configuration and its endpoints are used to initialize the host with the correct WSTrustServiceContract type. This type is also initialized with a reference to the custom SecurityTokenService implementation during host initialization. That's how the runtime directs messages to the custom STS.
In Figure 6, both STS endpoints rely on Windows credentials to authenticate callers (the default behavior of wsHttpBinding). The STS can expose multiple endpoints with alternate binding configurations to support different credential types. This also involves configuring an appropriate security token handler for each credential type. I'll discuss token handler configuration settings shortly.
The Geneva Framework supplies a custom ServiceHost type, WSTrustServiceHost, to be used for hosting STS instances. The following code illustrates how to construct the WSTrustServiceHost type in a self-hosting environment:
WSTrustServiceHost relies on a custom SecurityTokenServiceConfiguration instance to initialize the runtime with WS-Trust endpoints, to enable the metadata exchange behavior for the STS, and to configure a metadata exchange endpoint.
When hosted in IIS, the WSTrustServiceHostFactory type is used to achieve the same results. In the .svc file, the @ServiceHost configuration specifies the factory type and the custom SecurityTokenServiceConfiguration type, as follows:
The SecurityTokenServiceConfiguration type also exposes properties that can be used to set defaults for key size and token type, to disable access to metadata, to control token lifetime and clock skew, to set custom RST and RSTR serializers, to configure token handlers that authenticate callers, and to configure WS-Trust endpoints.
The following example shows how to disable metadata access and how to initialize WS-Trust endpoints (like those shown in Figure 6) programmatically rather than relying on configuration settings:
The base SecurityTokenServiceConfiguration type also reads the configuration section to initialize relevant STS configuration settings. Settings that can be declaratively configured for a custom STS include the maximum clock skew, security token handlers for authentication and issuance, a claims authentication manager, an issuer name registry, and token resolvers. Figure 8shows a few useful settings configured for an STS.
A custom ClaimsAuthenticationManager type is invoked for non-Windows credentials giving the option to supply a custom ClaimsPrincipal type to the runtime prior to issuing claims in GetOutputClaimsIdentity. Custom security token handlers might be configured to supply settings that override the default behavior of a particular handler or to alter the choice of security token handler for a particular credential type.
You probably expect a service behavior configuration to determine how authentication and authorization will take place for each STS endpoint. Recall from my last article that the Geneva Framework does things a little differently. In the case of authentication, the collection specifies the SecurityTokenHandler types available to authenticate incoming requests.
3a8082e126