Presentation: Changing the Identity Game with the Windows Identity Foundation

Rob Windsor and TVBUG are letting me present on November 8th on Claims-Based Authentication and Identification.  Here are the details:

Location: Room 1, Library, 2nd floor, North York Public Library

Date: Monday, November 8, 2010

Time:
6:30 to 6:50 (Pizza - Meet and Greet)
6:50 to 7:00 (Group Business)
7:00 to 9:00 (Presentation)

Topic: Changing the Identity Game with the Windows Identity Foundation

Abstract: Identity is a tricky thing to manage. These days every application requires some knowledge of the user, which inevitably requires users to log in and out of the applications to prove they are who they are as well as keep a record of their accounts. With the Windows Identity Foundation, there is a fundamental shift in the way we manage these users and their accounts. In this presentation we will take a look at the why's and dig into the how's of the Windows Identity Foundation by building an Identity aware application from scratch.

Directions

All TVBUG meetings are held at the North York Public Library or North York Memorial Hall. Both are located in the same building at Yonge Street and Park Home Avenue (North of the 401 between Sheppard and Finch across from Empress Walk). If you are taking the Subway get off at the North York Centre Station. The library meeting rooms are on the 2nd Floor. Memorial Hall meeting rooms are on the Concourse Level near the food court.

Using Claims Based Identities with SharePoint 2010

When SharePoint 2010 was developed, Microsoft took extra care to include support for a claims-based identity model.  There are quite a few benefits to doing it this way, one of which is that it simplifies managing identities across organizational structures.  So lets take a look at adding a Secure Token Service as an Authentication Provider to SharePoint 2010.

First, Some Prerequisites

  • You have to use PowerShell for most of this.  You wouldn’t/shouldn’t be adding too many Providers to SharePoint all that often so there isn’t a GUI for this.
  • The claims that SharePoint will know about must be known during setup.  This isn’t that big a deal, but…

Telling SharePoint about the STS

Once you’ve collected all the information you need, open up PowerShell as an Administrator and add the SharePoint snap-in on the server.

Add-PSSnapin Microsoft.SharePoint.PowerShell

Next we need to create the certificate and claim mapping objects:

$cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2("d:\path\to\adfsCert.cer")

$claim1 = New-SPClaimTypeMapping -IncomingClaimType "http://schemas.microsoft.com/ws/2008/06/identity/claims/role" -IncomingClaimTypeDisplayName "Role" –SameAsIncoming

$claim2 = New-SPClaimTypeMapping "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress" -IncomingClaimTypeDisplayName "EmailAddress" –SameAsIncoming

There should be three lines.  They will be word-wrapped.

The certificate is pretty straightforward.  It is the public key of the STS.  The claims are also pretty straightforward.  There are two claims: the roles of the identity, and the email address of the identity.  You can add as many as the STS will support.

Next is to define the realm of the Relying Party; i.e. the SharePoint server.

$realm = "urn:" + $env:ComputerName + ":adfs"

By using a URN value you can mitigate future changes to addresses.  This becomes especially useful in an intranet/extranet scenario.

Then we define the sign-in URL for the STS.  In this case, we are using ADFS:

$signinurl = https://[myAdfsServer.fullyqualified.domainname]/adfs/ls/

Mind the SSL.

And finally we put it all together:

New-SPTrustedIdentityTokenIssuer -Name "MyDomainADFS2" -Description "ADFS 2 Federated Server for MyDomain" -Realm $realm -ImportTrustCertificate $cert -ClaimsMappings $claim1,$claim2 -SignInUrl $signinurl -IdentifierClaim $claim2.InputClaimType

This should be a single line, word wrapped.  If you wanted to you could just call New-SPTrustedIdentityTokenIssuer and then fill in the values one at a time.  This might be useful for debugging.

At this point SharePoint now knows about the STS but none of the sites are set up to use it.

Authenticating SharePoint sites using the STS

For a good measure restart SharePoint/IIS.  Go into SharePoint Administration and create a new website and select Claims Based Authentication at the top:

image

Fill out the rest of the details and then when you get to Claims Authentication Types select Trusted Identity Provider and then select your STS.  In this case it is my ADFS Server:

image

Save the site and you are done.  Try navigating to the site and it should redirect you to your STS.  You can then manage users as you would normally with Active Directory accounts.

Claims Transformation and Custom Attribute Stores in Active Directory Federation Services 2

Active Directory Federation Services 2 has an amazing amount of power when it comes to claims transformation.  To understand how it works lets take a look at a set of claims rules and the flow of data from ADFS to the Relying Party:

image

We can have multiple rules to transform claims, and each one takes precedence via an Order:

image

In the case above, Transform Rule 2 transformed the claims that Rule 1 requested from the attribute store, which in this case was Active Directory.  This becomes extremely useful because there are times when some of the data you need to pull out of Active Directory isn’t in a useable format.  There are a couple options to fix this:

  • Make the receiving application deal with it
  • Modify it before sending it off
  • Ignore it

Lets take a look at the second option (imagine an entire blog post on just ignoring it…).  ADFS allows us to transform claims before they are sent off in the token by way of the Claims Rule Language.  It follows the pattern: "If a set of conditions is true, issue one or more claims."  As such, it’s a big Boolean system.  Syntactically, it’s pretty straightforward.

To issue a claim by implicitly passing true:

=> issue(Type = "http://MyAwesomeUri/claims/AwesomeRole", Value = "Awesome Employee");

What that did was ignored the fact that there weren’t any conditions and will always pass a value.

To check a condition:

c:[Type == "http://schemas.microsoft.com/ws/2008/06/identity/claims/role", Value == "SomeRole"]
    => issue(Type = "http://MyAwesomeUri/claims/AwesomeRole", Value = "AwesomeRole");

Breaking down the query, we are checking that a claim created in a previous step has a specific type; in this case role and the claim’s value is SomeRole.  Based on that we are going to append a new claim to the outgoing list with a new type and a new value.

That’s pretty useful in it’s own right, but ADFS can actually go even further by allowing you to pull data out of custom data stores by way of Custom Attribute Stores.  There are four options to choose from when getting data:

  1. Active Directory (default)
  2. LDAP (Any directory that you can query via LDAP)
  3. SQL Server (awesome)
  4. Custom built store via custom .NET assembly

Let’s get some data from a SQL database.  First we need to create the attribute store.  Go to Trust Relationships/Attribute Stores in the ADFS MMC Console (or you could also use PowerShell):

image

Then add an attribute store:

image

All you need is a connection string to the database in question:

image

The next step is to create the query to pull data from the database.  It’s surprisingly straightforward.  This is a bit of a contrived example, but lets grab the certificate name and the certificate hash from a database table where the certificate name is equal to the value of the http://MyCertUri/UserCertName claim type:

