Share via


Claims-Based Single Sign-On for the Web and Microsoft Azure

The Premise | Goals and Requirements | Overview of the Solution | Inside the Implementation - a-Expense before Claims, a-Expense with Claims, a-Order before Claims, a-Order with Claims | Signing out of an Application | Setup and Physical Deployment - Using a Mock Issuer, Isolating Active Directory, Handling Single Sign-out in the Mock Issuer, Converting to a Production Issuer, Enabling Internet Access | Variation—Moving to Azure | Questions | More Information

Download code samples

Download PDF

Download Paperback

This chapter walks you through an example of single sign-on for intranet and extranet web users who all belong to a single security realm. You'll see examples of two existing applications that become claims-aware. One of the applications uses forms authentication, and one uses Windows authentication. Once the applications use claims-based authentication, you'll see how it's possible to interact with the applications either from the company's internal network or from the public Internet.

For single sign-on, the issuer also creates a session with the user that works with different applications.

This basic scenario doesn't show how to establish trust relationships across enterprises. (That is discussed in Chapter 4, "Federated Identity for Web Applications.") It focuses on how to implement single sign-on and single sign-off within a security domain as a preparation for sharing resources with other security domains, and how to migrate applications to Azure™. In short, this scenario contains the commonly used elements that will appear in all claims-aware applications.

The Premise

Adatum is a medium-sized company that uses Microsoft Active Directory® directory service to authenticate the employees in its corporate network. Adatum's sales force uses a-Order, Adatum’s order processing system, to enter, process, and manage customer orders. Adatum employees also use aExpense, an expense tracking and reimbursement system for business-related expenses.

Both applications are built with ASP.NET 4.0 and are deployed in Adatum's data center. Figure 1 shows a whiteboard diagram of the structure of a-Order and a-Expense.

Ff359102.65d44da6-449c-4c64-a8fc-18cd3e30961e-thumb(en-us,PandP.10).png

Figure 1

Adatum infrastructure before claims

The two applications handle authentication differently. The a-Order application uses Windows authentication. It recognizes the credentials used when employees logged on to the corporate network. The application doesn't need to prompt them for user names and passwords. For authorization, a-Order uses roles that are derived from groups stored in Active Directory. In this way, a-Order is integrated into the Adatum infrastructure.

The user experience for a-Expense is a bit more complicated. The a-Expense application uses its own authentication, authorization, and user profile information. This data is stored in custom tables in an application database. Users enter a user name and password in a web form whenever they start the application. The a-Expense application's authentication approach reflects its history. The application began as a Human Resources project that was developed outside of Adatum's IT department. Over time, other departments adopted it. Now it's a part of Adatum's corporate IT solution.

Ff359102.note(en-us,PandP.10).gifPoe Says:
Poe Keeping the user database for forms-based authentication up to date is painful since this maintenance isn't integrated into Adatum's process for managing employee accounts.

The a-Expense access control rules use application-specific roles. Access control is intermixed with the application's business logic.

Some of the user profile information that a-Expense uses also exists in Active Directory, but because a-Expense isn't integrated with the corporate enterprise directory, it can't access it. For example, Active Directory contains each employee's cost center, which is also one of the pieces of information maintained in the a-Expense user profile database. Changing a user's cost center in a-Expense is messy and error prone. All employees have to manually update their profiles when their cost centers change.

Goals and Requirements

Adatum has a number of goals in moving to a claims-based identity solution. One goal is to add the single sign-on capability to its network. This allows employees to log on once and then be able to access all authorized systems, including a-Expense. With single sign-on, users will not have to enter a user name and password when they use a-Expense.

Your choice of an identity solution should be based on clear goals and requirements.

A second goal is to enable Adatum employees to access corporate applications from the Internet. Members of the sales force often travel to customer sites and need to be able to use a-Expense and aOrder without the overhead of establishing a virtual private network (VPN) session.

A third goal is to plan for the future. Adatum wants a flexible solution that it can adapt as the company grows and changes. Right now, a priority is to implement an architecture that allows them to host some applications in a cloud environment such as Azure. Moving operations out of their data center will reduce their capital expenditures and make it simpler to manage the applications. Adatum is also considering giving their customers access to some applications, such as a-Order. Adatum knows that claims-based identity and access control are the foundations needed to enable these plans.

Ff359102.note(en-us,PandP.10).gifPoe Says:
Poe Dealing with change is one of the challenges of IT operations.

While meeting these goals, Adatum wants to make sure its solution reuses its existing investment in its enterprise directory. The company wants to make sure user identities remain under central administrative control and don't span multiple stores. Nonetheless, Adatum wants its business units to have the flexibility to control access to the data they manage. For example, not everyone at Adatum is authorized to use the a-Expense application. Currently, access to the program is controlled by application-specific roles stored in a departmentally administered database. Adatum's identity solution must preserve this flexibility.

Finally, Adatum also wants its identity solution to work with multiple platforms and vendors. And, like all companies, Adatum wants to ensure that any Internet access to corporate applications is secure.

With these considerations in mind, Adatum's technical staff has made the decision to modify both the aExpense and the a-Order applications to support claims-based single sign-on.

Overview of the Solution

