Skip to content
This repository was archived by the owner on Jul 7, 2025. It is now read-only.

Commit 9571738

Browse files
authored
Add support for organization and user installation retrieval and repository scoping (#84)
1 parent 3a5af6b commit 9571738

15 files changed

+302
-152
lines changed

.github/workflows/test.yml

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,16 @@ jobs:
1515
node-version: 20
1616
cache: npm
1717
- run: npm ci
18+
- run: npm run typecheck
1819
- run: npm run build
19-
- run: npm run prettier -- --check
2020
# Optional integration test of the action using a dedicated GitHub App.
21-
- id: generate_token
21+
- id: create_token
2222
if: ${{ vars.TEST_GITHUB_APP_ID != '' }}
2323
uses: ./
2424
with:
2525
# The only required permission is `Repository permissions > Metadata: Read-only`.
2626
app_id: ${{ vars.TEST_GITHUB_APP_ID }}
2727
private_key: ${{ secrets.TEST_GITHUB_APP_PRIVATE_KEY }}
28-
- run: node --eval "assert('${{ steps.generate_token.outputs.token }}'.length > 0);"
29-
if: ${{ steps.generate_token.outcome != 'skipped' }}
28+
- if: ${{ steps.create_token.outcome != 'skipped' }}
29+
run: node --eval "assert('${{ steps.create_token.outputs.token }}'.length > 0);"
30+
- run: npm run prettier -- --check

README.md

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,33 +18,32 @@ jobs:
1818
job:
1919
runs-on: ubuntu-latest
2020
steps:
21-
- name: Generate token
22-
id: generate_token
23-
uses: tibdex/github-app-token@v1
21+
- id: create_token
22+
uses: tibdex/github-app-token@v2
2423
with:
2524
app_id: ${{ secrets.APP_ID }}
2625

2726
# Optional.
2827
# github_api_url: https://api.example.com
2928

3029
# Optional.
31-
# installation_id: 1337
30+
# installation_retrieval_mode: id
31+
32+
# Optional.
33+
# installation_retrieval_payload: 1337
3234

3335
# Optional.
3436
# Using a YAML multiline string to avoid escaping the JSON quotes.
3537
# permissions: >-
36-
# {"members": "read"}
38+
# {"pull_requests": "read"}
3739

3840
private_key: ${{ secrets.PRIVATE_KEY }}
3941

4042
# Optional.
41-
# repository: owner/repo
43+
# repositories: >-
44+
# ["actions/toolkit", "github/docs"]
4245

43-
- name: Use token
44-
env:
45-
TOKEN: ${{ steps.generate_token.outputs.token }}
46-
run: |
47-
echo "The generated token is masked: ${TOKEN}"
46+
- run: "echo 'The created token is masked: ${{ steps.create_token.outputs.token }}'"
4847
```
4948
5049
[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).

action.yml

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,25 +5,49 @@ inputs:
55
app_id:
66
description: ID of the GitHub App.
77
required: true
8-
installation_id:
9-
description: The ID of the installation for which the token will be requested (defaults to the ID of the repository's installation).
108
github_api_url:
119
description: The API URL of the GitHub server.
1210
default: ${{ github.api_url }}
11+
installation_retrieval_mode:
12+
description: >-
13+
The mode used to retrieve the installation for which the token will be requested.
14+
15+
One of:
16+
- id: use the installation with the specified ID.
17+
- 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).
18+
- 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).
19+
- 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).
20+
default: repository
21+
installation_retrieval_payload:
22+
description: >-
23+
The payload used to retrieve the installation.
24+
25+
Examples for each retrieval mode:
26+
- id: 1337
27+
- organization: github
28+
- repository: tibdex/github-app-token
29+
- user: tibdex
30+
default: ${{ github.repository }}
1331
permissions:
14-
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).
32+
description: >-
33+
The JSON-stringified permissions granted to the token.
34+
Defaults to all permissions granted to the GitHub app.
35+
See https://docs.github.com/en/rest/apps/apps?apiVersion=2022-11-28#create-an-installation-access-token-for-an-app's `permissions`.
1536
private_key:
1637
description: Private key of the GitHub App (can be Base64 encoded).
1738
required: true
18-
repository:
19-
description: The full name of the repository for which the token will be requested.
20-
default: ${{ github.repository }}
39+
repositories:
40+
description: >-
41+
The JSON-stringified array of the full names of the repositories the token should have access to.
42+
Defaults to all repositories that the installation can access.
43+
See https://docs.github.com/en/rest/apps/apps?apiVersion=2022-11-28#create-an-installation-access-token-for-an-app's `repositories`.
2144
outputs:
2245
token:
23-
description: An installation token for the GitHub App on the requested repository.
46+
description: An installation access token for the GitHub App.
2447
runs:
2548
using: node20
26-
main: dist/index.js
49+
main: dist/main/index.js
50+
post: dist/post/index.js
2751
branding:
2852
icon: unlock
2953
color: gray-dark

package-lock.json

Lines changed: 2 additions & 14 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,25 @@
11
{
22
"name": "github-app-token",
3-
"version": "1.9.0",
3+
"version": "2.0.0",
44
"license": "MIT",
55
"type": "module",
66
"files": [
77
"action.yml",
88
"dist"
99
],
1010
"scripts": {
11-
"prebuild": "tsc --build",
12-
"build": "ncc build src/index.ts --minify --target es2021 --v8-cache",
13-
"prettier": "prettier --ignore-path .gitignore \"./**/*.{js,json,md,ts,yml}\""
11+
"build": "npm run build:main && npm run build:post",
12+
"build:main": "npm run compile -- --out ./dist/main src/main.ts ",
13+
"build:post": "npm run compile -- --out ./dist/post src/post.ts",
14+
"compile": "ncc build --minify --no-cache --target es2022 --v8-cache",
15+
"prettier": "prettier --ignore-path .gitignore \"./**/*.{js,json,md,ts,yml}\"",
16+
"typecheck": "tsc --build"
1417
},
1518
"dependencies": {
1619
"@actions/core": "^1.10.0",
1720
"@actions/github": "^5.1.1",
1821
"@octokit/auth-app": "^6.0.0",
1922
"@octokit/request": "^8.1.1",
20-
"ensure-error": "^4.0.0",
2123
"is-base64": "^1.1.0"
2224
},
2325
"devDependencies": {
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import { getOctokit } from "@actions/github";
2+
import { createAppAuth } from "@octokit/auth-app";
3+
import { request } from "@octokit/request";
4+
5+
import {
6+
InstallationRetrievalDetails,
7+
retrieveInstallationId,
8+
} from "./installation-retrieval.js";
9+
10+
export type InstallationAccessTokenCreationOptions = Readonly<{
11+
appId: string;
12+
githubApiUrl: URL;
13+
installationRetrievalDetails: InstallationRetrievalDetails;
14+
permissions?: Record<string, string>;
15+
privateKey: string;
16+
repositories?: string[];
17+
}>;
18+
19+
export const createInstallationAccessToken = async ({
20+
appId,
21+
githubApiUrl,
22+
installationRetrievalDetails,
23+
permissions,
24+
privateKey,
25+
repositories,
26+
}: InstallationAccessTokenCreationOptions): Promise<string> => {
27+
try {
28+
const app = createAppAuth({
29+
appId,
30+
privateKey,
31+
request: request.defaults({
32+
baseUrl: githubApiUrl
33+
.toString()
34+
// Remove optional trailing `/`.
35+
.replace(/\/$/, ""),
36+
}),
37+
});
38+
39+
const authApp = await app({ type: "app" });
40+
const octokit = getOctokit(authApp.token);
41+
42+
const installationId = await retrieveInstallationId(
43+
installationRetrievalDetails,
44+
{ octokit },
45+
);
46+
47+
const {
48+
data: { token },
49+
} = await octokit.request(
50+
"POST /app/installations/{installation_id}/access_tokens",
51+
{ installation_id: installationId, permissions, repositories },
52+
);
53+
return token;
54+
} catch (error: unknown) {
55+
throw new Error("Could not create installation access token.", {
56+
cause: error,
57+
});
58+
}
59+
};

src/fetch-installation-token.ts

Lines changed: 0 additions & 61 deletions
This file was deleted.

src/index.ts

Lines changed: 0 additions & 49 deletions
This file was deleted.

0 commit comments

Comments
 (0)