JWT - Generate JSON Web Token

Octopus.Script exported 2021-08-09 by harrisonmeister belongs to ‘JWT’ category.

Generates a Json Web Token (JWT) for use with applications that require a JWT token. The resulting JWT will be stored as a sensitive output variable called JWT.

A private key needs to be provided that will sign the combined JWT header and payload.

Currently, the following three signing algorithms are supported:

  1. RS256 - RSASSA-PKCS1-v1_5 using SHA-256
  2. RS384 - RSASSA-PKCS1-v1_5 using SHA-384
  3. RS512 - RSASSA-PKCS1-v1_5 using SHA-512

The default is RS256.

Notes:

  • Tested on Windows and Linux (PowerShell Core)
  • Tested with Octopus 2020.1

Parameters

When steps based on the template are included in a project’s deployment process, the parameters below can be set.

Private key used to sign the JWT

Generate.JWT.PrivateKey =

Provide the private key in PEM format to be used to sign the JWT.

Accepted headers:

  • ----BEGIN RSA PRIVATE KEY-----
  • -----BEGIN PRIVATE KEY----

Note: It’s recommended to use a sensitive variable to provide this value. If you instead enter the private key directly, the step template will attempt to detect this by replacing spaces with new lines “n`.

JWT signing algorithm

Generate.JWT.Signing.Algorithm = RS256

The JWA algorithm to use when signing the JWT. The default is RS256 (RSASSA-PKCS1-v1_5 using SHA-256).

This value is also used for the alg algorithm header.

JWT expiry time in minutes

Generate.JWT.ExpiresAfterMinutes = 20

The number of minutes after the JWT is created that it expires. This value is used for the exp expiration time claim. The default is 20 minutes.

JWT Issuer (optional)

Generate.JWT.Issuer =

Optional - The iss isuer claim identifies the principal that issued the JWT.

JWT Subject (optional)

Generate.JWT.Subject =

Optional subject (sub) of the JWT.

Audience claims (optional)

Generate.JWT.Audience =

Optional - The aud audience claim identifies the recipients that the JWT is intended for. This is typically the base address of the resource being accessed, such as https://example-domain.com

Note:

  • For multiple audiences, enter each entry on a new line.
  • Any entries that are an empty string will be ignored.

Group claims (optional)

Generate.JWT.Groups =

Optional - A groups private claim that identifies groups associated with this JWT.

Note:

  • For multiple groups, enter each entry on a new line.
  • Any entries that are an empty string will be ignored.

TTL claim (optional)

Generate.JWT.TTL =

Optional - A ttl private claim that identifies the time-to-live for the JWT.

This value can be useful when authenticating with a Secrets Manager and the ttl value is mapped through to a resulting token used for authentication.

Max TTL claim (optional)

Generate.JWT.TTL.Max =

Optional - A max_ttl private claim that identifies the time-to-live for the JWT.

This value can be useful when authenticating with a Secrets Manager and the max_ttl value is mapped through to a resulting token used for authentication.

Custom private claim name (optional)

Generate.JWT.PrivateClaim.Name =

Optional - The name of a custom private claim that can be included in this JWT.

Note:

  • Don’t include the value for the private claim. Set the value in the Generate.JWT.PrivateClaim.Value parameter instead.
  • If this value is null or an empty string, the private claim won’t be added to the JWT payload.

Custom private claim value (optional)

Generate.JWT.PrivateClaim.Value =

Optional - The value for the custom private claim as specified in the parameter Generate.JWT.PrivateClaim.Name to be included in this JWT.

Script body

Steps based on this template will execute the following PowerShell script.

$ErrorActionPreference = 'Stop'

# Variables
$GENERATE_JWT_PRIVATE_KEY = $OctopusParameters["Generate.JWT.PrivateKey"]
$GENERATE_JWT_ALGORITHM = $OctopusParameters["Generate.JWT.Signing.Algorithm"]
$GENERATE_JWT_EXPIRES_MINS = $OctopusParameters["Generate.JWT.ExpiresAfterMinutes"]

# Optional 
$GENERATE_JWT_ISSUER = $OctopusParameters["Generate.JWT.Issuer"]
$GENERATE_JWT_SUBJECT = $OctopusParameters["Generate.JWT.Subject"]
$GENERATE_JWT_GROUPS = $OctopusParameters["Generate.JWT.Groups"]
$GENERATE_JWT_AUDIENCE = $OctopusParameters["Generate.JWT.Audience"]
$GENERATE_JWT_TTL = $OctopusParameters["Generate.JWT.TTL"]
$GENERATE_JWT_MAX_TTL = $OctopusParameters["Generate.JWT.TTL.Max"]
$GENERATE_JWT_PRIVATE_CLAIM_NAME = $OctopusParameters["Generate.JWT.PrivateClaim.Name"]
$GENERATE_JWT_PRIVATE_CLAIM_VALUE = $OctopusParameters["Generate.JWT.PrivateClaim.Value"]

# Validation
if ([string]::IsNullOrWhiteSpace($GENERATE_JWT_PRIVATE_KEY)) {
    throw "Required parameter Generate.JWT.PrivateKey not specified."
}
if ([string]::IsNullOrWhiteSpace($GENERATE_JWT_ALGORITHM)) {
    throw "Required parameter Generate.JWT.Signing.Algorithm not specified."
}
if ([string]::IsNullOrWhiteSpace($GENERATE_JWT_EXPIRES_MINS)) {
    throw "Required parameter Generate.JWT.ExpiresAfterMinutes not specified."
}
if ([string]::IsNullOrWhiteSpace($GENERATE_JWT_AUDIENCE)) {
    throw "Required parameter Generate.JWT.Audience not specified."
}

# Optional fields that require validation
if ([string]::IsNullOrWhiteSpace($GENERATE_JWT_PRIVATE_CLAIM_NAME) -eq $False) {
    if ([string]::IsNullOrWhiteSpace($GENERATE_JWT_PRIVATE_CLAIM_VALUE)) {
        throw "A private claim name has been specified with no value found in Generate.JWT.PrivateClaim.Value."
    }
}

# Helper functions
###############################################################################

function ConvertTo-JwtBase64 {
    param (
        $Value
    )
    if ($Value -is [string]) {
        $ConvertedValue = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($Value)) -replace '\+', '-' -replace '/', '_' -replace '='
    }
    elseif ($Value -is [byte[]]) {
        $ConvertedValue = [Convert]::ToBase64String($Value) -replace '\+', '-' -replace '/', '_' -replace '='
    }

    return $ConvertedValue
}

###############################################################################

# Signing functions
###############################################################################

$RsaPrivateKey_Header = "-----BEGIN RSA PRIVATE KEY-----"
$RsaPrivateKey_Footer = "-----END RSA PRIVATE KEY-----"
$Pkcs8_PrivateKey_Header = "-----BEGIN PRIVATE KEY-----"
$Pkcs8_PrivateKey_Footer = "-----END PRIVATE KEY-----"

function ExtractPemData {
    param (
        [string]$Pem,
        [string]$Header,
        [string]$Footer
    )

    $Start = $Pem.IndexOf($Header) + $Header.Length
    $End = $Pem.IndexOf($Footer, $Start) - $Start
    $EncodedPem = ($Pem.Substring($Start, $End).Trim())  -Replace " ", "`n"
    
    $PemData = [Convert]::FromBase64String($EncodedPem)
    return [byte[]]$PemData
}