The first step was to analyze which pieces of identity information were common throughout the company and which were specific to particular applications. The idea was to make maximum use of the existing investment in directory information. Upon review, Adatum discovered that their Active Directory store already contained the necessary information. In particular, the enterprise directory maintained user names and passwords, given names and surnames, e-mail addresses, employee cost centers, office locations, and telephone numbers.

Claims can take advantage of existing directory information.

Since this information was already in Active Directory, the claims-based identity solution would not require changing the Active Directory schema to suit any specific application.

Ff359102.note(en-us,PandP.10).gifBharath Says:
Bharath Nobody likes changing their Active Directory schema. Adding app-specific rules or claims from a non–Active Directory data store to a claims issuer is easier.

They determined that the main change would be to introduce an issuer of claims for the organization. Adatum's applications will trust this issuer to authenticate users.

Adatum envisions that, over time, all of its applications will eventually trust the issuer. Since information about employees is a corporate asset, the eventual goal is for no application to maintain a custom employee database. Adatum recognizes that some applications have specialized user profile information that will not (and should not) be moved to the enterprise directory. Adatum wants to avoid adding application-specific attributes to its Active Directory store, and it wants to keep management as decentralized as possible.

For the initial rollout, the company decided to focus on a-Expense and a-Order. The a-Order application only needs configuration changes that allow it to use Active Directory groups and users as claims. Although there is no immediate difference in the application's structure or functionality, this change will set the stage for eventually allowing external partners to access a-Order.

The a-Expense application will continue to use its own application-specific roles database, but the rest of the user attributes will come from claims that the issuer provides. This solution will provide single sign-on for aExpense users, streamline the management of user identities, and allow the application to be accessible remotely from the Internet.

Ff359102.note(en-us,PandP.10).gifJana Says:
Jana Staging is helpful. You can change authentication first without affecting authorization.

Note

You might ask why Adatum chose claims-based identity rather than Windows authentication for a-Expense. Like claims, Windows authentication provides single sign-on, and it is a simpler solution than issuing claims and configuring the application to process claims.
There's no disagreement here: Windows authentication is extremely well suited for intranet single sign-on and should be used when that is the only requirement.
Adatum's goals are broader than just single sign-on, however. Adatum wants its employees to have remote access to a-Expense and a-Order without requiring a VPN connection. Also, Adatum wants to move aExpense to Azure and eventually allow customers to view their pending orders in the aOrder application over the Internet. The claims-based approach is best suited to these scenarios.

Figure 2 shows the proposal, as it was presented on Adatum's whiteboards by the technical staff. The diagram shows how internal users will be authenticated.

Ff359102.e5d23ace-03bb-4f37-a980-3d3b672259ab-thumb(en-us,PandP.10).png

Figure 2

Moving to claims-based identity

This claims-based architecture allows Adatum employees to work from home just by publishing the application and the issuer through the firewall and proxies. Figure 3 shows the way Adatum employees can use the corporate intranet from home.

Ff359102.177fcf5b-cdd9-4cb3-a093-ddde5e4ea609-thumb(en-us,PandP.10).png

Figure 3

Claims-based identity over the Internet

Ff359102.note(en-us,PandP.10).gifPoe Says:
Poe The Active Directory Federation Services (ADFS) proxy role provides intermediary services between an Internet client and an ADFS server that is behind a firewall.

Once the issuer establishes the remote user's identity by prompting for a user name and password, the same claims are sent to the application, just as if the employee is inside the corporate firewall.

This solution makes Adatum's authentication strategy much more flexible. For example, Adatum could ask for additional authentication requirements, such as smart cards, PINs, or even biometric data, when someone connects from the Internet. Because authentication is now the responsibility of the issuer, and the applications always receive the same set of claims, the applications don't need to be rewritten. The ability to change the way you authenticate users without having to change your applications is a real benefit of using claims.

You can also look at this proposed architecture from the point of view of the HTTP message stream. For more information, see the message sequence diagrams in Chapter 2, "Claims-Based Architectures."

Inside the Implementation

Now is a good time to walk through the process of converting a-Expense into a claims-aware application in more detail. As you go through this section, you may want to download the Microsoft Visual Studio® solution 1SingleSignOn from https://claimsid.codeplex.com. This solution contains implementations of a-Expense and a-Order, with and without claims. If you are not interested in the mechanics, you should skip to the next section.

Ff359102.note(en-us,PandP.10).gifJana Says:
Jana By default, the downloadable implementations run standalone on your workstation, but you can also configure them for a multi-tiered deployment.

a-Expense before Claims

Before claims, the a-Expense application used forms authentication to establish user identity. It's worth taking a moment to review the process of forms authentication so that the differences with the claims-aware version are easier to see. In simple terms, forms authentication consists of a credentials database and an HTTP redirect to a logon page.

Ff359102.note(en-us,PandP.10).gifJana Says:
Jana Many web applications store user profile information in cookies rather than in the session state because cookies scale better on the server side. Scale wasn't a concern here because a-Expense is a departmental application.

Figure 4 shows the a-Expense application with forms authentication.

Ff359102.2203fc1a-ab92-4c7c-b3f5-77ed0eb3125d-thumb(en-us,PandP.10).png

Figure 4

a-Expense with forms authentication

The logon page serves two purposes in a-Expense. It authenticates the user by asking for credentials that are then checked against the password database, and it also copies application-specific user profile information into the ASP.NET's session state object for later use. Examples of profile information are the user's full name, cost center, and assigned roles. The a-Expense application keeps its user profile information in the same database as user passwords, which is typical for applications that use forms authentication.

Note

a-Expense intentionally uses custom code for authentication, authorization, and profiles instead of using Membership, Roles, and Profile providers. This is typical of legacy applications that might have been written before ASP.NET 2.0.

In ASP.NET, adding forms authentication to a web application requires three steps: an annotation in the application's Web.config file to enable forms authentication, a logon page that asks for credentials, and a handler method that validates those credentials against application data. Here is how those pieces work.

The Web.config file for a-Expense enables forms authentication with the following XML declarations:

<authentication mode="Forms">
     <forms loginUrl="~/login.aspx" 
            requireSSL="true" ... />
</authentication>

<authorization>
    <deny users="?" />
</authorization>

The authentication element tells the ASP.NET runtime (or Microsoft Internet Information Services (IIS) 7.0 when running both in ASP.NET integrated mode and classic mode) to automatically redirect any unauthenticated page request to the specified login URL. An authorization element that denies access to unauthenticated users (denoted by the special symbol "?") is also required to make this redirection work.

Next, you'll find that a-Expense has a Login.aspx page that uses the built-in ASP.NET Login control, as shown here.

<asp:Login ID="Login1"  
           OnAuthenticate="Login1OnAuthenticate" ... >
</asp:Login>

Finally, if you look at the application, you'll notice that the handler of the Login.aspx page's OnAuthenticate event looks like the following.

public partial class Login : Page
{
  protected void Login1OnAuthenticate(object sender,
                                      AuthenticateEventArgs e)
  {
     var repository = new UserRepository();
     if (!repository.ValidateUser(this.Login1.UserName, 
                                           this.Login1.Password))
     {
         e.Authenticated = false;
         return;
     }
     var user = repository.GetUser(this.Login1.UserName);
     if (user != null)
     {
         this.Session["LoggedUser"] = user;
         e.Authenticated = true;
     }
  }
}

This logic is typical for logon pages. You can see in the code that the user name and password are checked first. Once credentials are validated, the user profile information is retrieved and stored in the session state under the LoggedUser key. Notice that the details of interacting with the database have been put inside of the application's UserRepository class.

Setting the Authenticated property of the AuthenticateEventArgs object to true signals successful authentication. ASP.NET then redirects the request back to the original page.

At this point, normal page processing resumes with the execution of the page's OnLoad method. In the a-Expense application, this method retrieves the user's profile information that was saved in the session state object and initializes the page's controls. For example, the logic might look like the following.

protected void OnLoad(EventArgs e)
{
    var user = (User)Session["LoggedUser"];

    var repository = new ExpenseRepository();
    var expenses = repository.GetExpenses(user.Id);
    this.MyExpensesGridView.DataSource = expenses;
    this.DataBind();
    
    base.OnLoad(e);
}

The session object contains the information needed to make access control decisions. You can look in the code and see how a-Expense uses an application-defined property called AuthorizedRoles to make these decisions.

a-Expense with Claims

The developers only had to make a few changes to a-Expense to replace forms authentication with claims. The process of validating credentials was delegated to a claims issuer simply by removing the logon page and configuring the ASP.NET pipeline to include the Windows Identity Foundation (WIF) WSFederationAuthenticationModule. This module detects unauthenticated users and redirects them to the issuer to get tokens with claims. Without a logon page, the application still needs to write profile and authorization data into the session state object, and it does this in the Session_Start method. Those two changes did the job.

You only need a few changes to make the application claims-aware.

Figure 5 shows how authentication works now that a-Expense is claims-aware.

a-Expense with claims processing

Ff359102.7aeca593-a2e4-409b-8e48-9c4d7bc4abe8-thumb(en-us,PandP.10).png

Figure 5

a-Expense with claims processing

The Web.config file of the claims-aware version of a-Expense contains a reference to WIF-provided modules. This Web.config file is automatically modified when you run the FedUtil wizard either through the command line (FedUtil.exe) or through the Add STS Reference command by right-clicking the web project in Visual Studio.

Ff359102.note(en-us,PandP.10).gifMarkus Says:
Markus Making a-Expense use claims was easy with WIF's FedUtil.exe utility. See Appendix A.

If you look at the modified Web.config file, you'll see that there are changes to the authorization and authentication sections as well as new configuration sections. The configuration sections include the information needed to connect to the issuer. They include, for example, the Uniform Resource Indicator (URI) of the issuer and information about signing certificates.

Ff359102.note(en-us,PandP.10).gifJana Says:
Jana We're just giving the highlights here. You'll also want to check out the WIF and ADFS product documentation.

The first thing you'll notice in the Web.config file is that the authentication mode is set to None, while the requirement for authenticated users has been left in place.

<authentication mode="None" />

<authorization>
    <deny users="?" />
</authorization>
Ff359102.note(en-us,PandP.10).gifMarkus Says:
Markus This may seem a little weird. What's going on is that authentication has been moved to a different part of the HTTP pipeline.

Note

