Monitoring Entra Auth Methods
Monitoring the details of authentication methods that are present on Entra ID accounts can be really useful. However the native approaches for this have some limitations, the IdentityInfo table doesn't pass any useful information about enabled authentication methods and while monitoring the AuditLogs table does give some information, this still lacks detail such as phone numbers, and won't allow us to hunt retrospectively through existing data.
Scraping the Data
The Graph API contains an endpoint that returns exactly the information that we're looking for here, so I've built a simple logic app which
- Pulls a full list of users in the tenant
- Queries the API for auth methods for each user
- Pushes the output into a custom Sentinel table
You can download a template to deploy this app yourself here: https://github.com/mrrothe/AzLogicApps/blob/main/entra_auth_audit-template.json
You will need to give the app permissions to access the data from your tenant, one way it to assign a system-managed-identity to the logic app, however this requires assigning entra ID roles with a lot of privilege. A better alternative is to create a new app registration and configure the app to authenticate to the Graph API using OAuth. The app registration needs the following graph API permissions:
- UserAuthenticationMethod.Read.All
- User.Read.All
Using the Data
Once the data is in Sentinel, it's straightforward to parse out the various fields from the JSON
To summarize the number of users with each type of auth method
userAuthMethods_CL
| extend
username=url_decode(extract(@"users\('(.+)'\)/authentication/", 1, _odata_context_s))
| summarize arg_max(TimeGenerated, *) by username
| mv-expand todynamic(value_s)
| extend method=tostring(value_s["@odata.type"])
| summarize count() by method
To lookup the country name for each registered phone number
let dialingCodes=externaldata(callingCode:string, countryName:string)
[@'https://raw.githubusercontent.com/karani-gk/Country-Calling-Codes/refs/heads/main/Countries.csv'] with(format="csv");
userAuthMethods_CL
| extend
username=url_decode(extract(@"users\('(.+)'\)/authentication/", 1, _odata_context_s))
| summarize arg_max(TimeGenerated, *) by username
| mv-expand todynamic(value_s)
| extend method=tostring(value_s["@odata.type"])
| where method == "#microsoft.graph.phoneAuthenticationMethod"
| extend phoneNumber_ = tostring(value_s.phoneNumber)
| extend countryCode=substring(phoneNumber_, 0, 3)
| lookup (dialingCodes) on $left.countryCode==$right.callingCode
To list the models of Fido2 security keys in use
userAuthMethods_CL
| extend
username=url_decode(extract(@"users\('(.+)'\)/authentication/", 1, _odata_context_s))
| summarize arg_max(TimeGenerated, *) by username
| mv-expand todynamic(value_s)
| extend method=tostring(value_s["@odata.type"])
| where method =='#microsoft.graph.fido2AuthenticationMethod'
| summarize count() by Model=tostring(value_s.model)