Multi-domain ADFS with alternateID login

So, I got a question the other day on using ADFS in combination with some 3rd party applications in a very large AD environment. Basically the problem statement was: “ we don’t want to use UPN and we don’t want to use domain\username. Users should be able to login using either (only) their employeeID or samAccountName”.

In a single domain environment that’s an easy task. You could easily add some additional code into the ADFS server to append the domain name or the @UPN. But when you are talking about multiple domain trees in a forest it can become a challenge. But it is possible to overcome.

Using ADFS on Server 2016 or 2012 R2 with the latest updates, ADFS supports an alternateID to be used as the login attribute. This means we do not solely rely on the SamAccount name or UPN, but can also use for example the mail attribute to login. As long as the attribute has an @sign in the value you can use it. The beauty is that in my case the AD consisted out of a single forest. Which means, we can use any attribute if it is replicated to the Global Catalog of the forest.

In my scenario I chose the employeeID attribute and instead of actually using the sAMaccountName or a number, I chose to use idA and idB as the identifiers for my two users to ensure clarity in this demo.

Note that for this specific setup, we had a 3rd party application dependent on the ADFS providing DOMAIN\USERNAME to the application itself, but the customer wanted the formsbased login page accept a single value identity (thus without @blabla or domain\) and its password.

Now before we can utilize this attribute, as said, we must make it replicate to the Global Catalogs and add it to the index. For this, we go to the schema admin.

For the schema MMC console, please register the schmmgmt.dll first: regsvr32 schmmgmt.dll

In the schema MMC we go to the employeeID attribute and select the options: Index this attribute and Replicate this attribute to the Global Catalog.

After this is enabled, we need to ensure that all domain controllers are updated with this setting. To achieve this, we run the replication command (or wait for replication to kick in..) repadmin /syncall /AeD

(If you don’t replicate or made a mistake in the schema, the above error will be displayed on configuring ADFS)

After the replication has succeeded we can configure the ADFS to use the employeeID to be the login ID PS C:\> Set-AdfsClaimsProviderTrust -TargetIdentifier “AD AUTHORITY” -AlternateLoginID employeeID -LookupForests forestroot.local

(note that we provide the FOREST name, not the individual domain names. As such, we do not need to mention azureinfra.local as it is a domain-tree in the forest called forestroot.local)

The ADFS server will use the employeeID now as the anchor. If we configured the employeeID for userA and userB now to match idA@dummy.local and idB@dummy.local they can login.

But we wanted to remove the @dummy.local. So in order to do this, we need to perform some scripting. In short, the ADFS login form URL will add the @dummy.local as soon as the user clicks login.

To change the ADFS login form script, we can edit the onload.js. Best way to do this is to first make a copy of the existing login Theme, export the existing theme files, change the onload.js of that copy and activate the copy as the active theme.

  • On the primary ADFS server I made a folder under C:\themes
  • Issue: PS C:\>new-AdfsWebTheme -name custom -sourceName default
  • Issue: PS C:\>Export-AdfsWebTheme –Name default –DirectoryPath c:\themes
  • Go into c:\themes\script and open onload.js
  • Edit (see below sniplet)
  • Issue: PS C:\>Set-AdfsWebTheme -TargetName custom -AdditionalFileResource @{uri=’/adfs/portal/script/onload.js’;path=”C:\themes\script\onload.js”}
  • Issue: PS C:\>Set-AdfsWebConfig -ActiveThemeName custom
  • Restart the ADFS service

 

Now the part to add to the onload.js at the bottom of the file:


if (typeof Login != 'undefined'){  
    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 && !userName.value.match('[@\\\\]'))   
    {  
        var userNameValue = userName.value + '@dummy.local';  
        document.forms['loginForm'].UserName.value = userNameValue;  
    }  

    if (!userName.value) {  
       u.setError(userName, e.userNameFormatError);  
       return false;  
    }  

    if (!password.value)   
    {  
        u.setError(password, e.passwordEmpty);  
        return false;  
    }  
    document.forms['loginForm'].submit();  
    return false;  
};  
}  

And that’s it.. if you fill the employeeID now with blabla@dummy.local in any domain in the forest, that value will be used as the login name for the form of ADFS. The only thing we need to make sure now, is that employeeID fields are unique across the forest.

Note that the above was not tested with O365 or Azure AD. Additional configuration might be required to ensure that O365/AAD receives the correct user UPN which matches the UPN replicated to AAD for login. There are certain limitations of using AlternateID with ADFS. Please check: https://docs.microsoft.com/en-us/windows-server/identity/ad-fs/operations/configuring-alternate-login-id

Tagged , ,