The forms authentication module that a-Expense previously used has been deactivated by setting the authentication mode attribute to None. Instead, the WSFederationAuthenticationModule (FAM) and SessionAuthenticationModule (SAM) are now in charge of the authentication process.

The application's Login.aspx page is no longer needed and can be removed from the application.

Next, you will notice that the Web.config file contains two new modules, as shown here.

<httpModules>
    <add name="WSFederationAuthenticationModule" 
         type="Microsoft.IdentityModel.Web.
                        WSFederationAuthenticationModule, ..." />

    <add name="SessionAuthenticationModule" 
         type="Microsoft.IdentityModel.Web.
                             SessionAuthenticationModule, ..." />
</httpModules>

When the modules are loaded, they're inserted into the ASP.NET processing pipeline in order to redirect the unauthenticated requests to the issuer, handle the reply posted by the issuer, and transform the user token sent by the issuer into a ClaimsPrincipal object. The modules also set the value of the HttpContext.User property to the ClaimsPrincipal object so that the application has access to it.

The WSFederationAuthenticationModule redirects the user to the issuer's logon page. It also parses and validates the security token that is posted back. This module writes an encrypted cookie to avoid repeating the logon process. The SessionAuthenticationModule detects the logon cookie, decrypts it, and repopulates the ClaimsPrincipal object.

Ff359102.note(en-us,PandP.10).gifJana Says:
Jana The ClaimsPrincipal object implements the IPrincipal interface that you already know. This makes it easy to convert existing applications.

The Web.config file contains a new section for the Microsoft.IdentityModel that initializes the WIF environment.

<configSections>
<section name="microsoft.identityModel" 
         type="Microsoft.IdentityModel.Configuration.
                                MicrosoftIdentityModelSection, 
                                Microsoft.IdentityModel, ..." />
</configSections>

The identity model section contains several kinds of information needed by WIF, including the address of the issuer and the certificates (the serviceCertificate and trustedIssuers elements) that are needed to communicate with the issuer.

<microsoft.identityModel>
  <service>
    <audienceUris>
      <add value=
              "https://{adatum hostname}/a-Expense.ClaimsAware/" 
      />
    </audienceUris>
...

Note

The value of "adatum hostname" changes depending on where you deploy the sample code. In the development environment, it is "localhost."

Security tokens contain an audience URI. This indicates that the issuer has issued a token for a specific "audience" (application). Applications, in turn, will check that the incoming token was actually issued for them. The audienceUris element lists the possible URIs. Restricting the audience URIs prevents malicious clients from reusing a token from a different application with an application that they are not authorized to access..

<federatedAuthentication>
   <wsFederation passiveRedirectEnabled="true" 
       issuer="https://{adatum hostname}/{issuer endpoint} "    
       realm="https://{adatum hostname}/a-Expense.ClaimsAware/" 
       requireHttps="true" />
   <cookieHandler requireSsl="true" path="/a-Expense.ClaimsAware/" />
</federatedAuthentication>

The federatedAuthentication section identifies the issuer and the protocol required for communicating with it.

Ff359102.note(en-us,PandP.10).gifBharath Says:
Bharath Using HTTPS mitigates man-in-the-middle and replay attacks. This is optional during development, but be sure to use HTTPS in production environments.
<serviceCertificate>
  <certificateReference x509FindType="FindByThumbprint" 
    findValue="5a074d678466f59dbd063d1a98b1791474723365" />
</serviceCertificate>

The service certificate section gives the location of the certificate used to decrypt the token, in case it was encrypted. Encrypting the token is optional, and it's a decision of the issuer to do it or not. You don't need to encrypt the token if you're using HTTPS, but encryption is generally recommended as a security best practice.

<issuerNameRegistry   
   type="Microsoft.IdentityModel.Tokens.ConfigurationBasedIssuerNameRegistry,  
         Microsoft.IdentityModel, ... >
   <trustedIssuers>
      <add thumbprint=" f260042d59e14817984c6183fbc6bfc71baf5462" 
           name="adatum" />
   </trustedIssuers>
 </issuerNameRegistry>

A thumbprint is the result of hashing an X.509 certificate signature. SHA-1 is a common algorithm for doing that. Thumbprints uniquely identify a certificate and the issuer. The issuerNameRegistry element contains the list of thumbprints of the issuers it trusts. Issuers are identified by the thumbprint of their signing X.509 certificate. If the thumbprint does not match the certificate embedded in the incoming token signature, WIF will throw an exception. If the thumbprint matches, the name attribute will be mapped to the Claim.Issuer property. 

In the code example, the name attribute adatum is required for the scenario because the a-Expense application stores the federated user name in the roles database. A federated user name has the format: adatum\username.

The following procedure shows you how to find the thumbprint of a specific certificate.

To find a thumbprint

  1. On the taskbar, click Start, and then type mmc in the search box.

  2. Click mmc. A window appears that contains the Microsoft Management Console (MMC) application.

  3. On the File menu, click Add/Remove Snap-in.

  4. In the Add or Remove Snap-ins dialog box, click Certificates, and then click Add.

  5. In the Certificates snap-in dialog box, select Computer account, and then click Next.

  6. In the Select Computer dialog box, select Local computer, click Finish, and then click OK.

  7. In the left pane, a tree view of all the certificates on your computer appears. If necessary, expand the tree. Expand the Personal folder. Expand the Certificates folder.

  8. Click the certificate whose thumbprint you want.

  9. In the Certificate Information dialog box, click the Details tab, and then scroll down until you see the thumbprint.

    Note

    In Windows 7, you'll need to double-click to open the dialog, which has the title Certificate, not Certificate Information.

