mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-15 10:35:17 +00:00
misc: initial setup for github integration with Github app auth
This commit is contained in:
@@ -109,7 +109,8 @@ export const integrationAuthServiceFactory = ({
|
||||
actorAuthMethod,
|
||||
integration,
|
||||
url,
|
||||
code
|
||||
code,
|
||||
installationId
|
||||
}: TOauthExchangeDTO) => {
|
||||
if (!Object.values(Integrations).includes(integration as Integrations))
|
||||
throw new BadRequestError({ message: "Invalid integration" });
|
||||
@@ -123,7 +124,7 @@ export const integrationAuthServiceFactory = ({
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.Integrations);
|
||||
|
||||
const tokenExchange = await exchangeCode({ integration, code, url });
|
||||
const tokenExchange = await exchangeCode({ integration, code, url, installationId });
|
||||
const updateDoc: TIntegrationAuthsInsert = {
|
||||
projectId,
|
||||
integration,
|
||||
@@ -141,6 +142,16 @@ export const integrationAuthServiceFactory = ({
|
||||
updateDoc.metadata = {
|
||||
authMethod: "oauth2"
|
||||
};
|
||||
} else if (integration === Integrations.GITHUB && installationId) {
|
||||
updateDoc.metadata = {
|
||||
installationId,
|
||||
installationName: tokenExchange.installationName,
|
||||
authMethod: "app"
|
||||
};
|
||||
}
|
||||
|
||||
if (installationId && integration === Integrations.GITHUB) {
|
||||
return integrationAuthDAL.create(updateDoc);
|
||||
}
|
||||
|
||||
const { shouldUseSecretV2Bridge, botKey } = await projectBotService.getBotKey(projectId);
|
||||
|
||||
@@ -9,6 +9,7 @@ export type TOauthExchangeDTO = {
|
||||
integration: string;
|
||||
code: string;
|
||||
url?: string;
|
||||
installationId?: string;
|
||||
} & TProjectPermission;
|
||||
|
||||
export type TSaveIntegrationAccessTokenDTO = {
|
||||
|
||||
@@ -96,7 +96,9 @@ export enum IntegrationUrls {
|
||||
GCP_SECRET_MANAGER_SERVICE_NAME = "secretmanager.googleapis.com",
|
||||
GCP_SECRET_MANAGER_URL = `https://${GCP_SECRET_MANAGER_SERVICE_NAME}`,
|
||||
GCP_SERVICE_USAGE_URL = "https://serviceusage.googleapis.com",
|
||||
GCP_CLOUD_PLATFORM_SCOPE = "https://www.googleapis.com/auth/cloud-platform"
|
||||
GCP_CLOUD_PLATFORM_SCOPE = "https://www.googleapis.com/auth/cloud-platform",
|
||||
|
||||
GITHUB_USER_INSTALLATIONS = "https://api.github.com/user/installations"
|
||||
}
|
||||
|
||||
export const getIntegrationOptions = async () => {
|
||||
@@ -138,6 +140,7 @@ export const getIntegrationOptions = async () => {
|
||||
isAvailable: true,
|
||||
type: "oauth",
|
||||
clientId: appCfg.CLIENT_ID_GITHUB,
|
||||
clientSlug: appCfg.CLIENT_SLUG_GITHUB,
|
||||
docsLink: ""
|
||||
},
|
||||
{
|
||||
|
||||
@@ -2,7 +2,7 @@ import jwt from "jsonwebtoken";
|
||||
|
||||
import { getConfig } from "@app/lib/config/env";
|
||||
import { request } from "@app/lib/config/request";
|
||||
import { BadRequestError, NotFoundError } from "@app/lib/errors";
|
||||
import { BadRequestError, ForbiddenRequestError, InternalServerError, NotFoundError } from "@app/lib/errors";
|
||||
|
||||
import { Integrations, IntegrationUrls } from "./integration-list";
|
||||
|
||||
@@ -234,12 +234,73 @@ const exchangeCodeNetlify = async ({ code }: { code: string }) => {
|
||||
};
|
||||
};
|
||||
|
||||
const exchangeCodeGithub = async ({ code }: { code: string }) => {
|
||||
const exchangeCodeGithub = async ({ code, installationId }: { code: string; installationId?: string }) => {
|
||||
const appCfg = getConfig();
|
||||
if (!appCfg.CLIENT_ID_GITHUB || !appCfg.CLIENT_SECRET_GITHUB) {
|
||||
throw new BadRequestError({ message: "Missing client id and client secret" });
|
||||
|
||||
if (!installationId && (!appCfg.CLIENT_ID_GITHUB || !appCfg.CLIENT_SECRET_GITHUB)) {
|
||||
throw new InternalServerError({ message: "Missing client id and client secret" });
|
||||
}
|
||||
|
||||
if (installationId && (!appCfg.CLIENT_ID_GITHUB_APP || !appCfg.CLIENT_SECRET_GITHUB_APP)) {
|
||||
throw new InternalServerError({
|
||||
message: "Missing Github app client ID and client secret"
|
||||
});
|
||||
}
|
||||
|
||||
if (installationId) {
|
||||
// handle app installations
|
||||
const res = (
|
||||
await request.get<ExchangeCodeGithubResponse>(IntegrationUrls.GITHUB_TOKEN_URL, {
|
||||
params: {
|
||||
client_id: appCfg.CLIENT_ID_GITHUB_APP,
|
||||
client_secret: appCfg.CLIENT_SECRET_GITHUB_APP,
|
||||
code,
|
||||
redirect_uri: `${appCfg.SITE_URL}/integrations/github/oauth2/callback`
|
||||
},
|
||||
headers: {
|
||||
Accept: "application/json",
|
||||
"Accept-Encoding": "application/json"
|
||||
}
|
||||
})
|
||||
).data;
|
||||
|
||||
// use access token to validate installation ID
|
||||
const installationsRes = (
|
||||
await request.get<{
|
||||
installations: {
|
||||
id: number;
|
||||
account: {
|
||||
login: string;
|
||||
};
|
||||
}[];
|
||||
}>(IntegrationUrls.GITHUB_USER_INSTALLATIONS, {
|
||||
headers: {
|
||||
Accept: "application/json",
|
||||
Authorization: `Bearer ${res.access_token}`,
|
||||
"Accept-Encoding": "application/json"
|
||||
}
|
||||
})
|
||||
).data;
|
||||
|
||||
const matchingInstallation = installationsRes.installations.find(
|
||||
(installation) => installation.id === +installationId
|
||||
);
|
||||
|
||||
if (!matchingInstallation) {
|
||||
throw new ForbiddenRequestError({
|
||||
message: "User has no access to the provided installation"
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
accessToken: "",
|
||||
refreshToken: null,
|
||||
accessExpiresAt: null,
|
||||
installationName: matchingInstallation.account.login
|
||||
};
|
||||
}
|
||||
|
||||
// handle normal oauth
|
||||
const res = (
|
||||
await request.get<ExchangeCodeGithubResponse>(IntegrationUrls.GITHUB_TOKEN_URL, {
|
||||
params: {
|
||||
@@ -346,6 +407,7 @@ type TExchangeReturn = {
|
||||
url?: string;
|
||||
teamId?: string;
|
||||
accountId?: string;
|
||||
installationName?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -355,11 +417,13 @@ type TExchangeReturn = {
|
||||
export const exchangeCode = async ({
|
||||
integration,
|
||||
code,
|
||||
url
|
||||
url,
|
||||
installationId
|
||||
}: {
|
||||
integration: string;
|
||||
code: string;
|
||||
url?: string;
|
||||
installationId?: string;
|
||||
}): Promise<TExchangeReturn> => {
|
||||
switch (integration) {
|
||||
case Integrations.GCP_SECRET_MANAGER:
|
||||
@@ -384,7 +448,8 @@ export const exchangeCode = async ({
|
||||
});
|
||||
case Integrations.GITHUB:
|
||||
return exchangeCodeGithub({
|
||||
code
|
||||
code,
|
||||
installationId
|
||||
});
|
||||
case Integrations.GITLAB:
|
||||
return exchangeCodeGitlab({
|
||||
|
||||
Reference in New Issue
Block a user