c:[type == http://MyCertUri/UserCertName]
   => issue(store = "MyAttributeStore",
         types = ("http://test/CertName", "http://test/CertHash"),
         query = "SELECT CertificateName, CertificateHash FROM UserCertificates WHERE CertificateName='{0}'", param = c.value);

For each column you request in the SQL query, you need a claim type as well.  Also, unlike most SQL queries, to use parameters we need to use a format similar to String.Format instead of using @MyVariable syntaxes.

In a nutshell this is how you deal with claims transformation.  For a more in depth article on how to do this check out TechNet: http://technet.microsoft.com/en-us/library/dd807118(WS.10).aspx.

Converting Claims to Windows Tokens and User Impersonation

In a domain environment it is really useful to switch user contexts in a web application.  This could be if you are needing to log in with credentials that have elevated permissions (or vice-versa) or just needing to log in as another user.

It’s pretty easy to do this with Windows Identity Foundation and Claims Authentication.  When the WIF framework is installed, a service is installed (that is off by default) that can translate Claims to Windows Tokens.  This is called (not surprisingly) the Claims to Windows Token Service or (c2WTS).

Following the deploy-with-least-amount-of-attack-surface methodology, this service does not work out of the box.  You need to turn it on and enable which user’s are allowed to impersonate via token translation.  Now, this doesn’t mean which users can switch, it means which users running the process are allowed to switch.  E.g. the process running the IIS application pools local service/network service/local system/etc (preferably a named service user other than system users).

To allow users to do this go to C:\Program Files\Windows Identity Foundation\v3.5\c2wtshost.exe.config and add in the service users to <allowedCallers>:

<windowsTokenService>
  <!--
      By default no callers are allowed to use the Windows Identity Foundation Claims To NT Token Service.
      Add the identities you wish to allow below.
    -->
  <allowedCallers>
    <clear/>
    <!-- <add value="NT AUTHORITY\Network Service" /> -->
    <!-- <add value="NT AUTHORITY\Local Service" /> –>
    <!-- <add value="nt authority\system" /> –>
    <!-- <add value="NT AUTHORITY\Authenticated Users" /> -->
  </allowedCallers>
</windowsTokenService>

You should notice that by default, all users are not allowed.  Once you’ve done that you can start up the service.  It is called Claims to Windows Token Service in the Services MMC snap-in.

That takes care of the administrative side of things.  Lets write some code.  But first, some usings:

using System;
using System.Linq;
using System.Security.Principal;
using System.Threading;
using Microsoft.IdentityModel.Claims;
using Microsoft.IdentityModel.WindowsTokenService;

The next step is to actually generate the token.  From an architectural perspective, we want to use the UPN claims type as that’s what the service wants to see.  To get the claim, we do some simple LINQ:

IClaimsIdentity identity = (ClaimsIdentity)Thread.CurrentPrincipal.Identity;
string upn = identity.Claims.Where(c => c.ClaimType == ClaimTypes.Upn).First().Value;

if (String.IsNullOrEmpty(upn))
{
    throw new Exception("No UPN claim found");
}

Following that we do the impersonation:

WindowsIdentity windowsIdentity = S4UClient.UpnLogon(upn);

using (WindowsImpersonationContext ctxt = windowsIdentity.Impersonate())
{
    DoSomethingAsNewUser();

    ctxt.Undo(); // redundant with using { } statement
}

To release the token we call the Undo() method, but if you are within a using { } statement the Undo() method is called when the object is disposed.

One thing to keep in mind though.  If you do not have permission to impersonate a user a System.ServiceModel.Security.SecurityAccessDeniedException will be thrown.

That’s all there is to it.

Implementation Details

In my opinion, these types of calls really shouldn’t be made all that often.  Realistically you need to take a look at how impersonation fits into the application and then go from there.  Impersonation is pretty weighty topic for discussion, and frankly, I’m not an expert.

Converting Bootstrap Tokens to SAML Tokens

there comes a point where using an eavesdropping application to catch packets as they fly between Secure Token Services and Relying Parties becomes tiresome.  For me it came when I decided to give up on creating a man-in-the-middle between SSL sessions between ADFS and applications.  Mainly because ADFS doesn’t like that.  At all.

Needless to say I wanted to see the tokens.  Luckily, Windows Identity Foundation has the solution by way of the Bootstrap token.  To understand what it is, consider how this whole process works.  Once you’ve authenticated, the STS will POST a chunk of XML (the SAML Token) back to the RP.  WIF will interpret it as necessary and do it’s magic generating a new principal with the payload.  However, in some instances you need to keep this token intact.  This would be the case if you were creating a web service and needed to forward the token.  What WIF does is generate a bootstrap token from the SAML token, in the event you needed to forward it off to somewhere.

Before taking a look at it, let's add in some useful using statements:

using System;
using System.IdentityModel.Tokens;
using System.Text;
using System.Threading;
using System.Xml;
using Microsoft.IdentityModel.Claims;
using Microsoft.IdentityModel.Tokens;
using Microsoft.IdentityModel.Tokens.Saml11;

The bootstrap token is attached to IClaimsPrincipal identity:

SecurityToken bootstrapToken = ((IClaimsPrincipal)Thread.CurrentPrincipal).Identities[0].BootstrapToken;

However if you do this out of the box, BootstrapToken will be null.  By default, WIF will not save the token.  We need to explicitly enable this in the web.config file.  Add this line under <microsoft.IdentityModel><service><securityTokenHandlers>:

<securityTokenHandlerConfiguration saveBootstrapTokens="true" />

Once you’ve done that, WIF will load the token.

The properties are fairly straightforward, but you can’t just get a blob from it:

image

Luckily we have some code to convert from the bootstrap token to a chunk of XML:

SecurityToken bootstrapToken = ((IClaimsPrincipal)Thread.CurrentPrincipal).Identities[0].BootstrapToken;

StringBuilder sb = new StringBuilder();

using (var writer = XmlWriter.Create(sb))
{
     new Saml11SecurityTokenHandler(new SamlSecurityTokenRequirement()).WriteToken(writer, bootstrapToken);
}

string theXml = sb.ToString();

We get a proper XML document:

image

That’s all there is to it.

Build your own Directory Federation Service

This is more of a random collection of thoughts because earlier today I came to the conclusion that I need something very similar to Active Directory Federation Services, except for non-domain users.  This is relatively easy to do; all I need is to create a Secure Token Service with a user store for the back end. 

The simplest approach is to use ASP.NET Membership and Roles with SqlProvider’s wrapped up by some WIF special sauce.  Turns out Dominick Baier already did just that with StarterSTS.

The problems I have with this is that it’s a pain to manage when you start getting more than a hundred or so users.  Extending user properties is hard to do too.  So my solution is to use something that is designed for user identities… an LDAP directory.  If it’s good enough for Active Directory, it’ll be plenty useful for this situation.

Reasoning
As an aside, the reason I’m not using Active Directory in the first place is because I need to manage a few thousand well known users without CAL’s.  This would amount to upwards of a couple hundred thousand dollars in licensing costs that just isn’t in the budget.  Further, most of these users probably wouldn’t use any of our systems that use Active Directory for authentication, but nevertheless, we need accounts for them.

Also, it would be a lot easier to manage creation and modification of user accounts because there are loads of processes that have been designed to pull user data out of HR applications into LDAP directories instead of custom SQL queries.

So lets think about what makes up Active Directory Federation Services.  It has roles that provides:

  • Token Services
  • A Windows Authentication end-point
  • An Attribute store-property-to-claim mapper (maps any LDAP properties to any claim types)
  • An application management tool (MMC snap-in and PowerShell cmdlets)
  • Proxy Services (Allows requests to pass NAT’ed zones)

That’s a pretty lightweight product when you compare it to the other services in Microsoft’s Identity stack. 

We can simplify it even further by breaking down the roles we need.

Token Services

This is actually pretty easy to accomplish.  Refer back to the WIF magic sauce.

Authentication end-point

This is just (well, you know what I mean) a web page login control.  We can’t do Windows Authentication without Kerberos (or NTLM), and we can’t do Kerberos without Active Directory (technically it could be done, but you’d be crazy to try).

Attribute store-property-to-claim mapper

ADFS can connect to a bunch of different attribute stores, including custom built stores if you provide assemblies.  We only really need to map to a few LDAP properties, and make it easy to map to other properties in the future.

Application management tool

This would be to manage the mapper and a few STS settings like URI names and certificates.  This, I think, would be a relatively simple application if we designed the configuration database properly.

Proxy Services

Proxies are a pain in the butt.  Useful in general, but we don’t really need to think about this at the moment.

Some Warnings

There are some things that are worth mentioning.  We have to be really careful about what we create because we are developing a serious piece of the security infrastructure.  Yes, it is for a group of employees that won’t have much access to anything dangerous (if they need access, they’d be migrated to Active Directory), but nevertheless we are creating the main ingress point for the majority of our employees.  It also needs to be accessible from the internet.

It may sound like I think it’ll be a synch to develop this system and have it work securely, but in reality there is a lot that will need to go into it to protect the network, the employees, and the data this could possibly interact with.  It is tough to develop applications securely.  It is far harder to develop secure applications whose sole responsibility is security related.

Next Steps

The next step is to design the thing.  I know how it will exist in relation to the systems it will be used to provide identity to, but aside from that, the architecture of the thing is still unknown.  With any luck I can accomplish rough designs tomorrow on the train, on my way to visit family for the holiday.

Better yet, maybe while visiting with family. Winking smile

Using the ASP.NET Roles Provider with Windows Identity Foundation

Using the Windows Identity Foundation to handle user authentication and identity management can require you to drastically rethink how you will build your application.  There are a few fundamental differences between how authentication and roles will be handled when you switch to a Claims model. 

As an example if you used an STS to provide Claims to your application, you wouldn’t (couldn’t really) use the FormsAuthentication class.

Another thing to keep in mind is how you would handle Roles.  WIF sort of handles roles if you were to use <location> tags in web.config files like:

  <location path="test.aspx">
    <system.web>
      <authorization>
        <deny users="*" />
        <allow roles="admin" />
      </authorization>
    </system.web>
  </location>

WIF would handle this in an earlier part of the page lifecycle, and only allow authenticated users with a returned Role claim of admin.  This works well for some cases, but not all.

In larger applications we may want custom Roles, and the ability to map these roles to the Roles provided by the STS. 

This is by no means a place to tell you when you should use what architectural design, but a lot of times we want somewhere in the middle of these extremes…

Sometimes we just want to use the Roles class to check for role membership based on the Role claims.  From what I can find there is no RolesProvider implementation for WIF, so I wrote a very simple provider.  It is by all rights a hack.  The reason I say this is because there are quite a few methods that just can’t be implemented.  For instance, getting roles for other users is impossible, or adding a user to a role, or creating a role, deleting a role, etc.  This is all impossible because we can’t send anything back to the STS telling it what to do with the roles.

We are also limited to the scope of the roles.  I can only get the roles of the currently logged in user, nothing beyond.  So, with all the usual warnings (it works on my machine, don’t blame me if it steals your soul, etc)…

using System;
using System.Linq;
using System.Threading;
using System.Web.Security;
using Microsoft.IdentityModel.Claims;

public class ClaimsRoleProvider : RoleProvider
{
    IClaimsIdentity claimsIdentity;
    ClaimCollection userClaims;

    private void initClaims()
    {
        claimsIdentity = ((IClaimsPrincipal)(Thread.CurrentPrincipal)).Identities[0];
        userClaims = claimsIdentity.Claims;
    }

    public override string ApplicationName
    {
        get
        {
            initClaims();
            return claimsIdentity.GetType().ToString();
        }
        set
        {
            throw new NotImplementedException();
        }
    }

    public override bool RoleExists(string roleName)
    {
        initClaims();

        return userClaims.Where(r => r.Value == roleName).Any();
    }

    public override bool IsUserInRole(string username, string roleName)
    {
        initClaims();

        return userClaims.Where(r => r.Value == roleName).Any();
    }

    public override string[] GetRolesForUser(string username)
    {
        initClaims();

        return userClaims.Where(r => r.ClaimType == ClaimTypes.Role).Select(r => r.Value).ToArray();
    }

    public override string[] GetAllRoles()
    {
        initClaims();

        return userClaims.Where(r => r.ClaimType == ClaimTypes.Role).Select(r => r.Value).ToArray();
    }

    #region Not implementable

    public override string[] GetUsersInRole(string roleName)
    {
        throw new NotImplementedException();
    }

    public override void RemoveUsersFromRoles(string[] usernames, string[] roleNames)
    {
        throw new NotImplementedException();
    }

    public override void CreateRole(string roleName)
    {
        throw new NotImplementedException();
    }

    public override bool DeleteRole(string roleName, bool throwOnPopulatedRole)
    {
        throw new NotImplementedException();
    }

    public override string[] FindUsersInRole(string roleName, string usernameToMatch)
    {
        throw new NotImplementedException();
    }

    public override void AddUsersToRoles(string[] usernames, string[] roleNames)
    {
        throw new NotImplementedException();
    }

    #endregion
}

The next step is to modify the web.config to use this provider.  I put this in a separate assembly so it could be re-used.

    <roleManager enabled="true" defaultProvider="claimsRoleProvider">
      <providers>
        <clear />
        <add name="claimsRoleProvider" type="ClaimsRoleProvider, MyAssem.Providers,
Version=1.0.0.0, Culture=neutral, PublicKeyToken=4a27739ef3347280" />
      </providers>
    </roleManager>

One final thing to be aware of… Roles.IsUserInRole(string roleName) uses IPrincipal.Identity.Name in it’s overloaded version in lieu of a username parameter which could result in this ArgumentNullException:

Value cannot be null.
Parameter name: username

Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.ArgumentNullException: Value cannot be null.
Parameter name: username
Source Error:

Line 17:         var claims = from c in claimsIdentity.Claims select c;
Line 18: 
Line 19:         bool inRole = Roles.IsUserInRole("VPN");
Line 20:         
Line 21:         foreach (var r in claims)

Since the IClaimsIdentity is getting generated based on the claims it receives, it sets the Name property to whatever claim value is associated with the http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name claim type. If one isn't present, it will be set to null.

It took way too long for me to figure that one out. :)