function DecodeIntSize {
    param (
        [System.IO.BinaryReader]$BinaryReader
    )
    
    [byte]$byteValue = $BinaryReader.ReadByte()
    
    # If anything other than 0x02, an ASN.1 integer follows.
    if ($byteValue -ne 0x02) {
        return 0;
    }
    
    $byteValue = $BinaryReader.ReadByte()
    # 0x81 == Data size in next byte.
    if ($byteValue -eq 0x81) { 
        $size = $BinaryReader.ReadByte()
    }
    # 0x82 == Data size in next 2 bytes.
    else {
        if ($byteValue -eq 0x82) {
            [byte]$high = $BinaryReader.ReadByte()
            [byte]$low = $BinaryReader.ReadByte()
            $byteValues = [byte[]]@($low, $high, 0x00, 0x00)
            $size = [System.BitConverter]::ToInt32($byteValues, 0)
        }
        else {
            # Otherwise, data size has already been read above.
            $size = $byteValue
        }
    }
    # Remove high-order zeros in data
    $byteValue = $BinaryReader.ReadByte()
    while ($byteValue -eq 0x00) {
        $byteValue = $BinaryReader.ReadByte()
        $size -= 1
    }

    $BinaryReader.BaseStream.Seek(-1, [System.IO.SeekOrigin]::Current) | Out-Null
    return $size
}

function PadByteArray {
    param (
        [byte[]]$Bytes,
        [int]$Size
    )

    if ($Bytes.Length -eq $Size) {
        return $Bytes
    }
    if ($Bytes.Length -gt $Size) {
        throw "Specified size '$Size' to pad is too small for byte array of size '$($Bytes.Length)'."
    }

    [byte[]]$PaddedBytes = New-Object Byte[] $Size
    [System.Array]::Copy($Bytes, 0, $PaddedBytes, $Size - $bytes.Length, $bytes.Length) | Out-Null
    return $PaddedBytes
}

function Compare-ByteArrays {
    param (
        [byte[]]$First,
        [byte[]]$Second
    )
    if ($First.Length -ne $Second.Length) {
        return $False
    }
    [int]$i = 0
    foreach ($byte in $First) {
        if ($byte -ne $Second[$i]) {
            return $False
        }
        $i = $i + 1
    }
    return $True
}

function CreateRSAFromPkcs8 {
    param (
        [byte[]]$KeyBytes
    )
    Write-Verbose "Reading RSA Pkcs8 private key bytes"

    # The encoded OID sequence for PKCS #1 rsaEncryption szOID_RSA_RSA = "1.2.840.113549.1.1.1"
    # this byte[] includes the sequence byte and terminal encoded null 
    [byte[]]$SeqOID = 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00
    [byte[]]$Seq = New-Object byte[] 15

    # Have to wrap $KeyBytes in another array :|
    $MemoryStream = New-Object System.IO.MemoryStream(, $KeyBytes) 
    $reader = New-Object System.IO.BinaryReader($MemoryStream)
    $StreamLength = [int]$MemoryStream.Length
    
    try {
        [UInt16]$Bytes = $reader.ReadUInt16()

        if ($Bytes -eq 0x8130) {
            $reader.ReadByte() | Out-Null
        }
        elseif ($Bytes -eq 0x8230) {
            $reader.ReadInt16() | Out-Null
        }
        else {
            return $null
        }
        
        [byte]$byteValue = $reader.ReadByte()

        if ($byteValue -ne 0x02) {
            return $null
        }

        $Bytes = $reader.ReadUInt16()

        if ($Bytes -ne 0x0001) {
            return $null
        }

        # Read the Sequence OID
        $Seq = $reader.ReadBytes(15)
        $SequenceMatches = Compare-ByteArrays -First $Seq -Second $SeqOID
        if ($SequenceMatches -eq $False) {
            Write-Verbose "Sequence OID doesnt match"
            return $null
        }

        $byteValue = $reader.ReadByte()
        # Next byte should be a Octet string
        if ($byteValue -ne 0x04) {
            return $null
        }
        # Read next byte / 2 bytes. 
        # Should be either: 0x81 or 0x82; otherwise it's the byte count.
        $byteValue = $reader.ReadByte()
        if ($byteValue -eq 0x81) {
            $reader.ReadByte() | Out-Null
        }
        else {
            if ($byteValue -eq 0x82) {
                $reader.ReadUInt16() | Out-Null
            }
        }

        # Remaining sequence *should* be the RSA Pkcs1 private Key bytes
        [byte[]]$RsaKeyBytes = $reader.ReadBytes([int]($StreamLength - $MemoryStream.Position))
        Write-Verbose "Attempting to create RSA object from remaining Pkcs1 bytes"
        $rsa = CreateRSAFromPkcs1 -KeyBytes $RsaKeyBytes
        return $rsa
    }
    catch {
        Write-Warning "CreateRSAFromPkcs8: Exception occurred - $($_.Exception.Message)"
        return $null
    }
    finally {
        if ($null -ne $reader) { $reader.Close() }
        if ($null -ne $MemoryStream) { $MemoryStream.Close() }
    }
}

function CreateRSAFromPkcs1 {
    param (
        [byte[]]$KeyBytes
    )
    Write-Verbose "Reading RSA Pkcs1 private key bytes"
    # Have to wrap $KeyBytes in another array :|
    $MemoryStream = New-Object System.IO.MemoryStream(, $KeyBytes) 
    $reader = New-Object System.IO.BinaryReader($MemoryStream)
    try {
               
        [UInt16]$Bytes = $reader.ReadUInt16()

        if ($Bytes -eq 0x8130) {
            $reader.ReadByte() | Out-Null
        }
        elseif ($Bytes -eq 0x8230) {
            $reader.ReadInt16() | Out-Null
        }
        else {
            return $null
        }
    
        $Bytes = $reader.ReadUInt16()
        if ($Bytes -ne 0x0102) {
            return $null
        }
    
        [byte]$byteValue = $reader.ReadByte()
        if ($byteValue -ne 0x00) {
            return $null
        }

        # Private key parameters are integer sequences.
        # For a summary of the RSA Parameters fields, 
        # See https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.rsaparameters#summary-of-fields
        
        $Modulus_Size = DecodeIntSize -BinaryReader $reader
        $Modulus = $reader.ReadBytes($Modulus_Size)

        $E_Size = DecodeIntSize -BinaryReader $reader
        $E = $reader.ReadBytes($E_Size)

        $D_Size = DecodeIntSize -BinaryReader $reader
        $D = $reader.ReadBytes($D_Size)

        $P_Size = DecodeIntSize -BinaryReader $reader
        $P = $reader.ReadBytes($P_Size)

        $Q_Size = DecodeIntSize -BinaryReader $reader
        $Q = $reader.ReadBytes($Q_Size)

        $DP_Size = DecodeIntSize -BinaryReader $reader
        $DP = $reader.ReadBytes($DP_Size)

        $DQ_Size = DecodeIntSize -BinaryReader $reader
        $DQ = $reader.ReadBytes($DQ_Size)

        $IQ_Size = DecodeIntSize -BinaryReader $reader
        $IQ = $reader.ReadBytes($IQ_Size)

        $rsa = New-Object System.Security.Cryptography.RSACryptoServiceProvider
        $rsaParameters = New-Object System.Security.Cryptography.RSAParameters
        $rsaParameters.Modulus = $Modulus
        $rsaParameters.Exponent = $E
        $rsaParameters.P = $P
        $rsaParameters.Q = $Q
        # Some RSAParameter values dont play well with byte buffers having leading zeroes removed.
        $rsaParameters.D = PadByteArray -Bytes $D -Size $Modulus.Length
        $rsaParameters.DP = PadByteArray -Bytes $DP -Size $P.Length
        $rsaParameters.DQ = PadByteArray -Bytes $DQ -Size $Q.Length
        $rsaParameters.InverseQ = PadByteArray -Bytes $IQ -Size $Q.Length
        $rsa.ImportParameters($rsaParameters)

        Write-Verbose "Completed RSA object creation"
        return $rsa
    }
    catch {
        Write-Warning "CreateRSA-FromPkcs1: Exception occurred - $($_.Exception.Message)"
        return $null
    }
    finally {
        if ($null -ne $reader) { $reader.Close() }
        if ($null -ne $MemoryStream) { $MemoryStream.Close() }
    }
}

