6.1.1 • Published 4 days ago

@octokit/auth-app v6.1.1

Weekly downloads
282,425
License
MIT
Repository
github
Last release
4 days ago

auth-app.js

GitHub App authentication for JavaScript

@latest Build Status

@octokit/auth-app implements authentication for GitHub Apps using JSON Web Token, installation access tokens, and OAuth user-to-server access tokens.

Standalone usage

⚠️ @octokit/auth-app is not meant for usage in the browser. A private key and client secret must not be exposed to users.

The private keys provided by GitHub are in PKCS#1 format, but the WebCrypto API only supports PKCS#8. You need to convert it first:

openssl pkcs8 -topk8 -inform PEM -outform PEM -nocrypt -in private-key.pem -out private-key-pkcs8.key

The OAuth APIs to create user-to-server tokens cannot be used because they do not have CORS enabled.

If you know what you are doing, load @octokit/auth-app directly from esm.sh

<script type="module">
  import { createAppAuth } from "https://esm.sh/@octokit/auth-app";
</script>

Install with npm install @octokit/auth-app

import { createAppAuth } from "@octokit/auth-app";

Authenticate as GitHub App (JSON Web Token)

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

// Retrieve JSON Web Token (JWT) to authenticate as app
const appAuthentication = await auth({ type: "app" });

resolves with

{
  "type": "app",
  "token": "jsonwebtoken123",
  "appId": 123,
  "expiresAt": "2018-07-07T00:09:30.000Z"
}

Authenticate as OAuth App (client ID/client secret)

The OAuth Application APIs require the app to authenticate using clientID/client as Basic Authentication

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

const appAuthentication = await auth({
  type: "oauth-app",
});

resolves with

{
  "type": "oauth-app",
  "clientId": "lv1.1234567890abcdef",
  "clientSecret": "1234567890abcdef1234567890abcdef12345678",
  "headers": {
    "authorization": "basic bHYxLjEyMzQ1Njc4OTBhYmNkZWY6MTIzNDU2Nzg5MGFiY2RlZjEyMzQ1Njc4OTBhYmNkZWYxMjM0NTY3OA=="
  }
}

Authenticate as installation

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,
});

resolves with

{
  "type": "token",
  "tokenType": "installation",
  "token": "token123",
  "installationId": 123,
  "createdAt": "2018-07-07T00:00:00.000Z",
  "expiresAt": "2018-07-07T00:59:00.000Z"
}

Authenticate as user

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

// Retrieve an oauth-access token
const userAuthentication = await auth({ type: "oauth-user", code: "123456" });

Resolves with

{
  "type": "token",
  "tokenType": "oauth",
  "token": "token123"
}

Usage with Octokit

Browsers

⚠️ @octokit/auth-app is not meant for usage in the browser. A private key and client secret must not be exposed to users.

The private keys provided by GitHub are in PKCS#1 format, but the WebCrypto API only supports PKCS#8. You need to convert it first:

openssl pkcs8 -topk8 -inform PEM -outform PEM -nocrypt -in private-key.pem -out private-key-pkcs8.key

The OAuth APIs to create user-to-server tokens cannot be used because they do not have CORS enabled.

If you know what you are doing, load @octokit/auth-app and @octokit/core (or a compatible module) directly from esm.sh

<script type="module">
  import { createAppAuth } from "https://esm.sh/@octokit/auth-app";
  import { Octokit } from "https://esm.sh/@octokit/core";
</script>

Node

Install with npm install @octokit/core @octokit/auth-app. Optionally replace @octokit/core with a compatible module

import { Octokit } from "@octokit/core";
import { createAppAuth, createOAuthUserAuth } from "@octokit/auth-app";
const appOctokit = new Octokit({
  authStrategy: createAppAuth,
  auth: {
    appId: 1,
    privateKey: "-----BEGIN PRIVATE KEY-----\n...",
    clientId: "1234567890abcdef1234",
    clientSecret: "1234567890abcdef1234567890abcdef12345678",
  },
});

// Send requests as GitHub App
const { slug } = await appOctokit.request("GET /app");
console.log("authenticated as %s", slug);

// Send requests as OAuth App
await appOctokit.request("POST /application/{client_id}/token", {
  client_id: "1234567890abcdef1234",
  access_token: "existingtoken123",
});
console.log("token is valid");

