delucaezequiel
delucaezequiel

Reputation: 649

Azure ARM access token wrong audience

I am trying to get information from Azure ARM about a specific resource. Currently I was able to get the access token from oAuth2 using the following article https://learn.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-client-creds-grant-flow, however when I try to use it I get the following error:

Invoke-RestMethod : {"error":{"code":"InvalidAuthenticationTokenAudience","message":"The access token has been obtained for wrong audience or resource '00000002-0000-0000-c000-000000000000'. It should exactly match with one of the allowed audiences 'https://management.core.windows.net/','https://management.core.windows.net','https://management.azure.com/','https://management.azure.com'."}}

Below is the code that I am attempting to run:

param
(
    [Parameter(Mandatory=$true)][System.String] $ResourceId,
    [Parameter(Mandatory=$true)][System.String] $APIVersion
)
function Urls-Authorization
{
    [OutputType([System.Uri])]
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory=$false)] [System.Guid] $TenantId
    )
    return [System.Uri]::new("https://login.microsoftonline.com/$($TenantId)/oauth2/token")
}
function Urls-Management
{
    [OutputType([System.Uri])]
    [CmdletBinding()]
    param()
    return [System.Uri]::new("https://management.azure.com")
}
function Urls-Resource
{
    [OutputType([System.Uri])]
    [CmdletBinding()]
    param()
    return [System.Uri]::new((Urls-Management), "$($ResourceId)?api-version=$($APIVersion)")
}
function ConvertFrom-SecureString
{
    [OutputType([System.String])]
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory=$true)] [SecureString] $SecureString
    )
    return [System.Runtime.InteropServices.Marshal]::PtrToStringAuto([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($SecureString))
}
function New-AccessToken
{
    [OutputType([System.String])]
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory=$true)] [System.Uri] $ResourceUrl,
        [Parameter(Mandatory=$true)] [System.Guid] $TenantId,
        [Parameter(Mandatory=$true)] [System.Guid] $ClientId,
        [Parameter(Mandatory=$true)] [SecureString] $Password,
        [Parameter(Mandatory=$false)] [System.DateTime] $Expires = ($(Get-Date).AddMinutes(60).ToUniversalTime()),
        [Parameter(Mandatory=$false)] [System.DateTime] $NotBefore = ([System.DateTime]::UtcNow)
    )
    #https://learn.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-client-creds-grant-flow
    $AuthorityUrl = Urls-Authorization -TenantId $TenantId
    try
    {
        $RequestBody = [ordered]@{
                                        'grant_type'    = 'client_credentials';
                                        'client_id'     = $ClientId;
                                        'scope   '      = $ResourceUrl;
                                        'client_secret' = "$(ConvertFrom-SecureString -SecureString $Password)";
                                    }
        $Response = Invoke-RestMethod -Uri $AuthorityUrl -Method Post -Body $RequestBody -ErrorAction Stop
    }
    catch
    {
        Write-Log -Type ERROR -Message "Token could not be acquired"
        throw
    }
    return $Response.access_token
}
function Headers
{
    [OutputType([Hashtable])]
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory=$false)] [System.String] $ContentType,
        [Parameter(Mandatory=$true)] [System.String] $AccessToken
    )
    $Headers = @{}
    if ($ContentType -ne $null) {$Headers.Add("Content-Type", $ContentType)}
    $Headers.Add("Authorization", "Bearer $($AccessToken)")
    return $Headers
}
Invoke-RestMethod -Method Get -Uri (Urls-Resource) -Headers (Headers -ContentType "application/json" -AccessToken (New-AccessToken -ResourceUrl (Urls-Management) -TenantId $env:AZURE_TENANT_ID -ClientId $env:AZURE_CLIENT_ID -Password (ConvertTo-SecureString -String $env:AZURE_CLIENT_SECRET -AsPlainText -Force))) | ConvertFrom-Json

As I mentioned before, I get the access token with New-AccessToken function. I even attempt the following and I get the same error:

  1. Change the ResourceUrl parameter in New-AccessToken from Urls-Management to Urls-Resource
  2. Add /.default to Urls-Management address
  3. Add change Urls-Management from https://management.azure.com to https://management.azure.com/

Do you know what can be happening? Any idea about how should I face this error?

Upvotes: 0

Views: 1096

Answers (1)

Vivere
Vivere

Reputation: 2280

I think the error is providing some good insights. I would change New-AccessToken to:

function New-AccessToken
{
    [OutputType([System.String])]
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory=$true)] [System.Uri] $ResourceUrl,
        [Parameter(Mandatory=$true)] [System.Guid] $TenantId,
        [Parameter(Mandatory=$true)] [System.Guid] $ClientId,
        [Parameter(Mandatory=$true)] [SecureString] $Password,
        [Parameter(Mandatory=$false)] [System.DateTime] $Expires = ($(Get-Date).AddMinutes(60).ToUniversalTime()),
        [Parameter(Mandatory=$false)] [System.DateTime] $NotBefore = ([System.DateTime]::UtcNow)
    )
    #https://learn.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-client-creds-grant-flow
    $AuthorityUrl = Urls-Authorization -TenantId $TenantId
    try
    {
        $RequestBody = [ordered]@{
                                        'grant_type'    = 'client_credentials';
                                        'client_id'     = $ClientId;
                                        'scope   '      = $ResourceUrl;
                                        'client_secret' = "$(ConvertFrom-SecureString -SecureString $Password)";
                                        'resource'      = 'https://management.azure.com/';
                                    }
        $Response = Invoke-RestMethod -Uri $AuthorityUrl -Method Post -Body $RequestBody -ErrorAction Stop
    }
    catch
    {
        Write-Log -Type ERROR -Message "Token could not be acquired"
        throw
    }
    return $Response.access_token
}

Basically, add 'resource' = 'https://management.azure.com/'; in the $RequestBody. I have a couple of Azure Rest calls in this repository, but it is in groovy (though this shouldn't be an issue).

Moreover, have you considered using the Az module? It implements all the rest logic behind the scenes and provides ready to use cmdlets that communicate with the Azure Resources.

Upvotes: 1

Related Questions