octokit/app.js

[DOCS]: How to get an installation access token

frank-zsy opened this issue · 7 comments

Describe the need

In the former version of app.js, I can use app.getInstallationAccessToken({ installationId }) to get an installation access token of a GitHub Apps to perform further actions like auth a GraphQL client. But in the latest version of this library, I can not find a method to get an installation token now. Could you please give me an example or add related code to the documentation?

SDK Version

octokit/app.js v13.1.1

API Version

latest

Relevant log output

No response

Code of Conduct

  • I agree to follow this project's Code of Conduct

In the former version of app.js, I can use app.getInstallationAccessToken({ installationId }) to get an installation access token of a GitHub Apps to perform further actions like auth a GraphQL client

I think another implication of this being inaccessible is that it means you can't use the access token to create a custom Octokit client that uses plug-ins like "@octokit/plugin-throttling" (although maybe there is a way and I'm just not aware?)

There is always the app.getInstallationOctokit that you can use to create an Octokit client for an installation

Starting with v10 of this package, it was repurposed and the method you are looking for has been moved to the @octokit/auth-app package, see #74

@wolfy1339 I think I'm missing something.

Looking at the "@octokit/auth-app" docs section titled "Authenticate as installation" I see:

const auth = createAppAuth({
  appId: 1,
  privateKey: "-----BEGIN PRIVATE KEY-----\n...",
  clientId: "lv1.1234567890abcdef",
  clientSecret: "1234567890abcdef12341234567890abcdef1234",
});

// Retrieve installation access token
const installationAuthentication = await auth({
  type: "installation",
  installationId: 123,
});

But I'm not sure how to get the installation ID. (That's why I was commenting on here to begin with.)

The only way I knew of to get that installation id was using @octokit/app:

import { App } from "@octokit/app";

const app = new App({ appId, privateKey: pem });

// First we need to get the installation id for the repo
const { data: installation } = await app.octokit.request(
  `GET /repos/${organization}/${repository}/installation`
);

Any pointers?


I guess I can use @octokit/app first to fetch the installation id, and then use @octokit/auth-app so I can also use plugins like @octokit/plugin-throttling.

async function getInstallationId() {
  const app = new App({ appId, privateKey });

  // First we need to get the installation id for the repo
  const { data: installation } = await app.octokit.request(
    `GET /repos/${organization}/${repository}/installation`
  );

  return installation.id;
}

const installationId = await getInstallationId();

const ThrottledOctokit = Octokit.plugin(throttling);
const octokit = new ThrottledOctokit({
  auth: {
    appId,
    privateKey,
    installationId,
  },
  authStrategy: createAppAuth,
  throttle: { ... },
});
gr2m commented

To get the token you can do this

const octokit = await app.getInstallationOctokit(installationId);
const { token } = await octokit.auth({ type: "installation" })

Unfortunately we don't have a helper method to get an octokit instance based on an owner or a repository, but you can use one of these endpoints

For example

const { data: installation } = await app.octokit.request("GET /repos/{owner}/{repo}/installation, { owner, repo })

Does that work for you?

I ended up doing more or less what I commented above.

import { App } from "@octokit/app";
import { createAppAuth } from "@octokit/auth-app";
import { throttling } from "@octokit/plugin-throttling";
import { Octokit } from "@octokit/rest";
import dotenv from "dotenv";

dotenv.config({ path: "./.env.local" });

const privateKey = process.env.PEM;
const appId = process.env.APP_ID;
const organization = process.env.ORGANIZATION;
const repository = process.env.REPOSITORY;

export async function getOctokit() {
  const installationId = await getInstallationId();

  const ThrottledOctokit = Octokit.plugin(throttling);
  const octokit = new ThrottledOctokit({
    auth: {
      appId,
      privateKey,
      installationId,
    },
    authStrategy: createAppAuth,
    throttle: {
      onRateLimit: (retryAfter, options, octokit, retryCount) => {
        octokit.log.warn(
          `Request quota exhausted for request ${options.method} ${options.url}`
        );

        if (retryCount < 2) {
          octokit.log.info(`Retrying after ${retryAfter} seconds!`);
          return true;
        }
      },
      onSecondaryRateLimit: (retryAfter, options, octokit) => {
        octokit.log.warn(
          `SecondaryRateLimit detected for request ${options.method} ${options.url}`
        );
      },
    },
  });

  return octokit;
}

async function getInstallationId() {
  const app = new App({ appId, privateKey });

  // First we need to get the installation id for the repo
  const { data: installation } = await app.octokit.request(
    `GET /repos/${organization}/${repository}/installation`
  );

  return installation.id;
}
gr2m commented

Here is a simpler version that does the same thing. We recommend to use octokit over @octokit/rest.

import { App, Octokit } from "octokit";
import dotenv from "dotenv";

dotenv.config({ path: "./.env.local" });

const privateKey = process.env.PEM;
const appId = process.env.APP_ID;
const owner = process.env.ORGANIZATION;
const repo = process.env.REPOSITORY;

export async function getOctokit() {
  const app = new App({ appId, privateKey }); 

  // https://docs.github.com/en/rest/apps/apps?#get-a-repository-installation-for-the-authenticated-app
  const { data: installation } = await app.octokit.request(
    `GET /repos/{owner}/{repo}/installation`, 
    { owner, repo }
  );

  return app.getInstallationOctokit(installation.id)
}

I think we answered the original question so I'm going to close the issue, but please feel free to ask follow up questions.