Hi there! Time for another lockdown post! Hope you’re all still doing well and we can leave this entire situation behind us sooner than later!

DISCLAIMER: Before I start I'd like to mention one very important thing. This post is not an official Jamf supported or recommended workflow. I'm also not an ADFS expert or admin, so don't hesitate (if you are) adding any comments or recommendations regarding the "do or do not" for this workflow.

So, what is this post all about? Let’s recap the Jamf Connect with ADFS situation for a moment.

When Jamf Connect was released, the product was initially focused on using Azure AD as iDP to provision user accounts and validate cloud passwords. As we know, Jamf Connect uses 2 separate protocols to achieve those goals: OIDC (OpenID Connect) to authenticate the user, and ROPG to validate the password. The reason for using the ROPG (Resource Owner Password Grant) protocol as a second authentication, is that Jamf Connect needs to validate the cloud password in order to set it as the local account password and keep it in sync. As Jamf Connect can not read the password entered within a a protected web app via OIDC, it asks the user to type the password again and validates it via ROPG.

This is all a walk in the park when you have a pure Azure – Cloud only – environment, but things can get a bit more complicated when the Azure tenant is federated with ADFS. Depending the exact environment, the ROPG flow might or might not flow well to the federated ADFS server, and along the way, through different Jamf Connect versions, additional configurations or workflows where needed to make it work, as in most cases authenticating ROPG via Azure would end up in error messages like ‘incorrect password’ when trying to validate the password. Initially there was a workaround where the entire configuration was pointed to the ADFS farm, as discussed here, but this caused some inconveniences in the way the user account was actually created ( DOMAIN\user).

Long story short, even if ‘password hash synchronisation’ is enabled on top of the federated user sign-in via ADFS, the ROPG validation of the password is most likely not going to work. That’s why Jamf Connect allows for a HYBRID config where we point OIDC to Azure and ROPG to ADFS. This avoids the ‘DOMAIN\user’ syntax for the user account and allows ROPG to validate the password directly against the ADFS farm. This works fine if the ADFS farm is running on Windows 2016, with ADFS 4.0 running farm behaviour level 3.

PS C:\Windows\system32> Get-AdfsFarmInformation

CurrentFarmBehavior FarmNodes
------------------- ---------
                  3 {adfs.travellingtechguy.dev}
Note: Having 'password hash synchronisation' (=PHS) enabled on top of ADFS federation is not the same as having PHS as user sign-in method. When Azure is federated with ADFS, the user sign-in method is FEDERATION, hence all authentications are redirected towards the ADFS farm. Enabling PHS as user sign-in method, de-federates the environment and actually uses the hash of the passwords for all authentications. Adding PHS to a still federated environment serves a backup to the federated sign-in (more about this here), but does not work with ROPG (it seems).

If you do flip the user sign-in to PHS, a pure Azure flavoured setup of Jamf Connect works fine, but in this case (as discussed in the note above) you are actually not ADFS-federated anymore… hence outside the scope of this blogpost.

For the completeness of this discussion, a few screenshots to show what I mean with the difference between ‘PHS enabled on top of ADFS Federation’ and ‘PHS as user sign-in method’:

First of all an example of an ADFS-federated tenant with PHS enabled on top:

Now, if we change the User Sign-in method… to PHS…

… it de-federates the tenant:

So, all is good right? No ADFS Federation active => pure Azure setup for Jamf Connect and if ADFS Federation is active => hybrid setup pointing ROPG to the ADFS farm.

Yes, this is all clear and works fine, however, I recently ran into an environment which was still federated with ADFS and PHS enabled, but Jamf Connect was working fine with a pure Azure config (both OIDC and ROPG pointed to Azure) ¯\_(ツ)_/¯

Yes, I came across situations where people were telling me the same in the past, using either Azure or Azure_v2 as endpoints and a pure Azure oriented config. But for me this never worked on an out-of-the-box default ADFS 4.0 farm. As long as federation was ‘enabled’ (see screenshots above), none of the possible settings to authenticate Jamf Connect through Azure worked for me, so I always sticked with the official hybrid setup pointing OIDC to Azure and ROPG to ADFS.

However, this lockdown did something to me :-). After seeing another environment where a pure Azure Jamf Connect config worked while ADFS was still federated… I woke up the next morning, triggered by unknown forces to investigate this again… so I tried again.

I federated my on-prem AD via ADFS to my Azure AD, added PHS on top of the federation and configured Jamf Connect Login as if it was a pure Azure environment… OIDC login -> successful, but as expected ROPG running into the ‘incorrect password’ error. So I checked the Azure AD sign-in logs and found the 50126 error on my authentication attempt… 50126, or AADSTS50126.

A quick Google search pointed me to this post: https://github.com/MicrosoftDocs/azure-docs/blob/master/articles/hdinsight/domain-joined/domain-joined-authentication-issues.md

Azure AD error code 50126 means the AllowCloudPasswordValidation policy has not been set by the tenant.

The Company Administrator of the Azure AD tenant should enable Azure AD to use password hashes for ADFS backed users. Apply the AllowCloudPasswordValidationPolicy to the app.

AllowCloudPasswordValidation? Wait a second, what’s that? Remember… I’m not an ADFS or Azure Certified admin! A bit of additional research pointed me to this: https://docs.microsoft.com/en-us/azure/active-directory/manage-apps/configure-authentication-for-federated-users-portal.

