From 05e1dbd485f65620e10199681060c445460b272d Mon Sep 17 00:00:00 2001 From: srozemuller Date: Thu, 2 Nov 2023 14:21:43 +0100 Subject: [PATCH 1/2] added jwt conversion function --- Az.Avd/Private/global-functions.ps1 | 5 ++-- Az.Avd/Private/parse-jwt.ps1 | 37 +++++++++++++++++++++++++++++ Az.Avd/Public/Connect-Avd.ps1 | 14 ++++++++--- 3 files changed, 51 insertions(+), 5 deletions(-) create mode 100644 Az.Avd/Private/parse-jwt.ps1 diff --git a/Az.Avd/Private/global-functions.ps1 b/Az.Avd/Private/global-functions.ps1 index cc5dfc19..8af2319c 100644 --- a/Az.Avd/Private/global-functions.ps1 +++ b/Az.Avd/Private/global-functions.ps1 @@ -28,7 +28,7 @@ function GetAuthToken { [string]$Resource ) - if ($null -eq $global:tokenRequest) { + if ($null -eq $global:tokenRequest.access_token) { Throw "Please connect to AVD first using the Connect-Avd command" } if ($null -eq $global:subscriptionId) { @@ -36,7 +36,8 @@ function GetAuthToken { $global:subscriptionId = Read-Host -Prompt "Please fill in the subscription Id" Write-Information "Subscription ID is set, if you want to changed the context, use Set-AvdContext -SubscriptionID " -InformationAction Continue } - $expireTime = Get-Date -UnixTimeSeconds $global:tokenRequest.expires_on + $tokenInfo = Convert-JWTtoken -token $global:tokenRequest.access_token + $expireTime = Get-Date -UnixTimeSeconds $tokenInfo.exp if ((Get-Date) -gt $expireTime) { Write-Warning "Current token has expired. Requesting a new token based on the refresh token." $global:authHeader = Connect-Avd -RefreshToken $global:tokenRequest.refresh_token -TenantID $TenantId diff --git a/Az.Avd/Private/parse-jwt.ps1 b/Az.Avd/Private/parse-jwt.ps1 new file mode 100644 index 00000000..4672e44d --- /dev/null +++ b/Az.Avd/Private/parse-jwt.ps1 @@ -0,0 +1,37 @@ +function Convert-JWTtoken { + + [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 (!$token.Contains(".") -or !$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 += "=" } + Write-Verbose "Base64 encoded (padded) header:" + Write-Verbose $tokenheader + #Convert from Base64 encoded string to PSObject all at once + Write-Verbose "Decoded header:" + [System.Text.Encoding]::ASCII.GetString([system.convert]::FromBase64String($tokenheader)) | ConvertFrom-Json | fl | Out-Default + + #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 += "=" } + Write-Verbose "Base64 encoded (padded) payoad:" + Write-Verbose $tokenPayload + #Convert to Byte array + $tokenByteArray = [System.Convert]::FromBase64String($tokenPayload) + #Convert to string array + $tokenArray = [System.Text.Encoding]::ASCII.GetString($tokenByteArray) + Write-Verbose "Decoded array in JSON format:" + Write-Verbose $tokenArray + #Convert from JSON to PSObject + $tokobj = $tokenArray | ConvertFrom-Json + Write-Verbose "Decoded Payload:" + + return $tokobj +} \ No newline at end of file diff --git a/Az.Avd/Public/Connect-Avd.ps1 b/Az.Avd/Public/Connect-Avd.ps1 index cff02a0c..21f0d8cb 100644 --- a/Az.Avd/Public/Connect-Avd.ps1 +++ b/Az.Avd/Public/Connect-Avd.ps1 @@ -41,8 +41,7 @@ function Connect-Avd { [ValidateNotNullOrEmpty()] [string]$RedirectUri = [string]::Empty, - [parameter(ParameterSetName = "ClientSecret", HelpMessage = "Specify the subscription ID to connect to")] - [parameter(ParameterSetName = "DeviceCode", HelpMessage = "Specify the subscription ID to connect to")] + [parameter(Mandatory, HelpMessage = "Specify the subscription ID to connect to")] [ValidateNotNullOrEmpty()] [string]$SubscriptionId, @@ -58,7 +57,10 @@ function Connect-Avd { [switch]$DeviceCode, [parameter(ParameterSetName = "Refresh", HelpMessage = "Specify to refresh an existing access token.")] - [string]$RefreshToken + [string]$RefreshToken, + + [parameter(ParameterSetName = "AccessToken", HelpMessage = "Provide an access token to use for authentication.")] + [string]$AccessToken ) Begin { $global:TenantId = $TenantID @@ -169,6 +171,12 @@ function Connect-Avd { # If not waiting for auth, throw error } } + "AccessToken" { + Write-Verbose "Using provided access token, that is ease for me. Thank you for that." + $global:tokenRequest = [PSCustomObject]@{ + access_token = $AccessToken + } + } } Write-Verbose "Token is $($global:tokenRequest)" $global:authHeader = @{ From 0c9a83917cfc2c43538468c88af06039d9b06560 Mon Sep 17 00:00:00 2001 From: srozemuller Date: Thu, 9 Nov 2023 08:36:56 +0100 Subject: [PATCH 2/2] JWT Token parse updated --- Az.Avd/Private/parse-jwt.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Az.Avd/Private/parse-jwt.ps1 b/Az.Avd/Private/parse-jwt.ps1 index 4672e44d..72bbb2b4 100644 --- a/Az.Avd/Private/parse-jwt.ps1 +++ b/Az.Avd/Private/parse-jwt.ps1 @@ -15,7 +15,7 @@ function Convert-JWTtoken { Write-Verbose $tokenheader #Convert from Base64 encoded string to PSObject all at once Write-Verbose "Decoded header:" - [System.Text.Encoding]::ASCII.GetString([system.convert]::FromBase64String($tokenheader)) | ConvertFrom-Json | fl | Out-Default + [System.Text.Encoding]::ASCII.GetString([system.convert]::FromBase64String($tokenheader)) | ConvertFrom-Json | fl | Out-Null #Payload $tokenPayload = $token.Split(".")[1].Replace('-', '+').Replace('_', '/')