The changes in the Web.config file are enough to delegate authentication to the issuer.

Ff359102.note(en-us,PandP.10).gifPoe Says:
Poe This may seem like a lot of configuration, but the FedUtil wizard handles it for you.

There's still one detail to take care of. Remember from the previous section that the logon handler (which has now been removed from the application) was also responsible for storing the user profile data in the session state object. This bit of logic is relocated to the Session_Start method found in the Global.asax file. The Session_Start method is automatically invoked by ASP.NET at the beginning of a new session, after authentication occurs. The user's identity is now stored as claims that are accessed from the thread's CurrentPrincipal property. Here is what the Session_Start method looks like.

protected void Session_Start(object sender, EventArgs e)
{
  if (this.Context.User.Identity.IsAuthenticated)
  {     
    string issuer = 
         ClaimHelper.GetCurrentUserClaim(
            System.IdentityModel.Claims.ClaimTypes.Name).
                                                  OriginalIssuer;
    string givenName = 
         ClaimHelper.GetCurrentUserClaim(                             
               WSIdentityConstants.ClaimTypes.GivenName).Value;

    string surname = 
         ClaimHelper.GetCurrentUserClaim(
                 WSIdentityConstants.ClaimTypes.Surname).Value;

    string costCenter = 
         ClaimHelper.GetCurrentUserClaim(
                           Adatum.ClaimTypes.CostCenter).Value;

    var repository = new UserRepository();
    string federatedUsername = 
                  GetFederatedUserName(issuer, this.User.Identity.Name);
    var user = repository.GetUser(federatedUsername);
    user.CostCenter = costCenter;
    user.FullName = givenName + " " + surname;                

    this.Context.Session["LoggedUser"] = user;
  }
}

Note that the application does not go to the application data store to authenticate the user because authentication has already been performed by the issuer. The WIF modules automatically read the security token sent by the issuer and set the user information in the thread's current principal object. The user's name and some other attributes are now claims that are available in the current security context.

Ff359102.note(en-us,PandP.10).gifBharath Says:
Bharath Putting globally significant data such as names and cost centers into claims while keeping app-specific attributes in a local store is a typical practice.

The user profile database is still used by a-Expense to store the application-specific roles that apply to the current user. In fact, a-Expense's access control is unchanged whether or not claims are used.

The preceding code example invokes methods of a helper class named ClaimHelper. One of its methods, the GetCurrentUserClaim method, queries for claims that apply in the current context. You need to perform several steps to execute this query:

  1. Retrieve context information about the current user by getting the static CurrentPrincipal property of the System.Threading.Thread class. This object has the run-time type IPrincipal.

  2. Use a run-time type conversion to convert the current principal object from IPrincipal to the type IClaimsPrincipal. Because a-Expense is now a claims-aware application, the run-time conversion is guaranteed to succeed.

  3. Use the Identities property of the IClaimsPrincipal interface to retrieve a collection of identities that apply to the claims principal object from the previous step. The object that is returned is an instance of the ClaimsIdentityCollection class. Note that a claims principal may have more than one identity, although this feature is not used in the a-Expense application.

  4. Retrieve the first identity in the collection. To do this, use the collection's indexer property with 0 as the index. The object that is returned from this lookup is the current user's claims-based identity. The object has type IClaimsIdentity.

  5. Retrieve a claims collection object from the claims identity object with the Claims property of the IClaimsIdentity interface. The object that is returned is an instance of the ClaimsCollection class. It represents the set of claims that apply to the claims identity object from the previous step.

  6. At this point, if you iterate through the claims collection, you can select a claim whose claim type matches the one you are looking for. The following expression is an example of how to do this.

    claims.Single(c => c.ClaimType == claimType)
    

    Note that the Single method assumes that there is one claim that matches the requested claim type. It will throw an exception if there is more than one claim that matches the desired claim type or if no match is found. The Single method returns an instance of the Claim class.

  7. Finally, you extract the claim's value with the Claim class's Value property. Claims values are strings.

Ff359102.note(en-us,PandP.10).gifMarkus Says:
Markus Look at the implementation of the ClaimHelper class in the sample code for an example of how to retrieve claims about the current user.

a-Order before Claims

Unlike a-Expense, the a-Order application uses Windows authentication. This has a number of benefits, including simplicity.

Enabling Windows authentication is as easy as setting an attribute value in XML, as shown here.

<authentication mode="Windows" />

The a-Order application's approach to access control is considerably simpler than what you saw in aExpense. Instead of combining authentication logic and business rules, a-Order simply annotates pages with roles in the Web.config file.

<authorization>
   <allow roles="Employee, Order Approver" />
   <deny users="*" />
</authorization>

The user interface of the a-Order application varies, depending on the user's current role.

base.OnInit(e);

this.OrdersGrid.Visible = !this.User.IsInRole(Adatum.Roles.OrderApprover);
this.OrdersGridForApprovers.Visible = this.User.IsInRole(Adatum.Roles.OrderApprover);

