Departmental Application Migration to Azure – Part 3 – ADFS with on-premise web app


This is part of a series of blogs.  See the preceding blog entries:

For me, authentication is the major challenge for this proof of concept.  The migration of our tiny database, the web application that will manage it, etc., that’s pretty trivial.  The issue is:  how are we going to authenticate against this cloud application?  The answer is:  the same way we do inside the company, with Active Directory Single Sign on.

Using AD SSO in the cloud is way trickier than on premise though.  The easiest way I found was to use ADFS 2.0.  In this article, I’ll develop a trivial web app using ADFS to authenticate.  The app will still be on premise, but it could be elsewhere.

I extensively used the WIF Developer Training Kit in order to achieve what I’m going to show in this blog post.  The part the training kit doesn’t cover is ADFS.

Prerequisite

You need to have ADFS 2.0 installed.  This was covered in the previous blog entry from this series.

Then you’ll need the Windows Identity Foundation (WIF) SDK.  You can download it at http://www.microsoft.com/downloads/details.aspx?familyid=C148B2DF-C7AF-46BB-9162-2C9422208504&displaylang=en.

I’ll use Visual Studio 2010.  I suppose you could use Visual Studio 2008, but I didn’t try it.

Basic web site

The first thing I’ll do is create a trivial web site.  So in Visual Studio, I create an ASP.NET Web Application called AdfsAuthenticatedWebSite:

image

This creates a boiler plate ASP.NET application.  First thing I’ll do is to clean-up this application:

  • Delete the Account folder since ADFS will perform those functions.
  • In web.config:
    • Delete the connection string.
    • Delete the authentication xml node.
    • Delete the membership xml node.
    • Delete the profile xml node.
    • Delete the roleManager xml node.

Then, I’ll set IIS to run the web site on https://localhost/AdfsAuthenticatedWebSite (on SSL) and create the virtual directory.  This is important because the web site needs to be secured in order to accept posted tokens.

image

You can check that the application is still compiling and executing.  The application is now using Windows Integrated Authentication (ASP.NET default’s).

Connecting the application to the STS

We are ready to start integrating the application with our STS.  STS stands for Security Token Service.  It’s a component able to authenticate a user and package the user’s claims into a standard token (typically SAML).  ADFS is a STS, so we’re going to connect to it.

Now, connecting to an STS is a two ways process.  The STS needs to know about your application (it doesn’t package the same types of claims for every application) and your application needs to know about the STS (minimally to know the encryption key to use to open the token).

We’ll start by having the application know about ADFS.  Right click the project and select Add STS reference.

image

The Wizard needs to access the application web.config file, because it’s going to modify it.  It also need to know about your application URI, which sorts of identify your app.

image

On the next screen, we’ll select the Use an existing STS option and give the URL to ADFS Federation metadata.  If you followed my ADFS installation procedure, the metadata is located at https://localhost/FederationMetadata/2007-06/federationmetadata.xml.

image

The next screen is about the encryption of the token.  I chose the certificate I’ve created for ADFS to encrypt the token.

image

The next screen shows you the claims available.  The last screen gives you the option to schedule a sync between the application and the STS.  I didn’t use this.

The wizard did two things:

  1. Generated the application’s metadata and put it in the folder FederationMetadata/2007-06
  2. Alter considerably the web.config file.

I try to test the web site afterwards, browsing to https://localhost/AdfsAuthenticatedWebSite.  If you have an error on the ADFS web site, that’s normal, it isn’t configured yet.

On my side, I got a security error on the application site due to my certificates being inaccessible by IIS user, something along the lines of ID1039: The certificate’s private key could not be accessed. Ensure the access control list (ACL) on the certificate’s private key grants access to the application pool user.  I found a solution to that problem on the web.  Basically, you have to give READ permission to the group IIS_IUSRS to the files located at %ALLUSERSPROFILE%\Microsoft\Crypto\RSA\MachineKeys.

Connecting the STS to the application

Now we are going to connect ADFS to our application.

We start by opening the ADFS Management Snap-In.  On the right-hand pane, we select Add Relying Party.

image

We start the wizard.  On the first screen, we need to import the application meta data.  We can point directly to its URL:  https://localhost/AdfsAuthenticatedWebSite/FederationMetadata/2007-06/FederationMetadata.xml.  This URL is the only one accessible anonymously on the web application since the rest of the web site is secured with ADFS which isn’t properly configured.

image

On the next screen, we enter the display name and some notes.  This data is only useful for display purpose within AD FS console.

image

On the next screen, we could opt-in to ADFS doing some authorization by denying access to some users.  I won’t do that here since I want to do it in my application and I want ADFS to only work as an STS.

image

On the next two screens, we basically just accept and move on.  The last screen will open the Edit Claim Rules dialog.

Entering Claim Rules

Here, we’ll concentrate on configuring ADFS to send certain claims to our application.  The way to do this in ADFS is by entering claim transformation rules.  Basically, Active Directory authenticate you and contains a bunch of claims (e.g. your name, email, your groups, etc.) and ADFS transforms those claims and put the result in a token.

I won’t do any fancy transformation:  I’ll recover the name, e-mail and a few groups of the user.  I’ll do that in two different claim transformation rules.

