Access a repository from a GitHub App

September 13th 2024 GitHub .NET

Authenticating with the GitHub API using a personal access token is perfectly fine if you're performing a one-time task for yourself. However, you're better off authenticating as a GitHub App if you're developing an automation for an organization and want it to do its job for a longer time.

There are many reasons for that:

  • Personal access tokens are tied to your account and its permissions.
  • Classic personal access tokens can be generated without expiration time but you can't restrict their access to a specific repository.
  • Fine-grained access tokens on the other hand can be restricted to a specific repository, but also must have their expiration time set. And the organization must be explicitly configured to allow them access even if you have it.

GitHub Apps have none of these shortcomings. And they are not all that difficult to set up, although it can be a bit time consuming to learn about all the steps that are required to make them work.

First, you need to register a new GitHub app for the organization. You do that from the organization settings page. You navigate to Developer settings and then GitHub Apps. From there you can create a New GitHub App. At the very least you need to:

  • Fill in the GitHub App name and Homepage URL
  • If you don't need them, you can disable web hooks by unchecking active under the Webhook section.
  • Grant the permissions, you require. I only selected Read-only access for Content which also granted me mandatory Read-only access to Metadata.

Once you click on the Create GitHub App button, you can already get the App ID from the About section. That's the first piece of information you need. The second piece of information is the private key which you need to generate first. Scroll the GitHub App page down all the way to the Private keys section and click on Generate a private key. This will automatically download a key in PEM format.

To actually give the GitHub App access to one or more repositories, you need to install it into an account. Start by clicking on the Install App in the menu on the left of the GitHub App page. To restrict its access to specific repositories, choose the for these repositories option and select one or more repositories from the dropdown. Click on Install and you're done.

Now, it's time to write some code to authenticate with the GitHub API as a GitHub App. In .NET, I suggest you use the Octokit NuGet package. Its documentation has a dedicated section on GitHub Apps. As suggested there, you should also install the GitHubJwt NuGet package to simplify the process of generating a temporary JWT token:

var generator = new GitHubJwt.GitHubJwtFactory(
    new GitHubJwt.StringPrivateKeySource(settings?.PrivateKey),
    new GitHubJwt.GitHubJwtFactoryOptions
    {
        AppIntegrationId = settings?.AppId ?? -1, // The GitHub App Id
        ExpirationSeconds = 600 // 10 minutes is the maximum time allowed
    }
);

var jwtToken = generator.CreateEncodedJwtToken();

This is where you need the App ID and private key from the GitHub App registration step. In the code above I am reading them from the settings instance.

When authenticated with this token, you can retrieve all the installations of the GitHub App and request the token for any one of them:

var appClient = new GitHubClient(new ProductHeaderValue(settings?.AppName))
{
    Credentials = new Credentials(jwtToken, AuthenticationType.Bearer)
};

var installations = await appClient.GitHubApps.GetAllInstallationsForCurrent();
var installation = installations.First(
    i => i.Account.Login == settings?.InstallationAccount
);
var response = await appClient.GitHubApps.CreateInstallationToken(installation.Id);

Since the GitHub Apps are always installed into an account (organization or user), you can use the Account.Login property of the installation to identify a particular installation, i.e. based on the owner of the repository you want to access.

You can now authenticate with the installation token to access the regular GitHub APIs just like you would with your personal access token. In the following code, for example, I get the latest commit in the repository:

var installationClient = new GitHubClient(new ProductHeaderValue(settings?.AppName))
{
    Credentials = new Credentials(response.Token)
};

var commits = installationClient.Repository.Commit.GetAll(
    settings?.InstallationAccount,
    settings?.RepositoryName
);
var latestCommit = commits.Result.First();

I pushed the code for a small sample project to my GitHub repository. I didn't include my App ID and private key, so if you want to try the code yourself, you'll need to register and install your own GitHub App, and update the values in apsettings.json with your own values. When setting the private key value, don't include the header and footer with the BEGIN and END keywords.

Authenticating with the GitHub API as a GitHub App is a bit more complicated than with the personal access token. But the overhead is only the initial configuration and the authentication code. In exchange, you avoid being dependent on a particular user and avoid all the issues with token expiration. I'd say the trade off is well worth it.

Get notified when a new blog post is published (usually every Friday):

Copyright
Creative Commons License