// create a new octokit instance that is authenticated as the user
const userOctokit = await appOctokit.auth({
  type: "oauth-user",
  code: "code123",
  factory: (options) => {
    return new Octokit({
      authStrategy: createOAuthUserAuth,
      auth: options,
    });
  },
});

// Exchanges the code for the user access token authentication on first request
// and caches the authentication for successive requests
const {
  data: { login },
} = await userOctokit.request("GET /user");
console.log("Hello, %s!", login);

In order to create an octokit instance that is authenticated as an installation, with automated installation token refresh, set installationId as auth option

const installationOctokit = new Octokit({
  authStrategy: createAppAuth,
  auth: {
    appId: 1,
    privateKey: "-----BEGIN PRIVATE KEY-----\n...",
    installationId: 123,
  },
});

// transparently creates an installation access token the first time it is needed
// and refreshes it when it expires
await installationOctokit.request("POST /repos/{owner}/{repo}/issues", {
  owner: "octocat",
  repo: "hello-world",
  title: "title",
});

createAppAuth(options) or new Octokit({ auth })

Automatically set to octokit.request when using with an Octokit constructor.

For standalone usage, you can pass in your own @octokit/request instance. For usage with enterprise, set baseUrl to the hostname + /api/v3. Example:

import { request } from "@octokit/request";
createAppAuth({
  appId: 1,
  privateKey: "-----BEGIN PRIVATE KEY-----\n...",
  request: request.defaults({
    baseUrl: "https://ghe.my-company.com/api/v3",
  }),
});
const CACHE = {};
createAppAuth({
  appId: 1,
  privateKey: "-----BEGIN PRIVATE KEY-----\n...",
  cache: {
    async get(key) {
      return CACHE[key];
    },
    async set(key, value) {
      CACHE[key] = value;
    },
  },
});
import consoleLogLevel from "console-log-level";
createAppAuth({
  appId: 1,
  privateKey: "-----BEGIN PRIVATE KEY-----\n...",
  log: consoleLogLevel({ level: "info" }),
});

auth(options) or octokit.auth(options)

The async auth() method accepts different options depending on your use case

JSON Web Token (JWT) Authentication

Authenticate as the GitHub app to list installations, repositories, and create installation access tokens.

OAuth App authentication

Create, reset, refresh, delete OAuth user-to-server tokens

Installation authentication

The auth({type: "installation", installationId, factory }) call with resolve with whatever the factory function returns. The factory function will be called with all the strategy option that auth was created with, plus the additional options passed to auth, besides type and factory.

For example, you can create a new auth instance for an installation which shares the internal state (especially the access token cache) with the calling auth instance:

const appAuth = createAppAuth({
  appId: 1,
  privateKey: "-----BEGIN PRIVATE KEY-----\n...",
});

const installationAuth123 = await appAuth({
  type: "installation",
  installationId: 123,
  factory: createAppAuth,
});

Installation tokens expire after one hour. By default, tokens are cached and returned from cache until expired. To bypass and update a cached token for the given installationId, set refresh to true.

Defaults to false.

User authentication (web flow)

Exchange code received from the web flow redirect described in step 2 of GitHub's OAuth web flow

The auth({type: "oauth-user", factory }) call with resolve with whatever the factory function returns. The factory function will be called with all the strategy option that auth was created with, plus the additional options passed to auth, besides type and factory.

For example, you can create a new auth instance for an installation which shares the internal state (especially the access token cache) with the calling auth instance:

import { createAppAuth, createOAuthUserAuth } from "@octokit/auth-oauth-app";

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

const userAuth = await appAuth({
  type: "oauth-user",
  code,
  factory: createOAuthUserAuth,
});

// will create token upon first call, then cache authentication for successive calls,
// until token needs to be refreshed (if enabled for the GitHub App)
const authentication = await userAuth();

User authentication (device flow)

Create a token using GitHub's device flow.

The device flow does not require a client secret, but it is required as strategy option for @octokit/auth-app, even for the device flow. If you want to implement the device flow without requiring a client secret, use @octokit/auth-oauth-device.

Required. A function that is called once the device and user codes were retrieved.