First, let’s add a rule.

image

We’ll use the default claim rule template, which is to send LDAP attributes as claims.  LDAP is the protocol used by Active Directory.

image

I enter Fetch attributes as the claim rule name and choose Active Directory as the attribute store.  I’ll fetch 3 attributes by simply mapping the LDAP (AD) attributes to claim types, which are predefined standard types.

image

The next claim rule is slightly more fancy.  I’m using the Send Group Membership as Claims template.  I basically map an AD group (my company’s Architect group) to a claim role.  If the user is part of that AD group, there will be a claim with the specified outgoing claim value, otherwise, that claim won’t be there.

image

…  and I’m done configuring ADFS for now.

Last Glitch

Now I’m reading to use my web site at https://localhost/AdfsAuthenticatedWebSite.

Bang!  Definitely, nothing’s easy with this demo!

image

Ok, this is a known issue with WIF.  Basically, ADFS sends back the security token in the post back and my web site fails to validate that token and detects it as a cross site attack (which it is in some sense).

The known work around is to disable page validation across the board or to write a custom RequestValidator that will skip that validation for the specific WIF post back.  Here I’ll use the RequestValidator.  I create a file WifRequestValidator.cs with the following code in it:

using System;
using System.Web;
using System.Web.Util;
using Microsoft.IdentityModel.Protocols.WSFederation;

namespace AdfsAuthenticatedWebSite
{
    public class WifRequestValidator : RequestValidator
    {
        protected override bool IsValidRequestString(HttpContext context, string value, RequestValidationSource requestValidationSource, string collectionKey, out int validationFailureIndex)
        {
            validationFailureIndex = 0;

            if (requestValidationSource == RequestValidationSource.Form && collectionKey.Equals(WSFederationConstants.Parameters.Result, StringComparison.Ordinal))
            {
                SignInResponseMessage message = WSFederationMessage.CreateFromFormPost(context.Request) as SignInResponseMessage;

                if (message != null)
                {
                    return true;
                }
            }

            return base.IsValidRequestString(context, value, requestValidationSource, collectionKey, out validationFailureIndex);
        }
    }
}

I then add this component in the web.config:

<system.web>
  <httpRuntime requestValidationType = "AdfsAuthenticatedWebSite.WifRequestValidator" />

Doing that, I can finally login automatically to my site.

Using the claims

Now I have my identity set by the WIF module.  Let’s use the claims I configured in ADFS!

For that, I’ll modify the Default.aspx, more specifically I’ll zap the content of MainContent content place holder and replace it by:

<asp:Content ID="BodyContent" runat="server" ContentPlaceHolderID="MainContent">
    <p>Your claims:</p>
    <asp:GridView ID="gridView" runat="server" AutoGenerateColumns="False">
        <Columns>
            <asp:BoundField DataField="ClaimType" HeaderText="ClaimType" ReadOnly="True" />
            <asp:BoundField DataField="Value" HeaderText="Value" ReadOnly="True" />
        </Columns>
    </asp:GridView>
</asp:Content>

In the code behind, in the Page_Load method, we put:

protected void Page_Load(object sender, EventArgs e)
{
    IClaimsIdentity claimsIdentity = ((IClaimsPrincipal)(Thread.CurrentPrincipal)).Identities.FirstOrDefault();

    if (claimsIdentity != null)
    {
        gridView.DataSource = claimsIdentity.Claims;
        gridView.DataBind();
    }
}

The code basically binds all the claims to a grid view.  When I execute the page, I see the AD-extracted attributes and two others:  the authentication method and the time when the authentication took place.

This basically concludes the proof-of-concept I was doing.

Conclusion

In order to create a web site where the authentication is managed by ADFS and exposed as claims, I needed to:

  • Create a web site
  • Add a STS (ADFS) reference to it
  • Add the web site (application) as a relying party in ADFS
  • Configure some claims transformation in ADFS
  • Consume the claims in the application

I did face a few glitches, mostly:

  • The certificate wasn’t accessible by the app pool user
  • The token isn’t recognized as valid by my web page

Once this is setup though, the thing runs like a charm.  ADFS is pretty easy and hassle-free to use and consuming claims in the application is also easy.

Now, being claim based gives me two advantages.

The first one is the advantage of claims in general:  it decouples my application to the authentication mechanism (Active Directory in this case).  Tomorrow I can switch Active Directory for Live ID and my application won’t need modifications.  Ok, not too likely.  A more realistic gain is that it gives one-and-only-one framework for developers to consume a user identity.

The second advantage is that I’m now Azure ready!  Azure apps won’t go interrogating my Active Directory (which is behind a firewall anyway), but they can go interrogating ADFS.  ADFS can even be behind the firewall, as long as the user is also sitting behind the firewall since the authentication is happening on his desktop.

In a future blog, I’ll actually build an application for Azure doing the exact same thing.

Advertisements

2 thoughts on “Departmental Application Migration to Azure – Part 3 – ADFS with on-premise web app

  1. Pingback: Departmental Application Migration to Azure – Part 4 – ADFS with Azure web app « Vincent-Philippe Lauzon's blog

  2. Pingback: Departmental Application Migration to Azure « Vincent-Philippe Lauzon's blog

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s