Api/ElementsCommon.ps1

function ErrorDetail {
    [CmdletBinding()]
    Param (
        [Parameter(Position = 0, ValueFromPipelineByPropertyName = $true, Mandatory = $true)]
        [System.Management.Automation.ErrorRecord]
        ${Error}
    )

    Process {
        'Calling method: ErrorDetail' | Write-Debug
        if ($Error.ErrorDetails.Message) {
            Write-Host $Error.ErrorDetails.Message -ForegroundColor Red
        }
        Write-Error $Error.Exception
    }
}

function Get-IdentityServiceToken {
    [CmdletBinding()]
    [OutputType([string])]
    Param(
        [Parameter(Mandatory)]
        [string]$IdentityServiceUri,
        [Parameter(Mandatory)]
        [string]$ClientId,
        [Parameter(Mandatory)]
        [Alias("Certificate", "Cert")]
        [System.Security.Cryptography.X509Certificates.X509Certificate2]$SigningCertificate
    )
    PROCESS {
        'Calling method: Get-IdentityServiceToken' | Write-Debug
        $encodedThumbprint = ConvertTo-Base64UrlEncodedString -Bytes $SigningCertificate.GetCertHash()
        $headerTable = [ordered]@{typ = "JWT"; alg = "RS256"; kid = $encodedThumbprint }
        $header = $headerTable | ConvertTo-Json -Compress | ConvertTo-Base64UrlEncodedString
        $now = Get-Date
        $currentEpochTime = Convert-DateTimeToEpoch -DateTime $now
        $notBefore = $currentEpochTime
        $futureEpochTime = Convert-DateTimeToEpoch -DateTime ($now.AddHours(1))
        $payloadTable = [ordered]@{sub = $ClientId; jti = ([System.Guid]::NewGuid()).ToString(); iss = $ClientId; aud = $IdentityServiceUri.TrimEnd('/') + "/connect/token"; nbf = $notBefore; exp = $futureEpochTime; iat = $currentEpochTime }
        $payload = $payloadTable | ConvertTo-Json -Compress | ConvertTo-Base64UrlEncodedString
        $jwtPlainText = "{0}.{1}" -f $header, $payload
        $jwtSig = New-JwtRsaSignature -JsonWebToken $jwtPlainText -SigningCertificate $SigningCertificate
        $ClientAssertion = "{0}.{1}" -f $jwtPlainText, $jwtSig
        $RequestUri = $IdentityServiceUri.TrimEnd('/') + "/connect/token"
        $Body = @{
            grant_type            = 'client_credentials'
            client_assertion_type = 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer'
            client_assertion      = $ClientAssertion
        }
        $Response = Invoke-WebRequest -Uri $RequestUri -Method 'POST' -UseBasicParsing -Body $Body -ErrorAction Stop
        return (ConvertFrom-Json $Response).access_token
    }
}
function Get-IdentityServiceTokenBySecret {
    [CmdletBinding()]
    [OutputType([string])]
    Param(
        [Parameter(Mandatory)]
        [string]$IdentityServiceUri,
        [Parameter(Mandatory)]
        [string]$ClientId,
        [Parameter(Mandatory)]
        [string]$ClientSecret
    )
    PROCESS {
        'Calling method: Get-IdentityServiceTokenBySecret' | Write-Debug
        $RequestUri = $IdentityServiceUri.TrimEnd('/') + "/connect/token"
        $Body = @{
            grant_type            = 'client_credentials'
            client_id      = $ClientId
            client_secret  = $ClientSecret
        }
        $Response = Invoke-WebRequest -Uri $RequestUri -Method 'POST' -UseBasicParsing -Body $Body -ErrorAction Stop
        return (ConvertFrom-Json $Response).access_token
    }
}
function New-JwtRsaSignature {
    [CmdletBinding()]
    [OutputType([string])]
    Param(
        [System.Security.Cryptography.X509Certificates.X509Certificate2]$SigningCertificate,
        [String]$JsonWebToken
    )
    PROCESS {
        'Calling method: New-JwtRsaSignature' | Write-Debug
        $rsa = ([System.Security.Cryptography.X509Certificates.RSACertificateExtensions]::GetRSAPrivateKey($SigningCertificate))
        if ($null -eq $rsa) {
            # Requiring the private key to be present
            throw "There's no private key in the supplied certificate." 
        }
        [byte[]]$message = [System.Text.Encoding]::UTF8.GetBytes($JsonWebToken)
        $sigBytes = $rsa.SignData($message, [Security.Cryptography.HashAlgorithmName]::SHA256, [Security.Cryptography.RSASignaturePadding]::Pkcs1)
        return ConvertTo-Base64UrlEncodedString -Bytes $sigBytes
    }
}


function ConvertTo-Base64UrlEncodedString {
    [CmdletBinding()]
    [OutputType([string])]
    Param (
        [Parameter(Position = 0, ParameterSetName = "String", Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
        [string]$InputString,

        [Parameter(Position = 1, ParameterSetName = "Byte Array", Mandatory = $false, ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)]
        [byte[]]$Bytes
    )
    PROCESS {
        [string]$base64UrlEncodedString = ""

        if ($PSBoundParameters.ContainsKey("Bytes")) {
            $output = [Convert]::ToBase64String($Bytes)
            $output = $output.Split('=')[0] # Remove any trailing '='s
            $output = $output.Replace('+', '-') # 62nd char of encoding
            $output = $output.Replace('/', '_') # 63rd char of encoding
            $base64UrlEncodedString = $output

        }
        else {
            $encoder = [System.Text.UTF8Encoding]::new()
            [byte[]]$inputBytes = $encoder.GetBytes($InputString)
            $base64String = [Convert]::ToBase64String($inputBytes)
            [string]$base64UrlEncodedString = ""
            $base64UrlEncodedString = $base64String.Split('=')[0] # Remove any trailing '='s
            $base64UrlEncodedString = $base64UrlEncodedString.Replace('+', '-'); # 62nd char of encoding
            $base64UrlEncodedString = $base64UrlEncodedString.Replace('/', '_'); # 63rd char of encoding
        }
        return $base64UrlEncodedString
    }
}

function Convert-DateTimeToEpoch {
    [CmdletBinding()]
    [OutputType([System.Int64])]
    Param(
        [Parameter(Mandatory)]
        [DateTime]$DateTime
    )
    PROCESS {
        'Calling method: Convert-DateTimeToEpoch' | Write-Debug
        $dtut = $DateTime.ToUniversalTime()
        [TimeSpan]$ts = New-TimeSpan -Start  (Get-Date "01/01/1970") -End $dtut
        [Int64]$secondsSinceEpoch = [Math]::Floor($ts.TotalSeconds)
        return $secondsSinceEpoch
    }
}