Skip to content

Instantly share code, notes, and snippets.

@PanosGreg
Created January 30, 2026 13:45
Show Gist options
  • Select an option

  • Save PanosGreg/954ea7ce689d80998befc791b32bb284 to your computer and use it in GitHub Desktop.

Select an option

Save PanosGreg/954ea7ce689d80998befc791b32bb284 to your computer and use it in GitHub Desktop.
Convert JWT Blob to an object
function ConvertFrom-JwtToken {
<#
.DESCRIPTION
Decodes a JWT token. This was taken from link below. Thanks to Vasil Michev.
.LINK
https://www.michev.info/Blog/Post/2140/decode-jwt-access-and-id-tokens-via-powershell
#>
[OutputType([System.Collections.Specialized.OrderedDictionary])]
[cmdletbinding()]
param(
[Parameter(Mandatory = $True)]
[string]$Token
)
# Validate as per https://tools.ietf.org/html/rfc7519
# Access and ID tokens are fine, Refresh tokens will not work
if (-not $Token.Contains('.') -or -not $Token.StartsWith('eyJ')) {
Write-Error 'Invalid token' -ErrorAction Stop
}
# Header
$TokenHeader = $Token.Split('.')[0].Replace('-', '+').Replace('_', '/')
# Fix padding as needed, keep adding "=" until string length modulus 4 reaches 0
while ($tokenheader.Length % 4) {
Write-Verbose 'Invalid length for a Base-64 char array or string, adding ='
$TokenHeader += '='
}
# Convert from Base64 encoded string to PSObject all at once
$HeaderByteArray = [system.convert]::FromBase64String($TokenHeader)
$Header = ([System.Text.Encoding]::ASCII.GetString($HeaderByteArray) | ConvertFrom-Json)
# Payload
$TokenPayload = $Token.Split('.')[1].Replace('-', '+').Replace('_', '/')
# Fix padding as needed, keep adding "=" until string length modulus 4 reaches 0
while ($TokenPayload.Length % 4) {
Write-Verbose 'Invalid length for a Base-64 char array or string, adding ='
$TokenPayload += '='
}
$TokenByteArray = [System.Convert]::FromBase64String($TokenPayload)
$TokenArray = ([System.Text.Encoding]::ASCII.GetString($TokenByteArray) | ConvertFrom-Json)
# Convert $Header and $TokenArray from PSCustomObject to Hashtable so they can be added together
$HeaderAsHash = @{}
$TokenArrayAsHash = @{}
$Header.psobject.properties | ForEach-Object { $HeaderAsHash[$_.Name] = $_.Value }
$TokenArray.psobject.properties | ForEach-Object { $TokenArrayAsHash[$_.Name] = $_.Value }
$Output = [ordered]@{}
($HeaderAsHash + $TokenArrayAsHash).GetEnumerator() | Sort-Object Key | foreach {
$Output.Add($_.Key,$_.Value)
}
# Convert Expiry time to PowerShell DateTime
$Orig = (Get-Date -Year 1970 -Month 1 -Day 1 -Hour 0 -Minute 0 -Second 0 -Millisecond 0)
$TimeZone = Get-TimeZone
$UtcTime = $Orig.AddSeconds($Output.exp)
$Offset = $TimeZone.GetUtcOffset($(Get-Date)).TotalMinutes # <-- Daylight saving needs to be calculated
$LocalTime = $UtcTime.AddMinutes($Offset) # <-- Return local time
$Output.Add('ExpiresAt',$LocalTime)
$Method = {
$Span = $this.ExpiresAt - [System.DateTime]::Now
Write-Output $Span.ToString('h\h\r\ m\m\i\n\ s\s\e\c')
}
$Output | Add-Member -MemberType ScriptMethod -Name TokenExpiresIn -Value $Method -Force
Write-Output $Output
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment