AAD Password Grant PowerShell class for use with Business Central

After my post yesterday, I have a quick way to register Business Central APIs for AAD authentication. That’s working well and I can use Postman to test APIs but I need to be able to call APIs in PowerShell.

After a lot of time spent reading Microsoft articles on OAuth 2.0 like this and some articles on Graph, I Googled around the problem and found a solution on reddit/r/PowerShell.

The result is a class that you can use to get the authentication token using a password grant request.

It’s on my GitHub and below :-

#  References.
#  https://docs.microsoft.com/en-us/azure/active-directory/develop/v1-protocols-oauth-code
#  https://www.reddit.com/r/PowerShell/comments/9clts3/powershell_automation_with_oauth2/
#

#
# Class to get an OAuth 2.0 authentication token from BC using a Password Grant.
#
class AADPasswordGrant {
    [string]$token                  #Token that we need to get to Authenticate with later
    [string]$tenantId               #BC Tenant ID
    [string]$clientId               #The ApplicationId that was registed for BC in AAD.   
    [string]$username               #BC username
    [string]$password               #BC Password 
    [System.Security.SecureString]$securePasswordStr #BC Password we will convert to a secure string later
    [string]$securePasswordBStr     #BSTR version of the secure password  
    [string]$clientSecret           #Key that was generated when registring BC in AAD
    [string]$grantType      = "password"  #This must be password so we are not challenged or have to use a form.  
    [string]$callbackUrl    = "https://businesscentral.dynamics.com/"              #The same callback registered for BC in AAD
    [string]$accessTokenUrl = "https://login.windows.net/{tenantId}/oauth2/token"  #Url to request the token from
    [string]$resourceUrl    = "https://api.businesscentral.dynamics.com"           #The resource we want to talk to
    [string]$scopeUrl       = "https://api.businesscentral.dynamics.com"           #The resource we want to talk to
   
    AADPasswordGrant([string]$tenandId, [string]$clientId, [string]$clientSecret, [string]$userName, [string]$password) {
        $this.tenantId     = $tenandId
        $this.clientId     = $clientId
        $this.clientSecret = $clientSecret
        $this.userName     = $userName
        $this.securePasswordStr  = (ConvertTo-SecureString -String $password -AsPlainText -Force)
        $this.securePasswordBStr = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($this.securePasswordStr))
        $this.accessTokenUrl     = $this.accessTokenUrl.Replace('{tenantId}', $tenandId)
    }

    [void]TryGetAuthorisationToken () {
        $body = @{
            grant_type    = $this.grantType
            username      = $this.userName
            password      = $this.securePasswordBStr
            client_id     = $this.clientId
            client_secret = $this.clientSecret
            scope         = $this.scopeUrl
            redirect_uri  = $this.callbackUrl
            resource      = $this.resourceUrl
        }
        $authResult = Invoke-RestMethod -Method Post -Uri $this.accessTokenUrl -Body $body
        $this.token =$authResult.access_token
    }
}

You can test the class with the code below, using a BC tenant of your own. The values here are, of course, fudged and provided for example only.

#
# Test the Class Here
#
$tenantId     = 'fa75f4db-5231-6eb5-9ec4-ddb74cd648e'      #Your BC tennantID
$clientId     = 'b94639c8-553a-6a7d-ad54-d36463d3729'      #The id that BC is registered with in AAD  
$clientSecret = '5P00Zg29GGg6rnllUg0Fad9SSFC8lWVGJyOnsF0=' #The secret key that was created when registering BC for AAD Auth
$username     = 'bcuser@domain.onmicrosoft.com'            #Username for the passowrd grant
$password     = 'user.password@1234'                       #Password in plain text

[AADPasswordGrant]$aadPasswordGrant = [AADPasswordGrant]::new($tenantId, $clientId, $clientSecret, $username, $password)
$aadPasswordGrant.TryGetAuthorisationToken()

$companiesUrl = "https://api.businesscentral.dynamics.com/v1.0/api/microsoft/automation/beta/companies"
$requestHeaders = @{ 'Authorization' = 'Bearer ' + $aadPasswordGrant.token }
$result = Invoke-RestMethod -Uri $usersUrl -Headers $requestHeaders -Method Get
$result.value | Out-GridView

Hope it helps you out

/cal;