By popular demand, finally my second blog post online. Please feel free to feedback to me what you think about it.
Validating your JSON Web Token (JWT) with PowerShell
In some organizations, where you have a segregation in the management infrastructure, you might only manage part of the infrastructure (i.e. WebServers) and not the ADFS part of the infrastructure. In this case you are only the Administrator on the WebServer, you are just a normal user of the ADFS infrastructure. Your ADFS Administrator has setup ADFS for you, in line with the requirements that came with your application.
Background WAP and JSON Web Token (JWT):
In this example we will use Windows Azure Pack (WAP) as the application running on the WebServer. Several blogs have been written on how to setup ADFSv3 with WAP so it generates the required JSON Web Token (JWT). Referral to some by Marc van Eijk listed below:
http://www.hyper-v.nu/archives/mvaneijk/2014/01/windows-azure-pack-with-adfs-and-windows-azure-multi-factor-authentication-part-1/
http://www.hyper-v.nu/archives/mvaneijk/2014/02/windows-azure-pack-with-adfs-and-windows-azure-multi-factor-authentication-part-2/
http://www.hyper-v.nu/archives/mvaneijk/2014/03/windows-azure-pack-with-adfs-and-windows-azure-multi-factor-authentication-part-3/
Background WAP Tenant Public API:
A useful tool (by Ben Gelens) which we combine with the above setup of WAP & JSON Web Token (JWT) is the PowerShell Module WAPTenantPublicAPI. Just fire up your PowerShell 5 and install via the PowerShell Gallery:
Install-Module -Name WAPTenantPublicAPI
The use case – Validating your JSON Web Token (JWT) with PowerShell
Let’s go! Get a JWT Token:
Get-WAPToken -Credential (Get-Credential -UserName menno.stevens@madeofthings.net -Message 'ADFS Account') –URL https://fs.madeofthings.net -ADFS
Get-WAPToken is a CmdLet in the WAPTenantPublicAPI. The CmdLet formats the request for your ADFS infrastructure in the right way, authenticates and stores your token in the $token variable.
Decoding your JSON Web Token (JWT) with PowerShell
Let’s decode!
As per JWT Specifications (https://tools.ietf.org/html/rfc7519), the token is split in 3 parts, separated by a ‘.’ (dot). The easiest way I know to decode the first 2 parts with PowerShell is using these few lines (core from Shriram [MSFT] in the Technet Gallery):
foreach ($i in 0..1) { $data=$token.Split('.')[$i].Replace('-', '+').Replace('_', '/') switch ($data.Length % 4) { 0 {break} 2 {$data += '=='} 3 {$data += '='} } [System.Text.Encoding]::UTF8.GetString([convert]::FromBase64String($data)) '---' }
The result is shown below:But what about the 3rd part?
That is the signature proving the token is not tampered with. And we can read it easily.
Using the same few PowerShell script-lines, just replace “foreach ($i in 0..1)” with “foreach ($i in 0..2)”:Right… But that 3rd part of your token (the garbage) does not proof anything. We can’t validate the JWT Token that way.
Validating your JSON Web Token (JWT) with PowerShell
Let’s validate!
As explained in the introduction, we use Windows Azure Pack (WAP) as the example application. You have to retrieve the ADFS/JWT Certificate you are going to validate your Token against from your configuration. Or get the Certificate from your ADFS Administrator.
To be short, in our example case, login to the Azure Pack Webserver and retrieve the installed certificate using the following commands. And store it in the $cert variable which MUST be of the type “X509Certificate2”.
$Connectionstring='Data Source=MYSQLSERVER;Integrated Security=SSPI;Trusted_Connection=yes' $TenantSiteTenant=Get-MgmtSvcRelyingPartySettings -ConnectionString $Connectionstring -Target 'Tenant' $certs=$TenantSiteTenant.Certificates|%{[System.Security.Cryptography.X509Certificates.X509Certificate2]([System.Convert]::FromBase64String($_))} $cert=$certs|where Issuer -like 'CN=DigiCert*'
PowerShell and Windows don’t really understand the JSON Web Token (JWT). But again, PowerShell 5 to the rescue. Get the Package “System.IdentityModel.Tokens.Jwt” from the NuGet Repository. I found that I got the best results with version 3.0.2. You might want to improve my scripting and try a newer version. Get it installed with this command:
Install-Package System.IdentityModel.Tokens.Jwt -Version 3.0.2
And load the DLL into PowerShell with:
Add-Type -Path 'C:\the..absolute..path..to\v3.02\net45\System.IdentityModel.Tokens.Jwt.dll'
Now the script-lines that actually will validate your JWT Token in the $token variable against the Certificate in the $cert variable:
$X509SecurityToken=New-Object System.IdentityModel.Tokens.X509SecurityToken($cert) $JwtSecurityToken=(New-Object System.IdentityModel.Tokens.JwtSecurityTokenHandler).ReadToken($token) $TokenProperties=@{ SigningToken=$X509SecurityToken; AllowedAudience=$JwtSecurityToken.Audience; ValidIssuer=$JwtSecurityToken.Issuer } $TokenValidationParameters=New-Object System.IdentityModel.Tokens.TokenValidationParameters -Property $TokenProperties $ValidateTokenResult=(New-Object System.IdentityModel.Tokens.JwtSecurityTokenHandler).ValidateToken($JwtSecurityToken,$TokenValidationParameters) Write-Verbose -Verbose "The token validation result is: $($ValidateTokenResult.Identity.IsAuthenticated)"
Let’s see some results.
The below is an example where a valid $token is validating against the wrong $cert:
Here an example where a valid $token is validating against a valid $cert, but the CRL Service of the CA was not available:
Now a valid $token, against a valid $cert with the CRL Service of the CA running. But with an obvious explanation “The token is expired”:
Now some messing around with the $token. And result is that something totally has gone wrong “Check to make sure the SecurityAlgorithm is supported”:
And finally, this one seems right:
So with this Boolean value of $ValidateTokenResult.Identity.IsAuthenticated being “True”, we can say we have actually validated that the value of $token is a valid one. Now it is save to use the claims provided.