DEV Community

Adrián Bailador
Adrián Bailador

Posted on

How to Authenticate a GitHub App in .NET Using JWT

Integrating a GitHub App with your repositories is a great way to automate tasks, monitor events, and enhance your workflow. However, before your app can interact with the GitHub API, it must authenticate securely. In this article, I’ll explain step by step how to authenticate a GitHub App in .NET using JSON Web Tokens (JWT).

What Do You Need Before Starting?

Before diving into the code, make sure you have the following:

  1. Your GitHub App Set Up:

    • If you don’t already have one, create a GitHub App via GitHub Developer Settings.
    • Note down the App ID and download the private key (.pem).
  2. A .NET Development Environment:

    • Install the latest version of the .NET SDK.
    • Use an editor like Visual Studio or Visual Studio Code.
  3. Required Libraries:

    • Install the following NuGet packages:
     dotnet add package System.IdentityModel.Tokens.Jwt
     dotnet add package Microsoft.IdentityModel.Tokens
    
  4. A Plan to Secure Your Private Key:

    • Never store your private key directly in the code. Use services like Azure Key Vault, AWS Secrets Manager, or environment variables to keep it safe.

Step 1: Generate the JWT

The JWT (JSON Web Token) is a crucial piece for authenticating your GitHub App. It contains information such as the App ID and an expiration time and must be signed with your private key.

Code to Generate the JWT

using System;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Cryptography;
using Microsoft.IdentityModel.Tokens;

public class GitHubJwtGenerator
{
    private readonly string _privateKey;
    private readonly int _appId;

    public GitHubJwtGenerator(string privateKey, int appId)
    {
        _privateKey = privateKey;
        _appId = appId;
    }

    public string GenerateJwt()
    {
        // Load private key
        using var rsa = RSA.Create();
        rsa.ImportFromPem(_privateKey.ToCharArray());

        var securityKey = new RsaSecurityKey(rsa);
        var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.RsaSha256);

        // JWT payload
        var now = DateTimeOffset.UtcNow;
        var tokenDescriptor = new SecurityTokenDescriptor
        {
            Issuer = _appId.ToString(),
            IssuedAt = now.UtcDateTime,
            Expires = now.AddMinutes(10).UtcDateTime, // Valid for 10 minutes
            SigningCredentials = credentials
        };

        var tokenHandler = new JwtSecurityTokenHandler();
        var token = tokenHandler.CreateToken(tokenDescriptor);

        return tokenHandler.WriteToken(token);
    }
}
Enter fullscreen mode Exit fullscreen mode

How to Use It

Load the private key from the .pem file and generate the JWT:

var privateKey = System.IO.File.ReadAllText("path/to/private-key.pem");
int appId = 123456; // App ID

var jwtGenerator = new GitHubJwtGenerator(privateKey, appId);
string jwt = jwtGenerator.GenerateJwt();

Console.WriteLine("Generated JWT: " + jwt);
Enter fullscreen mode Exit fullscreen mode

Note: Ensure the .pem file is not publicly accessible. Use restrictive permissions or secret management services.

Step 2: Get the Installation Token

With the JWT ready, you now need to use it to obtain an installation token, which allows you to perform specific actions on GitHub.

Code to Get the Token

using System.Net.Http;
using System.Net.Http.Headers;
using System.Text.Json;
using System.Threading.Tasks;

public class GitHubAuthService
{
    private readonly string _jwt;

    public GitHubAuthService(string jwt)
    {
        _jwt = jwt;
    }

    public async Task<string> GetInstallationTokenAsync(long installationId)
    {
        using var httpClient = new HttpClient();

        // Configure the authorisation header
        httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _jwt);
        httpClient.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue("MyGitHubApp", "1.0"));

        // URL to get the token
        var url = $"https://api.github.com/app/installations/{installationId}/access_tokens";

        var response = await httpClient.PostAsync(url, null);

        if (!response.IsSuccessStatusCode)
        {
            var error = await response.Content.ReadAsStringAsync();
            throw new Exception($"Failed to get installation token: {error}");
        }

        var responseContent = await response.Content.ReadAsStringAsync();
        var json = JsonDocument.Parse(responseContent);

        return json.RootElement.GetProperty("token").GetString();
    }
}
Enter fullscreen mode Exit fullscreen mode

How to Use It

var installationId = 987654321; // Installation ID
var authService = new GitHubAuthService(jwt);

try
{
    string installationToken = await authService.GetInstallationTokenAsync(installationId);
    Console.WriteLine("Installation Access Token: " + installationToken);
}
catch (Exception ex)
{
    Console.WriteLine("Error: " + ex.Message);
}
Enter fullscreen mode Exit fullscreen mode

Step 3: Use the Installation Token

With the installation token in hand, you can interact with the GitHub API. Here’s an example of how to list the repositories accessible by the installation:

Code to List Repositories

public async Task ListRepositoriesAsync(string installationToken)
{
    using var httpClient = new HttpClient();
    httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Token", installationToken);
    httpClient.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue("MyGitHubApp", "1.0"));

    var response = await httpClient.GetAsync("https://api.github.com/installation/repositories");

    if (!response.IsSuccessStatusCode)
    {
        var error = await response.Content.ReadAsStringAsync();
        throw new Exception($"Failed to list repositories: {error}");
    }

    var content = await response.Content.ReadAsStringAsync();
    Console.WriteLine("Repositories: " + content);
}
Enter fullscreen mode Exit fullscreen mode

Security Best Practices

  1. Protect your private key: Use secret management services like Azure Key Vault or environment variables.
  2. Short-lived tokens: Keep JWTs valid for 10 minutes or less.
  3. Minimal permissions: Ensure your GitHub App has only the permissions it strictly needs.

Conclusion

By following these steps, you can generate the JWT, obtain the installation token, and use it to interact with the GitHub API. With proper security practices, your integration will be reliable and secure.

Speedy emails, satisfied customers

Postmark Image

Are delayed transactional emails costing you user satisfaction? Postmark delivers your emails almost instantly, keeping your customers happy and connected.

Sign up

Top comments (0)

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs

👋 Kindness is contagious

Explore a sea of insights with this enlightening post, highly esteemed within the nurturing DEV Community. Coders of all stripes are invited to participate and contribute to our shared knowledge.

Expressing gratitude with a simple "thank you" can make a big impact. Leave your thanks in the comments!

On DEV, exchanging ideas smooths our way and strengthens our community bonds. Found this useful? A quick note of thanks to the author can mean a lot.

Okay