Single Sign-On Between the Cloud and On-Premise using ADFS 2

One of the issues I hear about hosting services in the cloud has to do with managing Identity.  Since the service isn’t local, it’s harder to tie it into services like Active Directory.  What do I mean by this?

I’m kind of particular how certain things work.  I hate having more than one set of credentials across applications.  Theoretically since we can’t join our Azure Servers to our domain, there’s a good chance we will need separate credentials between our internal domain and Cloud Services.  However, it’s possible to make our Cloud Applications use our Active Directory credentials via a Claims Service.

With Federation Services it’s surprisingly easy to do.  Yesterday we talked about installing Active Directory Federation Services and federating an application.  Today we will talk about what it takes to get things talking between Azure and ADFS.

As a recap, yesterday we:

  1. Installed prerequisites
  2. Installed ADFS 2.0 on a domain joined server
  3. Created a relying party
  4. Created claims mappings to data in Active Directory
  5. Created a simple Claims-Aware application

So what do we need to do next?  There really isn’t much we need to do:

  1. Build Azure App
  2. Federate it using FedUtil.exe

Building an Azure application isn’t trivial, but we don’t need to know much to Federate it. 

How do we federate it?  Follow these steps providing the Azure details for the application URI and the Federation Metadata from ADFS. 

One of the gotcha’s with deploying to Azure though is that the Microsoft.IdentityModel assembly is not part of the GAC, and it’s not in Azure builds.  Therefore we need to copy the assembly to the bin folder for deployment.  We do that by going to the Microsoft.IdentityModel reference properties and setting Copy Local to true:

