Skip to content

Instantly share code, notes, and snippets.

@987Nabil
Created April 21, 2022 08:11
Show Gist options
  • Save 987Nabil/594764a7444cb5eee0636607d06f43c5 to your computer and use it in GitHub Desktop.
Save 987Nabil/594764a7444cb5eee0636607d06f43c5 to your computer and use it in GitHub Desktop.
Get a GitHub App token via bash
# MIT No Attribution
# Copyright 2022 Nabil Abdel-Hafeez
# Permission is hereby granted, free of charge, to any person obtaining a copy of this
# software and associated documentation files (the "Software"), to deal in the Software
# without restriction, including without limitation the rights to use, copy, modify,
# merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so.
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
# INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
# PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#!/usr/bin/env bash
appId=$APP_ID
secret=$APP_SECRET
repo=$GH_REPO
header='{ "typ": "JWT", "alg": "RS256" }'
payload="{ \"iss\": \"$appId\" }"
payload=$(
echo "${payload}" | jq --arg time_str "$(date +%s)" \
'
($time_str | tonumber) as $time_num
| .iat=$time_num - 15
| .exp=($time_num + 60 * 5)
'
)
b64enc() { openssl enc -base64 -A | tr '+/' '-_' | tr -d '='; }
json() { jq -c . | LC_CTYPE=C tr -d '\n'; }
rs_sign() { openssl dgst -binary -sha256 -sign <(printf '%s\n' "$1"); }
signed_content="$(json <<<"$header" | b64enc).$(json <<<"$payload" | b64enc)"
sig=$(printf %s "$signed_content" | rs_sign "$secret" | b64enc)
installation_id=$(jq .id <<< "$(curl --location -g -s \
--request GET "https://api.github.com/repos/${repo}/installation" \
--header 'Accept: application/vnd.github.machine-man-preview+json' \
--header "Authorization: Bearer ${signed_content}.${sig}")")
unscoped_token=$(jq .token -r <<< "$(curl --location -g -s \
--request POST "https://api.github.com/app/installations/${installation_id}/access_tokens" \
--header 'Accept: application/vnd.github.v3+json' \
--header "Authorization: Bearer ${signed_content}.${sig}")")
repo_id=$(jq .id <<< "$(curl -g -s \
-H "Accept: application/vnd.github.v3+json" \
--header "Authorization: token $unscoped_token" \
"https://api.github.com/repos/$repo")")
token=$(jq .token -r <<< "$(curl --location -g -s \
--request POST "https://api.github.com/app/installations/${installation_id}/access_tokens" \
--header 'Accept: application/vnd.github.v3+json' \
--header "Authorization: Bearer ${signed_content}.${sig}" \
-d "{\"repository_ids\":[$repo_id]}" )")
echo "$token"
@987Nabil
Copy link
Author

The token is scoped to the repo GH_REPO. If you want an unscoped token, you can just use the unscoped_token from line 50

@oakkitten
Copy link

Thanks! A note for dumdums like myself; APP_SECRET is not a client secret, but the contents of the private key of the app. export APP_SECRET="$(cat github-app-private-key.pem)" worked for me.

@987Nabil
Copy link
Author

Thanks! A note for dumdums like myself; APP_SECRET is not a client secret, but the contents of the private key of the app. export APP_SECRET="$(cat github-app-private-key.pem)" worked for me.

Yes, that is right 😃

@sushant-4452
Copy link

/home/jenkins/github-runners/actions-runner-4/_work/_temp/ddf59c76-2fdf-4d9f-81fb-d380966ab822.sh: line 4: RSA: command not found
I am getting the above error while trying to execute this script.
I have checked openssl is present, and also jq is present.
I am using ubuntu 20.04

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