The objective of this hands-on lab is to create an on-premisses ASP.NET web application (relying party) that consumes identity information from an already configured federation provider in Windows Azure’s Access Control Service. This service will, in turn, consume identity information from Google or Facebook.
Sections:
At the end of this section, you should have an ASP.NET web application, ready to be changed to use identity information from an external provider. Visual Studio comes with templates that generate an example site with several pages that is enough for this HOL.
Open Microsoft Visual Studio 2010, and create an ASP.NET Web Application. Click on File -> New -> Project, select the ASP.NET Web Application template (C#) and name the project “FirstRelyingParty”. Make sure the selected framework version is 4.0.
Build (Build -> Build Solution) and test the application with the development server (Debug -> Start Without Debugging).
At the end of this section, you should have the ASP.NET web application configured on IIS, served through an HTTP endpoint. This is the first part of IIS configurations to have the application ready for changes related to identity federation.
Open Internet Information Services (IIS) Manager to create a new site. IIS Manager can be found in Control Panel -> System and Security -> Administrative Tools or, with the command line, c:\windows\system32\inetsrv\inetmgr.exe
.
Create the new site: Click on “Sites” and then click on “Add Web Site”. Fill “Site name” with “www.rp.ciws”, “Physical path” with the project directory and “Host name” with “www.rp.ciws”. Click “OK”.
Change the framework version of the application pool to v4.0: Click on “Application Pools”, select the application pool “www.rp.ciws” and click on “Basic Settings”. Change the value of “.NET Framework version” to “.NET Framework v4.0.30319”.
Make sure the user IIS APPPOOL\www.rp.ciws
has read permission on the folder. This is the identity of the application pool that was created and the identity for impersonation in anonymous authentication. You can learn more about application pool identities on the IIS site. If the directory isn’t below a user’s folder (c:\Users\<user>
), all users already have read permissions and no change is needed. For computers connected to a domain, the interface shown below doesn’t allow to change permissions to local users. You can use the icacls
command to change them.
icacls FirstRelyingParty /grant "IIS APPPOOL\www.rp.ciws":(OI)(CI)(R)
Change the hosts
file located at C:\Windows\System32\drivers\etc
, adding a new line with 127.0.0.1 www.rp.ciws
.
Test the web site in a browser with the new domain: http://www.rp.ciws
.
At the end of this section, you should have the ASP.NET web application being served through an HTTPS endpoint. This is the last part of IIS configurations before changes related to identity federation.
Add the site public and private key to the Windows personal certificate store.
Open c:\windows\system32\mmc.exe
Add the “Certificates” snap-in: File -> Add/Remove Snap-in, select “Certificates” and click on “Add >”. Choose “Computer account” for the account to manage certificates for.
Import the certificate: Expand the Personal -> Certificates node and select All Tasks -> Import on the context menu.
Locate the PFX file on disk.
Type the password for the private key (“changeit”).
Select “Automatically select the certificate store based on the type of certificate”.
Finish the wizard. The imported certificate will be shown on the list. The icon with a key indicates that we have the private key that corresponds to this certificate. This certificate is signed by “ca-int”, an intermediate certification authority (its certificate can be found on “Intermediate Certification Authorities\Certificates”). The certification chain ends with “ca-root”, a trusted root since the wizard placed its certificate below “Trusted Root Certification Authorities\Certificates”.
Add a HTTPS binding to the site: On IIS Manager, expand the “Sites” node and select “www.rp.ciws”. Click on “Bindings” and on “Add”. Choose “https” on “Type”, set the port to 8443 and choose “www.rp.ciws” on “SSL certificate”. Test the site with the new binding.
At the of this section, you should have the ASP.NET web application created in the previous section using federated identity.
Add a STS to the application, using the FedUtil wizard. On the project context menu, select “Add STS Reference”. This menu option starts the program C:\Program Files (x86)\Windows Identity Foundation SDK\v4.0\FedUtil.exe
.
Use the following settings:
“Application configuration location” is the location of the Web.config
file for the project and “Application URI” is https://www.rp.ciws:8443
Select “Use an existing STS” and “STS WS-Federation metadata document location” https://demos-ciws.accesscontrol.appfabriclabs.com/FederationMetadata/2007-06/FederationMetadata.xml
Select “Disable certificate chain validation”.
Select “No encryption”.
Open Web.config and see the changes made by the wizard. Note:
New microsoft.identityModel
element.
New ASP.NET modules on system.webServer
.
Commented authentication
element that was used for forms authentication.
New authorization
element, denying anonymous access to the site.
Test the web site. Since we aren’t authenticated yet, we are redirected to the STS. Login using your credentials from Google or Facebook. See that, after login, an error is returned:
A potentially dangerous Request.Form value was detected from the client (wresult=”<t:RequestSecurityTo…”).
On ASP.NET, the first time a collection of input values (Request.Form
, Request.QueryString
, Request.Cookies
or Request.Params
) is accessed, their values are checked for malicious input like < followed by a letter. This feature is called request validation and is used to try to prevent attacks like cross-site scripting. Since a parameter used in WS-Federation contains XML, the validator stops the request.
Copy C:\Program Files (x86)\Windows Identity Foundation SDK\v4.0\Samples\End-to-end\Federation for Web Apps\FederationPassiveRP\App_Code
to the project. The relevant file is SampleRequestValidator.cs
, that implements a WIF-aware custom request validator. These custom validators are a new feature of ASP.NET 4.0. For previous versions, the request validation must be turned off on the @Page
directive
<%@ Page ... ValidateRequest="false" %>
for the page that processes the post (Default.aspx
) or globally on Web.config
<system.web>
<pages validateRequest="false">
<system.web>
Add a reference to Microsoft.IdentityModel.dll
.
Register the custom request validator. Add <httpRuntime requestValidationType="SampleRequestValidator" />
to Web.config
, as a child node of system.web
.
Test the web site again. See that, after login, another error is returned:
The data protection operation was unsuccessful. This may have been caused by not having the user profile loaded for the current thread’s user context, which may be the case when the thread is impersonating.
In IIS Manager, select the “www.rp.ciws” application pool and click on “Advanced Settings”. Change “Load User Profile” to “True”.
.
Test the web site again. The LoginView
control on the top right side of the page should show the name of the logged in user.
Add the following to Default.aspx.cs:
using System.Threading;
using Microsoft.IdentityModel.Claims;
// ...
protected void Page_Load(object sender, EventArgs e)
{
var princ = Thread.CurrentPrincipal as IClaimsPrincipal;
var response = HttpContext.Current.Response;
response.Write("<ul>");
foreach(var claim in princ.Identities[0].Claims)
{
response.Write(String.Format("<li>claim: type = {0}, value = {1}",claim.ClaimType, claim.Value));
}
response.Write("</ul>");
}
Rebuild the solution and test the web site again. View the claims on the top of the page
The previous steps can be automated using PowerShell and command-line tools with, for example, the following commands:
Create site on IIS and change the framework version of the application pool:
$site = "C:\path\to\FirstRelyingParty\FirstRelyingParty"
Import-Module WebAdministration
New-Item IIS:\AppPools\www.rp.ciws
Set-ItemProperty IIS:\AppPools\www.rp.ciws managedRuntimeVersion v4.0
New-Item IIS:\Sites\www.rp.ciws -bindings @{protocol="http"; bindingInformation="*:80:www.rp.ciws"}
-physicalPath $site -applicationPool www.rp.ciws
Give read permissions to the web site folder:
icacls $site /grant "IIS APPPOOL\www.rp.ciws:(OI)(CI)(R)"
Add the domain to the hosts file:
echo "127.0.0.1 www.rp.ciws" | Out-File -encoding ASCII -append c:\windows\system32\drivers\etc\hosts
Import www.rp.ciws’ certificate and private key to the local machine’s personal store:
$pfx = new-object System.Security.Cryptography.X509Certificates.X509Certificate2
$pfx.Import("c:\path\to\www.rp.ciws.pfx", "changeit", "PersistKeySet")
$store = new-object System.Security.Cryptography.X509Certificates.X509Store("MY", "LocalMachine")
$store.Open("MaxAllowed")
$store.Add($pfx)
$store.Close()
Add a HTTPS binding:
New-Item IIS:\SslBindings\0.0.0.0!8443 -thumbprint $pfx.Thumbprint -store "MY"
New-ItemProperty IIS:\Sites\www.rp.ciws -name Bindings -value @{protocol = "https"; bindingInformation="*:8443:";
certificateStoreName="MY"; certificateHash=$pfx.Thumbprint}
Configure the STS for the site:
echo @"
<?xml version="1.0" encoding="utf-8"?>
<fedUtilUserInput>
<application location="$site\Web.config" applicationUri="https://www.rp.ciws:8443">
<securityTokenService federationMetadataLocation="
https://demos-ciws.accesscontrol.appfabriclabs.com/FederationMetadata/2007-06/FederationMetadata.xml">
<disableCertificateValidation />
<claimTypeRequirements useAllStsClaims="true" />
</securityTokenService>
</application>
</fedUtilUserInput>
"@ | out-file -encoding UTF8 fedinput.xml
fedutil /s fedinput.xml
Enable user profile:
Set-ItemProperty IIS:\AppPools\www.rp.ciws -name processModel.loadUserProfile -value $true