Adding initial support for GitHub Apps. (#1738)

* Added authentication using bearer token.

* Added Installation and AccessToken clients.

* Added new clients to Reactive project

* added support for DateTime serialized as FileTime

* added support for StatusEventPayload

* added support for StatusEventPayload

* Added test for StatusEventPayload and fixed serializer to return that event payload type.

* WIP - added ApplicationClient and related Api Urls.

* Continued implementing Installations support.

* Fixing build (WIP)

* fixed build

* added Account property to Installation. prefer nameof(x) over literal "x".

* fixed according to code review.

* fixed build.

* switched Installation ID from int to long.

* added Permissions and Events properties to Installation.

* added documentation to Application and Installation properties in IGitHubClient.

* wip - added tests to new clients

* wip - fix build

* wip - fixed build.

* added InstallationsClient tests.

* added integration test for InstallationsClient.

* changes requested in code review.

* add Get method for App

* Create GitHubApp response model instead of re-using existing Application response model

* add Get method to observable client

* fixed build (both locally and failed test).

* Fixed documentation and added some missing XML docs.

* added DebuggerDisplay to StatusEventPayload

* updated XML docs and added some missing bits. prefer nameof(x) over literal "x".

* Add xml comments to AccessToken response model and use DateTimeOffset rather than DateTime

* Tidy up XmlComments and make consistent across client and observable client and interfaces

* fixup unit tests to independently verify preview header

* Implement GetInstallation method

* revert commits unrelated to GitHubApps - these can be done on a separate PR if required

* this extra authenticator class doesnt appear to be used anywhere

* undo project file change as it doesnt appear to be necessary

* Revert "Merge remote-tracking branch 'remote/GitHubApps' into GitHubApps"

This reverts commit c53cc110b8d807f62fdfeaa7df19e1532d050007, reversing
changes made to 0c9e413d420a4725738644ea5b13af6ec102d456.

* Revert "Revert "Merge remote-tracking branch 'remote/GitHubApps' into GitHubApps""

This reverts commit 02d52b8adf814b6945c60cb59a907a8cd34b1ce7.

* add XmlDoc comments to response models and flesh out installation permissions

* name AcceptHeaders member consistently

* accidentally lost changes to Credentials.cs

* Enhance Intergation test framework to handle GitHubApp settings and discoer tests appropriately
Get code ready for GitHubJWT nuget package but for now just hardcode a JWT in ENV VAR
Add 1 integration test for each method and ensure they are working!

* fixed compiler warnings.

* Added support for Installation=>Id field that arrives in a Pull Request Event payload.

(See the last field in the sample JSON of https://developer.github.com/v3/activity/events/types/#pullrequestevent)

* Change integration test project to netcoreapp2.0 so we can use GitHubJwt nuget package in integration tests

* First cut at some GitHubApp doco

* update mkdocs config

* Moved the Installation property to ActivityPayload, so it's available in all payloads.

This feature is not undocumented, unfortunately, but valid:
https://platform.github.community/t/determining-which-installation-an-event-came-from/539/11

* Split Installation to Installation and InstallationId, and added a comfort method for gaining its AccessToken.

* fixed InstallationId CreateAccessToken to receive IGitHubAppsClient. added (and fixed) docs.

* reverted object-oriented style comfort method and it's docs.

* update all test projects to netcoreapp2.0

* tweak build configs to use 2.0.3 SDK

* also need to update cake frosting build to netcoreapp2.0

* tweak docs some more

* fix convention test failures

* test projects still had some old runtime parts in them!

* travis osx image needs to be at least 10.12 for .NET Core 2.0

* shell script might need the same argument tweak for cake

* more doc tweaks

* Make sure compiler warning output isnt somehow causing Linux and OSX builds to fail

* moar logging for linux/OSX builds

* stop sourcelink on linux/OSX builds to see if that is the problem

* set verbosity to detailed for the dotnet build step

* try new sourcelink and list out remotes

* is travis being weird with git clone?

* SourceLink may be defaulting to true on CI server so explicitly set it as false rather than omitting it

* detailed is a bit too verbose for travis, try normal

* turn sourcelink back on for Linux/OSX

* fix compiler warning

* Try SourceLink.Create.CommandLine instead of SourceLink.Create.GitHub

* CliToolReferences did not update to latest versions

* remove debug origin info

* turn off msbuild output

* go back to SourceLink.Create.GitHub!

* time diff between dev PC and API causes issues if specifying a full 600 second token

* handle extra date format that Installation end point now returns

* field needs to be protected in order to be deserialized

* provide even more buffer for client vs server clock drift

* Update to latest GitHubJwt reference

* go back to SDK 1 since SDK 2 is having sporadic travisCI faliures in TestSourceLink build step

* get appveyor working

* update sourcelink back to latest, and use SDK 1.04 (runtime 1.0.5)
This commit is contained in:
Itai Bar-Haim
2018-04-16 13:42:23 +03:00
committed by Ryan Gribble
parent 7aeea34fb2
commit 5ffc96995f
44 changed files with 1280 additions and 184 deletions

131
docs/github-apps.md Normal file
View File

@@ -0,0 +1,131 @@
# Working with GitHub Apps
## Overview
GitHub Apps are a new type of integration offering narrow, specific permissions, compared to OAuth apps or user authentication.
To learn more, head to the GitHub Apps section under the GitHub Developer [Getting started with Building Apps](https://developer.github.com/apps/getting-started-with-building-apps/#using-github-apps) documentation.
A GitHub App (known in Octokit as a `GitHubApp`) specifies permissions (read, write, none) it will be granted for various scopes and also registers for various webhook events.
A GitHub App is installed in an `Organization` or `User` account (known in Octokit as an `Installation`) where it is further limited to nominated (or all) repositories for that account.
The [GitHub Api Documentation](https://developer.github.com/v3/apps/) on GitHub Apps contains more detailed information.
## Authentication
The below walkthrough outlines how to authenticate as a `GitHubApp` and an `Installation` using Octokit.net.
Be sure to see the [GitHub Api Documentation](https://developer.github.com/apps/building-github-apps/authentication-options-for-github-apps/#authentication-options-for-github-apps) on GitHub Apps authentication, if you require more details.
## GitHub App Walkthrough
Each GitHub App has a private certificate (PEM file) generated through the GitHub website and possessed by the owner of the GitHub App. Authentication occurs using a time based JWT token, signed by the GitHub App's private certificate.
``` csharp
// A time based JWT token, signed by the GitHub App's private certificate
var jwtToken = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE1MjA0Mjc3MTQsImV4cCI6MTUyMDQyODMxNCwiaXNzIjo5NzM1fQ.K-d3FKWKddMygFqvPZYWQusqhbF1LYfcIM0VbBq4uJsS9VkjhyXALlHmTJWjdblzx-U55lkZc_KWdJd6GlDxvoRb5w_9nrLcIFRbYVgi9XTYpCc3o5j7Qh3FvKxA1bzEs8XGrxjjE7-WJn_xi85ugFKTy9tlIRPa-PHeIOvNp4fz4ru8SFPoD4epiraeEyLfpU_ke-HYF7Ws7ar19zQkfJKRHSIFm1LxJ5MGKWT8pQBBUSGxGPgEG_tYI83aYw6cVx-DLV290bpr23LRUC684Wv_XabUDzXjPUYynAc01APZF6aN8B0LHdPbG8I6Yd74sQfmN-aHz5moz8ZNWLNm8Q";
// Use the JWT as a Bearer token
var gitHubClient = new GitHubClient(new ProductHeaderValue("MyApp"))
{
Credentials = new Credentials(jwtToken, AuthenticationType.Bearer)
};
```
The authenticated `GitHubApp` can query various top level information about itself:
``` csharp
// Get the current authenticated GitHubApp
var app = await appClient.GetCurrent();
// Get a list of installations for the authenticated GitHubApp
var installations = await appClient.GetAllInstallationsForCurrent();
// Get a specific installation of the authenticated GitHubApp by it's installation Id
var installation = await appClient.GetInstallation(123);
```
In order to do more than top level calls, a `GitHubApp` needs to authenticate as a specific `Installation` by creating a temporary installation token, and using that for authentication:
``` csharp
// Create an Installation token for Insallation Id 123
var response = await appClient.CreateInstallationToken(123);
// NOTE - the token will expire!
response.ExpiresAt;
// Create a new GitHubClient using the installation token as authentication
var installationClient = new GitHubClient(new ProductHeaderValue("MyApp-Installation123"))
{
Credentials = new Credentials(response.Token)
};
```
Once authenticated as an `Installation`, a [subset of regular API endpoints](https://developer.github.com/v3/apps/available-endpoints/) can be queried, using the permissions of the `GitHubApp` and repository settings of the `Installation`:
``` csharp
// Create a Comment on an Issue
// - Assuming the GitHub App has read/write permission for issues scope
// - Assuming we are operating on a repository that the Installation has access to
var response = await installationClient.Issue.Comment.Create("owner", "repo", 1, "Hello from my GitHubApp Installation!");
```
## A Note on identifying Installation Id's
GitHub Apps can be registered for webhook events.
WebHook payloads sent to these registrations now include an extra field to indicate the Id of the GitHub App Installation that is associated with the received webhook.
Example webhook for an opened Pull Request:
``` json
{
"action": "opened",
"number": 1,
"pull_request": {
...
},
"repository": {
...
},
"sender": {
...
},
"installation": {
"id": 1234
}
}
```
You can retrieve this `installation.id` from your webhook payload, and use it to create the Installation token as above, to further interact with the repository as that Installation of the GitHub App.
Although Octokit.net doesn't have explicit webhook support, the `ActivityPayload` response classes do generally align with webhook payloads (assuming the octokit.net custom deserializer is used), so we have added the field to these:
``` csharp
// json payload from the received webhook
var json = "...";
// Deserialize the pull_request event
var serializer = new Octokit.Internal.SimpleJsonSerializer();
var payload = serializer_.Deserialize<PullRequestEventPayload>(json);
// Create an Installation token for the associated Insallation Id
var response = await appClient.CreateInstallationToken(payload.Installation.Id);
```
## A Note on JWT Tokens
Octokit.net expects that you will pass in the appropriately signed JWT token required to authenticate the `GitHubApp`.
Luckily one of our contributors [@adriangodong](https://github.com/adriangodong) has created a library `GitHubJwt` ( [GitHub](https://github.com/adriangodong/githubjwt) | [NuGet](https://www.nuget.org/packages/githubjwt) ) which you can use to help with this (as long as you are targetting `netstandard2.0` or above).
``` csharp
var generator = new GitHubJwt.GitHubJwtFactory(
new GitHubJwt.FilePrivateKeySource("/path/to/pem_file"),
new GitHubJwt.GitHubJwtFactoryOptions
{
AppIntegrationId = 123, // The GitHub App Id
ExpirationSeconds = 600 // 10 minutes is the maximum time allowed
}
);
var jwtToken = generator.CreateEncodedJwtToken();
```