So I gave that a try…

Hereby a quick overview of the actions and tweaks I did to my ADFS server:

1. Run Windows PowerShell as administrator and Install the Connect-AzureD cmdlet if needed:

PS Install-Module AzureAD

2. Connect to your Azure AD and authenticate with an AzureAD administrator:

PS Connect-AzureAD

3. Check if a service principal has been created already for Jamf Connect (replace “Jamf Connect” with the actual name of the app created in Azure). This command will returning nothing if there is no known service principal with that name found.

PS Get-AzureADServicePrincipal -SearchString "Jamf Connect"

If needed, create one by using the ‘Application ID’ of the Jamf Connect app you find in Azure -> App Registrations (or Enterprise applications):

PS New-AzureADServicePrincipal -AppId 1d884884-XXXX-XXXX-XXXX-71548a60aa76

4. Check the newly created service principal again:

PS Get-AzureADServicePrincipal -SearchString "Jamf Connect"

You should now get something like below. Copy the ObjectId, as we’ll need it later.

(Alternatively to manually creating the service principal first, it seems that you can also get this ObjectId from Azure AD -> Enterprise Applications -> Jamf Connect. Assigning the policy which we will create below to an app which has no service principle yet, seems to add the service principal on the go)

5. Check if a HomeRealmDiscoveryPolicy is already enabled or not?

PS Get-AzureADPolicy

If there is none, the command will return nothing. If there is, you will get something like below. Copy the Id as we’ll need it later.

If such a policy does exist, you’ll need to check how this policy is configured exactly and for what purpose. In my default ADFS install there was none, so I had to create it. I’ll leave checking existing policies outside the scope of this post and kindly refer you to the ADFS admin who configured it 🙂

If no such policy exist, we need to create one:

PS New-AzureADPolicy -Definition @("{`"HomeRealmDiscoveryPolicy`":{`"AllowCloudPasswordValidation`":true}}") -DisplayName EnableDirectAuthPolicy -Type HomeRealmDiscoveryPolicy

Run “Get-AzureADPolicy” again which should now return something like the screenshot above, giving you the policy Id which we’ll need in the next command.

6. Now that we have our service principle (or ‘Object Id’ from Azure Ad -> Enterprise Applications -> Jamf Connect), and the ‘Policy ID’ for the AllowCloudPasswordValidation policy, we need to assign this policy to our Jamf Connect Azure app:

PS Add-AzureADServicePrincipalPolicy -Id bab6a070-XXXX-XXXX-XXXX-607a76ac9417 -RefObjectId 39fb1140-62de-4227-83d9-9c595930fa92

Here the ‘Id’ is the ObjectId of the app, and the ‘RefObjectId’ is the id of the policy we created.

7. Verify which application the policy is assigned to:

PS Get-AzureADPolicyAppliedObject -Id 39fb1140-62de-4227-83d9-9c595930fa92

Done… now let’s put it to a test!

So, for the ease of replicating and grabbing screenshots, I’ll use the Jamf Connect Configuration tool. However, I’ve been able to replicate the exact same behaviour in real Jamf Connect apps, both Login as Verify.

First of all, before going through this AllowCloudPasswordValidation tweak, ROPG was failing against my ADFS-federated Azure tenant:

This with the most basic config possible. Just pointing Jamf Connect towards Azure and defining the app ID.

Now, after allowing the Cloud Password Validation, without changing ANYTHING in my Jamf Connect setup:

And if I disable the AllowCloudPasswordValidation again?

PS  Remove-AzureADServicePrincipalPolicy -Id bab6a070-XXXX-XXXX-XXXX-607a76ac9417 -PolicyID 39fb1140-62de-4227-83d9-9c595930fa92

ROPG fails again!

So this would basically give us the following Jamf Connect Login plist. As simple as it gets, a pure Azure oriented plist:

<plist version="1.0">

And for Jamf Connect Verify:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">

To me, this proofs that there is an easier, and more streamlined, configuration possible to allow Jamf Connect to fully work with ADFS-Federated environments!

Now, like I said in the very beginning of this post, this is not an officially documented or Jamf-supported configuration. Just something I ran into after diving into the matter again.

The following questions remain open:

  • This worked for my Windows 2016 / ADFS 4.0 default setup, but I’ve already received some feedback that some people were not able to replicate this in a Windows 2019 ADFS environment and/or multi-tenant Azure AD. Remains to be tested again, and where applicable, identify possible differences in the environments.
  • Are there any security considerations or negative side-effects of this for the federated environment?

For the record, one additional note from the Microsoft Documentation on this:

Enable direct authentication for legacy applications
Best practice is for applications to use AAD libraries and interactive sign-in to authenticate users. The libraries take care of the federated user flows. Sometimes legacy applications aren't written to understand federation. They don't perform home realm discovery and do not interact with the correct federated endpoint to authenticate a user. If you choose to, you can use HRD Policy to enable specific legacy applications that submit username/password credentials to authenticate directly with Azure Active Directory. Password Hash Sync must be enabled.

So while the main goal of this post, as always, was to share my findings, this is also a call out to the community!

Are you able to replicate this? Yes, no? What is your exact setup regarding the Azure ADFS-Federation? Are you an ADFS / Azure Expert who can provide some useful knowledge on this? Please let me know!

That’s it! As always, if you liked the post, hit the like button, tell your friends about it and leave a comment down below!