Private/Get-AACAccessToken.ps1

function Get-AACAccessToken {
    <#
    .SYNOPSIS
        Returns a valid Azure Resource Manager access token for the current
        session, silently refreshing it first if it is expired or about to expire.
    .DESCRIPTION
        Every command that calls an Azure REST API goes through this function
        rather than reading $script:AACSession.AccessToken directly, so the
        refresh-on-expiry behavior is guaranteed to be applied consistently.
    #>

    [CmdletBinding()]
    [OutputType([string])]
    param()

    # No web-request progress bar over a Spectre display on a token refresh.
    $ProgressPreference = 'SilentlyContinue'

    if (-not $script:AACSession) {
        throw 'Not connected to Azure. Run Connect-AAC first.'
    }

    # Refresh a little early (2 minutes of slack) so a token that is valid right
    # now doesn't expire mid-flight during a slow REST call.
    if ((Get-Date) -lt $script:AACSession.ExpiresOn.AddSeconds(-120)) {
        return $script:AACSession.AccessToken
    }

    if (-not $script:AACSession.RefreshToken) {
        throw 'The Azure sign-in has expired and no refresh token is available. Run Connect-AAC again.'
    }

    $tokenEndpoint = "https://login.microsoftonline.com/$($script:AACSession.TenantId)/oauth2/v2.0/token"
    $body = @{
        grant_type    = 'refresh_token'
        refresh_token = $script:AACSession.RefreshToken
        client_id     = $script:AACSession.ClientId
        scope         = ($script:AACSession.Scope -join ' ')
    }

    try {
        $response = Invoke-RestMethod -Uri $tokenEndpoint -Method Post -Body $body -ContentType 'application/x-www-form-urlencoded' -ErrorAction Stop -Verbose:$false
    }
    catch {
        throw "Failed to refresh the Azure access token: $($_.Exception.Message). Run Connect-AAC again."
    }

    $script:AACSession.AccessToken = $response.access_token
    $refreshToken = Get-AACPropertyValue -InputObject $response -Name 'refresh_token'
    if ($refreshToken) {
        $script:AACSession.RefreshToken = $refreshToken
    }
    $script:AACSession.ExpiresOn = (Get-Date).AddSeconds([int]$response.expires_in)

    return $script:AACSession.AccessToken
}