When using Client Credentials flow I usually have some PowerShell like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
$authBody = @{ "tenant" = "$tenantId" "client_id" = "$appId" "scope" = "xxxx" "grant_type" = "client_credentials" "client_secret" = $clientSecret } Write-Verbose "Getting Azure AD token" $aadtoken = Invoke-RestMethod -Method POST -Uri "https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/token" -Body $authBody -ContentType "application/x-www-form-urlencoded" $header = @{ "Authorization" = "Bearer " + $aadtoken.access_token } |
And I am always stumped with the “scope” part.
For other flows I can put a space separated list of scopes – like this:
1 2 3 4 5 6 7 8 9 10 |
$authBody = @{ "tenant" = "$tenantId" "client_id" = "$appId" "scope" = "User.Read openid" } Write-Verbose "Getting Azure AD token" $aadtoken = Invoke-RestMethod -Method POST -Uri "https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/devicecode" -Body $authBody # There's more stuff to do... |
But that doesn’t work with Client Credentials. You get an error:
Client credential flows must have a scope value with /.default suffixed to the resource identifier (application ID URI).
The solution is simple, just that I keep forgetting. If I am authenticating against a particular App Registration (e.g. an App Registration is authorized to access a Logic App or Azure Function; and I use other App Registrations that are authorized to authenticate against the first App Registration) then I must use that App Registration Id with the “/.default
“.
1 2 3 4 5 6 7 |
$authBody = @{ "tenant" = "$tenantId" "client_id" = "$appId" "scope" = "${appRegId}/.default" "grant_type" = "client_credentials" "client_secret" = $clientSecret } |
But what if I am authenticating against Graph itself? Or some other resource? Then I must use a different URL like in this doc. For Graph it would be:
1 2 3 4 5 6 7 |
$authBody = @{ "tenant" = "$tenantId" "client_id" = "$appId" "scope" = "https://graph.microsoft.com/.default" "grant_type" = "client_credentials" "client_secret" = $clientSecret } |
But it would be, say, the following for Azure Key Vault:
1 2 3 4 5 6 7 |
$authBody = @{ "tenant" = "$tenantId" "client_id" = "$appId" "scope" = "https://vault.azure.net/.default" "grant_type" = "client_credentials" "client_secret" = $clientSecret } |
I could also request for specific scopes than just default to whatever is already admin consented to. For that I’ll have to change the scope line like this: https://graph.microsoft.com/User.Read
Good to remember!