image

That isn’t the only gotcha.  We need to keep in mind how data is transferred between Cloud and intranet.  In most cases, nothing goes on behind the scenes; it passes across the client’s browser through POST calls.  If the client’s browser is on the local intranet, when it hits the cloud app it will redirect to an intranet location.  This works because the client has access to both the cloud app and can access ADFS.  This isn’t necessarily the case with people who work offsite, or are partners with the company.

We need to have the ADFS Server accessible to the public.  This is kind of an ugly situation.  Leaving the politics out of this, we are sticking a domain joined system out in the public that’s sole responsibility is authentication and identity mapping.

One way to mitigate certain risks is to use an ADFS Proxy Service.  This service will sit on a non-domain joined system sitting on an edge network that creates a connection to the ADFS Server sitting inside the corporate network.  External applications would use the Proxy service.

Installing the Proxy service is relatively simple, but a topic for another post.

Installing ADFS 2 and Federating an Application

From Microsoft Marketing, ADFS 2.0 is:

Active Directory Federation Services 2.0 helps IT enable users to collaborate across organizational boundaries and easily access applications on-premises and in the cloud, while maintaining application security. Through a claims-based infrastructure, IT can enable a single sign-on experience for end-users to applications without requiring a separate account or password, whether applications are located in partner organizations or hosted in the cloud.

