Using alterative AD attributes to authenticate to AD FS 3.0

Introduction

We have a requirement at the moment to modify AD FS 3.0 (which is a role in Windows Server 2012 R2) to allow users to authenticate without having to specify the domain name. 

This is for two reasons – the current external system doesn’t have a requirement to prefix a domain (or to authenticate with UPN format), and the organisation would prefer users to not have to worry about knowing the domain name (which is a DMZ domain).

AD FS 3.0 supports this scenario – sort of – but the landing page which handles authentication has some hard coded forms validation logic which won’t let users authenticate with a username which doesn’t have the DOMAIN\\ prefix or that is not in UPN format.  Awkward.

The Problem

AD FS 3.0 is the first release which doesn’t run under IIS.  As a result, this self hosted solution doesn’t have web content directly available for customization.  However, using PowerShell commands, it is possible to customize the user interface, although only client-side elements, like scripts (.js).

Since our issue is with client side validation, we have a way forward.  This article will demonstrate how to remove the domain prefix (or UPN format) requirement without having to modify ADFS binaries directly (which as a general rule you should never do).

Assigning an Alternate AD attribute to use for identifying a user’s credential (i.e. ‘username’) is simplicity itself.  In a PowerSHell console (elevated permissions) execute the following where [AD ATTRIBUTE] is the schema field you want to use to identify users.

Set-AdfsClaimsProviderTrust -TargetIdentifier “AD AUTHORITY” –AlternateLoginID [AD ATTRIBUTE] –LookupForest <your forest>

You can pretty much use any AD schema which makes sense, e.g. CN which is what I’m using in this scenario.

ad

You don’t get this view by default, if you want to view AD schema attributes, you need to switch the AD management console to Advanced View:

ad-advanced

Fixing the client-side validation

This part is trickier.  You’re going to have to modify the out-of-the-box AD FS behaviour in order to modify the way ADFS validates the username field. 

You’re going to need to create a new AD FS theme (based on the default) and then dump the default web theme to the file system using PowerShell commands:

New-AdfsWebTheme -name Custom -SourceName default
Export-AdfsWebTheme -Name Custom -DirectoryPath c:\AdfsTheme

You’ll need to configure this on each ADFS host if you have multiple. For more information on customizing AD FS 3.0 have a look at this TechNet article.

Once you’ve completed this step, have a look at the contents of the folder you specified to the –DirectoryPath parameter (e.g. c:\AdfsTheme).  There should be a subfolder called script, and it will contain a file called onload.js. 

We’re going to edit that file.

The Concept

The out-of-the-box implementation adds some client side JavaScript which checks the username field when the user clicks the submit button, or on a keypress (e..g Enter key).  We need to hijack that script and replace it with a cut down implementation, removing the domain format checking.

We unfortunately can’t do this with a script injected only on logon pages (using the SignInPageDescriptionText location)!  That approach injects custom script above the out-of-the-box script, which means we can’t modify form validation behaviour.  We have to instead change the onload.js which is run on every ADFS web page (the downside).

Here’s the out-of-the-box validation script, which you can see by viewing the page source of the ADFS logon page.  Note that the Sign In page description text field is located above this (id=”introduction””.

  oob

What we will do is add an implementation to the onload.js file which replaces the OOB implementation – we do this by appending the following to the end of the onload.js file’s content:

// rewire form validation
function override_form_validation() {
    Login.submitLoginRequest = function () {
                var u = new InputUtil();
                var e = new LoginErrors();

                var userName = document.getElementById(Login.userNameInput);
                var password = document.getElementById(Login.passwordInput);

                if (!userName.value) {
                    u.setError(userName, “Username must be supplied”);
                    return false;
                }

                if (!password.value) {
                    u.setError(password, e.passwordEmpty);
                    return false;
                }

                document.forms[‘loginForm’].submit();
                return false;
            };
}

if(location.href.indexOf(‘SignOn’) > 0){
    override_form_validation();
}

The last part executes the overridden form validation only if the page’s URL contains the text “SignOn”.

Publishing the Changes

Once we’re done modifying the JavaScript, we use a PowerShell console to publish the updated file back to AD FS.  Note that you need to do this on each AD FS server if you have multiple.

Set-AdfsWebTheme -TargetName Custom –AdditionalFileResource @{Uri=”/adfs/portal/script/onload.js”; Path=”c:\AdfsTheme\script\onload.js”}

ps

This script will run on each ADFS page.

Adding additional script files and referencing them

If you would like to add separate script files to the custom theme, you can do this too.  Simply use PowerShell and the following command:

Set-AdfsWebTheme -TargetName Custom -AdditionalFileResource @{Uri=’/adfs/portal/script/yourfile.js’;path=”c:\AdfsTheme\script\yourfile.js“}

To reference the script, use another PowerShell command to inject a reference to load the script where appropriate:

Set-AdfsGlobalWebContent –SignInPageDescriptionText “<script type=””text/javascript”” src=””/adfs/portal/script/yourfile.js“”></script>”

There’s also –SignOutPageDescriptionText as an option as well.  Check out the command help documentation for more places to inject your own custom scripts.

Leave a comment

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.