Implementing Single Sign-On and Sign-Out in ASP.NET Apps
Windows Identity Foundation features for passive single sign-on and single sign-out
February 25, 2010
Browser-based federation (passive federation) relies on HTTP GET and POST, browser redirects, and cookies to accomplish the goal of federated authentication per the WS-Federation specification. Two important features of identity federation are single sign-on and single sign-out. As I've discussed in other articles for this column, Windows Identity Foundation (WIF) supplies a framework for implementing identity federation for both ASP.NET applications and Windows Communication Foundation (WCF) services. For more information, see "A Crash Course in Windows Identity Foundation." Fortunately, when you use WIF with ASP.NET, you'll find that many of the single sign-on and single sign-out features are built-in.
I'll explain how single sign-on and single sign-out work, while reviewing the WIF settings necessary to facilitate it in both your ASP.NET applications—including any customizations necessary for specific scenarios. Although describing how to build a custom Security Token Service (STS) with WIF is beyond the scope of this article, I will call out a few necessary features you should include at the security token service (STS), with code samples to support it.
Passive Federation
In a simple passive federation scenario, a web application (also called the relying party, or RP) grants users from a trusted domain (the identity provider, or IdP) access to features and functionality. The flow of communication is illustrated in Figure 1.
The user browses to the RP (1), the user is redirected to the identity provider STS (IP-STS) for authentication (2), the STS authenticates the user and issues a security token for her (3), the security token is posted to the RP via browser redirect (4), and the RP authorizes access based on the claims carried by the security token (5).
You can set up passive federation for an ASP.NET application very easily by using the Federation Utility supplied by WIF (fedutil.exe, or FedUtil). First, get the Uniform Resource Identifier (URI) for the federation metadata document produced by IP-STS. This document (described in the WS-Federation specification) provides the necessary information to configure WIF for the RP. From Visual Studio you can select Add STS Reference from the project context menu, walk through the wizard supplying the federation metadata URI, and supply a certificate for token encryption. When the wizard has finished, the web.config for the ASP.NET application is updated with the required settings to federate with IP-STS. You can tweak the settings with any customizations from there.
Two HTTP modules are installed to support passive federation: WSFederationAuthenticationModule (FAM) and SessionAuthenticationModule (SAM). Figure 2 shows a typical identity model configuration for these modules.
Figure 2: Identity model configuration for passive federation
Considering the scenario illustrated in Figure 1, the significance of each setting is as follows:
The certificate for IP-STS is listed in the section, so that the RP will authorize access when the token is presented.
The application requires the issued security token to be encrypted using the RP's public key. The RP private key is listed in to enforce this.
Issued tokens should include the RP realm in the "applies to" element—indicating for what target realm or application the token was issued. This realm must be listed in the section for the token to be considered valid.
From the section, passive federation is enabled, the URI for IP-STS is given for the issuer URI, and the RP base URI is specified as the realm (which matches the setting).
So that the FAM will be engaged to protect RP resources, the and section must be configured as follows:
Now, when the user browses to any protected page in the RP site, the user will be redirected to IP-STS for authentication. The FAM processes the response from IP-STS and establishes a secure session via the SAM—the latter of which writes the session cookie to be presented by the browser on subsequent calls. The only additional code required by the RP is to authorize access based on claims presented in the security token, something I have discussed in previous articles. In other words, passive federation is very easy to configure with WIF.
There is indeed another easy way to enable passive federation with WIF, and that is to place the FederatedPassiveSignIn control on a login page and enable Forms authentication to redirect to that login page. In this case, you would not configure the FAM and instead let the control handle redirection to the IP-STS and processing the response with the issued security token. The control in fact calls to the FAM and the SAM direction to achieve this, but you'd still configure the SAM in the HTTP modules list to rehydrate and validate the session cookie on each request. This approach adds an additional redirect to the passive federation process and usually implies that the user will explicitly initiate federated sign-in from the control button (though this is not required). Generally, I prefer to use the FAM and, if user interaction is required to sign-in, write a few lines of code.
Single Sign-On
Single sign-on comes into play when the user accesses multiple applications in the context of a federated session. The goal is for the user to authenticate once to her identity provider and have the user's federated identity be used to grant her access to each application in the federated solution. One way to achieve this is to have all RP applications trust the same IP-STS, as shown in Figure 3.
The user browses to RP1 (1) and is redirected to the IP-STS for authentication (2). The IP-STS presents a challenge or a login page if applicable to collect credentials (3), and the user posts her credentials to authenticate (4). A cookie may be written to establish a secure session with the IP-STS (5), and RP1 receives the issued security token from the IP-STS (6). The RP will write a session cookie for the secure session to avoid future redirects to the IP-STS for the lifetime of the session or validity of the issued token (7).
When the user browses to RP2 (8), she is redirected to the IP-STS. Since there is already a session cookie for the IP-STS, authentication is not required and IP-STS issues a token for RP2 (9). RP2 also writes a session cookie to avoid future redirects to IP-STS (10). As long as the user does not sign out and the session cookie and issued token are still valid, requests for resources at RP1 or RP2 will not be redirected to IP-STS. Single sign-on is achieved since the user isn't asked for her credentials more than once for the lifetime of the browser session. In fact, single sign-on can survive browser sessions if the session cookie is persisted and remains valid.
There are more complicated scenarios where multiple identity providers are part of the federated single sign-on story, but that is beyond the scope of this article. The point is to understand what is required of any RP or STS to facilitate the scenario. Supporting single sign-on doesn't require any additional work at the RP. The process I described for enabling passive federation in the previous section also enables single sign-on. Thanks to the FAM and SAM, the RP redirects all unauthorized calls, creates a session cookie for the authenticated user, and processes the session cookie when it is presented to validate the secure session. Ostensibly, a custom STS built with WIF using one of the Visual Studio project templates will also handle single sign-on without any extra effort.
Single Sign-Out
Single sign-on means the user logs in once and has access to multiple applications. Single sign-out (or federated sign-out) means that the user can sign out once and will be logged out of all applications he accessed during the federated session. Single sign-out is explicit—that is, the user has the intent to sign out; expired session cookies or tokens do not result in single sign-out.
Based on the simple federation scenario from Figure 1, single sign-out would involve only one RP and one IP-STS. The user starts by initiating single sign-out at the RP (1). The RP will remove the federated session cookie (2) and redirect to the IP-STS with a sign-out request (3). The IP-STS removes its session cookie (4) and redirects back to the RP at the designated reply URI (5)—perhaps a login page that is presented to the user.
You can initiate federated single sign-out two ways. You can put the FederatedPassiveSignInStatus control on any page and the user can click the control to sign out. This triggers a direct call to the FAM to remove the session cookie and perform federated sign-out—sending the sign-out message to the IP-STS. Without the control, you can provide a sign-out button and call the FAM directly, supplying a URI for IP-STS to reply to:
string absoluteUri =
HttpContext.Current.Request.Url.AbsoluteUri;
string loginPageUri =
absoluteUri.Substring(0, absoluteUri.LastIndexOf("/") + 1)
+ "Login.aspx";
WSFederationAuthenticationModule.FederatedSignOut
(null, new Uri(loginPageUri));
As for IP-STS, the default implementation will handle the sign-out request and reply to the RP upon completion.
Federated sign-out is an optional part of the WS-Federation specification, but it is useful to know how to implement these steps correctly at the RP and STS. According to the WS-Federation specification, single sign-out is separated into two parts: sign-out and clean-up. Sign-out is the request initiated by the user to the primary STS (IP-STS in this scenario), and clean-up is driven by the primary STS to notify all RPs and any other STSs involved in the federated session to clean up their respective sessions. Looking at Figure 4, the reply to the sign-out request (5) should be a clean-up request with these query string parameters (wreply is optional):
wa=wsignoutcleanup1.0
wreply=\[Uri\]
The RP would respond to this by removing its session cookie and redirecting back to the IP-STS—at which point IP-STS could send other clean-up requests as appropriate. The default behavior of WIF at the RP is to clean up prior to sending the sign-out request to IP-STS—which is a good thing in the event that IP-STS does not comply and reply back to the RP with the clean-up request. When you create a passive STS with the WIF template, the default implementation replies without the clean-up request as illustrated in Figure 4 (5). It isn't really necessary to change this behavior, but an RP should still support the clean-up message to support federated sign-out initiated by another RP; thus a custom STS built with WIF should facilitate this result.
Figure 5 illustrates this expanded scenario—assuming the user has signed in to RP1 and RP2 who share IP-STS as their identity provider.
Steps (1), (2), and (3) are the same as in Figure 4: The user initiates sign-out, and RP1 clears its cookie and redirects to the IP-STS with a sign-out request. The IP-STS then sends a clean-up request to both RP1 and RP2. IP-STS can issue clean-up requests one at time, supplying a URI for each RP to redirect back to—and after a series of redirects each RP will have successfully cleaned up. Using redirects is a fragile mechanism that can be left incomplete if even one RP or STS in the chain does not properly clean up and reply. Instead, each clean-up request can return a graphic indicating successful sign-out. Figure 5 illustrates how IP-STS implements this result. IP-STS redirects to a clean-up page with two image elements (4).
Each image element has a source image pointing to the clean-up URI from its respective RP (5)(7). Each RP returns its clean-up image and cleans up its session cookies (6)(8).
The STS page that triggers sending clean-up messages to RP1 and RP2 uses the following URI as the source of two image elements:
https://localhost/RP1/?wa=wsignoutcleanup1.0
https://localhost/RP2/?wa=wsignoutcleanup1.0
Each RP must be able to respond to the clean-up request, returning a clean-up image and removing their respective cookies. In fact this is the default behavior supplied by the FAM. The FAM knows how to process a clean-up request and return the default clean-up image—so your work is done! Likewise, if you are using the FederatedPassiveSignInStatus control, as long as IP-STS requests clean up at the page hosting the control, it calls the FAM to process the clean-up message accordingly.
Built-in Federation
WIF certainly makes setting up passive federation easy with Visual Studio templates, FedUtil, and a library with built-in functionality for most of what you need. In fact, very little customization is necessary to achieve many federation scenarios at both the RP and STS; the challenge lies more in understanding what you are trying to accomplish well enough to make those changes. Hopefully this summary of single sign-on and single sign-out gives you some practical background on those specific scenarios along with the customizations to make it all happen. Check out the code samples and have fun!
Michele Leroux Bustamante ([email protected]) is chief architect for IDesign, chief security architect for BiTKOO, a Microsoft Regional Director for San Diego, and a Microsoft MVP for Connected Systems. Her latest book is Learning WCF (O'Reilly), and she blogs at www.dasblonde.net.
Resources for this article can be found at www.dasblonde.net/downloads/wif/singlesignon.zip.
About the Author
You May Also Like