Multifactor Authentication with ADFS v2

More and more companies are requiring that their IT departments start introducing multifactor authentication into their systems as the threat landscape changes over time.

Multifactor authentication isn’t a new concept – it’s been around for quite a few years within the enterprise.  Microsoft has supported this for a decade in Windows.

However, it’s just now starting to become a popular solution in the consumer space.  Proof of this is PayPal introducing their hardware tokens a few years ago as well as Google introducing its support for two-factor authentication not too long ago.

I can’t really explain why it took this long to make the shift.  So, frankly, I’m not going to try. Smile

This post is simply about getting ADFS to support multifactor authentication.

The premise is simple.  We want two secrets from the principal.  Their password and a pin.  The password is secret; it’s what you use to log into Windows.  The pin can be any number of things.  It could be a one-time-password (OTP) that is generated via a cryptographically sound algorithm, or it could be a secret stored on a smart card.

For the sake of this article, we don’t actually care what it is.  Let’s assume it’s an OTP.

First things first.  ADFS works via Windows Authentication, which allows for the single sign-on experience.  We don’t actually want this because we need to be able to enter in the secondary credential.  Within the web.config file for the ADFS website, you have a section called localAuthenticationTypes.  By default Integrated is the authentication type of choice, which is Windows Auth.  Let’s remove it, and everything else but Forms.  This will tell ADFS to render the FormsSignin.aspx page when authentication is required.

<localAuthenticationTypes>
<!--  <add name="Integrated" page="auth/integrated/" /> -->
  <add name="Forms" page="FormsSignIn.aspx" />
<!--  <add name="TlsClient" page="auth/sslclient/" /> –>
<!--  <add name="Basic" page="auth/basic/" />-->
</localAuthenticationTypes>

When we go to log into ADFS, we are now presented with a new page:

image

There isn’t anything particularly exciting about this page on a code level.  The markup is a table with some textboxes.  The code is just a callout to a method in the base class that validates the credentials from the textboxes via Windows Authentication:

protected void SubmitButton_Click( object sender, EventArgs e )
{
    try
    {
        SignIn( UsernameTextBox.Text, PasswordTextBox.Text );
    }
    catch ( AuthenticationFailedException ex )
    {
        HandleError( ex.Message );
    }
}

In a two-factor authentication world, we now have half the requirements.  Now we need to collect the pin, so take the markup and add a new row, with a textbox.

image

Next we need to validate the second credential.  The validation is somewhat arbitrary because I haven’t defined what sort of credential it is, so we just need to mock something up:

protected void SubmitButton_Click( object sender, EventArgs e )
{
    try
    {
        string userName = UsernameTextBox.Text;
        string pin = PinTextBox.Text;
        
        if(!ValidatePin(userName, pin));
             throw new AuthenticationFailedException();
             
        SignIn(userName, PasswordTextBox.Text);
    }
    catch ( AuthenticationFailedException ex )
    {
        HandleError( ex.Message );
    }
}

Next you compile it, and deploy.  Don’t forget what i said about properly securing your ADFS deployment Smile.

How you handle the pin validation is dependent on the technology, but that is about all there is to it when it comes to a simple two-factor authentication mechanism for ADFS v2.