function CreateSigningKey {
    param (
        [string]$Key
    )
    try {
        $Key = $Key.Trim()
        switch -Wildcard($Key) {
            "$Pkcs8_PrivateKey_Header*" {
                $KeyBytes = ExtractPemData -PEM $Key -Header $Pkcs8_PrivateKey_Header -Footer $Pkcs8_PrivateKey_Footer
                $SigningKey = CreateRSAFromPkcs8 -KeyBytes $KeyBytes
                return $SigningKey
            }
            "$RsaPrivateKey_Header*" {
                $KeyBytes = ExtractPemData -PEM $Key -Header $RsaPrivateKey_Header -Footer $RsaPrivateKey_Footer
                $SigningKey = CreateRSAFromPkcs1 -KeyBytes $KeyBytes
                return $SigningKey
            }
            default {
                Write-Verbose "The PEM header could not be found. Accepted headers: 'BEGIN PRIVATE KEY', 'BEGIN RSA PRIVATE KEY'"
                return $null
            }
        }
    }
    catch {
        Write-Warning "Couldn't create signing key: $($_.Exception.Message)"
        return $null
    }
}

###############################################################################

# Local variables
$audiences = @()
if (![string]::IsNullOrWhiteSpace($GENERATE_JWT_AUDIENCE)) {
    @(($GENERATE_JWT_AUDIENCE -Split "`n").Trim()) | ForEach-Object {
        if (![string]::IsNullOrWhiteSpace($_)) {
            $audiences += $_
        }
    }
}

$groups = @()
if (![string]::IsNullOrWhiteSpace($GENERATE_JWT_GROUPS)) {
    @(($GENERATE_JWT_GROUPS -Split "`n").Trim()) | ForEach-Object {
        if (![string]::IsNullOrWhiteSpace($_)) {
            $groups += $_
        }
    }
}

$StepName = $OctopusParameters["Octopus.Step.Name"]
$OutputVariableName = "JWT"

Write-Verbose "Generate.JWT.Signing.Algorithm: $GENERATE_JWT_ALGORITHM"
Write-Verbose "Generate.JWT.ExpiresAfterMinutes: $GENERATE_JWT_EXPIRES_MINS"
Write-Verbose "Generate.JWT.Issuer: $GENERATE_JWT_ISSUER"
Write-Verbose "Generate.JWT.Subject: $GENERATE_JWT_SUBJECT"
Write-Verbose "Generate.JWT.Audience(s): $($audiences -Join ",")"
Write-Verbose "Generate.JWT.Group(s): $($groups -Join ",")"
Write-Verbose "Generate.JWT.TTL: $GENERATE_JWT_TTL"
Write-Verbose "Generate.JWT.TTL.Max: $GENERATE_JWT_MAX_TTL"
Write-Verbose "Generate.JWT.PrivateClaim.Name: $GENERATE_JWT_PRIVATE_CLAIM_NAME"
Write-Verbose "Generate.JWT.PrivateClaim.Value: $GENERATE_JWT_PRIVATE_CLAIM_VALUE"
Write-Verbose "Step Name: $StepName"

