-
-
Notifications
You must be signed in to change notification settings - Fork 55
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support managing a GitHub App's JWT in GitHubAPI #201
base: main
Are you sure you want to change the base?
Conversation
The about-events page dropped that anchor link; this is the new page listing webhook events.
This is needed because apps.py needs to reference GitHubAPI in its type defs, but abc.py also imports apps.py
This change makes it possible to cache the app ID and private key for a GitHub App in the GitHubApi client. When set, the client will automatically generate and use a JWT to authenticate as the GitHub App (similar to caching the oauth_token on GitHubApi when working as an app installation or with a PAT). The cached JWT is available from the `jwt` property on the class. When the `jwt` property is accessed, the cached jwt token's expiration is tested, and a new jwt is automatically generated if necessary.
- Ensure a GitHubApi caches either an oauth_token or the GitHub App credentials, but not both - Test that the GithubApi.jwt property is cached while the token is valid, but regenerates if a token is expired. - Test that a jwt directly passed to a request method overrides the cached JWT
Thanks for the PR! Sorry for not responding. I just came back from traveling and still jetlagged. I'll be able to review next week. |
Going to close and re-open to see if readthedocs preview will kick in. |
Codecov Report
@@ Coverage Diff @@
## main #201 +/- ##
=========================================
Coverage 100.00% 100.00%
=========================================
Files 9 9
Lines 471 494 +23
Branches 87 93 +6
=========================================
+ Hits 471 494 +23
|
|
||
import time | ||
import jwt | ||
|
||
from gidgethub.abc import GitHubAPI | ||
if TYPE_CHECKING: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This perhaps should be done as a separate PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure I can do that. Let me know and I'll set up another PR that can be merged before this one. The TYPE_CHECKING
guard is needed to prevent a circular import since abc
now imports from apps
in this PR.
In the apps I've built, the authentication flow is as follows:
Right now once we receive the installation access token, I need to manage the expiration of it myself, and this is the part I'm hoping can be managed internally in gidgethub. With this change, it seems like user will either use either always |
Thanks for the review @Mariatta! You're right that this change is designed around a pattern where a specific instance of The API in my app I'm hoping to make possible is a GitHubAPI factory class where I can get a class GitHubAPIFactory:
def __init__(
self, *, id: str, key: str, name: str, http_client: httpx.AsyncClient
) -> None:
...
def create_app_client(self) -> GitHubAPI:
"""Create a client authenticated as the GitHub App."""
...
async def create_installation_client(
self, installation_id: str
) -> GitHubAPI:
"""Create a client authenticated as an installation of the GitHub App
given an installation ID (provided e.g. by a webhook payload).
"""
...
async def create_installation_client_for_repo(
self, owner: str, repo: str
) -> GitHubAPI:
"""Create a client authenticated as an installation of the GitHub App
for a specific repository.
"""
... It's this Another approach that put this capability into # Create a client authenticated as the app, using a JWT by default
app_client = GitHubApi("myapp", app_id=app_id, private_key=key)
# Installation client given an ID
installation_client = app_client.create_installation_client(installation_id)
# Installation client given a repo that internally gets the ID
installation_client = app_client.create_installation_client_for_repo(owner, repo) With either of these set ups, the user gets the configs for the GitHub App only once, and all the details of calling the APIs in gidgethub's |
This PR makes it possible for a GitHubAPI instance to automatically authenticate requests as a GitHub App (as opposed to an app installation). By setting the
app_id
andprivate_key
arguments, GitHubAPI will automatically generate and use a JWT to authenticate as the app. Passing anoauth_token
orjwt
argument directly to one of the request methods overrides the managed JWT as the authentication method.The JWT is available from the
GitHubApi.jwt
property. The computed JWT is cached, but is regenerated if necessary.Let me know what you think about this caching functionality. The
verify_exp
option onjwt.decode()
is what I landed on for testing an existing JWT's validity. I'm not sure how much more efficient this is than treating JWT's as entirely ephemeral, but seems to work well.I also did some other clean-up activities to get the tests/linters to work. I've put those in separate commits.
Closes #199