Skip to content

Instantly share code, notes, and snippets.

@rajbos
Last active January 17, 2024 09:14
Show Gist options
  • Star 13 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save rajbos/8581083586b537029fe8ab796506bec3 to your computer and use it in GitHub Desktop.
Save rajbos/8581083586b537029fe8ab796506bec3 to your computer and use it in GitHub Desktop.
Load jwt token from GitHub App for authentication
#!/bin/bash
# Purpose
# grab the jwt token
# make API calls as the GitHub App used
# get a temporary jwt token from the key file and app id (hardcoded in the file:)
generated_jwt=$(./github-app-jwt.sh)
github_api_url="https://api.github.com/app"
installation_id=21043970
org="devops-actions"
# show the jwt during testing
echo "Generated jwt:"
echo "${generated_jwt}"
echo ""
# call the urls with it
echo "Calling [${github_api_url}], result:"
curl -s \
-H "Authorization: Bearer ${generated_jwt}" \
-H "Accept: application/vnd.github.machine-man-preview+json" \
"${github_api_url}"
github_api_url="https://api.github.com/app/installations"
echo "Calling [${github_api_url}], result:"
curl -s \
-H "Authorization: Bearer ${generated_jwt}" \
-H "Accept: application/vnd.github.v3+json" \
"${github_api_url}"
# get the token by POSTING to the url:
github_api_url="https://api.github.com/app/installations/$installation_id/access_tokens"
echo "Calling [${github_api_url}], result:"
tokens=$(curl -s -X POST \
-H "Authorization: Bearer ${generated_jwt}" \
-H "Accept: application/vnd.github.v3+json" \
"${github_api_url}" )
echo "Token info: $tokens"
# extract the token, more information about expiry for example is present as well:
token=$(echo "$tokens" | jq -r '.token')
echo "Token: $token"
# from now until the token expires, you can use the token to make authenticated requests to the GitHub API:
# get the repositories this token has access to
github_api_url="https://api.github.com/installation/repositories"
echo "Calling [${github_api_url}], result:"
curl -s GET \
-H "Authorization: Bearer ${token}" \
-H "Accept: application/vnd.github.v3+json" \
"${github_api_url}"
# get the runner information for a repo
github_api_url="https://api.github.com/repos/rajbos/dotnetcore-webapp/actions/runners"
echo "Calling [${github_api_url}], result:"
curl -s \
-H "Authorization: Bearer ${token}" \
-H "Accept: application/vnd.github.machine-man-preview+json" \
"${github_api_url}"
# load the files in a directory
github_api_url="https://api.github.com/repos/rajbos/dotnetcore-webapp/contents/.github/workflows"
echo "Calling [${github_api_url}], result:"
curl -s \
-H "Authorization: Bearer ${token}" \
-H "Accept: application/vnd.github.v3+json" \
"${github_api_url}"
# load a file in a directory
github_api_url="https://api.github.com/repos/rajbos/dotnetcore-webapp/contents/README.md"
echo "Calling [${github_api_url}], result:"
curl -i -X GET \
-H "Authorization: Bearer ${token}" \
-H "Accept: application/vnd.github.v3+json" \
"${github_api_url}"
#!/bin/bash
# Purpose:
# grab the jwt token
# get a normal token from it
# git clone with that token
# use git config globally to always use this token, even instead of ssh
# git clone sshRepo with git config setting
# get a temporary jwt token from the key file and app id (hardcoded in the file:)
generated_jwt=$(./github-app-jwt.sh)
github_api_url="https://api.github.com/app"
installation_id=21043970
owner="devops-actions"
repo="load-used-actions"
sshRepo="load-available-actions"
# show the jwt during testing
echo "Generated jwt:"
echo "${generated_jwt}"
echo ""
# call the urls with it
echo "Calling [${github_api_url}], result:"
curl -s \
-H "Authorization: Bearer ${generated_jwt}" \
-H "Accept: application/vnd.github.machine-man-preview+json" \
"${github_api_url}"
github_api_url="https://api.github.com/app/installations"
echo "Calling [${github_api_url}], result:"
curl -s \
-H "Authorization: Bearer ${generated_jwt}" \
-H "Accept: application/vnd.github.v3+json" \
"${github_api_url}"
# get the token by POSTING to the url:
github_api_url="https://api.github.com/app/installations/$installation_id/access_tokens"
echo "Calling [${github_api_url}], result:"
tokens=$(curl -s -X POST \
-H "Authorization: Bearer ${generated_jwt}" \
-H "Accept: application/vnd.github.v3+json" \
"${github_api_url}" )
echo "Token info: $tokens"
# extract the token, more information about expiry for example is present as well:
token=$(echo "$tokens" | jq -r '.token')
echo "Token: $token"
# this token can be used to call the API's or used in a Git clone call using https
# clone with https:
rm -rf tempGitClone
mkdir tempGitClone
cd tempGitClone
git clone "https://x-access-token:$token@github.com/$owner/$repo.git"
cd $repo
ls -la
echo "setting up git config"
git config --global url."https://x-access-token:$token@github.com/".insteadOf "git@github.com:"
# clone different repo by using ssh call
git clone "git@github.com:$owner/$sshRepo.git"
cd $sshRepo
ls -la
echo "Cleanup folders"
cd ../../../
rm -rf tempGitClone
# clean up global configuration so we don't leave the token around
git config --global --remove-section url."https://x-access-token:$token@github.com/"
exit 0
#!/bin/bash
# Purpose
# grab the jwt token
# call the api with that token to retrieve an access token
# get an installation token to use for registring a runner
# get a temporary jwt token from the key file and app id (hardcoded in the file:)
generated_jwt=$(./github-app-jwt.sh)
github_api_url="https://api.github.com/app"
installation_id=21043970
org="devops-actions"
# show the jwt during testing
echo "Generated jwt:"
echo "${generated_jwt}"
echo ""
# call the urls with it
echo "Calling [${github_api_url}], result:"
curl -s \
-H "Authorization: Bearer ${generated_jwt}" \
-H "Accept: application/vnd.github.machine-man-preview+json" \
"${github_api_url}"
github_api_url="https://api.github.com/app/installations"
echo "Calling [${github_api_url}], result:"
curl -s \
-H "Authorization: Bearer ${generated_jwt}" \
-H "Accept: application/vnd.github.v3+json" \
"${github_api_url}"
# get the token by POSTING to the url:
github_api_url="https://api.github.com/app/installations/$installation_id/access_tokens"
echo "Calling [${github_api_url}], result:"
tokens=$(curl -s -X POST \
-H "Authorization: Bearer ${generated_jwt}" \
-H "Accept: application/vnd.github.v3+json" \
"${github_api_url}" )
echo "Token info: $tokens"
# extract the token, more information about expiry for example is present as well:
token=$(echo "$tokens" | jq -r '.token')
echo "Token: $token"
# this token can be used for calling the API or Git clone over https
encoded_token=$(echo -n "x:$token" | base64)
echo "Encoded token: $encoded_token"
github_api_url="https://api.github.com/orgs/$org/actions/runners/registration-token"
echo "Calling [${github_api_url}], result:"
runner_installation_token_result=$(curl -s -X POST \
-H "Authorization: Basic ${encoded_token}" \
-H "Accept: application/vnd.github.v3+json" \
"${github_api_url}")
# this token can be used to register a runner with, only valid for 1 hour (and probably 1 installation)
runner_installation_token=$(echo "$runner_installation_token_result" | jq -r '.token')
echo "Runner installation token: $runner_installation_token"
#!/bin/bash
# Generate JWT for Github App
#
# Found at https://gist.github.com/carestad/bed9cb8140d28fe05e67e15f667d98ad from Alexander Karlstad:
#
# Inspired by implementation by Will Haley at:
# http://willhaley.com/blog/generate-jwt-with-bash/
# From:
# https://stackoverflow.com/questions/46657001/how-do-you-create-an-rs256-jwt-assertion-with-bash-shell-scripting
thisdir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
set -o pipefail
# Change these variables:
app_id=148825
app_private_key="$(< /mnt/c/Users/RobBos/Downloads/path_to_file.pem)"
# Shared content to use as template
header='{
"alg": "RS256",
"typ": "JWT"
}'
payload_template='{}'
build_payload() {
jq -c \
--arg iat_str "$(date +%s)" \
--arg app_id "${app_id}" \
'
($iat_str | tonumber) as $iat
| .iat = $iat
| .exp = ($iat + 300)
| .iss = ($app_id | tonumber)
' <<< "${payload_template}" | tr -d '\n'
}
b64enc() { openssl enc -base64 -A | tr '+/' '-_' | tr -d '='; }
json() { jq -c . | LC_CTYPE=C tr -d '\n'; }
rs256_sign() { openssl dgst -binary -sha256 -sign <(printf '%s\n' "$1"); }
sign() {
local algo payload sig
algo=${1:-RS256}; algo=${algo^^}
payload=$(build_payload) || return
signed_content="$(json <<<"$header" | b64enc).$(json <<<"$payload" | b64enc)"
sig=$(printf %s "$signed_content" | rs256_sign "$app_private_key" | b64enc)
printf '%s.%s\n' "${signed_content}" "${sig}"
}
sign
# this file is an example how to call the github-app-jwt.sh file from pwsh (PowerShell Core running on Linux / WSL) so that we do not have to rewrite the entire jwt logic.
<#
.DESCRIPTION
Get-TokenFromApp uses the environment settings 'GH_APPID' and 'GH_PEM' as credentials
for the GitHub App to load an aceess token with. Be aware that this token is only valid for an hour.
Note: this token has only access to the repositories that the App has been installed to.
We cannot use this token to create new repositories or install the app in a repo.
#>
function Get-TokenFromApp {
# get a temporary jwt token from the key file and app id (hardcoded in the file:)
$generated_jwt = $(bash ./github-app-jwt.sh)
$github_api_url = "https://api.github.com/app"
#Write-Host "Loaded jwt token: [$($generated_jwt)]"
$github_api_url="https://api.github.com/app/installations"
Write-Debug "Calling [${github_api_url}]"
$installationId = ""
try {
$response = Invoke-RestMethod -Uri $github_api_url -Headers @{Authorization = "Bearer $generated_jwt" } -ContentType "application/json" -Method Get
Write-Debug "Found installationId: [$($response[0].id)]"
$installationId = $response[0].id
}
catch
{
Write-Error "Error in finding the app installations: $($_)"
}
$github_api_url="https://api.github.com/app/installations/$installationId/access_tokens"
Write-Host "Calling [${github_api_url}]"
$token = ""
try {
$response = Invoke-RestMethod -Uri $github_api_url -Headers @{Authorization = "Bearer $generated_jwt" } -ContentType "application/json" -Method POST -Body "{}"
$token = $response.token
Write-Host "Got an access token that will expire at: [$($response.expires_at)]"
}
catch
{
Write-Error "Error in getting an access token: $($_)"
}
# set token as env var so the gh cli can pick it up
$env:GH_TOKEN = $token
Write-Host "Found token with [$($token.length)]"
}
# with this setup you can configure git to dynamically rewrite all setups with git@your.server.com
# to https://x-access-token:your-token-here@serverUrl
# this will help with moving existing tooling over
serverurl="your.server.com"
https_repo_pattern="https://x-access-token:${token}@${serverurl}/"
ssh_repo_pattern='git@${serverurl}:'
# Validating if the redirection already exists with a different token value
current_value=$(git config --global --name-only --get-regexp 'url.https://x-access-token:.*')
if [[ -z $current_value ]]; then
echo "Adding URL translation"
git config --global url."${https_repo_pattern}".insteadOf "${ssh_repo_pattern}"
else
echo "Updating token in the URL translation"
git config --global --rename-section "${current_value%.insteadof}" "url.https://x-access-token:${token}@${serverurl}/"
fi
@rajbos
Copy link
Author

rajbos commented Jan 28, 2022

Learn more on GitHub Tokens and when and how to use which one in this blogpost: devopsjournal.io.

@BrianSidebotham
Copy link

I think these scripts should put you into DevOps legendary status! They are really great and very nicely implemented. Definitely appreciative of your efforts here!

@rajbos
Copy link
Author

rajbos commented Apr 7, 2023

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment