Private/Invoke-NSPFortinetSupportApi.ps1

function Invoke-NSPFortinetSupportApi {
    # One call to the support portal's JSON API (https://support.fortinet.com/app/...), with the
    # token Connect-NSPFortinetSupport stored. Returns the response's 'result'. The portal answers
    # every call with { status: { returnCode, message }, result }; returnCode 1 is success.
    #
    # The token lasts 30 minutes. When the session came from the browser, a token close to expiry
    # (or one the portal refuses) is renewed from the still-open browser window first.
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)][string]$Path,
        [hashtable]$Body,
        [ValidateSet('Get', 'Post')][string]$Method = 'Post'
    )

    $session = $script:NSPFortinetSupportSession
    if (-not $session) { throw 'Not connected to the Fortinet support portal. Run Connect-NSPFortinetSupport first.' }
    $renew = {
        if (-not $session.Port) { throw 'The Fortinet support portal token has expired (they last 30 minutes). Copy a fresh request and run Connect-NSPFortinetSupport -FromClipboard again.' }
        Write-Verbose 'Renewing the Fortinet support portal token from the browser.'
        $fresh = Get-NSPFortinetBrowserToken -Port $session.Port
        $session.Token = $fresh.Token
        $session.Expires = $fresh.Expires
    }
    if ($session.Expires -lt (Get-Date).AddMinutes(3)) { & $renew }

    for ($attempt = 1; $attempt -le 2; $attempt++) {
        $params = @{
            Uri = "https://support.fortinet.com/app/$Path"; Method = $Method; UserAgent = $session.UserAgent; UseBasicParsing = $true; ErrorAction = 'Stop'
            Headers = @{ Authorization = $session.Token; Origin = 'https://support.fortinet.com'; Referer = 'https://support.fortinet.com/support/' }
        }
        if ($Method -eq 'Post') {
            $params.ContentType = 'application/json'
            $params.Body = if ($Body) { $Body | ConvertTo-Json -Compress } else { '{}' }
        }
        try {
            $response = Invoke-RestMethod @params
            break
        } catch {
            $status = $null
            if ($_.Exception.PSObject.Properties['Response'] -and $_.Exception.Response) { $status = [int]$_.Exception.Response.StatusCode }
            if ($status -ne 401 -or $attempt -eq 2) {
                if ($status -in 401, 403) { throw 'The Fortinet support portal refused the session. Run Connect-NSPFortinetSupport again.' }
                throw
            }
            & $renew
        }
    }
    if ($null -eq $response -or $response -is [string] -or -not $response.PSObject.Properties['status']) {
        throw 'The Fortinet support portal did not return API data, so the session has probably ended. Run Connect-NSPFortinetSupport again.'
    }
    if ($response.status.returnCode -ne 1) { throw "Fortinet support portal: $($response.status.message) (returnCode $($response.status.returnCode), $Path)." }
    $result = $response.result
    if ($result -and $result.PSObject.Properties['isValid'] -and -not $result.isValid) { throw "Fortinet support portal: $($result.message) ($Path)." }
    $result
}