Functions/Get-JwkCollection.ps1

function Get-JwkCollection {
    <#
    .SYNOPSIS
        Gets a collection of JSON Web Keys (JWKs) from a URI.
    .DESCRIPTION
        Gets a collection of JSON Web Keys (JWKs) from a well known openid configuration endpoint or URI containing only JSON Web Keys.
   .PARAMETER Uri
        Specifies the Uniform Resource Identifier (URI) containing the JSON Web Keys. Can be a well-known OpenID Connect discovery endpoint or a link containing the JWKs directly.
    .PARAMETER AsJson
        Tells the function to return compressed JSON as opposed to an object.
    .PARAMETER Formatted
        Returns JSON as formatted (indented) output. Must be used with -AsJson.
    .PARAMETER IncludeX509Certificate
        If the JWK in the collection contains a populated x5c property, this parameter converts this value into an System.Security.Cryptography.X509Certificates.X509Certificate2 object. This parameter does not work with the AsJson parameter.
    .EXAMPLE
        $oidcUrl = 'https://accounts.google.com/.well-known/openid-configuration'
        Get-JwkCollection -Uri $oidcUrl
 
        Gets JSON Web Keys from google's well known openid configuration endpoint as objects.
    .EXAMPLE
        $jwkUrl = 'https://login.windows.net/common/discovery/keys'
        Get-JwkCollection -Uri $jwkUrl -AsJson
 
        Gets JSON Web Keys from Microsoft's JWK endpoint as compressed JSON.
    .EXAMPLE
        $jwkUrl = 'https://login.windows.net/common/discovery/keys'
        Get-JwkCollection -Uri $jwkUrl -AsJson -Formatted
 
        Gets JSON Web Keys from Microsoft's JWK endpoint as formatted JSON.
    .EXAMPLE
        $jwkUrl = 'https://login.windows.net/common/discovery/keys'
        Get-JwkCollection -Uri $jwkUrl -IncludeX509Certificate
 
        Gets JSON Web Keys from Microsoft's JWK endpoint as objects including the x5c string values converted into X509Certificate2 objects.
    .EXAMPLE
        $jwt = "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6IjJ5Q3Zabms3azhXNjZ3UjJMWFI5V0Nzd2hBYyIsImtpZCI6IjJ5Q3Zabms3azhXNjZ3UjJMWFI5V0Nzd2hBYyJ9.eyJpYXQiOjE2MTgyNTAzODksIm5iZiI6MTYxODI1MDM4OSwiZXhwIjoxNjE4MjU1MTg5LCJzdWIiOiJ0b255In0.X-RZm-3Hto5U-8Q-Wp1ggqWTFPkO5-Cz9lzoKsH5-1RR9GOrGPuWn-bjIv1YJ46h5Bw-KpiX-dOS47TAq2A0BWdAwczLVA6pzha1WswkT_u3cO1_KSoOjD9qFLjCgk-ns7A48iXpNcOoPBFXgfx8G0rRK68sSnokJ7N2NH-YNUOjg3U7DNJ_-iz8WZ5dNlOvpDsTy0BHMX-lho18sUmakUNpadJr-oD7BXIp--Z57UERBFibppaoxseYRo3VfmhgHibTxP-39mcxU6sH9a99fEEt80hj4w6rZobRxZV-pFPS22B8TBAfVf8L9faMLaXmgV7xtQohqQZgL6oKdJzFPQ"
        $jwkUri = "https://app.mycompany.com/common/discovery/keys"
 
        Get-JwkCollection -Uri $jwkUri -AsJson | ForEach-Object { Test-JsonWebToken -JsonWebToken $jwt -HashAlgorithm SHA256 -JsonWebKey $_ -SkipExpirationCheck }
 
        Attempts to validate a JSON Web Token signature against a collection of JSON Web Keys in https://app.mycompany.com/common/discovery/keys.
    .INPUTS
        System.Uri
    .OUTPUTS
        System.String or System.Management.Automation.PSCustomObject
    .LINK
        https://tools.ietf.org/html/rfc7517
        Test-JsonWebToken
        New-JsonWebKeySet
 
#>

    [CmdletBinding()]
    [Alias('gjwkc')]
    [OutputType([String[]], [PSCustomObject[]])]
    Param
    (
        [Parameter(Mandatory = $true,
            ValueFromPipeline = $true,
            ValueFromPipelineByPropertyName = $true,
            Position = 0)][Alias('Url', 'u')][System.Uri]$Uri,

        [Parameter(Mandatory = $false,
            ValueFromPipelineByPropertyName = $false,
            Position = 1)][Alias('aj', 'j', 'json')][Switch]$AsJson,

        [Parameter(Mandatory = $false,
            ValueFromPipelineByPropertyName = $false,
            Position = 2)][Switch]$Formatted,

        [Parameter(Mandatory = $false,
            ValueFromPipelineByPropertyName = $false,
            Position = 3)][Alias('ic', 'IncludeCert', 'IncludeCertificate', 'cert')][Switch]$IncludeX509Certificate
    )
    PROCESS {
        $jwks = @()

        $response = $null

        try {
            $response = Invoke-RestMethod -Method Get -Uri $Uri -ErrorAction Stop
        }
        catch {
            Write-Error -Exception $_.Exception -ErrorAction Stop
        }

        if ($null -eq $response.keys) {
            if ($null -ne $response.jwks_uri) {
                try {
                    $jwkUri = [Uri]::new($response.jwks_uri)
                    $response = Invoke-RestMethod -Method Get -Uri $jwkUri -ErrorAction Stop
                }
                catch {
                    Write-Error -Exception $_.Exception -ErrorAction Stop
                }
            }
            else {
                $ArgumentException = New-Object -TypeName ArgumentException -ArgumentList ("Zero JSON Web Keys found at {0}" -f $Uri)
                Write-Error -Exception $ArgumentException -ErrorAction Stop
            }
        }

        foreach ($key in $response.keys) {
            if (($null -eq $key.kty) -or ($null -eq $key.n) -or ($null -eq $key.e)) {
                $ArgumentException = New-Object -TypeName ArgumentException -ArgumentList 'JSON Web Key schema validation failed. Ensure that a valid JWK is passed that contains the key type expressed as "kty", a public exponent as "e”, and modulus as "n" parameters per RFC 7517.'
                Write-Error -Exception $ArgumentException -ErrorAction Stop
            }
            else {
                if ($key.kty -eq "RSA") {
                    if ($PSBoundParameters.ContainsKey("AsJson")) {
                        if ($PSBoundParameters.ContainsKey("Formatted")) {
                            $jwks += ($key | ConvertTo-Json)
                        }
                        else {
                            $jwks += ($key | ConvertTo-Json -Compress)
                        }
                    }
                    else {
                        if ($PSBoundParameters.ContainsKey("IncludeX509Certificate")) {
                            if ($key.x5c) {
                                try {
                                    $certBytes = [Encoding]::ASCII.GetBytes($key.x5c)
                                    $x509Cert = [X509Certificate2]::new($certBytes)

                                    $keyHashTable = $key | Convert-PSObjectToHashTable
                                    $keyHashTable.Add("X509Certificate", $x509Cert)

                                    $augmentedKey = [PSCustomObject]$keyHashTable

                                    $jwks += $augmentedKey
                                }
                                catch {
                                    $jwks += $key
                                    $ArgumentException = [System.ArgumentException]::new("Unable to decode and deserialize x5c value.")
                                    Write-Error -Exception $ArgumentException -Category InvalidArgument -ErrorAction Continue
                                }
                            }
                            else {
                                $jwks += $key
                                Write-Warning -Message "x5c property does not exist on JSON Web Key."
                            }
                        }
                        else {
                            $jwks += $key
                        }
                    }
                }
                else {
                    $ArgumentException = New-Object -TypeName ArgumentException -ArgumentList 'Only RSA JSON Web Keys are supported at this time.'
                    Write-Error -Exception $ArgumentException -ErrorAction Stop
                }
            }
        }
        return $jwks
    }
}