Skip to content

Commit

Permalink
Add support for organization and user installation retrieval and repo…
Browse files Browse the repository at this point in the history
…sitory scoping (#84)
  • Loading branch information
tibdex committed Sep 9, 2023
1 parent 3a5af6b commit 9571738
Show file tree
Hide file tree
Showing 15 changed files with 302 additions and 152 deletions.
9 changes: 5 additions & 4 deletions .github/workflows/test.yml
Expand Up @@ -15,15 +15,16 @@ jobs:
node-version: 20
cache: npm
- run: npm ci
- run: npm run typecheck
- run: npm run build
- run: npm run prettier -- --check
# Optional integration test of the action using a dedicated GitHub App.
- id: generate_token
- id: create_token
if: ${{ vars.TEST_GITHUB_APP_ID != '' }}
uses: ./
with:
# The only required permission is `Repository permissions > Metadata: Read-only`.
app_id: ${{ vars.TEST_GITHUB_APP_ID }}
private_key: ${{ secrets.TEST_GITHUB_APP_PRIVATE_KEY }}
- run: node --eval "assert('${{ steps.generate_token.outputs.token }}'.length > 0);"
if: ${{ steps.generate_token.outcome != 'skipped' }}
- if: ${{ steps.create_token.outcome != 'skipped' }}
run: node --eval "assert('${{ steps.create_token.outputs.token }}'.length > 0);"
- run: npm run prettier -- --check
21 changes: 10 additions & 11 deletions README.md
Expand Up @@ -18,33 +18,32 @@ jobs:
job:
runs-on: ubuntu-latest
steps:
- name: Generate token
id: generate_token
uses: tibdex/github-app-token@v1
- id: create_token
uses: tibdex/github-app-token@v2
with:
app_id: ${{ secrets.APP_ID }}

# Optional.
# github_api_url: https://api.example.com

# Optional.
# installation_id: 1337
# installation_retrieval_mode: id

# Optional.
# installation_retrieval_payload: 1337

# Optional.
# Using a YAML multiline string to avoid escaping the JSON quotes.
# permissions: >-
# {"members": "read"}
# {"pull_requests": "read"}

private_key: ${{ secrets.PRIVATE_KEY }}

# Optional.
# repository: owner/repo
# repositories: >-
# ["actions/toolkit", "github/docs"]

- name: Use token
env:
TOKEN: ${{ steps.generate_token.outputs.token }}
run: |
echo "The generated token is masked: ${TOKEN}"
- run: "echo 'The created token is masked: ${{ steps.create_token.outputs.token }}'"
```

[Another use case for this action can (or could) be found in GitHub's own docs](https://web.archive.org/web/20230115194214/https://docs.github.com/en/issues/planning-and-tracking-with-projects/automating-your-project/automating-projects-using-actions#example-workflow-authenticating-with-a-github-app).
40 changes: 32 additions & 8 deletions action.yml
Expand Up @@ -5,25 +5,49 @@ inputs:
app_id:
description: ID of the GitHub App.
required: true
installation_id:
description: The ID of the installation for which the token will be requested (defaults to the ID of the repository's installation).
github_api_url:
description: The API URL of the GitHub server.
default: ${{ github.api_url }}
installation_retrieval_mode:
description: >-
The mode used to retrieve the installation for which the token will be requested.
One of:
- id: use the installation with the specified ID.
- organization: use an organization installation (https://docs.github.com/en/rest/apps/apps?apiVersion=2022-11-28#get-an-organization-installation-for-the-authenticated-app).
- repository: use a repository installation (https://docs.github.com/en/rest/apps/apps?apiVersion=2022-11-28#get-a-repository-installation-for-the-authenticated-app).
- user: use a user installation (https://docs.github.com/en/rest/apps/apps?apiVersion=2022-11-28#get-a-user-installation-for-the-authenticated-app).
default: repository
installation_retrieval_payload:
description: >-
The payload used to retrieve the installation.
Examples for each retrieval mode:
- id: 1337
- organization: github
- repository: tibdex/github-app-token
- user: tibdex
default: ${{ github.repository }}
permissions:
description: The JSON-stringified permissions granted to the token (defaults to all the GitHub app permissions, see https://docs.github.com/en/rest/apps/apps#create-an-installation-access-token-for-an-app).
description: >-
The JSON-stringified permissions granted to the token.
Defaults to all permissions granted to the GitHub app.
See https://docs.github.com/en/rest/apps/apps?apiVersion=2022-11-28#create-an-installation-access-token-for-an-app's `permissions`.
private_key:
description: Private key of the GitHub App (can be Base64 encoded).
required: true
repository:
description: The full name of the repository for which the token will be requested.
default: ${{ github.repository }}
repositories:
description: >-
The JSON-stringified array of the full names of the repositories the token should have access to.
Defaults to all repositories that the installation can access.
See https://docs.github.com/en/rest/apps/apps?apiVersion=2022-11-28#create-an-installation-access-token-for-an-app's `repositories`.
outputs:
token:
description: An installation token for the GitHub App on the requested repository.
description: An installation access token for the GitHub App.
runs:
using: node20
main: dist/index.js
main: dist/main/index.js
post: dist/post/index.js
branding:
icon: unlock
color: gray-dark
16 changes: 2 additions & 14 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 7 additions & 5 deletions package.json
@@ -1,23 +1,25 @@
{
"name": "github-app-token",
"version": "1.9.0",
"version": "2.0.0",
"license": "MIT",
"type": "module",
"files": [
"action.yml",
"dist"
],
"scripts": {
"prebuild": "tsc --build",
"build": "ncc build src/index.ts --minify --target es2021 --v8-cache",
"prettier": "prettier --ignore-path .gitignore \"./**/*.{js,json,md,ts,yml}\""
"build": "npm run build:main && npm run build:post",
"build:main": "npm run compile -- --out ./dist/main src/main.ts ",
"build:post": "npm run compile -- --out ./dist/post src/post.ts",
"compile": "ncc build --minify --no-cache --target es2022 --v8-cache",
"prettier": "prettier --ignore-path .gitignore \"./**/*.{js,json,md,ts,yml}\"",
"typecheck": "tsc --build"
},
"dependencies": {
"@actions/core": "^1.10.0",
"@actions/github": "^5.1.1",
"@octokit/auth-app": "^6.0.0",
"@octokit/request": "^8.1.1",
"ensure-error": "^4.0.0",
"is-base64": "^1.1.0"
},
"devDependencies": {
Expand Down
59 changes: 59 additions & 0 deletions src/create-installation-access-token.ts
@@ -0,0 +1,59 @@
import { getOctokit } from "@actions/github";
import { createAppAuth } from "@octokit/auth-app";
import { request } from "@octokit/request";

import {
InstallationRetrievalDetails,
retrieveInstallationId,
} from "./installation-retrieval.js";

export type InstallationAccessTokenCreationOptions = Readonly<{
appId: string;
githubApiUrl: URL;
installationRetrievalDetails: InstallationRetrievalDetails;
permissions?: Record<string, string>;
privateKey: string;
repositories?: string[];
}>;

export const createInstallationAccessToken = async ({
appId,
githubApiUrl,
installationRetrievalDetails,
permissions,
privateKey,
repositories,
}: InstallationAccessTokenCreationOptions): Promise<string> => {
try {
const app = createAppAuth({
appId,
privateKey,
request: request.defaults({
baseUrl: githubApiUrl
.toString()
// Remove optional trailing `/`.
.replace(/\/$/, ""),
}),
});

const authApp = await app({ type: "app" });
const octokit = getOctokit(authApp.token);

const installationId = await retrieveInstallationId(
installationRetrievalDetails,
{ octokit },
);

const {
data: { token },
} = await octokit.request(
"POST /app/installations/{installation_id}/access_tokens",
{ installation_id: installationId, permissions, repositories },
);
return token;
} catch (error: unknown) {
throw new Error("Could not create installation access token.", {
cause: error,
});
}
};
61 changes: 0 additions & 61 deletions src/fetch-installation-token.ts

This file was deleted.

49 changes: 0 additions & 49 deletions src/index.ts

This file was deleted.

0 comments on commit 9571738

Please sign in to comment.