a-Order with Claims

Adding claims to a-Order is really just a configuration step. The application code needs no change.

Converting Windows authentication to claims only requires a configuration change.

If you download the project from https://claimsid.codeplex.com, you can compare the Web.config files before and after conversion to claims. It was just a matter of right-clicking the project in Visual Studio and then clicking Add STS Reference. The process is very similar to what you saw in the previous sections for the a-Expense application.

The claims types required are still the users and roles that were previously provided by Windows authentication.

Ff359102.note(en-us,PandP.10).gifBharath Says:
Bharath Don't forget that more than one value of a given claim type may be present. For example, a single identity can have several role claims.

Signing out of an Application

The FederatedPassiveSignInStatus control is provided by WIF. The following snippet from the Site.Master file shows how the single sign-on scenario uses it to sign out of an application.

<idfx:FederatedPassiveSignInStatus 
    ID="FederatedPassiveSignInStatus" 
     
    OnSignedOut="OnFederatedPassiveSignInStatusSignedOut" 
    SignOutText="Logout" 
    FederatedPassiveSignOut="true"   
    SignOutAction="FederatedPassiveSignOut" />

The idfx prefix identifies the control as belonging to the Microsoft.IdentityModel.Web.Controls namespace. The control causes a browser redirect to the ADFS issuer, which logs out the user and destroys any cookies related to the session.

In this single sign-on scenario, signing out from one application signs the user out from all the applications they are currently signed into in the single sign-on domain.

Note

For details about how the simulated issuer in this sample supports single sign-out, see the section "Handling Single Sign-out in the Mock Issuer" later in this chapter.

The a-Expense application uses an ASP.NET session object to maintain some user state, and it's important that this session data is cleared when a user signs out from the single sign-out domain. The a-Expense application manages this by redirecting to a special CleanUp.aspx page when the application handles the WSFederationAuthenticationModule_SignedOut event in the global.asax.cs file. The CleanUp.aspx page checks that the user has signed out and then abandons the session. The following code example shows the Page_Load event handler for this page.

protected void Page_Load(object sender, EventArgs e)
{
    if (this.User.Identity.IsAuthenticated)
    {
        this.Response.Redirect("~/Default.aspx", false);
    }
    else
    {
        this.Session.Abandon();
        var signOutImage = new byte[]
                            {
                                71, 73, … 
                                …
                            };

        this.Response.Cache.SetCacheability(HttpCacheability.NoCache);
        this.Response.ClearContent();
        this.Response.ContentType = "image/gif";
        this.Response.BinaryWrite(signOutImage);    
    }
}

The byte array represents a GIF image of the green check mark that the SignedOut.aspx page in the simulated issuer displays after the single sign-out is complete.

Ff359102.note(en-us,PandP.10).gifMarkus Says:
Markus The Cleanup.aspx page must be listed as unauthenticated in the Web.config file.

An alternative approach would be to modify the claims issuer to send the URL of the clean-up page in the wreply parameter when it sends a wsignoutcleanup1.0 message to the relying party. However this would mean that the issuer, not the relying party, is responsible for initiating the session clean-up process in the relying party.

Setup and Physical Deployment

The process for deploying a claims-aware web application follows many of the same steps you already know for non-claims-aware applications. The differences have to do with the special considerations of the issuer. Some of these considerations include providing a suitable test environment during development, migrating to a production issuer, and making sure the issuer and the web application are properly configured for Internet access.

Using a Mock Issuer

The downloadable versions of a-Expense and a-Order are set up by default to run on a standalone development workstation. This is similar to the way you might develop your own applications. It's generally easier to start with a single development machine.

Mock issuers simplify the development process.

To make this work, the developers of a-Expense and a-Order wrote a small stub implementation of an issuer. You can find this code in the downloadable Visual Studio solution. Look for the project with the URL https://localhost/Adatum.SimulatedIssuer.1.

When you first run the a-Expense and a-Order applications, you'll find that they communicate with the stand-in issuer. The issuer issues predetermined claims.

Ff359102.note(en-us,PandP.10).gifPoe Says:
Poe Using a simple, developer-created claims issuer is a good practice during development and unit testing. Your network administrator can help you change the application configuration to use production infrastructure components when it's time for acceptance testing and deployment.

It's not very difficult to write such a component, and you can reuse the sample that we provide.

Isolating Active Directory

The a-Order application uses Windows authentication. Since developers do not control the identities in their company's enterprise directory, it is sometimes useful to swap out Active Directory with a stub during the development of your application.

The a-Order application (before claims) shows an example of this. To use this technique, you need to make a small change to the Web.config file to disable Windows authentication and then add a hook in the session authentication pipeline to insert the user identities of your choosing. Disable Windows authentication with the following change to the Web.config file.

<authentication mode="None" />

The Global.asax file should include code that sets the identity with a programmer-supplied identity. The following is an example.

<script >

void Application_AuthenticateRequest(object sender, EventArgs e)
{
  this.Context.User = MaryMay;
}
    
private static IPrincipal MaryMay
{
  get
  {
    IIdentity identity = new GenericIdentity("mary");
    string[] roles = { "Employee", "Order Approver" };
    return new GenericPrincipal(identity, roles);
  }
}
       