The onVerification() callback can be used to pause until the user completes step 2, which might result in a better user experience.

const auth = auth({
  type: "oauth-user",
  onVerification(verification) {
    console.log("Open %s", verification.verification_uri);
    console.log("Enter code: %s", verification.user_code);
    await prompt("press enter when you are ready to continue");
  },
});

The auth({type: "oauth-user", factory }) call with resolve with whatever the factory function returns. The factory function will be called with all the strategy option that auth was created with, plus the additional options passed to auth, besides type and factory.

For example, you can create a new auth instance for an installation which shares the internal state (especially the access token cache) with the calling auth instance:

import { createAppAuth, createOAuthUserAuth } from "@octokit/auth-oauth-app";

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

const userAuth = await appAuth({
  type: "oauth-user",
  code,
  factory: createOAuthUserAuth,
});

// will create token upon first call, then cache authentication for successive calls,
// until token needs to be refreshed (if enabled for the GitHub App)
const authentication = await userAuth();

Authentication object

Depending on on the auth() call, the resulting authentication object can be one of

  1. JSON Web Token (JWT) authentication
  2. OAuth App authentication
  3. Installation access token authentication
  4. GitHub APP user authentication token with expiring disabled
  5. GitHub APP user authentication token with expiring enabled

JSON Web Token (JWT) authentication

OAuth App authentication

Installation access token authentication

GitHub APP user authentication token with expiring disabled

GitHub APP user authentication token with expiring enabled

auth.hook(request, route, parameters) or auth.hook(request, options)

auth.hook() hooks directly into the request life cycle. It amends the request to authenticate either as app or as installation based on the request URL. Although the "machine-man" preview has graduated to the official API, https://developer.github.com/changes/2020-08-20-graduate-machine-man-and-sailor-v-previews/, it is still required in versions of GitHub Enterprise up to 2.21 so it automatically sets the "machine-man" preview for all endpoints requiring JWT authentication.

The request option is an instance of @octokit/request. The arguments are the same as for the request() method.

auth.hook() can be called directly to send an authenticated request

const { data: installations } = await auth.hook(
  request,
  "GET /app/installations",
);

Or it can be passed as option to request().

const requestWithAuth = request.defaults({
  request: {
    hook: auth.hook,
  },
});

const { data: installations } = await requestWithAuth("GET /app/installations");

Note that auth.hook() does not create and set an OAuth authentication token. But you can use @octokit/auth-oauth-app for that functionality. And if you don't plan on sending requests to routes that require authentication with client_id and client_secret, you can just retrieve the token and then create a new instance of request() with the authentication header set:

const { token } = await auth({
  type: "oauth-user",
  code: "123456",
});
const requestWithAuth = request.defaults({
  headers: {
    authentication: `token ${token}`,
  },
});

Types

import {
  // strategy options
  StrategyOptions,
  // auth options
  AuthOptions,
  AppAuthOptions,
  OAuthAppAuthOptions,
  InstallationAuthOptions,
  OAuthWebFlowAuthOptions,
  OAuthDeviceFlowAuthOptions,
  // authentication objects
  Authentication,
  AppAuthentication,
  OAuthAppAuthentication,
  InstallationAccessTokenAuthentication,
  GitHubAppUserAuthentication,
  GitHubAppUserAuthenticationWithExpiration,
} from "@octokit/auth-app";

Implementation details

When creating a JSON Web Token, it sets the "issued at time" (iat) to 30s in the past as we have seen people running situations where the GitHub API claimed the iat would be in future. It turned out the clocks on the different machine were not in sync.

Installation access tokens are valid for 60 minutes. This library invalidates them after 59 minutes to account for request delays.

All OAuth features are implemented internally using @octokit/auth-oauth-app.

License

MIT

