Merge pull request #4416 from Infisical/fix-github-app-auth

Swap away from octokit for GitHub app auth and use gateway
This commit is contained in:
x032205
2025-08-26 06:36:06 +08:00
committed by GitHub
2 changed files with 42 additions and 17 deletions
@@ -1,5 +1,3 @@
import { createAppAuth } from "@octokit/auth-app";
import { request } from "@octokit/request";
import { AxiosError, AxiosRequestConfig, AxiosResponse } from "axios"; import { AxiosError, AxiosRequestConfig, AxiosResponse } from "axios";
import https from "https"; import https from "https";
import RE2 from "re2"; import RE2 from "re2";
@@ -8,6 +6,7 @@ import { verifyHostInputValidity } from "@app/ee/services/dynamic-secret/dynamic
import { TGatewayServiceFactory } from "@app/ee/services/gateway/gateway-service"; import { TGatewayServiceFactory } from "@app/ee/services/gateway/gateway-service";
import { getConfig } from "@app/lib/config/env"; import { getConfig } from "@app/lib/config/env";
import { request as httpRequest } from "@app/lib/config/request"; import { request as httpRequest } from "@app/lib/config/request";
import { crypto } from "@app/lib/crypto";
import { BadRequestError, ForbiddenRequestError, InternalServerError } from "@app/lib/errors"; import { BadRequestError, ForbiddenRequestError, InternalServerError } from "@app/lib/errors";
import { GatewayProxyProtocol, withGatewayProxy } from "@app/lib/gateway"; import { GatewayProxyProtocol, withGatewayProxy } from "@app/lib/gateway";
import { logger } from "@app/lib/logger"; import { logger } from "@app/lib/logger";
@@ -114,10 +113,13 @@ export const requestWithGitHubGateway = async <T>(
); );
}; };
export const getGitHubAppAuthToken = async (appConnection: TGitHubConnection) => { export const getGitHubAppAuthToken = async (
appConnection: TGitHubConnection,
gatewayService: Pick<TGatewayServiceFactory, "fnGetGatewayClientTlsByGatewayId">
) => {
const appCfg = getConfig(); const appCfg = getConfig();
const appId = appCfg.INF_APP_CONNECTION_GITHUB_APP_ID; const appId = appCfg.INF_APP_CONNECTION_GITHUB_APP_ID;
const appPrivateKey = appCfg.INF_APP_CONNECTION_GITHUB_APP_PRIVATE_KEY; let appPrivateKey = appCfg.INF_APP_CONNECTION_GITHUB_APP_PRIVATE_KEY;
if (!appId || !appPrivateKey) { if (!appId || !appPrivateKey) {
throw new InternalServerError({ throw new InternalServerError({
@@ -125,21 +127,42 @@ export const getGitHubAppAuthToken = async (appConnection: TGitHubConnection) =>
}); });
} }
appPrivateKey = appPrivateKey
.split("\n")
.map((line) => line.trim())
.join("\n");
if (appConnection.method !== GitHubConnectionMethod.App) { if (appConnection.method !== GitHubConnectionMethod.App) {
throw new InternalServerError({ message: "Cannot generate GitHub App token for non-app connection" }); throw new InternalServerError({ message: "Cannot generate GitHub App token for non-app connection" });
} }
const appAuth = createAppAuth({ const now = Math.floor(Date.now() / 1000);
appId, const payload = {
privateKey: appPrivateKey, iat: now,
installationId: appConnection.credentials.installationId, exp: now + 5 * 60,
request: request.defaults({ iss: appId
baseUrl: `https://${await getGitHubInstanceApiUrl(appConnection)}` };
})
});
const { token } = await appAuth({ type: "installation" }); const appJwt = crypto.jwt().sign(payload, appPrivateKey, { algorithm: "RS256" });
return token;
const apiBaseUrl = await getGitHubInstanceApiUrl(appConnection);
const { installationId } = appConnection.credentials;
const response = await requestWithGitHubGateway<{ token: string; expires_at: string }>(
appConnection,
gatewayService,
{
url: `https://${apiBaseUrl}/app/installations/${installationId}/access_tokens`,
method: "POST",
headers: {
Accept: "application/vnd.github+json",
Authorization: `Bearer ${appJwt}`,
"X-GitHub-Api-Version": "2022-11-28"
}
}
);
return response.data.token;
}; };
const parseGitHubLinkHeader = (linkHeader: string | undefined): Record<string, string> => { const parseGitHubLinkHeader = (linkHeader: string | undefined): Record<string, string> => {
@@ -174,7 +197,9 @@ export const makePaginatedGitHubRequest = async <T, R = T[]>(
const { credentials, method } = appConnection; const { credentials, method } = appConnection;
const token = const token =
method === GitHubConnectionMethod.OAuth ? credentials.accessToken : await getGitHubAppAuthToken(appConnection); method === GitHubConnectionMethod.OAuth
? credentials.accessToken
: await getGitHubAppAuthToken(appConnection, gatewayService);
const baseUrl = `https://${await getGitHubInstanceApiUrl(appConnection)}${path}`; const baseUrl = `https://${await getGitHubInstanceApiUrl(appConnection)}${path}`;
const initialUrlObj = new URL(baseUrl); const initialUrlObj = new URL(baseUrl);
@@ -207,7 +207,7 @@ export const GithubSyncFns = {
const token = const token =
connection.method === GitHubConnectionMethod.OAuth connection.method === GitHubConnectionMethod.OAuth
? connection.credentials.accessToken ? connection.credentials.accessToken
: await getGitHubAppAuthToken(connection); : await getGitHubAppAuthToken(connection, gatewayService);
const encryptedSecrets = await getEncryptedSecrets(secretSync, gatewayService); const encryptedSecrets = await getEncryptedSecrets(secretSync, gatewayService);
const publicKey = await getPublicKey(secretSync, gatewayService, token); const publicKey = await getPublicKey(secretSync, gatewayService, token);
@@ -264,7 +264,7 @@ export const GithubSyncFns = {
const token = const token =
connection.method === GitHubConnectionMethod.OAuth connection.method === GitHubConnectionMethod.OAuth
? connection.credentials.accessToken ? connection.credentials.accessToken
: await getGitHubAppAuthToken(connection); : await getGitHubAppAuthToken(connection, gatewayService);
const encryptedSecrets = await getEncryptedSecrets(secretSync, gatewayService); const encryptedSecrets = await getEncryptedSecrets(secretSync, gatewayService);