try {

    # Created + Expires
    $Created = (Get-Date).ToUniversalTime()
    $Expires = $Created.AddMinutes([int]$GENERATE_JWT_EXPIRES_MINS)

    $createDate = [Math]::Floor([decimal](Get-Date($Created) -UFormat "%s"))
    $expiryDate = [Math]::Floor([decimal](Get-Date($Expires) -UFormat "%s"))

    $JwtHeader = @{
        alg = $GENERATE_JWT_ALGORITHM;
        typ = "JWT";
    } | ConvertTo-Json -Compress
    
    $JwtPayload = [Ordered]@{
        iat = [long]$createDate;
        exp = [long]$expiryDate;
    }

    # Check for optional issuer: https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.1
    if ([string]::IsNullOrWhiteSpace($GENERATE_JWT_ISSUER) -eq $False) {
        $JwtPayload | Add-Member -NotePropertyName iss -NotePropertyValue $GENERATE_JWT_ISSUER
    }

    # Check for optional subject: https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.2
    if ([string]::IsNullOrWhiteSpace($GENERATE_JWT_SUBJECT) -eq $False) {
        $JwtPayload | Add-Member -NotePropertyName sub -NotePropertyValue $GENERATE_JWT_SUBJECT
    }

    # Check for optional audience: https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.3
    if ($audiences.Length -gt 0) {
        $JwtPayload | Add-Member -NotePropertyName aud -NotePropertyValue $audiences
    }
    # Check for optional "groups" field
    if ($groups.Length -gt 0) {
        $JwtPayload | Add-Member -NotePropertyName groups -NotePropertyValue $groups
    }

    # Check for optional ttl field
    if ([string]::IsNullOrWhiteSpace($GENERATE_JWT_TTL) -eq $False) {
        $JwtPayload | Add-Member -NotePropertyName ttl -NotePropertyValue $GENERATE_JWT_TTL
    }
    # Check for optional max_ttl field
    if ([string]::IsNullOrWhiteSpace($GENERATE_JWT_MAX_TTL) -eq $False) {
        $JwtPayload | Add-Member -NotePropertyName max_ttl -NotePropertyValue $GENERATE_JWT_MAX_TTL
    }

    # Check for an optional private claim name and value: https://datatracker.ietf.org/doc/html/rfc7519#section-4.3
    if ([string]::IsNullOrWhiteSpace($GENERATE_JWT_PRIVATE_CLAIM_NAME) -eq $False) {
        $JwtPayload | Add-Member -NotePropertyName $GENERATE_JWT_PRIVATE_CLAIM_NAME -NotePropertyValue $GENERATE_JWT_PRIVATE_CLAIM_VALUE
    } 

    $JwtPayload = $JwtPayload | ConvertTo-Json -Compress

    $base64Header = ConvertTo-JwtBase64 -Value $JwtHeader
    $base64Payload = ConvertTo-JwtBase64 -Value $JwtPayload

    $Jwt = $base64Header + '.' + $base64Payload

    $JwtBytes = [System.Text.Encoding]::UTF8.GetBytes($Jwt)
    $JwtSignature = $null
    
    switch ($GENERATE_JWT_ALGORITHM) {
        "RS256" {
            try { 

                $rsa = CreateSigningKey -Key $GENERATE_JWT_PRIVATE_KEY
                if ($null -eq $rsa) {
                    throw "Couldn't create RSA object"
                }
                $Signature = $rsa.SignData($JwtBytes, [Security.Cryptography.HashAlgorithmName]::SHA256, [Security.Cryptography.RSASignaturePadding]::Pkcs1) 
                $JwtSignature = ConvertTo-JwtBase64 -Value $Signature
            }
            catch { throw "Signing with SHA256 and Pkcs1 padding failed using private key: $($_.Exception.Message)" }
            finally { if ($null -ne $rsa) { $rsa.Dispose() } }
            
        }
        "RS384" {
            try { 
                $rsa = CreateSigningKey -Key $GENERATE_JWT_PRIVATE_KEY
                if ($null -eq $rsa) {
                    throw "Couldn't create RSA object"
                }
                $Signature = $rsa.SignData($JwtBytes, [Security.Cryptography.HashAlgorithmName]::SHA384, [Security.Cryptography.RSASignaturePadding]::Pkcs1) 
                $JwtSignature = ConvertTo-JwtBase64 -Value $Signature
            }
            catch { throw "Signing with SHA384 and Pkcs1 padding failed using private key: $($_.Exception.Message)" }
            finally { if ($null -ne $rsa) { $rsa.Dispose() } }
        }
        "RS512" {
            try { 
                $rsa = CreateSigningKey -Key $GENERATE_JWT_PRIVATE_KEY
                if ($null -eq $rsa) {
                    throw "Couldn't create RSA object"
                }
                $Signature = $rsa.SignData($JwtBytes, [Security.Cryptography.HashAlgorithmName]::SHA512, [Security.Cryptography.RSASignaturePadding]::Pkcs1) 
                $JwtSignature = ConvertTo-JwtBase64 -Value $Signature
            }
            catch { throw "Signing with SHA512 and Pkcs1 padding failed using private key: $($_.Exception.Message)" }
            finally { if ($null -ne $rsa) { $rsa.Dispose() } }
        }
        default {
            throw "The algorithm is not one of the supported: 'RS256', 'RS384', 'RS512'"
        }
    }
    if ([string]::IsNullOrWhiteSpace($JwtSignature) -eq $True) {
        throw "JWT signature empty."
    }

    $Jwt = "$Jwt.$JwtSignature"
    Set-OctopusVariable -Name $OutputVariableName -Value $Jwt -Sensitive
    Write-Host "Created output variable: ##{Octopus.Action[$StepName].Output.$OutputVariableName}"
}
catch {
    $ExceptionMessage = $_.Exception.Message
    $Message = "An error occurred generating a JWT: $ExceptionMessage"
    Write-Error $Message -Category InvalidResult
}

Provided under the Apache License version 2.0.

Report an issue

To use this template in Octopus Deploy, copy the JSON below and paste it into the Library → Step templates → Import dialog.

