Query Windows Hello for Business registrations and usage

So recently I was planning on requiring authentication stenghts in a Conditional Access policy – more precisely requiring Windows Hello for Business – when I realized that I’m not 100% sure that every user will meet this requirement. I wanted to make sure everybody has WHfB enrollment and that it is actively in use – so let’s see the process.

Note: I will use ‘Hello’ for simplicity, but don’t confuse Windows Hello with Windows Hello for Business – two totally different things.

TL;DR

  • Having a Hello for Business enrollment does not necessarily mean that it is actively used or that it is even a “valid” enrollment
  • Entra portal – Protection – Authentication methods – User registration details can be used to filter for those who have Hello
  • For a particular user, the Authentication methods blade can give information about Hello device registrations
  • Filtering the Sig-in logs to “Windows Sign In” application can give some overview about Hello usage
  • I wrote a script to have all this info in one ugly PowerShell object

First of all, I want to highlight this section from MS documentation:

Windows 10 or newer maintain a partitioned list of PRTs for each credential. So, there’s a PRT for each of Windows Hello for Business, password, or smartcard. This partitioning ensures that MFA claims are isolated based on the credential used, and not mixed up during token requests.

It means that when you log in to Windows using your password, the PRT used will not get the MFA claim even if the user has Hello registration on the device. And it can happen that the user reverts to password usage [eg. forgot the PIN code, the fingerprint reader didn’t recognize him/her, etc.] – and Windows tends to ask for the last credential used* – so Bye-bye Hello and hello again Password (sorry for this terrible joke).

*Update: This behaviour is controlled by the NgcFirst registry key, in the following hive: HKLM\Software\Microsoft\Windows\Currentversion\Authentication\CredentialProviders\{D6886603-9D2F-4EB2-B667-1971041FA96B}\<usersid>\NgcFirst
There is a ConsecutiveSwitchCount counter, which increases by 1 when the user logs in using a password. Also here, you can find the MaxSwitchCount DWORD which is set to 3 by default. When the user uses password login 3 times in a row, then it is considered an Opt-out, which is visible in the OptOut entry (set to 1)

User opted out from Windows Hello for Business authentication

But let’s get back to square one: when you open the Authentication methods blade on Entra, you have the User registration details which can be used to list users with Hello:

User registration details filtered to Hello registrations

Let’s open one user to see the devices registered:

Authentication methods for one user

Yes, sometimes the Detail column is not showing the computer name – however, if you click on the three dots menu and select View details, you can see the device Id and object Id – very user friedly, isn’t it?

Hello registration details

Note: when a device is deleted, the registration will remain but it will not be tied to any device

And the last piece is the sign-in log: if you filter the sign-ins to application “Windows Sign In” and open the entries, the Authentication Details will reveal the method used:

Windows Sign in event using Hello

My requirement was to have a table about each Hello registration for every user and a timestamp of the last Hello sign-in event. This is why I wrote the following script (assuming you use Graph Powershell in your environment):

#MSAL.PS module required

$tenantID = '<tenantID>'
$graphPowerShellAppId = '14d82eec-204b-4c2f-b7e8-296a70dab67e'

$token = Get-MsalToken -TenantId $tenantID -Interactive -ClientId $graphPowerShellAppId -Scopes "AuditLog.Read.All","Directory.Read.All","UserAuthenticationMethod.Read.All"
$accessToken = $token.AccessToken

 $headers = @{ 
    'Content-Type' = 'application/json'
    'Accept' = 'application/json'
    'Authorization' = "Bearer $accessToken" 
    }

#WHfB enrolledusers
Write-Host -ForegroundColor Green "Fetching information from User registration details"
$url = 'https://graph.windows.net/myorganization/activities/authenticationMethodUserDetails?$filter=((methodsRegistered/any(t:%20t%20eq%20%27windowsHelloForBusiness%27)))&$orderby=userPrincipalName%20asc&api-version=beta'
$response = Invoke-WebRequest -Method Get -Uri $url -Headers $headers -ErrorAction Stop | ConvertFrom-Json | % {$_.value} 

Write-host -ForegroundColor Green "Querying authentication methods"
$whfb_authInfo = $response.userprincipalname | % {
        Write-Host -ForegroundColor Yellow "Querying $_"
      $url = "https://graph.microsoft.com/v1.0/users/$($_)/authentication/methods"
    [pscustomobject]@{
    UPN = $_
    WHfBInfo = Invoke-WebRequest -Method Get -Uri $url -Headers $headers -ErrorAction Stop | ConvertFrom-Json | % {$_.value} | ? {$_.'@odata.type' -eq '#microsoft.graph.windowsHelloForBusinessAuthenticationMethod'}
  }
}

function Expand-WHfBMethod ($UPN,$id){
Write-Host -ForegroundColor Yellow "Expanding WHfB authentication method for $UPN"
$url = "https://graph.microsoft.com/beta/users/$($upn)/authentication/windowsHelloForBusinessMethods/$($id)/?" + '$expand=device'
#Write-Host $url -ForegroundColor Red
Invoke-WebRequest -Method Get -Uri $url -Headers $headers -ErrorAction Stop | ConvertFrom-Json 
}

function Search-WHfBWindowsSignIn ($UPN,$deviceid){
Write-Host -ForegroundColor Yellow "Searching WHfB Windows Sign-in event for $UPN on device $deviceid"
$url = "https://graph.microsoft.com/beta/auditLogs/signIns?" + '$filter=(userprincipalname eq' + " '" + $UPN + "') and (appid eq '38aa3b87-a06d-4817-b275-7a316988d93b')" + " and (devicedetail/deviceid eq '" + $deviceid + "')" #appId for Windows Sign-in
$response = Invoke-WebRequest -Method Get -Uri $url -Headers $headers -ErrorAction Stop | ConvertFrom-Json
$response.value | ? {$_.authenticationdetails.authenticationmethod -eq "Windows Hello for Business"} | sort createdDatetime | select -Last 1 | % {$_.createdDatetime}
}

$report_WHfb = foreach ($item in $whfb_authInfo){
    $item.whfbinfo | % {
        $whfbmethod = $null
        $whfbmethod = Expand-WHfBMethod -UPN $item.UPN -id $_.id
    [pscustomobject]@{
        UPN = $item.UPN
        DeviceDisplayName = $whfbmethod.displayname
        DevieID = $whfbmethod.device.deviceid
        HelloForBusinessMethodLastUsed = Search-WHfBWindowsSignIn -UPN $item.UPN -deviceid $whfbmethod.device.deviceid
        Enrollmentdate = $_.createdDateTime
        KeyStrenght = $_.keyStrength
    }
    }
}

$report_WHfb | ft

Example output:

Note: to find the HelloForBusinessMethodLastUsed value, the script is querying the sign-in logs which will take some time in a larger environment.

Note2: if the DeviceID field equals 00000000-0000-0000-0000-000000000000, then this is a Hello registration that is not corresponding to any Entra joined device – probably the device was deleted. You may want to review these entries and delete them.

Leave a Reply