Skip to content

Commit

Permalink
🚀 [Feature]: Adding support for logging in as a GitHub App (#126)
Browse files Browse the repository at this point in the history
## Description

- Adding support for logging in as a GitHub App

## Type of change

<!-- Use the check-boxes [x] on the options that are relevant. -->

- [ ] 📖 [Docs]
- [ ] 🪲 [Fix]
- [ ] 🩹 [Patch]
- [ ] ⚠️ [Security fix]
- [x] 🚀 [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
  • Loading branch information
MariusStorhaug authored Nov 4, 2024
1 parent 4e62312 commit bb8f984
Show file tree
Hide file tree
Showing 7 changed files with 574 additions and 1 deletion.
1 change: 1 addition & 0 deletions .github/workflows/Linter.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,4 @@ jobs:
VALIDATE_JSCPD: false
VALIDATE_MARKDOWN_PRETTIER: false
VALIDATE_YAML_PRETTIER: false
VALIDATE_GITLEAKS: false
35 changes: 35 additions & 0 deletions src/functions/public/Apps/Get-GitHubApp.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
filter Get-GitHubApp {
<#
.SYNOPSIS
Get the authenticated app
.DESCRIPTION
Returns the GitHub App associated with the authentication credentials used. To see how many app installations are associated with this
GitHub App, see the `installations_count` in the response. For more details about your app's installations, see the
"[List installations for the authenticated app](https://docs.github.com/rest/apps/apps#list-installations-for-the-authenticated-app)"
endpoint.
You must use a [JWT](https://docs.github.com/apps/building-github-apps/authenticating-with-github-apps/#authenticating-as-a-github-app)
to access this endpoint.
.EXAMPLE
Get-GitHubApp
Get the authenticated app.
.NOTES
[Get the authenticated app | GitHub Docs](https://docs.github.com/rest/apps/apps#get-the-authenticated-app)
#>
[OutputType([pscustomobject])]
[CmdletBinding()]
param ()

$inputObject = @{
APIEndpoint = '/app'
Method = 'GET'
}

Invoke-GitHubAPI @inputObject | ForEach-Object {
Write-Output $_.Response
}
}
106 changes: 106 additions & 0 deletions src/functions/public/Apps/Get-GitHubAppJSONWebToken.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
function Get-GitHubAppJSONWebToken {
<#
.SYNOPSIS
Generates a JSON Web Token (JWT) for a GitHub App.
.DESCRIPTION
Generates a JSON Web Token (JWT) for a GitHub App.
.EXAMPLE
Get-GitHubAppJWT -ClientId 'Iv987654321' -PrivateKeyFilePath '/path/to/private-key.pem'
Generates a JSON Web Token (JWT) for a GitHub App using the specified client ID and private key file path.
.EXAMPLE
Get-GitHubAppJWT -ClientId 'Iv987654321' -PrivateKey '--- BEGIN RSA PRIVATE KEY --- ... --- END RSA PRIVATE KEY ---'
Generates a JSON Web Token (JWT) for a GitHub App using the specified client ID and private key.
.OUTPUTS
System.String
.NOTES
[Generating a JSON Web Token (JWT) for a GitHub App | GitHub Docs](https://docs.github.com/en/apps/creating-github-apps/authenticating-with-a-github-app/generating-a-json-web-token-jwt-for-a-github-app#example-using-powershell-to-generate-a-jwt)
#>
[Diagnostics.CodeAnalysis.SuppressMessageAttribute(
'PSAvoidLongLines',
'',
Justification = 'Contains a long link.'
)]
[CmdletBinding(DefaultParameterSetName = 'PrivateKey')]
[Alias('Get-GitHubAppJWT')]
[OutputType([string])]
param(
# The client ID of the GitHub App.
# Can use the GitHub App ID or the client ID.
# Example: 'Iv23li8tyK9NUwl7rWlQ'
# Example: '123456'
[Parameter(Mandatory)]
[string] $ClientId,

# The path to the private key file of the GitHub App.
# Example: '/path/to/private-key.pem'
[Parameter(
Mandatory,
ParameterSetName = 'FilePath'
)]
[string] $PrivateKeyFilePath,

# The private key of the GitHub App.
# Example: @'
# -----BEGIN RSA PRIVATE KEY-----
# qwe
# ...
# -----END RSA PRIVATE KEY-----
# '@
[Parameter(
Mandatory,
ParameterSetName = 'PrivateKey'
)]
[string] $PrivateKey
)

if ($PrivateKeyFilePath) {
if (-not (Test-Path -Path $PrivateKeyFilePath)) {
throw "The private key path [$PrivateKeyFilePath] does not exist."
}

$PrivateKey = Get-Content -Path $PrivateKeyFilePath -Raw
}

$header = [Convert]::ToBase64String(
[System.Text.Encoding]::UTF8.GetBytes(
(
ConvertTo-Json -InputObject @{
alg = 'RS256'
typ = 'JWT'
}
)
)
).TrimEnd('=').Replace('+', '-').Replace('/', '_')

$payload = [Convert]::ToBase64String(
[System.Text.Encoding]::UTF8.GetBytes(
(
ConvertTo-Json -InputObject @{
iat = [System.DateTimeOffset]::UtcNow.AddSeconds(-10).ToUnixTimeSeconds()
exp = [System.DateTimeOffset]::UtcNow.AddMinutes(10).ToUnixTimeSeconds()
iss = $ClientId
}
)
)
).TrimEnd('=').Replace('+', '-').Replace('/', '_')

$rsa = [System.Security.Cryptography.RSA]::Create()
$rsa.ImportFromPem($PrivateKey)

$signature = [Convert]::ToBase64String(
$rsa.SignData(
[System.Text.Encoding]::UTF8.GetBytes("$header.$payload"),
[System.Security.Cryptography.HashAlgorithmName]::SHA256,
[System.Security.Cryptography.RSASignaturePadding]::Pkcs1
)
).TrimEnd('=').Replace('+', '-').Replace('/', '_')
$jwt = "$header.$payload.$signature"
$jwt
}
17 changes: 17 additions & 0 deletions tools/dev/Get-GitHubAppInstallations.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@

#List installations for the authenticated app
Invoke-RestMethod -Uri 'https://api.github.com/app/installations' -Headers @{
Authorization = "Bearer $token"
}


#Get an organization installation for the authenticated app
Invoke-RestMethod -Uri 'https://api.github.com/orgs/psmodule/installation' -Headers @{
Authorization = "Bearer $token"
}


#Get a repository installation for the authenticated app
Invoke-RestMethod -Uri 'https://api.github.com/repos/psmodule/.github/installation' -Headers @{
Authorization = "Bearer $token"
}
Loading

0 comments on commit bb8f984

Please sign in to comment.