So, it’s a Token Service plus some.  In a previous post I had said:

In other words it is a method for centralizing user Identity information, very much like how the Windows Live and OpenID systems work.  The system is reasonably simple.  I have a Membership data store that contains user information.  I want (n) number of websites to use that membership store, EXCEPT I don’t want each application to have direct access to membership data such as passwords.  The way around it is through claims.

The membership store in this case being Active Directory.

I thought it would be a good idea to run through how to install ADFS and set up an application to use it.  Since we already discussed how to federate an application using FedUtil.exe, I will let you go through the steps in the previous post.  I will provide information on where to find the Metadata later on in this post.

But First: The Prerequisites

  1. Join the Server to the Domain. (I’ve started the installation of ADFS three times on non-domain joined systems.  Doh!)
  2. Install the latest .NET Framework.  I’m kinda partial to using SmallestDotNet.com created by Scott Hanselman.  It’s easy.
  3. Install IIS.  If you are running Server 2008 R2 you can follow these steps in another post, or just go through the wizards.  FYI: The post installs EVERY feature.  Just remember that when you move to production.  Surface Area and what not…
  4. Install PowerShell.
  5. Install the Windows Identity Foundation: http://www.microsoft.com/downloads/details.aspx?FamilyID=eb9c345f-e830-40b8-a5fe-ae7a864c4d76&displaylang=en
  6. Install SQL Server.  This is NOT required.  You only need to install it if you want to use a SQL Database to get custom Claims data.  You could also use a SQL Server on another server…
  7. Download ADFS 2.0 RTW: http://www.microsoft.com/downloads/details.aspx?familyid=118c3588-9070-426a-b655-6cec0a92c10b&displaylang=en

