Functions/ConvertFrom-EncodedJsonWebToken.ps1

function ConvertFrom-EncodedJsonWebToken {
    <#
    .SYNOPSIS
        Decodes a JSON Web Token.
    .DESCRIPTION
        Decodes a structurally valid JSON Web Token, specifically the header and the payload. This function does not validate a JSON Web Token, it merely decodes the token for purposes of viewing the claims in the header and payload segments.
    .PARAMETER JsonWebToken
        A signed JSON Web Token that is to be decoded.
    .EXAMPLE
        $jwt = "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCNTMjU2IjoiYklYZkw1RmREaWkzR2E3QmlEWndrcUQ5RE5JIiwiamt1IjoiaHR0cHM6Ly9xYS5pZHBzcnYuZm1nbG9iYWwuY29tL0FjY291bnQvSndrQ29sbGVjdGlvbiJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9naXZlbm5hbWUiOiJUb255IiwiaXNzIjoiaHR0cHM6Ly9xYS5pZHBzcnYuZm1nbG9iYWwuY29tIiwiaHR0cDovL3NjaGVtYXMueG1sc29hcC5vcmcvd3MvMjAwNS8wNS9pZGVudGl0eS9jbGFpbXMvc3VybmFtZSI6Ikd1aW1lbGxpIiwiaHR0cDovL3NjaGVtYXMubWljcm9zb2Z0LmNvbS93cy8yMDA4LzA2L2lkZW50aXR5L2NsYWltcy9hdXRoZW50aWNhdGlvbm1ldGhvZCI6Imh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vd3MvMjAwOC8wNi9pZGVudGl0eS9hdXRoZW50aWNhdGlvbm1ldGhvZC91bnNwZWNpZmllZCIsImh0dHA6Ly9zY2hlbWFzLnhtbHNvYXAub3JnL3dzLzIwMDUvMDUvaWRlbnRpdHkvY2xhaW1zL25hbWVpZGVudGlmaWVyIjoidG9ueS5ndWltZWxsaUB5YWhvby5jb20iLCJhdWQiOiJ1cm46Zm1nOmlkZW50aXR5c2VydmVyLy9xYSIsImh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vd3MvMjAwOC8wNi9pZGVudGl0eS9jbGFpbXMvYXV0aGVudGljYXRpb25pbnN0YW50IjoiNy81LzIwMTYgOToxNjozMyBBTSIsImh0dHA6Ly9zY2hlbWFzLnhtbHNvYXAub3JnL3dzLzIwMDUvMDUvaWRlbnRpdHkvY2xhaW1zL2VtYWlsYWRkcmVzcyI6InRvbnkuZ3VpbWVsbGlAeWFob28uY29tIiwiaWRlbnRpdHlwcm92aWRlciI6Imh0dHBzOi8vc3NvMi1zdGcuZm1nbG9iYWwuY29tIiwianRpIjoiNmI1ZGY4NjBGNDUxNDlEZjhERUZEM0NBZDFiRjA0ZjMiLCJpYXQiOjE0Njc3MjQ1OTksIm5iZiI6MTQ2NzcyNDU2OSwiZXhwIjoxNDY3NzI0ODk5fQ.RA3F6deEmZlq3RL2NDF07Nv5SrrY31Qfw1LfVoxNmdXlPcai1UH9ad80Oq66sMyJUMOtXMOA5RkwRZQ6L5gNFTAIApDjAlBFte1d5ziCSjBhxYzMZ-f_pFeBOmsMsSI5-BaAInYch7usZ2efEP8AdYKrZdkO5bgovL-7WD0Ts9gjVssWN_uhIcj-mM67xHartinKstPXUB4LUrJHHOoORCChs1eNS5xTq6q1xoPj-dYmlC56OaHKuzqUy9iByWssZ-0_DC6EfwIOqL4i43sNXPrSuDAisGPd_pxnYzqgdPAaWj9WTae7X1VHzvCiz21V7TGvDj37cwiXHj2dT93vAQ"
 
        ConvertFrom-EncodedJsonWebToken -JsonWebToken $jwt
 
        Decodes the JSON Web Token defined in the $jwt variable.
    .EXAMPLE
        $jwt = "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCNTMjU2IjoiYklYZkw1RmREaWkzR2E3QmlEWndrcUQ5RE5JIiwiamt1IjoiaHR0cHM6Ly9xYS5pZHBzcnYuZm1nbG9iYWwuY29tL0FjY291bnQvSndrQ29sbGVjdGlvbiJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9naXZlbm5hbWUiOiJUb255IiwiaXNzIjoiaHR0cHM6Ly9xYS5pZHBzcnYuZm1nbG9iYWwuY29tIiwiaHR0cDovL3NjaGVtYXMueG1sc29hcC5vcmcvd3MvMjAwNS8wNS9pZGVudGl0eS9jbGFpbXMvc3VybmFtZSI6Ikd1aW1lbGxpIiwiaHR0cDovL3NjaGVtYXMubWljcm9zb2Z0LmNvbS93cy8yMDA4LzA2L2lkZW50aXR5L2NsYWltcy9hdXRoZW50aWNhdGlvbm1ldGhvZCI6Imh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vd3MvMjAwOC8wNi9pZGVudGl0eS9hdXRoZW50aWNhdGlvbm1ldGhvZC91bnNwZWNpZmllZCIsImh0dHA6Ly9zY2hlbWFzLnhtbHNvYXAub3JnL3dzLzIwMDUvMDUvaWRlbnRpdHkvY2xhaW1zL25hbWVpZGVudGlmaWVyIjoidG9ueS5ndWltZWxsaUB5YWhvby5jb20iLCJhdWQiOiJ1cm46Zm1nOmlkZW50aXR5c2VydmVyLy9xYSIsImh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vd3MvMjAwOC8wNi9pZGVudGl0eS9jbGFpbXMvYXV0aGVudGljYXRpb25pbnN0YW50IjoiNy81LzIwMTYgOToxNjozMyBBTSIsImh0dHA6Ly9zY2hlbWFzLnhtbHNvYXAub3JnL3dzLzIwMDUvMDUvaWRlbnRpdHkvY2xhaW1zL2VtYWlsYWRkcmVzcyI6InRvbnkuZ3VpbWVsbGlAeWFob28uY29tIiwiaWRlbnRpdHlwcm92aWRlciI6Imh0dHBzOi8vc3NvMi1zdGcuZm1nbG9iYWwuY29tIiwianRpIjoiNmI1ZGY4NjBGNDUxNDlEZjhERUZEM0NBZDFiRjA0ZjMiLCJpYXQiOjE0Njc3MjQ1OTksIm5iZiI6MTQ2NzcyNDU2OSwiZXhwIjoxNDY3NzI0ODk5fQ.RA3F6deEmZlq3RL2NDF07Nv5SrrY31Qfw1LfVoxNmdXlPcai1UH9ad80Oq66sMyJUMOtXMOA5RkwRZQ6L5gNFTAIApDjAlBFte1d5ziCSjBhxYzMZ-f_pFeBOmsMsSI5-BaAInYch7usZ2efEP8AdYKrZdkO5bgovL-7WD0Ts9gjVssWN_uhIcj-mM67xHartinKstPXUB4LUrJHHOoORCChs1eNS5xTq6q1xoPj-dYmlC56OaHKuzqUy9iByWssZ-0_DC6EfwIOqL4i43sNXPrSuDAisGPd_pxnYzqgdPAaWj9WTae7X1VHzvCiz21V7TGvDj37cwiXHj2dT93vAQ"
 
        ConvertFrom-EncodedJsonWebToken -JsonWebToken $jwt | Select-Object -ExpandProperty Payload | ConvertFrom-Json
 
        Decodes the JSON Web Token defined in the $jwt variable, expands the Payload segment and deserializes it via the ConvertFrom-Json cmdlet.
    .EXAMPLE
        $jwt = "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCNTMjU2IjoiYklYZkw1RmREaWkzR2E3QmlEWndrcUQ5RE5JIiwiamt1IjoiaHR0cHM6Ly9xYS5pZHBzcnYuZm1nbG9iYWwuY29tL0FjY291bnQvSndrQ29sbGVjdGlvbiJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9naXZlbm5hbWUiOiJUb255IiwiaXNzIjoiaHR0cHM6Ly9xYS5pZHBzcnYuZm1nbG9iYWwuY29tIiwiaHR0cDovL3NjaGVtYXMueG1sc29hcC5vcmcvd3MvMjAwNS8wNS9pZGVudGl0eS9jbGFpbXMvc3VybmFtZSI6Ikd1aW1lbGxpIiwiaHR0cDovL3NjaGVtYXMubWljcm9zb2Z0LmNvbS93cy8yMDA4LzA2L2lkZW50aXR5L2NsYWltcy9hdXRoZW50aWNhdGlvbm1ldGhvZCI6Imh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vd3MvMjAwOC8wNi9pZGVudGl0eS9hdXRoZW50aWNhdGlvbm1ldGhvZC91bnNwZWNpZmllZCIsImh0dHA6Ly9zY2hlbWFzLnhtbHNvYXAub3JnL3dzLzIwMDUvMDUvaWRlbnRpdHkvY2xhaW1zL25hbWVpZGVudGlmaWVyIjoidG9ueS5ndWltZWxsaUB5YWhvby5jb20iLCJhdWQiOiJ1cm46Zm1nOmlkZW50aXR5c2VydmVyLy9xYSIsImh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vd3MvMjAwOC8wNi9pZGVudGl0eS9jbGFpbXMvYXV0aGVudGljYXRpb25pbnN0YW50IjoiNy81LzIwMTYgOToxNjozMyBBTSIsImh0dHA6Ly9zY2hlbWFzLnhtbHNvYXAub3JnL3dzLzIwMDUvMDUvaWRlbnRpdHkvY2xhaW1zL2VtYWlsYWRkcmVzcyI6InRvbnkuZ3VpbWVsbGlAeWFob28uY29tIiwiaWRlbnRpdHlwcm92aWRlciI6Imh0dHBzOi8vc3NvMi1zdGcuZm1nbG9iYWwuY29tIiwianRpIjoiNmI1ZGY4NjBGNDUxNDlEZjhERUZEM0NBZDFiRjA0ZjMiLCJpYXQiOjE0Njc3MjQ1OTksIm5iZiI6MTQ2NzcyNDU2OSwiZXhwIjoxNDY3NzI0ODk5fQ.RA3F6deEmZlq3RL2NDF07Nv5SrrY31Qfw1LfVoxNmdXlPcai1UH9ad80Oq66sMyJUMOtXMOA5RkwRZQ6L5gNFTAIApDjAlBFte1d5ziCSjBhxYzMZ-f_pFeBOmsMsSI5-BaAInYch7usZ2efEP8AdYKrZdkO5bgovL-7WD0Ts9gjVssWN_uhIcj-mM67xHartinKstPXUB4LUrJHHOoORCChs1eNS5xTq6q1xoPj-dYmlC56OaHKuzqUy9iByWssZ-0_DC6EfwIOqL4i43sNXPrSuDAisGPd_pxnYzqgdPAaWj9WTae7X1VHzvCiz21V7TGvDj37cwiXHj2dT93vAQ"
 
        ConvertFrom-EncodedJsonWebToken -JsonWebToken $jwt | Select IssuedAt, NotBefore
 
        Returns the iat and exp claims in the payload deserialized as "IssuedAt" and "ExpiresAt" as DateTime.
    .INPUTS
        System.String
 
            A string is received by the JsonWebToken parameter.
    .OUTPUTS
        PSJsonWebToken.DecodedJsonWebToken
 
            An object containing the decoded header and payload segments. The signature segment remains encoded.
            If an exp claim is present, the object contains ExpiresAt and ExpirationInMinutes properties.
            If expiration is greater than one hour, the object also contains an ExpirationInHours property.
            If an exp claim is not present, these properties are not added.
    .LINK
        https://tools.ietf.org/html/rfc7519
        New-JsonWebToken
        ConvertFrom-Json
#>

    [CmdletBinding()]
    [Alias('cjwt', 'cfjwt', 'jwtd', 'djwt', 'DecodeJwt', 'ConvertFrom-EncodedJwt')]
    [OutputType([PSJsonWebToken.DecodedJsonWebToken])]
    Param (
        [Parameter(Mandatory = $true, ValueFromPipeline = $true, Position = 0)]
        [ValidateLength(16, 131072)][Alias("JWT", "Token")][String]$JsonWebToken
    )
    BEGIN {
        $decodeExceptionMessage = "Unable to decode JWT."
        $ArgumentException = New-Object -TypeName ArgumentException -ArgumentList $decodeExceptionMessage
    }
    PROCESS {
        [bool]$isValidJwt = Test-JwtStructure -JsonWebToken $JsonWebToken
        if (-not($isValidJwt)) {
            Write-Error -Exception $ArgumentException -Category InvalidArgument -ErrorAction Stop
        }

        $deserializedHeader = $JsonWebToken | Get-JsonWebTokenHeader
        $deserializedPayload = $JsonWebToken | Get-JsonWebTokenPayload

        $serializedHeader = $deserializedHeader | ConvertTo-Json -Compress
        $serializedPayload = $deserializedPayload | ConvertTo-Json -Compress
        $signatureString = $JsonWebToken.Split(".")[2]

        $decodedJsonWebToken = [PSJsonWebToken.DecodedJsonWebToken]::new()
        $decodedJsonWebToken.Header = $serializedHeader
        $decodedJsonWebToken.Payload = $serializedPayload
        $decodedJsonWebToken.Signature = $signatureString

        [bool]$containsPotentialThumbprint = $false
        [string]$encodedThumbprint = ""
        [string]$decodedThumbprint = ""

        if ($deserializedHeader.ContainsKey("x5t")) {
            $containsPotentialThumbprint = $true
            $encodedThumbprint = $deserializedHeader.x5t
        }
        elseif ($deserializedHeader.ContainsKey("kid")) {
            $containsPotentialThumbprint = $true
            $encodedThumbprint = $deserializedHeader.kid
        }

        if ($containsPotentialThumbprint) {
            [bool]$thumbprintDecodes = $false
            try {
                $decodedThumbprint = ConvertFrom-EncodedJwtThumbprint -EncodedThumbprint $encodedThumbprint
                $thumbprintDecodes = $true
            }
            catch {
                $thumbprintDecodes = $false
            }

            if ($thumbprintDecodes) {
                $decodedJsonWebToken | Add-Member -MemberType NoteProperty -Name SigningCertificateThumbprint -Value $decodedThumbprint
            }
        }

        if ($deserializedPayload.ContainsKey("nbf")) {
            try {
                $notBefore = Convert-EpochToDateTime -Epoch $deserializedPayload.nbf
                $decodedJsonWebToken | Add-Member -MemberType NoteProperty -Name NotBefore -Value $notBefore
            }
            catch {
                Write-Error -Exception $_.Exception
            }
        }

        if ($deserializedPayload.ContainsKey("iat")) {
            try {
                $issuedAt = Convert-EpochToDateTime -Epoch $deserializedPayload.iat
                $decodedJsonWebToken | Add-Member -MemberType NoteProperty -Name IssuedAt -Value $issuedAt
            }
            catch {
                Write-Error -Exception $_.Exception
            }
        }

        if ($deserializedPayload.ContainsKey("exp")) {
            try {
                $jwtExpiration = Get-JwtExpiration -JsonWebToken $JsonWebToken
                if ($null -ne $jwtExpiration) {
                    $jwtExpiration.PSObject.Properties | ForEach-Object {
                        $decodedJsonWebToken | Add-Member -MemberType NoteProperty -Name $_.Name -Value $_.Value
                    }
                }
            }
            catch {
                Write-Error -Exception $_.Exception
            }
        }
        return $decodedJsonWebToken
    }
}