Skip to content

Commit 0da229e

Browse files
🩹 [Patch]: Improve IAT loading and storing of target info (#198)
## Description This pull request includes updates to the PowerShell scripts for GitHub authentication and context management. The key changes enhance the handling of GitHub App tokens, add verbosity, and improve the flexibility of the authentication process. Enhancements to `Get-GitHubAppJSONWebToken` function: * Added `begin`, `process`, and `end` blocks to improve logging and structure. [[1]](diffhunk://#diff-4fa912030b38f84b885dd74c5e7cc85069ad6358e95c26ac529bf3de1e3ef13eR69-R74) [[2]](diffhunk://#diff-4fa912030b38f84b885dd74c5e7cc85069ad6358e95c26ac529bf3de1e3ef13eR129-R142) Improvements to `Connect-GitHubAccount` function: * Introduced a new `Shallow` parameter to avoid loading tokens for GitHub App installations. * Removed default values for `Owner` and `Repo` parameters to enhance flexibility. * Added logic to set `TargetType`, `Enterprise`, `TargetName`, `Owner`, and `Repo` from the GitHub event context. * Updated context dictionary to include `TargetType` and `TargetName`. * Replaced `Get-GitHubApp` with `Connect-GitHubApp -Shallow:$Shallow` for more consistent app connection handling. Updates to `Connect-GitHubApp` function: * Added a `Shallow` parameter to control whether to load full credentials or just metadata for GitHub App installations. [[1]](diffhunk://#diff-7d1951ca779d73ee11b11db4ade6f33f8ea5fcf052324a72d2c8e83304eaa045L67-R71) [[2]](diffhunk://#diff-7d1951ca779d73ee11b11db4ade6f33f8ea5fcf052324a72d2c8e83304eaa045R111-R118) Modification to `Set-GitHubContext` function: * Adjusted the context name format to include `TargetType` and `TargetName` for better clarity. ## Type of change <!-- Use the check-boxes [x] on the options that are relevant. --> - [ ] 📖 [Docs] - [ ] 🪲 [Fix] - [x] 🩹 [Patch] - [ ] ⚠️ [Security fix] - [ ] 🚀 [Feature] - [ ] 🌟 [Breaking change] ## Checklist <!-- Use the check-boxes [x] on the options that are relevant. --> - [x] I have performed a self-review of my own code - [x] I have commented my code, particularly in hard-to-understand areas
1 parent 75085cb commit 0da229e

File tree

6 files changed

+108
-68
lines changed

6 files changed

+108
-68
lines changed

src/functions/public/API/Invoke-GitHubAPI.ps1

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,12 +95,12 @@
9595

9696
process {
9797
$Token = $Context.Token
98-
Write-Debug "Token : [$Token]"
98+
Write-Debug "Token : [$Token]"
9999

100100
if ([string]::IsNullOrEmpty($TokenType)) {
101101
$TokenType = $Context.TokenType
102102
}
103-
Write-Debug "TokenType : [$($Context.TokenType)]"
103+
Write-Debug "TokenType : [$($Context.TokenType)]"
104104

105105
if ([string]::IsNullOrEmpty($ApiBaseUri)) {
106106
$ApiBaseUri = $Context.ApiBaseUri

src/functions/public/Apps/Get-GitHubAppJSONWebToken.ps1

Lines changed: 66 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -66,62 +66,77 @@ function Get-GitHubAppJSONWebToken {
6666
[object] $PrivateKey
6767
)
6868

69-
if ($PrivateKeyFilePath) {
70-
if (-not (Test-Path -Path $PrivateKeyFilePath)) {
71-
throw "The private key path [$PrivateKeyFilePath] does not exist."
69+
begin {
70+
$commandName = $MyInvocation.MyCommand.Name
71+
Write-Verbose "[$commandName] - Start"
72+
}
73+
74+
process {
75+
if ($PrivateKeyFilePath) {
76+
if (-not (Test-Path -Path $PrivateKeyFilePath)) {
77+
throw "The private key path [$PrivateKeyFilePath] does not exist."
78+
}
79+
80+
$PrivateKey = Get-Content -Path $PrivateKeyFilePath -Raw
7281
}
7382

74-
$PrivateKey = Get-Content -Path $PrivateKeyFilePath -Raw
83+
if ($PrivateKey -is [securestring]) {
84+
$PrivateKey = $PrivateKey | ConvertFrom-SecureString -AsPlainText
85+
}
86+
87+
$header = [Convert]::ToBase64String(
88+
[System.Text.Encoding]::UTF8.GetBytes(
89+
(
90+
ConvertTo-Json -InputObject @{
91+
alg = 'RS256'
92+
typ = 'JWT'
93+
}
94+
)
95+
)
96+
).TrimEnd('=').Replace('+', '-').Replace('/', '_')
97+
98+
$iat = [System.DateTimeOffset]::UtcNow.AddSeconds(-10).ToUnixTimeSeconds()
99+
$exp = [System.DateTimeOffset]::UtcNow.AddMinutes(10).ToUnixTimeSeconds()
100+
$payload = [Convert]::ToBase64String(
101+
[System.Text.Encoding]::UTF8.GetBytes(
102+
(
103+
ConvertTo-Json -InputObject @{
104+
iat = $iat
105+
exp = $exp
106+
iss = $ClientId
107+
}
108+
)
109+
)
110+
).TrimEnd('=').Replace('+', '-').Replace('/', '_')
111+
112+
$rsa = [System.Security.Cryptography.RSA]::Create()
113+
$rsa.ImportFromPem($PrivateKey)
114+
115+
$signature = [Convert]::ToBase64String(
116+
$rsa.SignData(
117+
[System.Text.Encoding]::UTF8.GetBytes("$header.$payload"),
118+
[System.Security.Cryptography.HashAlgorithmName]::SHA256,
119+
[System.Security.Cryptography.RSASignaturePadding]::Pkcs1
120+
)
121+
).TrimEnd('=').Replace('+', '-').Replace('/', '_')
122+
$jwt = "$header.$payload.$signature"
123+
[pscustomobject]@{
124+
Token = ConvertTo-SecureString -String $jwt -AsPlainText
125+
IssuedAt = $iat
126+
ExpiresAt = $exp
127+
Issuer = $ClientId
128+
}
75129
}
76130

77-
if ($PrivateKey -is [securestring]) {
78-
$PrivateKey = $PrivateKey | ConvertFrom-SecureString -AsPlainText
131+
end {
132+
$commandName = $MyInvocation.MyCommand.Name
133+
Write-Verbose "[$commandName] - End"
79134
}
80135

81-
$header = [Convert]::ToBase64String(
82-
[System.Text.Encoding]::UTF8.GetBytes(
83-
(
84-
ConvertTo-Json -InputObject @{
85-
alg = 'RS256'
86-
typ = 'JWT'
87-
}
88-
)
89-
)
90-
).TrimEnd('=').Replace('+', '-').Replace('/', '_')
91-
92-
$iat = [System.DateTimeOffset]::UtcNow.AddSeconds(-10).ToUnixTimeSeconds()
93-
$exp = [System.DateTimeOffset]::UtcNow.AddMinutes(10).ToUnixTimeSeconds()
94-
$payload = [Convert]::ToBase64String(
95-
[System.Text.Encoding]::UTF8.GetBytes(
96-
(
97-
ConvertTo-Json -InputObject @{
98-
iat = $iat
99-
exp = $exp
100-
iss = $ClientId
101-
}
102-
)
103-
)
104-
).TrimEnd('=').Replace('+', '-').Replace('/', '_')
105-
106-
$rsa = [System.Security.Cryptography.RSA]::Create()
107-
$rsa.ImportFromPem($PrivateKey)
108-
109-
$signature = [Convert]::ToBase64String(
110-
$rsa.SignData(
111-
[System.Text.Encoding]::UTF8.GetBytes("$header.$payload"),
112-
[System.Security.Cryptography.HashAlgorithmName]::SHA256,
113-
[System.Security.Cryptography.RSASignaturePadding]::Pkcs1
114-
)
115-
).TrimEnd('=').Replace('+', '-').Replace('/', '_')
116-
$jwt = "$header.$payload.$signature"
117-
[pscustomobject]@{
118-
Token = ConvertTo-SecureString -String $jwt -AsPlainText
119-
IssuedAt = $iat
120-
ExpiresAt = $exp
121-
Issuer = $ClientId
136+
clean {
137+
Remove-Variable -Name jwt -ErrorAction SilentlyContinue
138+
Remove-Variable -Name rsa -ErrorAction SilentlyContinue
139+
Remove-Variable -Name signature -ErrorAction SilentlyContinue
140+
[System.GC]::Collect()
122141
}
123-
Remove-Variable -Name jwt -ErrorAction SilentlyContinue
124-
Remove-Variable -Name rsa -ErrorAction SilentlyContinue
125-
Remove-Variable -Name signature -ErrorAction SilentlyContinue
126-
[System.GC]::Collect()
127142
}

src/functions/public/Auth/Connect-GitHubAccount.ps1

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,12 @@
109109
)]
110110
[switch] $SkipAppAutoload,
111111

112+
# Do not load credentials for the GitHub App Installations, just metadata.
113+
[Parameter(
114+
ParameterSetName = 'App'
115+
)]
116+
[switch] $Shallow,
117+
112118
# The default enterprise to use in commands.
113119
[Parameter()]
114120
[string] $Enterprise,
@@ -117,12 +123,12 @@
117123
[Parameter()]
118124
[Alias('Organization')]
119125
[Alias('Org')]
120-
[string] $Owner = $env:GITHUB_REPOSITORY_OWNER,
126+
[string] $Owner,
121127

122128
# Set the default repository to use in commands.
123129
[Parameter()]
124130
[Alias('Repository')]
125-
[string] $Repo = $env:GITHUB_REPOSITORY_NAME,
131+
[string] $Repo,
126132

127133
# API version used for API requests.
128134
[Parameter()]
@@ -175,6 +181,12 @@
175181
if (-not $customTokenProvided -and $gitHubTokenPresent) {
176182
$authType = 'Token'
177183
$Token = $gitHubToken
184+
$gitHubEvent = Get-Content -Path $env:GITHUB_EVENT_PATH -Raw | ConvertFrom-Json
185+
$TargetType = $gitHubEvent.repository.owner.type
186+
$Enterprise = $gitHubEvent.enterprise.slug
187+
$TargetName = $env:GITHUB_REPOSITORY_OWNER
188+
$Owner = $env:GITHUB_REPOSITORY_OWNER
189+
$Repo = $env:GITHUB_REPOSITORY_NAME
178190
}
179191
}
180192

@@ -280,8 +292,10 @@
280292
'ghs' {
281293
Write-Verbose 'Logging in using an installation access token...'
282294
$context += @{
283-
Token = ConvertTo-SecureString -AsPlainText $Token
284-
TokenType = $tokenType
295+
Token = ConvertTo-SecureString -AsPlainText $Token
296+
TokenType = $tokenType
297+
TargetType = $TargetType
298+
TargetName = $TargetName
285299
}
286300
$context['AuthType'] = 'IAT'
287301
}
@@ -303,7 +317,7 @@
303317

304318
if ($authType -eq 'App' -and -not $SkipAppAutoload) {
305319
Write-Verbose 'Loading GitHub App contexts...'
306-
Get-GitHubApp
320+
Connect-GitHubApp -Shallow:$Shallow
307321
}
308322

309323
} catch {

src/functions/public/Auth/Connect-GitHubApp.ps1

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,11 @@
6464
# The context to run the command in. Used to get the details for the API call.
6565
# Can be either a string or a GitHubContext object.
6666
[Parameter()]
67-
[object] $Context = (Get-GitHubContext)
67+
[object] $Context = (Get-GitHubContext),
68+
69+
# Do not load credentials for the GitHub App Installations, just metadata.
70+
[Parameter()]
71+
[switch] $Shallow
6872
)
6973

7074
$commandName = $MyInvocation.MyCommand.Name
@@ -104,7 +108,14 @@
104108
$installations | ForEach-Object {
105109
$installation = $_
106110
$contextParams = @{} + $defaultContextData.Clone()
107-
$token = New-GitHubAppInstallationAccessToken -InstallationID $installation.id
111+
if ($Shallow) {
112+
$token = [PSCustomObject]@{
113+
Token = [securestring]::new()
114+
ExpiresAt = [datetime]::MinValue
115+
}
116+
} else {
117+
$token = New-GitHubAppInstallationAccessToken -InstallationID $installation.id
118+
}
108119
$contextParams += @{
109120
InstallationID = $installation.id
110121
Token = $token.Token

src/functions/public/Auth/Context/Set-GitHubContext.ps1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ function Set-GitHubContext {
7171
}
7272
}
7373
'IAT' {
74-
$ContextName = "$($Context['HostName'])/$login/$($Context['Owner'])" -Replace '\[bot\]'
74+
$ContextName = "$($Context['HostName'])/$login/$($Context.TargetType)/$($Context.TargetName)" -Replace '\[bot\]'
7575
$Context += @{
7676
Name = $ContextName
7777
Type = 'Installation'

tests/GitHub.Tests.ps1

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ Describe 'GitHub' {
3939
{ Connect-GitHubAccount -Token $env:TEST_PAT } | Should -Not -Throw
4040
{ Connect-GitHubAccount } | Should -Not -Throw # Logs on with GitHub Actions' token
4141
(Get-GitHubContext -ListAvailable).Count | Should -Be 2
42-
Get-GitHubConfig -Name 'DefaultContext' | Should -Be 'github.com/github-actions/PSModule'
42+
Get-GitHubConfig -Name 'DefaultContext' | Should -Be 'github.com/github-actions/Organization/PSModule'
4343
Write-Verbose (Get-GitHubContext | Out-String) -Verbose
4444
}
4545

@@ -57,15 +57,15 @@ Describe 'GitHub' {
5757

5858
It 'Can list all contexts' {
5959
Write-Verbose (Get-GitHubContext -ListAvailable | Out-String) -Verbose
60-
(Get-GitHubContext -ListAvailable).Count | Should -Be 3
60+
(Get-GitHubContext -ListAvailable).Count | Should -Be 7
6161
}
6262

6363
It 'Can disconnect a specific context' {
64-
{ Disconnect-GitHubAccount -Context 'github.com/github-actions/PSModule' -Silent } | Should -Not -Throw
65-
(Get-GitHubContext -ListAvailable).Count | Should -Be 2
64+
{ Disconnect-GitHubAccount -Context 'github.com/github-actions/Organization/PSModule' -Silent } | Should -Not -Throw
65+
(Get-GitHubContext -ListAvailable).Count | Should -Be 6
6666
Connect-GitHubAccount
6767
Connect-GitHubAccount -ClientID $env:TEST_APP_CLIENT_ID -PrivateKey $env:TEST_APP_PRIVATE_KEY
68-
(Get-GitHubContext -ListAvailable).Count | Should -Be 3
68+
(Get-GitHubContext -ListAvailable).Count | Should -Be 7
6969
}
7070

7171
It 'Can get the authenticated GitHubApp' {
@@ -88,8 +88,8 @@ Describe 'GitHub' {
8888
}
8989

9090
It 'Can swap context to another' {
91-
{ Set-GitHubDefaultContext -Context 'github.com/github-actions/PSModule' } | Should -Not -Throw
92-
Get-GitHubConfig -Name 'DefaultContext' | Should -Be 'github.com/github-actions/PSModule'
91+
{ Set-GitHubDefaultContext -Context 'github.com/github-actions/Organization/PSModule' } | Should -Not -Throw
92+
Get-GitHubConfig -Name 'DefaultContext' | Should -Be 'github.com/github-actions/Organization/PSModule'
9393
}
9494

9595
# It 'Can be called with a GitHub App Installation Access Token' {

0 commit comments

Comments
 (0)