from datetime import datetime import jwt import requests import base64 def decode_string(base64_string: str) -> str: """Decodes a Base64 string. NOTE: This workaround was required because .env cannot handle multi-line strings, so it was required to convert it to a single line. There are mutliple ways to achieve the same result. """ base64_bytes = base64_string.encode('utf-8') string_bytes = base64.b64decode(base64_bytes) string = string_bytes.decode('utf-8') return string def get_installation_access_token( signing_key: str, app_id: str, installation_id: str ) -> str: """ Obtain and return a GitHub installation access token. Arguments: signing_key: base64 encoded signing key (you'll get this from the .env) app_id: The application ID installation_id: The ID of the app installation. Returns: The installation access token obtained from GitHub. """ # Refer https://github.com/orgs/community/discussions/48186 # Refer https://stackoverflow.com/questions/77325437/how-do-i-get-an-github-app-installation-token-to-authenticate-cloning-a-reposito now = int(datetime.now().timestamp()) signing_key = decode_string(signing_key) signing_key = jwt.jwk_from_pem(signing_key.encode()) payload = {"iat": now, "exp": now + 600, "iss": app_id} jwt_instance = jwt.JWT() encoded_jwt = jwt_instance.encode(payload, signing_key, alg="RS256") response = requests.post( "https://api.github.com/app/installations/" f"{installation_id}/access_tokens", headers={ "Authorization": f"Bearer {encoded_jwt}", "Accept": "application/vnd.github+json", "X-GitHub-Api-Version": "2022-11-28", }, ) if not 200 <= response.status_code < 300: raise RuntimeError( "Unable to get token. Status code was " f"{response.status_code}, body was {response.text}." ) return response.json()["token"] if __name__ == "__main__": import os print(get_installation_access_token( os.environ.get('CHATBOT_PUB_KEY'), os.environ.get('APP_ID'), os.environ.get('INSTALLATION_ID') ))