Get MFAStatus with API

Brass Contributor

Hi,

 

I'm trying to get a report for the MFA status for all my tenant users.

# Replace the values in the following variables with your own
$clientId = "your_client_id_here"
$clientSecret = "your_client_secret_here"
$tenantId = "your_tenant_id_here"

# Authenticate using Microsoft Graph API
$tokenBody = @{
    Grant_Type    = "client_credentials"
    Scope         = "https://graph.microsoft.com/.default"
    Client_Id     = $clientId
    Client_Secret = $clientSecret
}
$tokenResponse = Invoke-RestMethod -Method POST -Uri "https://login.microsoftonline.com/$tenantId/oauth2/v2.0/token" -Body $tokenBody
$accessToken = $tokenResponse.access_token

# Retrieve all users in the tenant
$users = Invoke-RestMethod -Method GET -Uri "https://graph.microsoft.com/v1.0/users" -Headers @{Authorization = "Bearer $accessToken"}

# Loop through each user and retrieve their MFA status
foreach ($user in $users.value) {
    $userId = $user.id
    $mfaStatus = Invoke-RestMethod -Method GET -Uri "https://graph.microsoft.com/v1.0/users/$userId/authentication/strongAuthenticationMethods" -Headers @{Authorization = "Bearer $accessToken"}
    $mfaEnabled = $mfaStatus.value | Where-Object {$_.state -eq "enabled"}

    Write-Output "$($user.displayName) - MFA Enabled: $($mfaEnabled -ne $null)"
}

 

I got this script but I'm always getting an error when I'm trying to execute it ... error is : 

Line |
  17 |  $users = Invoke-RestMethod -Method GET -Uri "https://graph.microsoft. …
     |           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     | {"error":{"code":"Authorization_RequestDenied","message":"Insufficient privileges to complete the
     | operation.","innerError":{"date":"2023-03-28T00:21:46","request-id":"d929a2d8-ca16-44b4-af0b-4d514c15ea78","client-request-id":"d929a2d8-ca16-44b4-af0b-4d514c15ea78"}}}

 

In my API permission, I've double check to be sur all permission are ok :

  • UserAuthenticationMethod.Read.All or UserAuthenticationMethod.ReadWrite.All (for Microsoft Graph API v1.0) OR AuthenticationMethod.Read.All or AuthenticationMethod.ReadWrite.All (for Microsoft Graph API beta)
  • User.Read.All or User.ReadWrite.All (for Microsoft Graph API v1.0) OR Directory.Read.All or Directory.ReadWrite.All (for Microsoft Graph API beta)

I've check again my clientID-clientSecret-TenanID and seems to be good : How to be sure this is OK? Any log in AzureAD to check if at least my script is able to authenticate?

 

Thanks in advance!

10 Replies
Not sure where you got this code, but there is no /users/$userId/authentication/strongAuthenticationMethods endpoint. The correct one should be /users/$userId/authentication/Methods, or if you are looking for the report instead, /reports/credentialUserRegistrationDetails.

The token part looks fine, but make sure the permissions are correctly reflected in the obtained access token. You can parse it with tools such as jwt.ms.
To be honnest, this code is from ChatGPT! :) You understand that I'm not a professionnal in powershell!! :)

I tried you modification and same error at same line .... like if I have an authorization problem.

Can you give me a cue to check if my authentication and permission are good with the API?

THanks
ChatCPT is smoking something good, again... :)

Let's start with the basics, did you create an app registration and grant the permissions? In other words, are you populating the variables with the correct values?

$clientId = "your_client_id_here"
$clientSecret = "your_client_secret_here"
$tenantId = "your_tenant_id_here"

Double- and triple-check if everything is OK there. Once you run the following:

$accessToken = $tokenResponse.access_token

you can do another check - decode the token and make sure the required permissions are correctly reflected therein. To do so, copy the token and head over to https://jwt.ms to decode it. You can copy it directly via:

$accessToken | clip
when I past nothing appear ... does it means I have a permission problem or clientID secret?!?! I've tripple-checked but ... maybe something wrong in my app registration?!
Might be. Which permissions did you add, and did you grant admin consent?
yes I grand admin consent ... and I grand all permission specified above :

UserAuthenticationMethod.Read.All or UserAuthenticationMethod.ReadWrite.All (for Microsoft Graph API v1.0) OR AuthenticationMethod.Read.All or AuthenticationMethod.ReadWrite.All (for Microsoft Graph API beta)
User.Read.All or User.ReadWrite.All (for Microsoft Graph API v1.0) OR Directory.Read.All or Directory.ReadWrite.All (for Microsoft Graph API beta)

Can I check some log in Azure Admin Center or other way to check if my request are accepted? To be sur client ID password are ok?

Is this normal I got nothing when I past in the token decoder?

Thanks again
You can check the $tokenResponse variable, should contain the server response.

Or you know what, just use this code instead:

 

#Variables to configure
$tenantID = "tenant.onmicrosoft.com" #your tenantID or tenant root domain
$appID = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" #the GUID of your app
$client_secret = "verylongsecurestring" #client secret for the app

#Prepare token request
$url = 'https://login.microsoftonline.com/' + $tenantId + '/oauth2/v2.0/token'

$body = @{
    grant_type = "client_credentials"
    client_id = $appID
    client_secret = $client_secret
    scope = "https://graph.microsoft.com/.default"
}

#Obtain the token
Write-Verbose "Authenticating..."
try { $tokenRequest = Invoke-WebRequest -Method Post -Uri $url -ContentType "application/x-www-form-urlencoded" -Body $body -UseBasicParsing -ErrorAction Stop }
catch { Write-Host "Unable to obtain access token, aborting..."; return }

$token = ($tokenRequest.Content | ConvertFrom-Json).access_token

$authHeader = @{
   'Content-Type'='application\json'
   'Authorization'="Bearer $token"
}
#endregion Authentication

It should actually throw an error if anything goes wrong. To check the result, use either $token or $authHeader.

@Vasil Michev 

Ok! My token variable have something in it.

Then I assume that this part is working ... After that, I retry this part of the code 

$tokenResponse = Invoke-RestMethod -Method POST -Uri "https://login.microsoftonline.com/$tenantId/oauth2/v2.0/token" -Body $tokenBody
$accessToken = $tokenResponse.access_token

# Retrieve all users in the tenant
$users = Invoke-RestMethod -Method GET -Uri "https://graph.microsoft.com/v1.0/users" -Headers @{Authorization = "Bearer $accessToken"}

# Loop through each user and retrieve their MFA status
foreach ($user in $users.value) {
    $userId = $user.id
    $mfaStatus = Invoke-RestMethod -Method GET -Uri "https://graph.microsoft.com/v1.0/users/$userId/authentication/Methods" -Headers @{Authorization = "Bearer $accessToken"}
    $mfaEnabled = $mfaStatus.value | Where-Object {$_.state -eq "enabled"}

    Write-Output "$($user.displayName) - MFA Enabled: $($mfaEnabled -ne $null)"
}

and I got this error : 

Invoke-RestMethod : Le serveur distant a retourné une erreur : (403) Interdit.
Au caractère Ligne:5 : 10
+ $users = Invoke-RestMethod -Method GET -Uri "https://graph.microsoft. ...
+          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation : (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-RestMethod], WebException
    + FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeRestMethodCommand

Thanks again to help me, that's really appreciate ... by the way, if you have another way to achieve my goal, I'm open! :)

 

"Something" doesn't mean it has the right stuff in it :) Use this:

$token | clip

then paste it over at jwt.ms to parse the token.

As for another way, the easiest one is to use the Graph explorer tool (https://developer.microsoft.com/en-us/graph/graph-explorer) and in particular this report: https://graph.microsoft.com/beta/reports/credentialUserRegistrationDetails
You will likely have to consent to additional permissions, but that's all done in-tool, via the Modify Permissions tab.