Exercise 1: Enhance an ASP.NET Membership Website with Identity Provider Capabilities and Use it from a Third Party Website

In this exercise you will enhance an existing ASP.NET website which uses the membership provider with a passive STS page so it can work as an identity provider for other websites. Then, you will configure a second website to take advantage of it (or, using the common security terminology, federate with it).

Figure 1

The general flow of the exercise’s solution

Task 1 - Preparing the Solutions

In this task you will take an existing website and configure it for using a pre-populated membership store file (ASPNETDB.mdf). In practical terms, this means giving write permissions to NETWORK SERVICE (or IIS_IUSRS for Windows 7 and Windows Server 2008 R2) for the App_Data folder in order to allow IIS to read and write the ASPNETDB.mdf database.

  1. Open Microsoft Visual Studio 2010 with administrator privileges. From Start | All Programs | Microsoft Visual Studio 2010, right click on Microsoft Visual Studio 2010 and choose Run as administrator.
  2. Open the MembershipSite.sln solution file located in the %YourInstallationFolder%\Labs\MembershipAndFederation\Source\Ex1-EnhancingWithFederation\Begin folder.
  3. Open a Windows Explorer instance and browse to the folder %YourInstallationFolder%\Labs\MembershipAndFederation\Source\Ex1-EnhancingWithFederation\Begin\MembershipSite.
  4. Assign Write permission to NETWORK SERVICE (or IIS_IUSRS for Windows 7 and Windows Server 2008 R2) user for the App_Data folder of the Begin solution. To do this, right-click the App_Data folder, select Properties, go to the Security tab and click Edit. On the Permissions for App_Data select the "NETWORK SERVICE" user and check the Write permission. Click OK on each dialog.

    Figure 2

    Assigning NETWORK SERVICE write permissions for App_Data

    Note:
    If you want to run the End solution, follow the same steps in the %YourInstallationFolder%\Labs\MembershipAndFederation\Source\Ex1-EnhancingWithFederation\End\MembershipSite folder.

Task 2 - Inspecting the Initial Solution

  1. Go back to the solution in Visual Studio.
  2. Press F5 to run the Web site and accept to enable debugging in the Web site.
  3. When redirected to the login page enter the username John and the password p@ssw0rd

    Figure 3

    Log in page

  4. Click Log In button.

    Figure 4

    Default page showing a welcome message for the authenticated user

  5. Close the browser.
    Note:
    You can continue inspecting Web.config file. The web site is configured as any usual ASP.NET Web site to use Forms authentication method with the SQL membership provider. In the following task you will modify this website adding a passive STS page which will reuse the same membership mechanism.