@balena/jellyfish-plugin-github@joshpress/git-cms@knapsack/github-api@everything-registry/sub-chunk-676@quantum-sec/xdr-updaterinsomnia-plugin-github-apps-helpergithub_exportergithub-app-installation-tokengithub-app-replgithub-app-tokengithub-apps-hello-worldgcf-utilsgh-checks-clilivebundle-ghapplivebundle-githublivebundle-github-consumernode-red-contrib-github-plusnotion-db-notification-store-github@probot-ng/octokit-auth-probot-ng@pvm/github@pvm/plugin-githuboctokit-auth-probot@backstage/integrationautomoto@vidavidorra/github-app-token@applitools/eyes-scm@azure-tools/sdk-generation-lib@tideflowio/tideflow-agent@yieldbits/probot@stoe/octokit-plugin-org-activity@swsdk/githubagba-mergertbdocs-action-draftcoachartie@ddcp/sns-to-github@desmos/typescript-checkstina-graphqltina-graphql-primitives@brigad/eslint-formatter-github-checksbar-balance-changesbot-beam@dendronhq/backend-common@digital-garden-builder/git-cms@digital-garden-builder/server@dianmora/contributors@dog-hero/gihub-check-runtag-syncer@ericlai616/reviewer-assignment-botvuepress-plugin-github-markdown@evdy-consumer/ma-ci-scriptsvtexadmin@electron/github-app-auth@eng-automation/integrations@forestryio/gql@jamietanna/renovate-graph@jupiterone/github-repo-standard@jupiterone/graph-github@floe/utils@mgoren/get-github-app-installation-token@justforlxz/tools@infinitebrahmanuniverse/nolb-_ocdw-azure-tools-sdk-generation-lib@haruyukilxz/tools@google-cloud/mono-repo-publish@nestjs-shared/probot@octokit/app@octokit/auth@ploys/deployments-core
7.0.0-beta.7

4 days ago

7.0.0-beta.8

4 days ago

6.1.1

12 days ago

6.1.0

17 days ago

6.0.5

18 days ago

7.0.0-beta.6

2 months ago

7.0.0-beta.5

2 months ago

7.0.0-beta.4

2 months ago

6.0.4

2 months ago

7.0.0-beta.2

2 months ago

7.0.0-beta.3

2 months ago

7.0.0-beta.1

2 months ago

6.0.3

3 months ago

6.0.2

4 months ago

6.0.1

7 months ago

6.0.0

10 months ago

4.0.10

12 months ago

4.0.12

11 months ago

4.0.11

11 months ago

4.0.13

11 months ago

5.0.6

10 months ago

5.0.5

10 months ago

5.0.4

10 months ago

5.0.3

10 months ago

5.0.2

10 months ago

5.0.1

10 months ago

5.0.0

10 months ago

4.0.9

1 year ago

4.0.8

1 year ago

4.0.7

2 years ago

4.0.6

2 years ago

4.0.5

2 years ago

4.0.4

2 years ago

4.0.1

2 years ago

4.0.0

2 years ago

4.0.3

2 years ago

4.0.2

2 years ago

3.6.1

3 years ago

3.6.0

3 years ago

3.5.3

3 years ago

3.5.2

3 years ago

3.5.1

3 years ago

3.4.1

3 years ago

3.5.0

3 years ago

3.4.0

3 years ago

3.3.0

3 years ago

3.2.0

3 years ago

3.1.0

3 years ago

3.0.0

3 years ago

2.11.0

3 years ago

2.10.6

3 years ago

2.10.5

3 years ago

2.10.4

3 years ago

2.10.3

3 years ago

2.10.2

3 years ago

2.10.1

3 years ago

2.10.0

3 years ago

2.9.0

3 years ago

2.8.0

3 years ago

2.7.0

4 years ago

2.6.0

4 years ago

2.5.1

4 years ago

2.5.0

4 years ago

2.4.15

4 years ago

2.4.14

4 years ago

2.4.13

4 years ago

2.4.12

4 years ago

2.4.11

4 years ago

2.4.10

4 years ago

2.4.9

4 years ago

2.4.8

4 years ago

2.4.7

4 years ago

2.4.6

4 years ago

2.4.5

4 years ago

2.4.4

4 years ago

2.4.3

4 years ago

2.4.2

4 years ago

2.4.1

4 years ago

2.4.0

4 years ago

2.3.0

4 years ago

2.2.0

4 years ago

2.1.3

5 years ago

2.1.2

5 years ago

2.1.1

5 years ago

2.1.0

5 years ago

2.0.1

5 years ago

2.0.0

5 years ago

1.2.2

5 years ago

1.2.1

5 years ago

1.2.0

5 years ago

1.1.0

5 years ago

1.0.0

5 years ago