If you invoke some Azure or Graph Api call and see an error similar to this one (RoleAssignmentRequestAcrsValidationFailed) π, you should read this post π.

The error code RoleAssignmentRequestAcrsValidationFailed, followed by the failed claim claims=%7B%22access_token%22%3A%7B%22acrs%22%3A%7B%22essential%22%3Atrue%2C%20%22value%22%3A%22c1%22%7D%7D%7D (unescaped it looks like this claims={"access_token":{"acrs":{"essential":true, "value":"c1"}}}) means the method used for authenticating the current session isnβt sufficient to run the selected action.
For me, it was when I activated the PIM-protected Azure Directory Role that required FIDO2 (Passkey) authentication strength (enforced via Azure Conditional Access Policy).

Anyway, if something like this happens, you need to reauthenticate using the failed claim and repeat the action.
How to authenticate to Azure/Graph with PowerShell using a specific claim
How to authenticate using a specific claim slightly differs based on the accessed service.
Making an Azure connection
We need to extract the claim from the Invoke-AzRestMethod response body
Unescape the HTML string and convert it to base64
Use such a claim in the Claims parameter of the Connect-AzAccount to fulfill interactively auth requirements
$response = Invoke-AzRestMethod -Uri $restUri -Method PUT -Payload ($requestBody | ConvertTo-Json -Depth 10)
$exception = ($response.Content | ConvertFrom-Json).error.message
if ($exception -and $exception -match 'claims=([^"]+)') {
$claimsChallenge = $matches[1]
$claimsChallenge = [System.Uri]::UnescapeDataString($claimsChallenge)
Write-Verbose "Claims challenge detected: $claimsChallenge"
$bytes = [System.Text.Encoding]::ASCII.GetBytes($claimsChallenge)
$encodedClaimsChallenge = [Convert]::ToBase64String($bytes)
$null = Connect-AzAccount -Claims $encodedClaimsChallenge -Tenant (Get-AzContext).tenant.id -ErrorAction Stop -WarningAction SilentlyContinue
}
Write-Verbose "Retrying activation request..."
$response = Invoke-AzRestMethod -Uri $restUri -Method PUT -Payload ($requestBody | ConvertTo-Json -Depth 10)
Making a Graph connection (with default scopes)
Itβs very similar to making just an Azure connection, with a slight difference. The claim is saved in the error message instead of the response body (when using Invoke-MgGraphRequest)
And you need to reauthenticate to the Graph api after new access token retrieval
try {
$uri = "v1.0/roleManagement/directory/roleAssignmentScheduleRequests"
$response = Invoke-MgGraphRequest -Method POST -Uri $uri -Body $body -ErrorAction Stop
} catch {
$exception = $_.ErrorDetails.Message
if ($exception -and $exception -match 'claims=([^"]+)') {
$claimsChallenge = $matches[1]
$claimsChallenge = [System.Uri]::UnescapeDataString($claimsChallenge)
Write-Verbose "Claims challenge detected: $claimsChallenge"
$bytes = [System.Text.Encoding]::ASCII.GetBytes($claimsChallenge)
$encodedClaimsChallenge = [Convert]::ToBase64String($bytes)
$null = Connect-AzAccount -Claims $encodedClaimsChallenge -Tenant (Get-AzContext).tenant.id -ErrorAction Stop -WarningAction SilentlyContinue
}
$secureToken = (Get-AzAccessToken -ResourceTypeName MSGraph).Token
Connect-MgGraph -AccessToken $secureToken -NoWelcome
Write-Verbose "Retrying activation request..."
$response = Invoke-MgGraphRequest -Method POST -Uri $uri -Body $body -ErrorAction Stop
}
Making a Graph connection (with specific scopes)
It again starts with the Azure reauthentication
But because we have to specify custom Graph api scopes, we cannot use Connect-AzAccount to retrieve the access token
Instead, we will need:
A list of required Graph Api scopes ($scope)
- (
RoleAssignmentSchedule.ReadWrite.Directory to activate the PIM role)
To import the Microsoft.Identity.Client.dll library to be able to create a web request with the required scopes using [Microsoft.Identity.Client.PublicClientApplicationBuilder]
To have a claim JSON string ($claim)
So, to authenticate using the specified claim, use Get-PIMGraphTokenWithClaim function from my AzurePIMStuff module.
$claimsChallenge = "<claim>"
$secureToken = Get-PIMGraphTokenWithClaim -Claim $claimsChallenge
Connect-MgGraph -AccessToken $secureToken -NoWelcome
Or if you need a different kind of scopes, etc, create the request using the following code
$clientId = "14d82eec-204b-4c2f-b7e8-296a70dab67e"
$scope = "<list of required scopes>"
$scope += "RoleAssignmentSchedule.ReadWrite.Directory"
$claim = '{"access_token":{"acrs":{"essential":true, "value":"c1"}}}'
if (!("Microsoft.Identity.Client.PublicClientApplicationBuilder" -as [Type])) {
$modulePath = (Get-Module Az.Accounts | Sort-Object Version -Descending | Select-Object -First 1).ModuleBase
if (!$modulePath) {
$modulePath = (Get-Module Az.Accounts -ListAvailable | Sort-Object Version -Descending | Select-Object -First 1).ModuleBase
}
if ($modulePath) {
$dllPath = Get-ChildItem -Path $modulePath -Filter "Microsoft.Identity.Client.dll" -Recurse -ErrorAction SilentlyContinue | Select-Object -First 1
if ($dllPath) {
Add-Type -Path $dllPath.FullName
} else {
throw "Microsoft.Identity.Client.dll not found in Az.Accounts module."
}
} else {
throw "No Az.Accounts module found. Please install the Az PowerShell module."
}
}
$pca = [Microsoft.Identity.Client.PublicClientApplicationBuilder]::Create($clientId).WithAuthority("https://login.microsoftonline.com/$tenantId").WithRedirectUri("http://localhost").Build()
$request = $pca.AcquireTokenInteractive($scope)
$request = $request.WithClaims($claim)
$token = $request.ExecuteAsync().GetAwaiter().GetResult().AccessToken
$secureToken = ConvertTo-SecureString $token -AsPlainText -Force
Connect-MgGraph -AccessToken $secureToken -NoWelcome
How to retrieve the claim
How to retrieve the claim of the failed request depends on the used command
Invoke-AzRestMethod
Invoke-AzRestMethod stores the claim challenge in the response body
$response = Invoke-AzRestMethod -Uri $restUri -Method PUT -Payload ($requestBody | ConvertTo-Json -Depth 10)
$exception = ($response.Content | ConvertFrom-Json).error.message
if ($exception -and $exception -match 'claims=([^"]+)') {
$claimsChallenge = $matches[1]
$claimsChallenge = [System.Uri]::UnescapeDataString($claimsChallenge)
$claimsChallenge
}
Invoke-MgGraphRequest
Invoke-MgGraphRequest stores the claim challenge in the thrown error object
try {
$uri = "v1.0/roleManagement/directory/roleAssignmentScheduleRequests"
$response = Invoke-MgGraphRequest -Method POST -Uri $uri -Body $body -ErrorAction Stop
} catch {
$exception = $_.ErrorDetails.Message
if ($exception -and $exception -match 'claims=([^"]+)') {
$claimsChallenge = $matches[1]
$claimsChallenge = [System.Uri]::UnescapeDataString($claimsChallenge)
$claimsChallenge
}
}
How to translate the authentication context ID to DisplayName
In my examples, I use c1 as an authentication context ID.
In the Azure portal, the Authentication contexts menu doesnβt show the ID. But you can use the Developer Tools browser feature to get it. And as you can see, it belongs to the PIM_Elevation_PassKey context in my case.

You can, of course, use the Graph api beta/identity/conditionalAccess/authenticationContextClassReferences to retrieve authentication contexts as well.
Invoke-MgGraphRequest -Uri "beta/identity/conditionalAccess/authenticationContextClassReferences"

Real-world examples
Activating the Azure Resource PIM role (IAM)
Activating the Azure Directory PIM role