</script>

Remove this code before you deploy your application.

Handling Single Sign-out in the Mock Issuer

The relying party applications (a-Order and a-Expense) use the FederatedPassiveSignInStatus control to allow the user to log in and log out. When the user clicks the log out link in one of the applications, the following sequence of events takes place:

  1. The user is logged out from the current application. The WSFederationAuthenticationModule (FAM) deletes any claims that the user has that relate to the current application.

  2. The FAM sends a wsignout1.0 WS-Federation command to the issuer.

  3. The mock issuer performs any necessary sign-out operations from other identity providers, for example, by signing the user out from Active Directory.

  4. The mock issuer sends a wsignoutcleanup1.0 message to all the relying party applications that the user has signed into. The mock issuer maintains this list for each user in a cookie.

    Note

    The mock issuer sends the wsignoutcleanup1.0 message to the relying party applications by embedding a specially constructed image tag in the sign out page that includes the wsignoutcleanup1.0 message in the querystring.

  5. When the FAM in a relying party application intercepts the wsignoutcleanup1.0 message, it deletes any claims that the user has that relate to that application.

Ff359102.note(en-us,PandP.10).gifMarkus Says:
Markus To find out more about the message flow when a user initiates the single sign-out process, take a look at Appendix B.

Converting to a Production Issuer

When you are ready to deploy to a production environment, you'll need to migrate from your simulated issuer that runs on your development workstation to a component such as ADFS 2.0.

Remove the mock issuers when you deploy the application.

Making this change requires two steps. First, you need to modify the web application's Web.config file using FedUtil so that it points to the production issuer. Next, you need to configure the issuer so that it recognizes requests from your web application and provides the appropriate claims.

Appendix A of this guide walks you through the process of using FedUtil and shows you how to change the Web.config files.

You can refer to documentation provided by your production issuer for instructions on how to add a relying party and how to add claims rules. Instructions for the samples included in this guide can be found at https://claimsid.codeplex.com.

Enabling Internet Access

One of the benefits of outsourcing authentication to an issuer is that existing applications can be accessed from the external Internet very easily. The protocols for claims-based identity are Internet-friendly. All you need to do is make the application and the issuer externally addressable. You don't need a VPN.

If you decide to deploy outside of the corporate firewall, be aware that you will need certificates from a certificate authority for the hosts that run your web application and issuer. You also need to make sure that you configure your URLs with fully qualified host names or static IP addresses. The ADFS 2.0 proxy role provides specific support for publishing endpoints on the Internet.

Variation—Moving to Azure

The last stage of Adatum's plan is to move a-Expense to Azure. Azure uses Microsoft data centers to provide developers with an on-demand compute service and storage to host, scale, and manage web applications on the Internet. This variation shows the power and flexibility of a claims-based approach. The a-Expense code doesn't change at all. You only need to edit its Web.config file.

As you go through this section, you may want to download the Visual Studio® solution from https://claimsid.codeplex.com.

It's easy to move a claims-aware application to Azure.

Figure 6 shows what Adatum's solution looks like.

Ff359102.2c14042b-1d57-45e9-82ea-a4bebf4d1332-thumb(en-us,PandP.10).png

Figure 6

a-Expense on Azure

From the perspective of Adatum's users, the location of the a-Expense application is irrelevant except that the application's URL might change once it is on Azure, but even that can be handled by mapping CNAMEs to an Azure URL. Otherwise, its behavior is the same as if it were located on one of Adatum's servers. This means that the sequence of events is exactly the same as before, when a-Expense became claims-aware. The first time a user accesses the application, he will not be authenticated, so the WIF module redirects him to the configured issuer that, in this case, is the Adatum issuer.

The issuer authenticates the user and then issues a token that includes the claims that a-Expense requires, such as the user's name and cost center. The issuer then redirects the user back to the application, where a session is established. Note that, even though it is located on the Internet, aExpense requires the same claims as when it was located on the Adatum intranet.

Obviously, for any user to use an application on Azure, it must be reachable from his computer. This scenario assumes that Adatum's network, including its DNS server, firewalls, and proxies, are configured to allow its employees to have access to the Internet.

Notice however, that the issuer doesn't need to be available to external resources. The a-Expense application never communicates with it directly. Instead, it uses browser redirections and follows the protocol for passive clients. For more information about this protocol, see chapter 2, "Claims-Based Architectures" and Appendix B.

Hosting a-Expense on Azure

The following procedures describe how to configure the certificates that you will upload to Azure and the changes you must make to the Web.config file. These procedures assume that you already have an Azure token. If you don't, see https://www.microsoft.com/windowsazure/getstarted/ to learn how to do this.

To configure the certificates

  1. In Visual Studio, open the Azure project, such as a-expense.cloud. Right-click the a-Expense.ClaimsAware role, and then click Properties.
  2. If you need a certificate's thumbprint, click Certificates. Along with other information, you will see the thumbprint.
  3. Click Endpoints, and then select HTTPS:. Set the Name field to HttpsIn. Set the Port field to the port number that you want to use. The default is 443. Select the certificate name from the SSL certificate name drop-down box. The default is localhost. The name should be the same as the name that is listed on the Certificates tab.