{
  "Id": "1ca0401c-dfca-420e-81ca-1f4b7cf02d2d",
  "Name": "JWT - Generate JSON Web Token",
  "Description": "Generates a [Json Web Token (JWT)](https://en.wikipedia.org/wiki/JSON_Web_Token) for use with applications that require a JWT token. The resulting JWT will be stored as a [sensitive output variable](https://octopus.com/docs/projects/variables/output-variables#sensitive-output-variables) called **JWT**.\n\nA private key needs to be provided that will sign the combined JWT header and payload.\n\nCurrently, the following three signing algorithms are supported:\n\n1. `RS256` - RSASSA-PKCS1-v1_5 using SHA-256\n2. `RS384` - RSASSA-PKCS1-v1_5 using SHA-384\n3. `RS512` - RSASSA-PKCS1-v1_5 using SHA-512\n\nThe default is `RS256`.\n\n**Notes:**\n- Tested on Windows and Linux (PowerShell Core)\n- Tested with Octopus **2020.1**",
  "Version": 5,
  "ExportedAt": "2021-08-09T11:51:09.772Z",
  "ActionType": "Octopus.Script",
  "Author": "harrisonmeister",
  "Packages": [],
  "Parameters": [
    {
      "Id": "71bacc82-21d7-4d08-a944-15dacd0a3405",
      "Name": "Generate.JWT.PrivateKey",
      "Label": "Private key used to sign the JWT",
      "HelpText": "Provide the private key in PEM format to be used to sign the JWT.\n\nAccepted headers: \n\n- `----BEGIN RSA PRIVATE KEY-----`\n- `-----BEGIN PRIVATE KEY----`\n\n**Note:** It's recommended to use a sensitive variable to provide this value. If you instead enter the private key directly, the step template will attempt to detect this by replacing spaces with new lines ``n`.",
      "DefaultValue": "",
      "DisplaySettings": {
        "Octopus.ControlType": "Sensitive"
      }
    },
    {
      "Id": "84759ecc-a3f6-4822-a678-833269a8bf0d",
      "Name": "Generate.JWT.Signing.Algorithm",
      "Label": "JWT signing algorithm",
      "HelpText": "The JWA [algorithm](https://datatracker.ietf.org/doc/html/rfc7518#section-3.1) to use when signing the JWT. The default is `RS256` (RSASSA-PKCS1-v1_5 using SHA-256).\n\nThis value is also used for the `alg` [algorithm header](https://datatracker.ietf.org/doc/html/rfc7515#section-4.1.1).",
      "DefaultValue": "RS256",
      "DisplaySettings": {
        "Octopus.ControlType": "Select",
        "Octopus.SelectOptions": "RS256|RS256: RSASSA-PKCS1-v1_5 using SHA-256\nRS384|RS384: RSASSA-PKCS1-v1_5 using SHA-384\nRS512|RS512: RSASSA-PKCS1-v1_5 using SHA-512"
      }
    },
    {
      "Id": "76d289c5-f6f9-4cfb-b876-13515745f03f",
      "Name": "Generate.JWT.ExpiresAfterMinutes",
      "Label": "JWT expiry time in minutes",
      "HelpText": "The number of minutes after the JWT is created that it expires. This value is used for the `exp` [expiration time claim](https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.4). The default is `20` minutes.",
      "DefaultValue": "20",
      "DisplaySettings": {
        "Octopus.ControlType": "SingleLineText"
      }
    },
    {
      "Id": "20d57320-15e4-4777-916b-8563192a326d",
      "Name": "Generate.JWT.Issuer",
      "Label": "JWT Issuer (optional)",
      "HelpText": "*Optional* - The `iss` [isuer claim](https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.1) identifies the principal that issued the JWT.",
      "DefaultValue": "",
      "DisplaySettings": {
        "Octopus.ControlType": "SingleLineText"
      }
    },
    {
      "Id": "e7026958-60cb-4652-9fe0-2ef2965de951",
      "Name": "Generate.JWT.Subject",
      "Label": "JWT Subject (optional)",
      "HelpText": "*Optional* subject (`sub`) of the JWT.",
      "DefaultValue": "",
      "DisplaySettings": {
        "Octopus.ControlType": "SingleLineText"
      }
    },
    {
      "Id": "12a68971-8e0b-4b90-9a27-4c81413d0fb7",
      "Name": "Generate.JWT.Audience",
      "Label": "Audience claims (optional)",
      "HelpText": "*Optional* - The `aud` [audience claim](https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.3) identifies the recipients that the JWT is intended for. This is typically the base address of the resource being accessed, such as `https://example-domain.com`\n\n*Note:* \n- For multiple audiences, enter each entry on a new line. \n- Any entries that are an empty string will be ignored.",
      "DefaultValue": "",
      "DisplaySettings": {
        "Octopus.ControlType": "MultiLineText"
      }
    },
    {
      "Id": "1bd2294c-64a3-4dad-bb9f-1a4cce5eaff5",
      "Name": "Generate.JWT.Groups",
      "Label": "Group claims (optional)",
      "HelpText": "*Optional* - A `groups` [private claim](https://datatracker.ietf.org/doc/html/rfc7519#section-4.3) that identifies groups associated with this JWT. \n\n*Note:* \n- For multiple groups, enter each entry on a new line. \n- Any entries that are an empty string will be ignored.",
      "DefaultValue": "",
      "DisplaySettings": {
        "Octopus.ControlType": "MultiLineText"
      }
    },
    {
      "Id": "cdc3fe41-4669-48c8-8e65-1265faa79361",
      "Name": "Generate.JWT.TTL",
      "Label": "TTL claim (optional)",
      "HelpText": "*Optional* - A `ttl` [private claim](https://datatracker.ietf.org/doc/html/rfc7519#section-4.3) that identifies the time-to-live for the JWT.\n\nThis value can be useful when authenticating with a Secrets Manager and the `ttl` value is mapped through to a resulting token used for authentication.",
      "DefaultValue": "",
      "DisplaySettings": {
        "Octopus.ControlType": "SingleLineText"
      }
    },
    {
      "Id": "51b2ec44-1a8d-43ce-8e06-4d0262eed45e",
      "Name": "Generate.JWT.TTL.Max",
      "Label": "Max TTL claim (optional)",
      "HelpText": "*Optional* - A `max_ttl` [private claim](https://datatracker.ietf.org/doc/html/rfc7519#section-4.3) that identifies the time-to-live for the JWT.\n\nThis value can be useful when authenticating with a Secrets Manager and the `max_ttl` value is mapped through to a resulting token used for authentication.",
      "DefaultValue": "",
      "DisplaySettings": {
        "Octopus.ControlType": "SingleLineText"
      }
    },
    {
      "Id": "028ec077-a40f-4aa3-8099-2e873ab710a5",
      "Name": "Generate.JWT.PrivateClaim.Name",
      "Label": "Custom private claim name (optional)",
      "HelpText": "*Optional* - The name of a custom [private claim](https://datatracker.ietf.org/doc/html/rfc7519#section-4.3) that can be included in this JWT. \n\n**Note:** \n\n- Don't include the value for the private claim. Set the value in the `Generate.JWT.PrivateClaim.Value` parameter instead.\n- If this value is null or an empty string, the private claim won't be added to the JWT payload.",
      "DefaultValue": "",
      "DisplaySettings": {
        "Octopus.ControlType": "SingleLineText"
      }
    },
    {
      "Id": "84a50912-284b-4c8a-bfee-87cd7cbc527e",
      "Name": "Generate.JWT.PrivateClaim.Value",
      "Label": "Custom private claim value (optional)",
      "HelpText": "*Optional* - The value for the custom [private claim](https://datatracker.ietf.org/doc/html/rfc7519#section-4.3) as specified in the parameter `Generate.JWT.PrivateClaim.Name` to be included in this JWT.",
      "DefaultValue": "",
      "DisplaySettings": {
        "Octopus.ControlType": "SingleLineText"
      }
    }
  ],
  "Properties": {
    "Octopus.Action.Script.ScriptSource": "Inline",
    "Octopus.Action.Script.Syntax": "PowerShell",
    "Octopus.Action.Script.ScriptBody": "$ErrorActionPreference = 'Stop'\n\n# Variables\n$GENERATE_JWT_PRIVATE_KEY = $OctopusParameters[\"Generate.JWT.PrivateKey\"]\n$GENERATE_JWT_ALGORITHM = $OctopusParameters[\"Generate.JWT.Signing.Algorithm\"]\n$GENERATE_JWT_EXPIRES_MINS = $OctopusParameters[\"Generate.JWT.ExpiresAfterMinutes\"]\n\n# Optional \n$GENERATE_JWT_ISSUER = $OctopusParameters[\"Generate.JWT.Issuer\"]\n$GENERATE_JWT_SUBJECT = $OctopusParameters[\"Generate.JWT.Subject\"]\n$GENERATE_JWT_GROUPS = $OctopusParameters[\"Generate.JWT.Groups\"]\n$GENERATE_JWT_AUDIENCE = $OctopusParameters[\"Generate.JWT.Audience\"]\n$GENERATE_JWT_TTL = $OctopusParameters[\"Generate.JWT.TTL\"]\n$GENERATE_JWT_MAX_TTL = $OctopusParameters[\"Generate.JWT.TTL.Max\"]\n$GENERATE_JWT_PRIVATE_CLAIM_NAME = $OctopusParameters[\"Generate.JWT.PrivateClaim.Name\"]\n$GENERATE_JWT_PRIVATE_CLAIM_VALUE = $OctopusParameters[\"Generate.JWT.PrivateClaim.Value\"]\n\n# Validation\nif ([string]::IsNullOrWhiteSpace($GENERATE_JWT_PRIVATE_KEY)) {\n    throw \"Required parameter Generate.JWT.PrivateKey not specified.\"\n}\nif ([string]::IsNullOrWhiteSpace($GENERATE_JWT_ALGORITHM)) {\n    throw \"Required parameter Generate.JWT.Signing.Algorithm not specified.\"\n}\nif ([string]::IsNullOrWhiteSpace($GENERATE_JWT_EXPIRES_MINS)) {\n    throw \"Required parameter Generate.JWT.ExpiresAfterMinutes not specified.\"\n}\nif ([string]::IsNullOrWhiteSpace($GENERATE_JWT_AUDIENCE)) {\n    throw \"Required parameter Generate.JWT.Audience not specified.\"\n}\n\n# Optional fields that require validation\nif ([string]::IsNullOrWhiteSpace($GENERATE_JWT_PRIVATE_CLAIM_NAME) -eq $False) {\n    if ([string]::IsNullOrWhiteSpace($GENERATE_JWT_PRIVATE_CLAIM_VALUE)) {\n        throw \"A private claim name has been specified with no value found in Generate.JWT.PrivateClaim.Value.\"\n    }\n}\n\n# Helper functions\n###############################################################################\n\nfunction ConvertTo-JwtBase64 {\n    param (\n        $Value\n    )\n    if ($Value -is [string]) {\n        $ConvertedValue = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($Value)) -replace '\\+', '-' -replace '/', '_' -replace '='\n    }\n    elseif ($Value -is [byte[]]) {\n        $ConvertedValue = [Convert]::ToBase64String($Value) -replace '\\+', '-' -replace '/', '_' -replace '='\n    }\n\n    return $ConvertedValue\n}\n\n###############################################################################\n\n# Signing functions\n###############################################################################\n\n$RsaPrivateKey_Header = \"-----BEGIN RSA PRIVATE KEY-----\"\n$RsaPrivateKey_Footer = \"-----END RSA PRIVATE KEY-----\"\n$Pkcs8_PrivateKey_Header = \"-----BEGIN PRIVATE KEY-----\"\n$Pkcs8_PrivateKey_Footer = \"-----END PRIVATE KEY-----\"\n\nfunction ExtractPemData {\n    param (\n        [string]$Pem,\n        [string]$Header,\n        [string]$Footer\n    )\n\n    $Start = $Pem.IndexOf($Header) + $Header.Length\n    $End = $Pem.IndexOf($Footer, $Start) - $Start\n    $EncodedPem = ($Pem.Substring($Start, $End).Trim())  -Replace \" \", \"`n\"\n    \n    $PemData = [Convert]::FromBase64String($EncodedPem)\n    return [byte[]]$PemData\n}\n\nfunction DecodeIntSize {\n    param (\n        [System.IO.BinaryReader]$BinaryReader\n    )\n    \n    [byte]$byteValue = $BinaryReader.ReadByte()\n    \n    # If anything other than 0x02, an ASN.1 integer follows.\n    if ($byteValue -ne 0x02) {\n        return 0;\n    }\n    \n    $byteValue = $BinaryReader.ReadByte()\n    # 0x81 == Data size in next byte.\n    if ($byteValue -eq 0x81) { \n        $size = $BinaryReader.ReadByte()\n    }\n    # 0x82 == Data size in next 2 bytes.\n    else {\n        if ($byteValue -eq 0x82) {\n            [byte]$high = $BinaryReader.ReadByte()\n            [byte]$low = $BinaryReader.ReadByte()\n            $byteValues = [byte[]]@($low, $high, 0x00, 0x00)\n            $size = [System.BitConverter]::ToInt32($byteValues, 0)\n        }\n        else {\n            # Otherwise, data size has already been read above.\n            $size = $byteValue\n        }\n    }\n    # Remove high-order zeros in data\n    $byteValue = $BinaryReader.ReadByte()\n    while ($byteValue -eq 0x00) {\n        $byteValue = $BinaryReader.ReadByte()\n        $size -= 1\n    }\n\n    $BinaryReader.BaseStream.Seek(-1, [System.IO.SeekOrigin]::Current) | Out-Null\n    return $size\n}\n\nfunction PadByteArray {\n    param (\n        [byte[]]$Bytes,\n        [int]$Size\n    )\n\n    if ($Bytes.Length -eq $Size) {\n        return $Bytes\n    }\n    if ($Bytes.Length -gt $Size) {\n        throw \"Specified size '$Size' to pad is too small for byte array of size '$($Bytes.Length)'.\"\n    }\n\n    [byte[]]$PaddedBytes = New-Object Byte[] $Size\n    [System.Array]::Copy($Bytes, 0, $PaddedBytes, $Size - $bytes.Length, $bytes.Length) | Out-Null\n    return $PaddedBytes\n}\n\nfunction Compare-ByteArrays {\n    param (\n        [byte[]]$First,\n        [byte[]]$Second\n    )\n    if ($First.Length -ne $Second.Length) {\n        return $False\n    }\n    [int]$i = 0\n    foreach ($byte in $First) {\n        if ($byte -ne $Second[$i]) {\n            return $False\n        }\n        $i = $i + 1\n    }\n    return $True\n}\n\nfunction CreateRSAFromPkcs8 {\n    param (\n        [byte[]]$KeyBytes\n    )\n    Write-Verbose \"Reading RSA Pkcs8 private key bytes\"\n\n    # The encoded OID sequence for PKCS #1 rsaEncryption szOID_RSA_RSA = \"1.2.840.113549.1.1.1\"\n    # this byte[] includes the sequence byte and terminal encoded null \n    [byte[]]$SeqOID = 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00\n    [byte[]]$Seq = New-Object byte[] 15\n\n    # Have to wrap $KeyBytes in another array :|\n    $MemoryStream = New-Object System.IO.MemoryStream(, $KeyBytes) \n    $reader = New-Object System.IO.BinaryReader($MemoryStream)\n    $StreamLength = [int]$MemoryStream.Length\n    \n    try {\n        [UInt16]$Bytes = $reader.ReadUInt16()\n\n        if ($Bytes -eq 0x8130) {\n            $reader.ReadByte() | Out-Null\n        }\n        elseif ($Bytes -eq 0x8230) {\n            $reader.ReadInt16() | Out-Null\n        }\n        else {\n            return $null\n        }\n        \n        [byte]$byteValue = $reader.ReadByte()\n\n        if ($byteValue -ne 0x02) {\n            return $null\n        }\n\n        $Bytes = $reader.ReadUInt16()\n\n        if ($Bytes -ne 0x0001) {\n            return $null\n        }\n\n        # Read the Sequence OID\n        $Seq = $reader.ReadBytes(15)\n        $SequenceMatches = Compare-ByteArrays -First $Seq -Second $SeqOID\n        if ($SequenceMatches -eq $False) {\n            Write-Verbose \"Sequence OID doesnt match\"\n            return $null\n        }\n\n        $byteValue = $reader.ReadByte()\n        # Next byte should be a Octet string\n        if ($byteValue -ne 0x04) {\n            return $null\n        }\n        # Read next byte / 2 bytes. \n        # Should be either: 0x81 or 0x82; otherwise it's the byte count.\n        $byteValue = $reader.ReadByte()\n        if ($byteValue -eq 0x81) {\n            $reader.ReadByte() | Out-Null\n        }\n        else {\n            if ($byteValue -eq 0x82) {\n                $reader.ReadUInt16() | Out-Null\n            }\n        }\n\n        # Remaining sequence *should* be the RSA Pkcs1 private Key bytes\n        [byte[]]$RsaKeyBytes = $reader.ReadBytes([int]($StreamLength - $MemoryStream.Position))\n        Write-Verbose \"Attempting to create RSA object from remaining Pkcs1 bytes\"\n        $rsa = CreateRSAFromPkcs1 -KeyBytes $RsaKeyBytes\n        return $rsa\n    }\n    catch {\n        Write-Warning \"CreateRSAFromPkcs8: Exception occurred - $($_.Exception.Message)\"\n        return $null\n    }\n    finally {\n        if ($null -ne $reader) { $reader.Close() }\n        if ($null -ne $MemoryStream) { $MemoryStream.Close() }\n    }\n}\n\nfunction CreateRSAFromPkcs1 {\n    param (\n        [byte[]]$KeyBytes\n    )\n    Write-Verbose \"Reading RSA Pkcs1 private key bytes\"\n    # Have to wrap $KeyBytes in another array :|\n    $MemoryStream = New-Object System.IO.MemoryStream(, $KeyBytes) \n    $reader = New-Object System.IO.BinaryReader($MemoryStream)\n    try {\n               \n        [UInt16]$Bytes = $reader.ReadUInt16()\n\n        if ($Bytes -eq 0x8130) {\n            $reader.ReadByte() | Out-Null\n        }\n        elseif ($Bytes -eq 0x8230) {\n            $reader.ReadInt16() | Out-Null\n        }\n        else {\n            return $null\n        }\n    \n        $Bytes = $reader.ReadUInt16()\n        if ($Bytes -ne 0x0102) {\n            return $null\n        }\n    \n        [byte]$byteValue = $reader.ReadByte()\n        if ($byteValue -ne 0x00) {\n            return $null\n        }\n\n        # Private key parameters are integer sequences.\n        # For a summary of the RSA Parameters fields, \n        # See https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.rsaparameters#summary-of-fields\n        \n        $Modulus_Size = DecodeIntSize -BinaryReader $reader\n        $Modulus = $reader.ReadBytes($Modulus_Size)\n\n        $E_Size = DecodeIntSize -BinaryReader $reader\n        $E = $reader.ReadBytes($E_Size)\n\n        $D_Size = DecodeIntSize -BinaryReader $reader\n        $D = $reader.ReadBytes($D_Size)\n\n        $P_Size = DecodeIntSize -BinaryReader $reader\n        $P = $reader.ReadBytes($P_Size)\n\n        $Q_Size = DecodeIntSize -BinaryReader $reader\n        $Q = $reader.ReadBytes($Q_Size)\n\n        $DP_Size = DecodeIntSize -BinaryReader $reader\n        $DP = $reader.ReadBytes($DP_Size)\n\n        $DQ_Size = DecodeIntSize -BinaryReader $reader\n        $DQ = $reader.ReadBytes($DQ_Size)\n\n        $IQ_Size = DecodeIntSize -BinaryReader $reader\n        $IQ = $reader.ReadBytes($IQ_Size)\n\n        $rsa = New-Object System.Security.Cryptography.RSACryptoServiceProvider\n        $rsaParameters = New-Object System.Security.Cryptography.RSAParameters\n        $rsaParameters.Modulus = $Modulus\n        $rsaParameters.Exponent = $E\n        $rsaParameters.P = $P\n        $rsaParameters.Q = $Q\n        # Some RSAParameter values dont play well with byte buffers having leading zeroes removed.\n        $rsaParameters.D = PadByteArray -Bytes $D -Size $Modulus.Length\n        $rsaParameters.DP = PadByteArray -Bytes $DP -Size $P.Length\n        $rsaParameters.DQ = PadByteArray -Bytes $DQ -Size $Q.Length\n        $rsaParameters.InverseQ = PadByteArray -Bytes $IQ -Size $Q.Length\n        $rsa.ImportParameters($rsaParameters)\n\n        Write-Verbose \"Completed RSA object creation\"\n        return $rsa\n    }\n    catch {\n        Write-Warning \"CreateRSA-FromPkcs1: Exception occurred - $($_.Exception.Message)\"\n        return $null\n    }\n    finally {\n        if ($null -ne $reader) { $reader.Close() }\n        if ($null -ne $MemoryStream) { $MemoryStream.Close() }\n    }\n}\n\nfunction CreateSigningKey {\n    param (\n        [string]$Key\n    )\n    try {\n        $Key = $Key.Trim()\n        switch -Wildcard($Key) {\n            \"$Pkcs8_PrivateKey_Header*\" {\n                $KeyBytes = ExtractPemData -PEM $Key -Header $Pkcs8_PrivateKey_Header -Footer $Pkcs8_PrivateKey_Footer\n                $SigningKey = CreateRSAFromPkcs8 -KeyBytes $KeyBytes\n                return $SigningKey\n            }\n            \"$RsaPrivateKey_Header*\" {\n                $KeyBytes = ExtractPemData -PEM $Key -Header $RsaPrivateKey_Header -Footer $RsaPrivateKey_Footer\n                $SigningKey = CreateRSAFromPkcs1 -KeyBytes $KeyBytes\n                return $SigningKey\n            }\n            default {\n                Write-Verbose \"The PEM header could not be found. Accepted headers: 'BEGIN PRIVATE KEY', 'BEGIN RSA PRIVATE KEY'\"\n                return $null\n            }\n        }\n    }\n    catch {\n        Write-Warning \"Couldn't create signing key: $($_.Exception.Message)\"\n        return $null\n    }\n}\n\n###############################################################################\n\n# Local variables\n$audiences = @()\nif (![string]::IsNullOrWhiteSpace($GENERATE_JWT_AUDIENCE)) {\n    @(($GENERATE_JWT_AUDIENCE -Split \"`n\").Trim()) | ForEach-Object {\n        if (![string]::IsNullOrWhiteSpace($_)) {\n            $audiences += $_\n        }\n    }\n}\n\n$groups = @()\nif (![string]::IsNullOrWhiteSpace($GENERATE_JWT_GROUPS)) {\n    @(($GENERATE_JWT_GROUPS -Split \"`n\").Trim()) | ForEach-Object {\n        if (![string]::IsNullOrWhiteSpace($_)) {\n            $groups += $_\n        }\n    }\n}\n\n$StepName = $OctopusParameters[\"Octopus.Step.Name\"]\n$OutputVariableName = \"JWT\"\n\nWrite-Verbose \"Generate.JWT.Signing.Algorithm: $GENERATE_JWT_ALGORITHM\"\nWrite-Verbose \"Generate.JWT.ExpiresAfterMinutes: $GENERATE_JWT_EXPIRES_MINS\"\nWrite-Verbose \"Generate.JWT.Issuer: $GENERATE_JWT_ISSUER\"\nWrite-Verbose \"Generate.JWT.Subject: $GENERATE_JWT_SUBJECT\"\nWrite-Verbose \"Generate.JWT.Audience(s): $($audiences -Join \",\")\"\nWrite-Verbose \"Generate.JWT.Group(s): $($groups -Join \",\")\"\nWrite-Verbose \"Generate.JWT.TTL: $GENERATE_JWT_TTL\"\nWrite-Verbose \"Generate.JWT.TTL.Max: $GENERATE_JWT_MAX_TTL\"\nWrite-Verbose \"Generate.JWT.PrivateClaim.Name: $GENERATE_JWT_PRIVATE_CLAIM_NAME\"\nWrite-Verbose \"Generate.JWT.PrivateClaim.Value: $GENERATE_JWT_PRIVATE_CLAIM_VALUE\"\nWrite-Verbose \"Step Name: $StepName\"\n\ntry {\n\n    # Created + Expires\n    $Created = (Get-Date).ToUniversalTime()\n    $Expires = $Created.AddMinutes([int]$GENERATE_JWT_EXPIRES_MINS)\n\n    $createDate = [Math]::Floor([decimal](Get-Date($Created) -UFormat \"%s\"))\n    $expiryDate = [Math]::Floor([decimal](Get-Date($Expires) -UFormat \"%s\"))\n\n    $JwtHeader = @{\n        alg = $GENERATE_JWT_ALGORITHM;\n        typ = \"JWT\";\n    } | ConvertTo-Json -Compress\n    \n    $JwtPayload = [Ordered]@{\n        iat = [long]$createDate;\n        exp = [long]$expiryDate;\n    }\n\n    # Check for optional issuer: https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.1\n    if ([string]::IsNullOrWhiteSpace($GENERATE_JWT_ISSUER) -eq $False) {\n        $JwtPayload | Add-Member -NotePropertyName iss -NotePropertyValue $GENERATE_JWT_ISSUER\n    }\n\n    # Check for optional subject: https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.2\n    if ([string]::IsNullOrWhiteSpace($GENERATE_JWT_SUBJECT) -eq $False) {\n        $JwtPayload | Add-Member -NotePropertyName sub -NotePropertyValue $GENERATE_JWT_SUBJECT\n    }\n\n    # Check for optional audience: https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.3\n    if ($audiences.Length -gt 0) {\n        $JwtPayload | Add-Member -NotePropertyName aud -NotePropertyValue $audiences\n    }\n    # Check for optional \"groups\" field\n    if ($groups.Length -gt 0) {\n        $JwtPayload | Add-Member -NotePropertyName groups -NotePropertyValue $groups\n    }\n\n    # Check for optional ttl field\n    if ([string]::IsNullOrWhiteSpace($GENERATE_JWT_TTL) -eq $False) {\n        $JwtPayload | Add-Member -NotePropertyName ttl -NotePropertyValue $GENERATE_JWT_TTL\n    }\n    # Check for optional max_ttl field\n    if ([string]::IsNullOrWhiteSpace($GENERATE_JWT_MAX_TTL) -eq $False) {\n        $JwtPayload | Add-Member -NotePropertyName max_ttl -NotePropertyValue $GENERATE_JWT_MAX_TTL\n    }\n\n    # Check for an optional private claim name and value: https://datatracker.ietf.org/doc/html/rfc7519#section-4.3\n    if ([string]::IsNullOrWhiteSpace($GENERATE_JWT_PRIVATE_CLAIM_NAME) -eq $False) {\n        $JwtPayload | Add-Member -NotePropertyName $GENERATE_JWT_PRIVATE_CLAIM_NAME -NotePropertyValue $GENERATE_JWT_PRIVATE_CLAIM_VALUE\n    } \n\n    $JwtPayload = $JwtPayload | ConvertTo-Json -Compress\n\n    $base64Header = ConvertTo-JwtBase64 -Value $JwtHeader\n    $base64Payload = ConvertTo-JwtBase64 -Value $JwtPayload\n\n    $Jwt = $base64Header + '.' + $base64Payload\n\n    $JwtBytes = [System.Text.Encoding]::UTF8.GetBytes($Jwt)\n    $JwtSignature = $null\n    \n    switch ($GENERATE_JWT_ALGORITHM) {\n        \"RS256\" {\n            try { \n\n                $rsa = CreateSigningKey -Key $GENERATE_JWT_PRIVATE_KEY\n                if ($null -eq $rsa) {\n                    throw \"Couldn't create RSA object\"\n                }\n                $Signature = $rsa.SignData($JwtBytes, [Security.Cryptography.HashAlgorithmName]::SHA256, [Security.Cryptography.RSASignaturePadding]::Pkcs1) \n                $JwtSignature = ConvertTo-JwtBase64 -Value $Signature\n            }\n            catch { throw \"Signing with SHA256 and Pkcs1 padding failed using private key: $($_.Exception.Message)\" }\n            finally { if ($null -ne $rsa) { $rsa.Dispose() } }\n            \n        }\n        \"RS384\" {\n            try { \n                $rsa = CreateSigningKey -Key $GENERATE_JWT_PRIVATE_KEY\n                if ($null -eq $rsa) {\n                    throw \"Couldn't create RSA object\"\n                }\n                $Signature = $rsa.SignData($JwtBytes, [Security.Cryptography.HashAlgorithmName]::SHA384, [Security.Cryptography.RSASignaturePadding]::Pkcs1) \n                $JwtSignature = ConvertTo-JwtBase64 -Value $Signature\n            }\n            catch { throw \"Signing with SHA384 and Pkcs1 padding failed using private key: $($_.Exception.Message)\" }\n            finally { if ($null -ne $rsa) { $rsa.Dispose() } }\n        }\n        \"RS512\" {\n            try { \n                $rsa = CreateSigningKey -Key $GENERATE_JWT_PRIVATE_KEY\n                if ($null -eq $rsa) {\n                    throw \"Couldn't create RSA object\"\n                }\n                $Signature = $rsa.SignData($JwtBytes, [Security.Cryptography.HashAlgorithmName]::SHA512, [Security.Cryptography.RSASignaturePadding]::Pkcs1) \n                $JwtSignature = ConvertTo-JwtBase64 -Value $Signature\n            }\n            catch { throw \"Signing with SHA512 and Pkcs1 padding failed using private key: $($_.Exception.Message)\" }\n            finally { if ($null -ne $rsa) { $rsa.Dispose() } }\n        }\n        default {\n            throw \"The algorithm is not one of the supported: 'RS256', 'RS384', 'RS512'\"\n        }\n    }\n    if ([string]::IsNullOrWhiteSpace($JwtSignature) -eq $True) {\n        throw \"JWT signature empty.\"\n    }\n\n    $Jwt = \"$Jwt.$JwtSignature\"\n    Set-OctopusVariable -Name $OutputVariableName -Value $Jwt -Sensitive\n    Write-Host \"Created output variable: ##{Octopus.Action[$StepName].Output.$OutputVariableName}\"\n}\ncatch {\n    $ExceptionMessage = $_.Exception.Message\n    $Message = \"An error occurred generating a JWT: $ExceptionMessage\"\n    Write-Error $Message -Category InvalidResult\n}"
  },
  "Category": "JWT",
  "HistoryUrl": "https://github.com/OctopusDeploy/Library/commits/master/step-templates//opt/buildagent/work/75443764cd38076d/step-templates/jwt-generate-json-web-token.json",
  "Website": "/step-templates/1ca0401c-dfca-420e-81ca-1f4b7cf02d2d",
  "Logo": "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",
  "$Meta": {
    "Type": "ActionTemplate"
  }
}

History

Page updated on Monday, August 9, 2021