Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Issue with JWT token authentication in PyGithub

I want to create a github app in python, and I'm stuck at the authentication part. Since they don't support python by default, I have to use a third party library. After I generate the JWT token I can successfully authenticate with curl, but not with the library.

I've tried using PyGithub and Github.py and both returned a "Bad credentials" error, so I must have overlooked something.

import jwt
from github import Github
from dotenv import load_dotenv


load_dotenv()
GITHUB_PRIVATE_KEY = os.getenv('GITHUB_PRIVATE_KEY')
GITHUB_APP_IDENTIFIER = os.getenv('GITHUB_APP_IDENTIFIER')
GITHUB_WEBHOOK_SECRET = os.getenv('GITHUB_WEBHOOK_SECRET')

message = {'iat': int(time.time()),
           'exp': int(time.time()) + (10 * 60),
           'iss': GITHUB_APP_IDENTIFIER}

token = jwt.encode(message, GITHUB_PRIVATE_KEY.strip().encode(), 'RS256')

gh = Github(jwt=token.decode())

for repo in gh.get_user().get_repos():
    print(repo.name)

This curl command returns my app's details:

curl -i -H "Authorization: Bearer YOUR_JWT" -H "Accept: application/vnd.github.machine-man-preview+json" https://api.github.com/app

I expect the code to authenticate and print my repos, however i get

Traceback (most recent call last):
  File "C:/python/jeev/testing.py", line 21, in <module>
    for repo in gh.get_user().get_repos():
  File "C:/python/jeev\venv\lib\site-packages\github\PaginatedList.py", line 62, in __iter__
    newElements = self._grow()
  File "C:/python/jeev\venv\lib\site-packages\github\PaginatedList.py", line 74, in _grow
    newElements = self._fetchNextPage()
  File "C:/python/jeev\venv\lib\site-packages\github\PaginatedList.py", line 199, in _fetchNextPage
    headers=self.__headers
  File "C:/python/jeev\venv\lib\site-packages\github\Requester.py", line 276, in requestJsonAndCheck
    return self.__check(*self.requestJson(verb, url, parameters, headers, input, self.__customConnection(url)))
  File "C:/python/jeev\venv\lib\site-packages\github\Requester.py", line 287, in __check
    raise self.__createException(status, responseHeaders, output)
github.GithubException.BadCredentialsException: 401 {'message': 'Bad credentials', 'documentation_url': 'https://developer.github.com/v3'}

Github3.py version:

import jwt
import github3
from dotenv import load_dotenv


load_dotenv()
GITHUB_PRIVATE_KEY = os.getenv('GITHUB_PRIVATE_KEY')
GITHUB_APP_IDENTIFIER = os.getenv('GITHUB_APP_IDENTIFIER')
GITHUB_WEBHOOK_SECRET = os.getenv('GITHUB_WEBHOOK_SECRET')

gh = github3.github.GitHub()
gh.login_as_app(GITHUB_PRIVATE_KEY.encode(), GITHUB_APP_IDENTIFIER)
gh.me()

Same 401 bad credentials exception is raised. I've included a print in the login_as_app function, so now it outputs the JWT token, i used it with the curl command and I get what I want. Weird.

like image 256
ilo Avatar asked Jul 26 '19 09:07

ilo


People also ask

How do I authenticate JWT tokens?

To authenticate a user, a client application must send a JSON Web Token (JWT) in the authorization header of the HTTP request to your backend API. API Gateway validates the token on behalf of your API, so you don't have to add any code in your API to process the authentication.

Does JWT require OAuth?

If you want to do real logout you must go with OAuth2. Authentication with JWT token can not logout actually. Because you don't have an Authentication Server that keeps track of tokens. If you want to provide an API to 3rd party clients, you must use OAuth2 also.

Is JWT used for authentication or authorization?

Both API key and JWT are used for authentication and authorization, but they do it differently. Authentication allows the user or application to use one or more methods of the API. Authorization defines how they can use those methods.

How is to Genrate JWT token?

Enter the downloaded private key in the Private Key field of the Verify Signature section. Ensure that you have removed the public certificate from the Public Key or Certificate field , if the certificate exists. A token is generated in the Encoded section. Copy this token on your system for further use.


Video Answer


2 Answers

Just to extend on @ilo s answer how to authenticate as an installation:

import github3

key_file = 'private-key.pem'

GITHUB_PRIVATE_KEY = open(key_file, 'r').read()
GITHUB_APP_IDENTIFIER = "6"

gh = github3.github.GitHub()

# Login as app
gh.login_as_app(GITHUB_PRIVATE_KEY.encode(), GITHUB_APP_IDENTIFIER)

# Login to the installation, assuming only a single one
installations = [installation.id for installation in gh.app_installations()]
gh.login_as_app_installation(GITHUB_PRIVATE_KEY.encode(), GITHUB_APP_IDENTIFIER, installations[0])

# Access information
# e.g. the rate limit
print(gh.rate_limit())
# or access token to checkout the repository
print(gh.session.auth.token)

If there are more than one authentication, the installations can be filtered by comparing installation.account['login'] with the organization/user name.

like image 74
kap Avatar answered Sep 24 '22 13:09

kap


With PyGithub, you have used the API wrong

This will do it

from github import Github, GithubIntegration

with open("apps-private-key.pem", "r") as secret:
    private_key = secret.read()

GITHUB_APP_ID = "1234"
integration = GithubIntegration(
    GITHUB_APP_ID, private_key, base_url="https://github.com/api/v3")

install = integration.get_installation("owner", "repository")
access = integration.get_access_token(install.id)

# And here it is :)
print(access.token)

gh = Github(login_or_token=access.token,
            base_url="https://github.com/api/v3")

# your operations

Took me a while to figure out this sequence, PyGithub's Docs are missing some bits.

like image 45
flymg Avatar answered Sep 21 '22 13:09

flymg