The Installation

image

Read the terms and accept them.  If you notice, you only have to read half of what you see because the rest is in French.  Maybe the lawyers are listening…these things are getting more readable.

image

Select Federation Server.  A Server Proxy allows you to use ADFS on a web server not joined to the domain.

image

We already installed all of these things.  When you click next it will check for latest hotfixes and ask if you want to open the configuration MMC snap-in.  Start it.

image

We want to start the configuration Wizard and then create a new Federation Service:

image

Next we want to create a Stand-alone federation server:

image

We need to select a certificate for ADFS to use.  By default it uses the SSL certificate of the default site in IIS.  So lets add one.  In the IIS Manager select the server and then select Server Certificates:

image

We have a couple options when it comes to adding a certificate.  For the sake of this post I’ll just create a self-signed certificate, but if you have a domain Certificate Authority you could go that route, or if this is a public facing service create a request and get a certificate from a 3rd party CA.

image

Once we’ve created the certificate we assign it to the web site.  Go to the website and select Bindings…

image

Add a site binding for https:

image

Now that we’ve done that we can go back to the Configuration Wizard:

image

Click next and it will install the service.  It will stop IIS so be aware of that.

image

You may receive this error if you are installing on Server 2008:

image

The fix for this is here: http://www.syfuhs.net/2010/07/23/ADFS20WindowsServiceNotStartingOnServer2008.aspx

You will need to re-run the configuration wizard if you do this.  It may complain about the virtual applications already existing.  You two options: 1) delete the applications in IIS as well as the folder C:\inetpub\adfs; 2) Ignore the warning.

Back to the installation, it will create two new Virtual Applications in IIS:

image

Once the wizard finishes you can go back to the MMC snap-in and fiddle around.  The first thing we need to do is create an entry for a Relying Party.  This will allow us to create a web application to work with it.

image

When creating an RP we have a couple options to provide configuration data.

image

Since we are going to create a web application from scratch we will enter in manual data.  If you already have the application built and have Federation Metadata available for it, by all means just use that.

We need a name:

image

Very original, eh?

Next we need to decide on what profile we will be using.  Since we are building an application from scratch we can take advantage of the 2.0 profile, but if we needed backwards compatibility for a legacy application we should select the 1.0/1.1 profile.

image

Next we specify the certificate to encrypt our claims sent to the application.  We only need the public key of the certificate.  When we run FedUtil.exe we can specify which certificate we want to use to decrypt the incoming tokens.  This will be the private key of the same certificate.  For the sake of this, we’ll skip it.

image

The next step gets a little confusing.  It asks which protocols we want to use if we are federating with a separate STS.  In this case since we aren’t doing anything that crazy we can ignore them and continue:

image

We next need to specify the RP’s identifying URI.

image

Allow anyone and everyone, or deny everyone and add specific users later?  Allow everyone…

image

When we finish we want to edit the claim rules:

image

This dialog will allow us to add mappings between claims and the data within Active Directory:

image

So lets add a rule.  We want to Send LDAP Attributes as Claims

image

First we specify what data in Active Directory we want to provide:

image

Then we specify which claim type to use:

image

And ADFS is configured!  Lets create our Relying Party.  You can follow these steps: Making an ASP.NET Website Claims Aware with the Windows Identity Foundation.  To get the Federation Metadata for ADFS navigate to the URL that the default website is mapped to + /FederationMetadata/2007-06/FederationMetadata.xml.  In my case it’s https://web1.nexus.internal.test/FederationMetadata/2007-06/FederationMetadata.xml.

Once you finish the utility it’s important that we tell ADFS that our new RP has Metadata available.  Double click on the RP to get to the properties.  Select Monitoring:

image

Add the URL for the Metadata and select Monitor relying party.  This will periodically call up the URL and download the metadata in the event that it changes.

At this point we can test.  Hit F5 and we will redirect to the ADFS page.  It will ask for domain credentials and redirect back to our page.  Since I tested it with a domain admin account I got this back:

image

It works!

For more information on ADFS 2.0 check out http://www.microsoft.com/windowsserver2008/en/us/ad-fs-2-overview.aspx or the WIF Blog at http://blogs.msdn.com/b/card/

Happy coding!