Securing Private Repository Data

Securing Private Repository Data

Authentication and Attestation with Vaunt

Introduction

Vaunt is a developer relations platform that is designed to help developers connect, collaborate, and share experiences with each other. One of the core features of Vaunt is the ability to discover and aggregate contributions made to repositories that Vaunt has been installed on. This unlocks several unique capabilities, including the ability to award custom achievements to contributors.

To accommodate organizations and users that want to install Vaunt on private repositories we needed a way to ensure that the repository details and contribution data is only shared with the owner or admin of the repository itself.

In addition, we wanted to keep API access simple for those that are only using Vaunt on public repositories and not require user authentication when it is not needed. This is crucial for features such as Developer cards, which are typically embedded directly in a user or repositories readme.

To address private authentication, we approach authorization through two different methods. First, since we are getting the data itself from a provider such as GitHub we use the provider as a way to authenticate the owner of that data. We call this form of authentication "attestation" as we are establishing the identity of the user through a separate provider. Second, we handle authorization through our system using JWT Authentication which is signed directly by the end user.

In this article, we will dive deeper into these approaches and outline steps developers can take to securely access their private data.

Overview of Attestation

We refer to the authorization method through a separate provider as attestation. The only thing we use the provider authentication for is to verify that the user sending the request is the owner of any private repositories they are requesting or is an admin of the Organization they are trying to retrieve private information on. Currently, we support GitHub as an external provider as it is one of the platforms on which the Vaunt app can be installed.

GitHub

We have implemented attestation on GitHub through Personal Access Tokens. See Creating a fine-grained personal access token on GitHub for instructions on creating a token. With Vaunt’s attestation checks, a token for your user does not need to have any additional permissions selected. It is only used to verify that you are the entity that you are requesting data for. Additionally, you do not need to select any repositories for access with this token. The only thing Vaunt will do is verify that the entity matches the authenticated user that generated the GitHub PAT. See an example of the permissions for a user token below.

If you want to create a token that can be used to validate an admin of an organization to retrieve data, create a token for that organization and give the token Read-only access to the Members under the organization's permissions. Read access to members is used to confirm with GitHub that you are an Admin of the organization. Currently, an admin of the organization must be the one to create the token for attestation. See an example of the permissions for an organization token below.

Then, make the following API request to Vaunt, replacing <entity> with the user and <GITHUB_PAT> with the token:

curl -v -H "content-type: application/json" -H "Authorization: Bearer <GITHUB_PAT>" http://api.vaunt.dev/v1/github/entities/<entity>/token

A successful response should look like this:

{
  "meta_data": {
    "count": 1,
    "data_type": 4
  },
  "data": {
    "token": "<VAUNT_JWT>"
  }
}

JWT Authentication

There are two ways to create a JWT to authenticate with Vaunt. The first method as described in the previous section is to call the /token endpoint of the Vaunt API with a Personal Access Token from a provider such as GitHub. This token will be prefixed with “v_” to indicate it was generated by Vaunt. Additionally, a user can create and sign their own JWT using an Ed25519 key. The process for this is described in the following section.

After attestation is completed and the user can generate a valid Vaunt JWT, the token can be used in all future Authenticated API requests. For example you can retrieve repositories, including private, for the entity with this API request:

curl -v -H "content-type: application/json" -H "Authorization: Bearer <VAUNT_JWT>" http://api.vaunt.dev/v1/github/entities/<entity>/repositories?limit=10

One of the limitations of JWT Authentication is that the Token, when generated by Vaunt, cannot be revoked. For safety purposes, we do not use long-term expirations in our Tokens. For use cases that need a long-term way to authorize requests, we allow setting an Ed25519 public key that will be used to validate self-signed JWTs.

Using Ed25519 keys for User JWT Authorization

For some use cases, it may be necessary for a user to be able to issue and sign their own JWTs that can be used for Authentication on Vaunt. By generating a public and private Ed25519 key users can sign their own JWT to be used with Vaunt without having to ever reuse the provider authentication to generate tokens. You must still use a provider for attestation on the API request to set a public key. This API endpoint takes a JSON data object that contains the public key bytes as a base64 string.

Example Flow

You must first generate an Ed25519 key pair in PEM format. We recommend using OpenSSL, which can be done with this command:

openssl genpkey -algorithm ed25519 -outform PEM -out ed25519key.pem

Use the following command to get just the public key:

openssl pkey -in ed25519key.pem -pubout -out ed25519key.pub

Your public key file should look something like this:

-----BEGIN PUBLIC KEY-----
<PUBLIC_KEY>
-----END PUBLIC KEY-----

To send this public key to Vaunt to use for validating requests you will need to base64 encode the file itself:

cat ed25519key.pub | base64

Then, you can set the public key with the /keys endpoint through a PUT request along with the provider attestation token for Authorization.

In this request, the <PROVIDER_TOKEN> should be the Personal Access Token that was generated through GitHub earlier. Also <YOUR-KEY> should be the base64 string of the public key that was generated in the previous step. Make sure to keep the private key secret as it is used to prove the identity of the entity.

curl -X PUT -H "Authorization: Bearer <PROVIDER_TOKEN>" https://api.vaunt.dev/v1/{provider}/entities/{name}/keys --data '{ "data": {"key": "<YOUR-KEY>"}}'

After this, you can generate a JWT signed with the Ed25519 private key to make Authenticated requests to other Vaunt API endpoints.

Claims

A generated JWT must be signed with the Ed25519 key and have the following claims.

ClaimMeaningDetails
iatIssued AtThe time that the JWT was created.
expExpires AtThe expiration time of the JWT, after which it can't be used.
issIssuerThe entity name. This value is used to find the right public key to verify the signature of the JWT.

Summary

In this post, we have discussed how Vaunt utilizes Attestation and Authentication using JWTs to ensure that only authorized users have access to private contribution information. Including how to send requests to the public Vaunt API endpoints to generate Tokens or set keys for JWT verification.

You can get started with Vaunt’s new Authentication by following our guides here.

Vaunt is available today, and the first 100 application installs will have access to use Vaunt on both public and private repositories for free.

We are just getting started! To stay in the loop on future developments follow us on Twitter or join our Discord! Don't hesitate to make feature requests.

Get started with Vaunt by installing it today!