Task 3 - Creating the Passive STS Page

  1. On the Solution Explorer, right-click the project (https://localhost/MembershipSiteEx01) and select Add New Item.
  2. Add a new Web Form using the name PassiveSTS.aspx and click Add.
  3. Add a reference to the Microsoft.IdentityModel.dll assembly to the partner Web site. To do this, right-click on the https://localhost/MembershipSiteEx01 project, select Add Reference and add the Microsoft.IdentityModel component in the .NET tab.

    Note:
    If you did not find the Microsoft.IdentityModel.dll assembly on the Add Reference dialog, you can add the reference including the following line in the configuration/system.web/compilation/assemblies section of the Web.config file:

    <add assembly="Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />

  4. Open the PassiveSTS.aspx.cs page.
  5. In the PassiveSTS class, replace the existing code with the following:

    (Code Snippet - Membership And Federation Lab - Ex 1 PassiveSTS Class)

    C#

    using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.UI; using System.Web.UI.WebControls; using Microsoft.IdentityModel.Protocols.WSFederation; using Microsoft.IdentityModel.Web; using Microsoft.IdentityModel.SecurityTokenService; using System.Globalization; public partial class PassiveSTS : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { } protected void Page_PreRender(object sender, EventArgs e) { string action = Request.QueryString[WSFederationConstants.Parameters.Action]; try { if (action == WSFederationConstants.Actions.SignIn) { // Process signin request. SignInRequestMessage requestMessage = (SignInRequestMessage)WSFederationMessage.CreateFromUri(Request.Url); if (User != null && User.Identity != null && User.Identity.IsAuthenticated) { SecurityTokenService sts = new MembershipSTS(MembershipSTSConfiguration.Current); SignInResponseMessage responseMessage = FederatedPassiveSecurityTokenServiceOperations.ProcessSignInRequest(requestMessage, User, sts); FederatedPassiveSecurityTokenServiceOperations.ProcessSignInResponse(responseMessage, Response); } else { throw new UnauthorizedAccessException(); } } else if (action == WSFederationConstants.Actions.SignOut) { // Process signout request. SignOutRequestMessage requestMessage = (SignOutRequestMessage)WSFederationMessage.CreateFromUri(Request.Url); FederatedPassiveSecurityTokenServiceOperations.ProcessSignOutRequest(requestMessage, User, requestMessage.Reply, Response); } else { throw new InvalidOperationException( String.Format(CultureInfo.InvariantCulture, "The action '{0}' (Request.QueryString['{1}']) is unexpected. Expected actions are: '{2}' or '{3}'.", String.IsNullOrEmpty(action) ? "<EMPTY>" : action, WSFederationConstants.Parameters.Action, WSFederationConstants.Actions.SignIn, WSFederationConstants.Actions.SignOut)); } } catch (Exception exception) { throw new Exception("An unexpected error occurred when processing the request. See inner exception for details.", exception); } } }
    FakePre-629f90dd288f47e1a993154998f653af-645c353b77794abb8a6b0e23a7864098FakePre-8bf1e533ed284c2b931c31c83045a7ab-88b2a528ee014c49a306955e2582fd71FakePre-76725d16fab44a29bc38b01cfe52fa09-f811b390e9c9423782008c7c85dd6c03FakePre-2457a326eafa4f50a015bd1605f326b1-a09e1223629f4a479740526314f17690
    
    Note:
     This code is the same code created by the ASP.NET Security Token Service Web Site Visual Studio template. It provides the basic functionality of a passive token issuer that supports sign-in and sign-out. The solution will not compile right now, but in the following steps you will add a couple of assets that will make it work.

  6. Open a windows explorer window and browse to %YourInstallationFolder%\Labs\MembershipAndFederation\Source\Assets.
  7. Copy the App_Code and FederationMetadata folders and its content to the Web site folder (right-click the project https://localhost/MembershipSiteEx01/ and select Open Folder in Windows Explorer, then paste the files in the project folder).
  8. Switch back to Visual Studio and click Refresh button on the Solution Explorer window.

    Figure 5

    App_Code and FederationMetadata folders added

    Note:
    The code you just added is a simplified version of the code generated by the Windows Identity Foundation Visual Studio templates. The STS will issue claims with the user name and the roles he has. For further understanding you can inspect the files just added and take a look at the GetOutputClaimsIdentity method in the MembershipSTS class. This is the method that we can use for customizing the identity information in the token that we will send to the third party website.

Task 4 - Creating the Partner Web Site

  1. On Visual Studio go to File | Add | New Web Site.
  2. On the Add New Web Site dialog select ASP.Net Empty Web Site project type, choose HTTP for the location and make sure language is set to Visual C#, and then provide the following name for the web site: https://localhost/MembershipSitePartnerEx01/ and click OK.
  3. Add a reference to the Microsoft.IdentityModel.dll assembly to the partner Web site. To do this, right-click on the https://localhost/MembershipSitePartnerEx01/ project, select Add Reference and add the Microsoft.IdentityModel component in the .NET tab.

    Note:
    If you did not find the Microsoft.IdentityModel.dll assembly on the Add Reference dialog, you can add the reference including the following line in the configuration/system.web/compilation/assemblies section of the Web.config file:

    <add assembly="Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />

  4. Add a new Web Form and use the name Default.aspx.
  5. Open the Default.aspx page to add a FederatedPassiveSignInStatus control and a welcome message and set the body background color to #AFC1CE to differentiate the partner site from the membership site.

    ASPX

    <%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>
    <%@ Register assembly="Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" namespace="Microsoft.IdentityModel.Web.Controls" tagprefix="idfx" %> <title>Membership and Federation - Exercise 1</title> <body style="background:#AFC1CE"> <h1>Welcome to your partner site</h1> <idfx:FederatedPassiveSignInStatus ID="SignInStatus1" runat="server" /> FakePre-9a81215357b6490d91e8027222d68a1a-3d9561a7606d47549ebf7904558b410cFakePre-a3e8188f422043dc81a197a435b8cbd6-7647b4d0a4f84585999a2b892eae75f5FakePre-806fe8585c7e48f29bae3fc935f3f661-ea7db9f88a634b6e870dc3ae1705b799FakePre-1fef5d8e43894bdaa771a4df59966c1c-d1739c09668e41c083e5956d241ebca2<%@ Register assembly="Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" namespace="Microsoft.IdentityModel.Web.Controls" tagprefix="idfx" %> <title>Membership and Federation - Exercise 1</title> <body style="background:#AFC1CE"> <h1>Welcome to your partner site</h1> <idfx:FederatedPassiveSignInStatus ID="SignInStatus1" runat="server" /> FakePre-41067e198fc848d9b421eabf5f7faa0e-2770cf8de7d7422391449e7d1194a1bf<%@ Register assembly="Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" namespace="Microsoft.IdentityModel.Web.Controls" tagprefix="idfx" %> <title>Membership and Federation - Exercise 1</title> <body style="background:#AFC1CE"> <h1>Welcome to your partner site</h1> <idfx:FederatedPassiveSignInStatus ID="SignInStatus1" runat="server" /> FakePre-6bc134c87b8a493ba6f282a5fe30753e-425113bb3e67433bbdc077ec92d0e7a3FakePre-f0b75f7d84cd41ca935c19bb8e206098-b418203e3bf841a3934b485e69bee2e6<%@ Register assembly="Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" namespace="Microsoft.IdentityModel.Web.Controls" tagprefix="idfx" %> <title>Membership and Federation - Exercise 1</title> <body style="background:#AFC1CE"> <h1>Welcome to your partner site</h1> <idfx:FederatedPassiveSignInStatus ID="SignInStatus1" runat="server" /> FakePre-28b9886ad58a438aab45375fd4dcc6e0-b94af95fa6ae46ddaa2b1f7beb0f152dFakePre-a8d660363dc0427d8339d257fec75711-aee4e5d3981f41368366a2eb0e967544FakePre-c93701d918f84e58bfd39b64e92430f3-bfb990fd2c5f49d1933f9be8c531b406FakePre-9ca2f82da29b4195b4efc3bfc3c3c80c-26f5c178389d48898a22e2cf031cbe8e

  6. On the Solution Explorer, right-click the https://localhost/MembershipSitePartnerEx01/ project and select Add STS reference.

    Figure 6

    Add STS reference option

  7. When the Federation Utility window shows up perform the following tasks for each step in the wizard.
    1. On the Welcome page click Next to continue using the pre-populated fields.

      Figure 7

      Welcome page

    2. On the STS options page, select the third option button ("Use an existing STS"), set the STS metadata location to https://localhost/MembershipSiteEx01/FederationMetadata/2007-06/FederationMetadata.xml and click Next.

      Figure 8

      Selecting a STS option

    3. If the following warning message appears saying that there is a mismatch between the DNS name of the machine and the certificate subject name in the federation metadata press Yes.

      Figure 9

      Warning message because of certificate and DNS name mismatch on the development environment

    4. If you get a STS signing certificate chain validation error, select Disable certificate chain validation and press Next. If not, continue in the following step.
    5. Select the Enable encryption option and then select the Select an existing certificate from store option.

      Figure 10

      Using encryption with localhost certificate

    6. Click Select Certificate and select the certificate for localhost.

      Figure 11

      Selecting the certificate

    7. Click Next.
    8. In the Offered Claims page, click Next.

      Figure 12

      Offered claims window

    9. On the Summary page, review the changes and click Finish.

      Figure 13

      Summary

  8. In the web.config file, add the following line inside the system.webServer section.

    XML

    <validation validateIntegratedModeConfiguration="false"/>

Task 5 - Validating the STS Issues Tokens to a Specific Relying Party

To add additional security to the STS, you can limit the issuance of tokens to specific relying parties only. In this task, you will configure the STS to issue tokens for the partner site only.

  1. Open the MembershipSTS.cs file from the https://localhost/MembershipSiteEx01/ project inside the App_Code folder.
  2. Update the GetScope method to validate the AppliesTo property and throw an exception is the URI is not from the partner site. To do this, update the GetScope method as follows.

    (Code Snippet - Membership And Federation Lab - Ex 1 AppliesTo Validation)

    C#

    protected override Scope GetScope(IClaimsPrincipal principal, RequestSecurityToken request)
    FakePre-b7e8ae53f85347f9ab205ee8d5d080f8-11307aff79084de6b93ca7911f672d82 Uri rpUri = new Uri("https://localhost/MembershipSitePartnerEx01/"); if (!request.AppliesTo.Uri.Equals(rpUri)) { throw new InvalidRequestException(String.Format("The AppliesTo address {0} is not valid.", request.AppliesTo.Uri.AbsoluteUri)); }FakePre-f478d643293b4c438e042315dc613d32-3503a79e879e438396ba4ba98c7b065eFakePre-08afd3b023a8415fa21ca0047fc5fd29-c8634a89bc594ac38c8fdb9fc36a38f3FakePre-063914891db346ab8d3b389c324c29be-f913d1634e2e403587c194947d60cf56FakePre-facbd8d38bf44c2c8b5cfc46c5d617b6-41920fff7ad844948b385f0d2070a588FakePre-316837d294d54ba697325b9ac5c53094-f8b96a3757be42979c90078350f7a15cFakePre-c840b1a1aedc4d4199ac9036b7b80420-1fea71db58bc41baa6df5df8dab1a9bcFakePre-7e9bc2e4d89f4c8f96bb479c660352f3-6a117c61f7404452b80572ce40f1ecf0

    Note:
    The AppliesTo will return the URL for the Relying Party that is requesting for tokens to the STS. In the previous code you are checking that the Relying Party that is requesting the token is a valid one.

Task 6 - Customizing Authorization with Different Roles

  1. Add a new Web Forms to the https://localhost/MembershipSitePartnerEx01 project and name it SecretPage.aspx.
  2. Add the following message to the SecretPage.aspx.

    ASPX

    <%@ Page Language="C#" %>
    FakePre-1e39517417f8479bb2232d1a26cb02dd-ac3b9f563f4e4bbc84d05450f672a642FakePre-f0708d58ea514eca82832ef6e5193a81-62d7d94120b64167837bde80f929371bFakePre-911e5bf6ae7842a2afa0d33c2745ec8f-90e3ce20409d4259ad5654386cd471c1FakePre-c8dbeeff47d1400581349525e69aaa23-8fc2c32dde0248609a8f4081b05d38fbFakePre-3873e7073fa14b989a1e8d49b6ff3585-540d3c23d7ef47ba9fdd99c4592aed8bFakePre-c1293357b9564322a8975b6fffeeddb2-fb98e93651f54440bffb15d797006b9aFakePre-63a5b648e22245f0b06492a56c08e546-c04ee7d7815f4b19916ea1841e420844<body style="background:#AFC1CE"> <p>This is a secret page; you can get here only if you are a manager.</p>FakePre-a1b7ddd5042741358a2da0e6a5981862-89a47f4151174fdb83b20860f6c9c4faFakePre-e3da2d7657464ce5af56876e9bd42a98-402120b638674349ac9dc19b3bd956ab<body style="background:#AFC1CE"> <p>This is a secret page; you can get here only if you are a manager.</p>FakePre-a7d8f827588a49d2a90ed3fdd49920c4-709e202d042e4e37acc940996f7f39c0FakePre-cd56ee5ae4f94ddea42420018aa32aec-0cfcb8b16910434f82f258305b7973b3FakePre-4d7cbf3faf044dfcaa6017b2ecbf49b0-62cbdb40df6648e1b2a0d5560a90920bFakePre-104d7ab92438482e9fab25debb6c2f49-01ed43fb7cf248818d2b2b99b5c7342b

  3. Open the web.config file from the https://localhost/MembershipSitePartnerEx01 project.
  4. Add the following settings under the configuration element to enable access to SecretPage.aspx only for users in Manager role.

    (Code Snippet - Membership And Federation Lab - Ex 1 SecretPage.aspx Authorization Settings)

    XML

    <configuration>
    FakePre-c146344de690475ab3cabf3b0b36ebfe-2becb391b61340549f42b9431dfdd503FakePre-3d13acb5369a41499530faa4961566d9-d12979ebdd7a4174939257aa4afdfc72 <location path="SecretPage.aspx"> <system.web> <authorization> <allow roles="Manager"/> <deny users="*"/> </authorization> </system.web> </location>FakePre-31b777cc6c4440bfa5b975483a8694e2-54a349fbf63f4c30b10d2d731c8c2d82FakePre-bbce9080bd9f4b2d9a7e6a04657b2173-75ae5dbe6cc744fabe4533431c04a1aaFakePre-afe5a26af7714c70825081d85af9ebdf-8f2dfa254a4e4bd69666ce6376f7a02e

    Note:
    This is plain old ASP.NET configuration. It will work because the token contains role claims (https://schemas.microsoft.com/ws/2008/06/identity/claims/role) and the ClaimsPrincipal implements IsInRole by checking the list of claims where claim type is the one defined above.

  5. Build the solution, to do this press CTRL+SHIFT+B.

Exercise 1: Verification

  1. Open a browser and navigate to https://localhost/MembershipSitePartnerEx01/.

    Figure 14

    Log in page from MembershipSiteEx01

    Note:
    You were redirected to the Log in page from MembershipSiteEx01 since MembershipSitePartnerEx01 is federating the authentication to MembershipSiteEx01. Once you get logged in you will be redirected back to the MembershipSitePartnerEx01.

    Note:
    If you want to run the End solution, assign Write permission to NETWORK SERVICE user (or IIS_IUSRS for Windows 7 and Windows Server 2008 R2) in the App_Data folder of the End solution.

    Note:
    If your SQL Express instance name is other than SQLEXPRESS, add the following configuration to the web.config under the Web Site https://localhost/MembershipSiteEx01 (show in bold):

    <connectionStrings>

    <remove name="LocalSqlServer"/>

    <add name="LocalSqlServer" connectionString="data source=.\<YOURINSTANCESQLEXPRESSNAME>;Integrated Security=SSPI;AttachDBFilename=|DataDirectory|aspnetdb.mdf;User Instance=true" providerName="System.Data.SqlClient"/>

    </connectionStrings>

  2. Enter valid credentials (john/p@ssw0rd) and click Log In.

    Figure 15

    Logged in MembershipSitePartnerEx01

    Note:
    Notice that you logged in MembershipSiteEx01 and now you are authenticated in the MembershipSitePartnerEx01 also.

  3. Browse to https://localhost/MembershipSitePartnerEx01/SecretPage.aspx.

    Figure 16

    Access Denied for user John

    Note:
    The current user John does not have the required role for accessing the SecretPage.aspx

  4. Go back (press backspace key).
  5. Click on Sign out.
  6. Log in using the Paul's credentials. Enter the username paul and the password p@ssw0rd
  7. Now browse to https://localhost/MembershipSitePartnerEx01/SecretPage.aspx.

    Figure 17

    Access granted for user Paul

  8. Go back (press backspace key).
  9. Click Sign out.
  10. Navigate to https://localhost/MembershipSiteEx01/, enter valid credentials (john/p@ssw0rd) and click Log In.

    Figure 18

    Logged in MembershipSiteEx01

  11. Now browse to https://localhost/MembershipSitePartnerEx01/

    Figure 19

    Logged in MembershipSitePartnerEx01 without Authenticating Again (Single Sign On)

    Note:
    You did not have to enter your credentials again since the web site is federating against the same STS which you already have a cookie for.

    The STS will issue tokens only when the AppliesTo element of the RequestSecurityToken is equal to https://localhost/MembershipSitePartnerEx01. As an extra exercise you can try creating another relying party website and configure it to federate against this STS. It will throw an exception.

  12. Close the Web browser.