PyGithub 中的 JWT 令牌身份验证问题

Posted

技术标签:

【中文标题】PyGithub 中的 JWT 令牌身份验证问题【英文标题】:Issue with JWT token authentication in PyGithub 【发布时间】:2019-12-04 15:05:34 【问题描述】:

我想在 python 中创建一个 github 应用程序,但我被困在身份验证部分。由于默认情况下它们不支持 python,因此我必须使用第三方库。生成 JWT 令牌后,我可以成功地使用 curl 进行身份验证,但不能使用库。

我尝试过使用 PyGithub 和 Github.py 并且都返回了“Bad credentials”错误,所以我一定忽略了一些东西。

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)

此 curl 命令返回我的应用程序的详细信息:

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

我希望代码能够验证并打印我的存储库,但是我得到了

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 版本:

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()

引发了相同的 401 错误凭据异常。 我在 login_as_app 函数中包含了一个打印,所以现在它输出 JWT 令牌,我将它与 curl 命令一起使用,我得到了我想要的。很奇怪。

【问题讨论】:

【参考方案1】:

只是为了扩展@ilo 的回答如何作为安装进行身份验证:

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)

如果有多个身份验证,则可以通过将installation.account['login'] 与组织/用户名进行比较来过滤安装。

【讨论】:

【参考方案2】:

RTFM 的典型情况:我应该作为安装进行身份验证。

【讨论】:

【参考方案3】:

使用 PyGithub,你用错了 API

这样就可以了

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

我花了一段时间才弄清楚这个顺序,PyGithub 的文档缺少一些位。

【讨论】:

private_key 变量的结构是什么样的?它有“-----BEGIN PRIVATE KEY-----”/“-----END PRIVATE KEY-----”部分吗? 没关系,将它们添加到键的开头和结尾似乎有效。谢谢!

以上是关于PyGithub 中的 JWT 令牌身份验证问题的主要内容,如果未能解决你的问题,请参考以下文章

如何在 django rest 框架中验证 jwt 身份验证中的令牌

为啥验证函数中的令牌参数在 jwt 身份验证中显示错误?

ASP.NET Core 中的 Jwt 令牌身份验证

JWT 身份验证方案中的刷新令牌是不是应该使用与访问令牌不同的秘密进行签名?

根据 JWT 身份验证令牌中的声明验证来自请求的 IP

JWT 访问令牌:矛盾?