Note that the certificate that is uploaded is only used for SSL and not for token encryption. A certificate from Adatum is only necessary if you need to encrypt tokens.

Note

Both Azure and WIF can decrypt tokens. You must upload the certificate in the Azure portal and configure the web role to deploy to the certificate store each time there is a new instance. The WIF <serviceCertificate> section should point to that deployed certificate.

The following procedure shows you how to publish the a-Expense application to Azure.

To publish a-Expense to Azure

  1. In Microsoft Visual Studio 2010, open the a-expense.cloud solution.

  2. Upload the localhost.pfx certificate to the Azure project. The certificate is located at [samples-installation-directory]\Setup\DependencyChecker\certs\localhost.pfx. The password is "xyz."

  3. Modify the a-Expense.ClaimsAware application's Web.config file by replacing the <microsoft.identityModel> section with the following XML code. You must replace the {service-url} element with the service URL that you selected when you created the Azure project.

    <microsoft.identityModel>
      <service>     
        <audienceUris>       
          <add value="https://{service-url}.cloudapp.net/" />     
        </audienceUris>     
        <federatedAuthentication>       
          <wsFederation passiveRedirectEnabled="true"
            issuer="https://{adatum hostname}/{issuer endpoint}/"  
            realm="https://{service-url}.cloudapp.net/" 
             requireHttps="true" />       
             <cookieHandler requireSsl="true" />     
        </federatedAuthentication>          
        <issuerNameRegistry
          type=
           "Microsoft.IdentityModel.Tokens.
                    ConfigurationBasedIssuerNameRegistry,
                    Microsoft.IdentityModel, Version=3.5.0.0,  
                Culture=neutral,
                PublicKeyToken=31bf3856ad364e35">       
          <trustedIssuers>         
          <!--Adatum's identity provider -->         
            <add thumbprint=
                  "f260042d59e14817984c6183fbc6bfc71baf5462"  
                 name="adatum" />       
          </trustedIssuers>     
        </issuerNameRegistry>
       <certificateValidation 
                 certificateValidationMode="None" />   
      </service> 
    </microsoft.identityModel>
    
  4. Right-click the a-expense.cloud project, and then click Publish. This generates a ServiceConfiguration file and the actual package for Azure.

  5. Deploy the ServiceConfiguration file and package to the Azure project.

Once the a-Expense application is deployed to Azure, you can log on to https://windows.azure.com to test it.

Note

If you were to run this application on more than one role instance in Azure (or in an on-premise web farm), the default cookie encryption mechanism (which uses DPAPI) is not appropriate, since each machine has a distinct key.
In this case, you would need to replace the default SessionSecurityHandler object and configure it with a different cookie transformation such as RsaEncryptionCookieTransform or a custom one. The "web farm" sample included in the WIF SDK illustrates this in detail.

Questions

  1. Before Adatum updated the a-Expense and a-Order applications, why was it not possible to use single sign-on?
    1. The applications used different sets of roles to manage authorization.
    2. a-Order used Windows authentication and a-Expense used ASP.NET forms authentication.
    3. In the a-Expense application, the access rules were intermixed with the application's business logic.
    4. You cannot implement single sign-on when user profile data is stored in multiple locations.
  2. How does the use of claims facilitate remote web-based access to the Adatum applications?
    1. Using Active Directory for authentication makes it difficult to avoid having to use VPN to access the applications.
    2. Using claims means that you no longer need to use Active Directory.
    3. Protocols such as WS-Federation transport claims in tokens as part of standard HTTP messages.
    4. Using claims means that you can use ASP.NET forms-based authentication for all your applications.
  3. In a claims enabled ASP.NET web application, you typically find that the authentication mode is set to None in the Web.config file. Why is this?
    1. The WSFederationAuthenticationModule is now responsible for authenticating the user.
    2. The user must have already been authenticated by an external system before they visit the application.
    3. Authentication is handled in the On_Authenticate event in the global.asax file.
    4. The WSFederationAuthenticationModule is now responsible for managing the authentication process.
  4. Claims issuers always sign the tokens they send to a relying party. However, although it is considered best practice, they might not always encrypt the tokens. Why is this?
    1. Relying parties must be sure that the claims come from a trusted issuer.
    2. Tokens may be transferred using SSL.
    3. The claims issuer may not be able to encrypt the token because it does not have access to the encryption key.
    4. It's up to the relying party to state whether or not it accepts encrypted tokens.
  5. The FederatedPassiveSignInStatus control automatically signs a user out of all the applications she signed into in the single sign-on domain.
    1. True.
    2. False. You must add code to the application to perform the sign-out process.
    3. It depends on the capabilities of the claims issuer. The issuer is responsible for sending sign-out messages to all relying parties.
    4. If your relying party uses HTTP sessions, you must add code to explicitly abandon the session.

More Information

Appendix A of this guide walks through the use of FedUtil and also shows you how to edit the Web.config files and where to locate your certificates.

MSDN® contains a number of helpful articles, including MSDN Magazine's "A Better Approach For Building Claims-Based WCF Services" (https://msdn.microsoft.com/en-us/magazine/dd278426.aspx).

To learn more about Azure, see the Microsoft Azure Platform at https://www.microsoft.com/windowsazure/.

Next | Home