mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-10 19:28:51 +00:00
feat: finished oidc form functions
This commit is contained in:
Vendored
+3
-1
@@ -113,6 +113,9 @@ import {
|
|||||||
TLdapGroupMaps,
|
TLdapGroupMaps,
|
||||||
TLdapGroupMapsInsert,
|
TLdapGroupMapsInsert,
|
||||||
TLdapGroupMapsUpdate,
|
TLdapGroupMapsUpdate,
|
||||||
|
TOidcConfigs,
|
||||||
|
TOidcConfigsInsert,
|
||||||
|
TOidcConfigsUpdate,
|
||||||
TOrganizations,
|
TOrganizations,
|
||||||
TOrganizationsInsert,
|
TOrganizationsInsert,
|
||||||
TOrganizationsUpdate,
|
TOrganizationsUpdate,
|
||||||
@@ -255,7 +258,6 @@ import {
|
|||||||
TWebhooksInsert,
|
TWebhooksInsert,
|
||||||
TWebhooksUpdate
|
TWebhooksUpdate
|
||||||
} from "@app/db/schemas";
|
} from "@app/db/schemas";
|
||||||
import { TOidcConfigs, TOidcConfigsInsert, TOidcConfigsUpdate } from "@app/db/schemas/oidc-configs";
|
|
||||||
|
|
||||||
declare module "knex/types/tables" {
|
declare module "knex/types/tables" {
|
||||||
interface Tables {
|
interface Tables {
|
||||||
|
|||||||
@@ -6,17 +6,17 @@ export async function up(knex: Knex): Promise<void> {
|
|||||||
if (!(await knex.schema.hasTable(TableName.OidcConfig))) {
|
if (!(await knex.schema.hasTable(TableName.OidcConfig))) {
|
||||||
await knex.schema.createTable(TableName.OidcConfig, (tb) => {
|
await knex.schema.createTable(TableName.OidcConfig, (tb) => {
|
||||||
tb.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
|
tb.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
|
||||||
tb.string("issuer");
|
tb.string("issuer").notNullable();
|
||||||
tb.string("authorizationEndpoint");
|
tb.string("authorizationEndpoint").notNullable();
|
||||||
tb.string("jwksUri");
|
tb.string("jwksUri").notNullable();
|
||||||
tb.string("tokenEndpoint");
|
tb.string("tokenEndpoint").notNullable();
|
||||||
tb.string("userinfoEndpoint");
|
tb.string("userinfoEndpoint").notNullable();
|
||||||
tb.text("encryptedClientId");
|
tb.text("encryptedClientId").notNullable();
|
||||||
tb.string("clientIdIV");
|
tb.string("clientIdIV").notNullable();
|
||||||
tb.string("clientIdTag");
|
tb.string("clientIdTag").notNullable();
|
||||||
tb.text("encryptedClientSecret");
|
tb.text("encryptedClientSecret").notNullable();
|
||||||
tb.string("clientSecretIV");
|
tb.string("clientSecretIV").notNullable();
|
||||||
tb.string("clientSecretTag");
|
tb.string("clientSecretTag").notNullable();
|
||||||
tb.boolean("isActive").notNullable();
|
tb.boolean("isActive").notNullable();
|
||||||
tb.timestamps(true, true, true);
|
tb.timestamps(true, true, true);
|
||||||
tb.uuid("orgId").notNullable().unique();
|
tb.uuid("orgId").notNullable().unique();
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ export * from "./kms-root-config";
|
|||||||
export * from "./ldap-configs";
|
export * from "./ldap-configs";
|
||||||
export * from "./ldap-group-maps";
|
export * from "./ldap-group-maps";
|
||||||
export * from "./models";
|
export * from "./models";
|
||||||
|
export * from "./oidc-configs";
|
||||||
export * from "./org-bots";
|
export * from "./org-bots";
|
||||||
export * from "./org-memberships";
|
export * from "./org-memberships";
|
||||||
export * from "./org-roles";
|
export * from "./org-roles";
|
||||||
|
|||||||
@@ -9,17 +9,17 @@ import { TImmutableDBKeys } from "./models";
|
|||||||
|
|
||||||
export const OidcConfigsSchema = z.object({
|
export const OidcConfigsSchema = z.object({
|
||||||
id: z.string().uuid(),
|
id: z.string().uuid(),
|
||||||
issuer: z.string().nullable().optional(),
|
issuer: z.string(),
|
||||||
authorizationEndpoint: z.string().nullable().optional(),
|
authorizationEndpoint: z.string(),
|
||||||
jwksUri: z.string().nullable().optional(),
|
jwksUri: z.string(),
|
||||||
tokenEndpoint: z.string().nullable().optional(),
|
tokenEndpoint: z.string(),
|
||||||
userinfoEndpoint: z.string().nullable().optional(),
|
userinfoEndpoint: z.string(),
|
||||||
encryptedClientId: z.string().nullable().optional(),
|
encryptedClientId: z.string(),
|
||||||
clientIdIV: z.string().nullable().optional(),
|
clientIdIV: z.string(),
|
||||||
clientIdTag: z.string().nullable().optional(),
|
clientIdTag: z.string(),
|
||||||
encryptedClientSecret: z.string().nullable().optional(),
|
encryptedClientSecret: z.string(),
|
||||||
clientSecretIV: z.string().nullable().optional(),
|
clientSecretIV: z.string(),
|
||||||
clientSecretTag: z.string().nullable().optional(),
|
clientSecretTag: z.string(),
|
||||||
isActive: z.boolean(),
|
isActive: z.boolean(),
|
||||||
createdAt: z.date(),
|
createdAt: z.date(),
|
||||||
updatedAt: z.date(),
|
updatedAt: z.date(),
|
||||||
|
|||||||
@@ -101,6 +101,7 @@ import { integrationAuthServiceFactory } from "@app/services/integration-auth/in
|
|||||||
import { kmsDALFactory } from "@app/services/kms/kms-dal";
|
import { kmsDALFactory } from "@app/services/kms/kms-dal";
|
||||||
import { kmsRootConfigDALFactory } from "@app/services/kms/kms-root-config-dal";
|
import { kmsRootConfigDALFactory } from "@app/services/kms/kms-root-config-dal";
|
||||||
import { kmsServiceFactory } from "@app/services/kms/kms-service";
|
import { kmsServiceFactory } from "@app/services/kms/kms-service";
|
||||||
|
import { oidcConfigDALFactory } from "@app/services/oidc/oidc-config-dal";
|
||||||
import { oidcConfigServiceFactory } from "@app/services/oidc/oidc-config-service";
|
import { oidcConfigServiceFactory } from "@app/services/oidc/oidc-config-service";
|
||||||
import { incidentContactDALFactory } from "@app/services/org/incident-contacts-dal";
|
import { incidentContactDALFactory } from "@app/services/org/incident-contacts-dal";
|
||||||
import { orgBotDALFactory } from "@app/services/org/org-bot-dal";
|
import { orgBotDALFactory } from "@app/services/org/org-bot-dal";
|
||||||
@@ -241,6 +242,7 @@ export const registerRoutes = async (
|
|||||||
const ldapConfigDAL = ldapConfigDALFactory(db);
|
const ldapConfigDAL = ldapConfigDALFactory(db);
|
||||||
const ldapGroupMapDAL = ldapGroupMapDALFactory(db);
|
const ldapGroupMapDAL = ldapGroupMapDALFactory(db);
|
||||||
|
|
||||||
|
const oidcConfigDAL = oidcConfigDALFactory(db);
|
||||||
const accessApprovalPolicyDAL = accessApprovalPolicyDALFactory(db);
|
const accessApprovalPolicyDAL = accessApprovalPolicyDALFactory(db);
|
||||||
const accessApprovalRequestDAL = accessApprovalRequestDALFactory(db);
|
const accessApprovalRequestDAL = accessApprovalRequestDALFactory(db);
|
||||||
const accessApprovalPolicyApproverDAL = accessApprovalPolicyApproverDALFactory(db);
|
const accessApprovalPolicyApproverDAL = accessApprovalPolicyApproverDALFactory(db);
|
||||||
@@ -846,7 +848,10 @@ export const registerRoutes = async (
|
|||||||
userAliasDAL,
|
userAliasDAL,
|
||||||
licenseService,
|
licenseService,
|
||||||
tokenService,
|
tokenService,
|
||||||
smtpService
|
smtpService,
|
||||||
|
orgBotDAL,
|
||||||
|
permissionService,
|
||||||
|
oidcConfigDAL
|
||||||
});
|
});
|
||||||
|
|
||||||
await superAdminService.initServerCfg();
|
await superAdminService.initServerCfg();
|
||||||
|
|||||||
@@ -12,6 +12,9 @@ import { z } from "zod";
|
|||||||
|
|
||||||
import { OidcConfigsSchema } from "@app/db/schemas/oidc-configs";
|
import { OidcConfigsSchema } from "@app/db/schemas/oidc-configs";
|
||||||
import { getConfig } from "@app/lib/config/env";
|
import { getConfig } from "@app/lib/config/env";
|
||||||
|
import { writeLimit } from "@app/server/config/rateLimiter";
|
||||||
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
|
import { AuthMode } from "@app/services/auth/auth-type";
|
||||||
|
|
||||||
export const registerOidcRouter = async (server: FastifyZodProvider) => {
|
export const registerOidcRouter = async (server: FastifyZodProvider) => {
|
||||||
const appCfg = getConfig();
|
const appCfg = getConfig();
|
||||||
@@ -101,6 +104,7 @@ export const registerOidcRouter = async (server: FastifyZodProvider) => {
|
|||||||
jwksUri: true,
|
jwksUri: true,
|
||||||
tokenEndpoint: true,
|
tokenEndpoint: true,
|
||||||
userinfoEndpoint: true,
|
userinfoEndpoint: true,
|
||||||
|
isActive: true,
|
||||||
orgId: true
|
orgId: true
|
||||||
}).extend({
|
}).extend({
|
||||||
clientId: z.string(),
|
clientId: z.string(),
|
||||||
@@ -122,4 +126,95 @@ export const registerOidcRouter = async (server: FastifyZodProvider) => {
|
|||||||
return oidc;
|
return oidc;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
method: "PATCH",
|
||||||
|
url: "/config",
|
||||||
|
config: {
|
||||||
|
rateLimit: writeLimit
|
||||||
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT]),
|
||||||
|
schema: {
|
||||||
|
body: z
|
||||||
|
.object({
|
||||||
|
issuer: z.string(),
|
||||||
|
authorizationEndpoint: z.string(),
|
||||||
|
jwksUri: z.string(),
|
||||||
|
tokenEndpoint: z.string(),
|
||||||
|
userinfoEndpoint: z.string(),
|
||||||
|
clientId: z.string(),
|
||||||
|
clientSecret: z.string(),
|
||||||
|
isActive: z.boolean()
|
||||||
|
})
|
||||||
|
.partial()
|
||||||
|
.merge(z.object({ orgSlug: z.string() })),
|
||||||
|
response: {
|
||||||
|
200: OidcConfigsSchema.pick({
|
||||||
|
id: true,
|
||||||
|
issuer: true,
|
||||||
|
authorizationEndpoint: true,
|
||||||
|
jwksUri: true,
|
||||||
|
tokenEndpoint: true,
|
||||||
|
userinfoEndpoint: true,
|
||||||
|
orgId: true,
|
||||||
|
isActive: true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handler: async (req) => {
|
||||||
|
const oidc = await server.services.oidc.updateOidcCfg({
|
||||||
|
actor: req.permission.type,
|
||||||
|
actorId: req.permission.id,
|
||||||
|
actorAuthMethod: req.permission.authMethod,
|
||||||
|
actorOrgId: req.permission.orgId,
|
||||||
|
...req.body
|
||||||
|
});
|
||||||
|
return oidc;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
method: "POST",
|
||||||
|
url: "/config",
|
||||||
|
config: {
|
||||||
|
rateLimit: writeLimit
|
||||||
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT]),
|
||||||
|
schema: {
|
||||||
|
body: z.object({
|
||||||
|
issuer: z.string(),
|
||||||
|
authorizationEndpoint: z.string(),
|
||||||
|
jwksUri: z.string(),
|
||||||
|
tokenEndpoint: z.string(),
|
||||||
|
userinfoEndpoint: z.string(),
|
||||||
|
clientId: z.string(),
|
||||||
|
clientSecret: z.string(),
|
||||||
|
isActive: z.boolean(),
|
||||||
|
orgSlug: z.string()
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: OidcConfigsSchema.pick({
|
||||||
|
id: true,
|
||||||
|
issuer: true,
|
||||||
|
authorizationEndpoint: true,
|
||||||
|
jwksUri: true,
|
||||||
|
tokenEndpoint: true,
|
||||||
|
userinfoEndpoint: true,
|
||||||
|
orgId: true,
|
||||||
|
isActive: true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
handler: async (req) => {
|
||||||
|
const oidc = await server.services.oidc.createOidcCfg({
|
||||||
|
actor: req.permission.type,
|
||||||
|
actorId: req.permission.id,
|
||||||
|
actorAuthMethod: req.permission.authMethod,
|
||||||
|
actorOrgId: req.permission.orgId,
|
||||||
|
...req.body
|
||||||
|
});
|
||||||
|
return oidc;
|
||||||
|
}
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -13,6 +13,18 @@ export type TGetOidcCfgDTO = {
|
|||||||
orgSlug: string;
|
orgSlug: string;
|
||||||
} & TGenericPermission;
|
} & TGenericPermission;
|
||||||
|
|
||||||
|
export type TCreateOidcCfgDTO = {
|
||||||
|
issuer: string;
|
||||||
|
authorizationEndpoint: string;
|
||||||
|
jwksUri: string;
|
||||||
|
tokenEndpoint: string;
|
||||||
|
userinfoEndpoint: string;
|
||||||
|
clientId: string;
|
||||||
|
clientSecret: string;
|
||||||
|
isActive: boolean;
|
||||||
|
orgSlug: string;
|
||||||
|
} & TGenericPermission;
|
||||||
|
|
||||||
export type TUpdateOidcCfgDTO = Partial<{
|
export type TUpdateOidcCfgDTO = Partial<{
|
||||||
issuer: string;
|
issuer: string;
|
||||||
authorizationEndpoint: string;
|
authorizationEndpoint: string;
|
||||||
|
|||||||
@@ -0,0 +1,93 @@
|
|||||||
|
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
||||||
|
|
||||||
|
import { apiRequest } from "@app/config/request";
|
||||||
|
|
||||||
|
import { oidcConfigKeys } from "./queries";
|
||||||
|
|
||||||
|
export const useUpdateOIDCConfig = () => {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
return useMutation({
|
||||||
|
mutationFn: async ({
|
||||||
|
issuer,
|
||||||
|
authorizationEndpoint,
|
||||||
|
jwksUri,
|
||||||
|
tokenEndpoint,
|
||||||
|
userinfoEndpoint,
|
||||||
|
clientId,
|
||||||
|
clientSecret,
|
||||||
|
isActive,
|
||||||
|
orgSlug
|
||||||
|
}: {
|
||||||
|
issuer?: string;
|
||||||
|
authorizationEndpoint?: string;
|
||||||
|
jwksUri?: string;
|
||||||
|
tokenEndpoint?: string;
|
||||||
|
userinfoEndpoint?: string;
|
||||||
|
clientId?: string;
|
||||||
|
clientSecret?: string;
|
||||||
|
isActive?: boolean;
|
||||||
|
orgSlug: string;
|
||||||
|
}) => {
|
||||||
|
const { data } = await apiRequest.patch("/api/v1/oidc/config", {
|
||||||
|
issuer,
|
||||||
|
authorizationEndpoint,
|
||||||
|
jwksUri,
|
||||||
|
tokenEndpoint,
|
||||||
|
userinfoEndpoint,
|
||||||
|
clientId,
|
||||||
|
orgSlug,
|
||||||
|
clientSecret,
|
||||||
|
isActive
|
||||||
|
});
|
||||||
|
|
||||||
|
return data;
|
||||||
|
},
|
||||||
|
onSuccess(_, dto) {
|
||||||
|
queryClient.invalidateQueries(oidcConfigKeys.getOIDCConfig(dto.orgSlug));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useCreateOIDCConfig = () => {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
return useMutation({
|
||||||
|
mutationFn: async ({
|
||||||
|
issuer,
|
||||||
|
authorizationEndpoint,
|
||||||
|
jwksUri,
|
||||||
|
tokenEndpoint,
|
||||||
|
userinfoEndpoint,
|
||||||
|
clientId,
|
||||||
|
clientSecret,
|
||||||
|
isActive,
|
||||||
|
orgSlug
|
||||||
|
}: {
|
||||||
|
issuer: string;
|
||||||
|
authorizationEndpoint: string;
|
||||||
|
jwksUri: string;
|
||||||
|
tokenEndpoint: string;
|
||||||
|
userinfoEndpoint: string;
|
||||||
|
clientId: string;
|
||||||
|
clientSecret: string;
|
||||||
|
isActive: boolean;
|
||||||
|
orgSlug: string;
|
||||||
|
}) => {
|
||||||
|
const { data } = await apiRequest.post("/api/v1/oidc/config", {
|
||||||
|
issuer,
|
||||||
|
authorizationEndpoint,
|
||||||
|
jwksUri,
|
||||||
|
tokenEndpoint,
|
||||||
|
userinfoEndpoint,
|
||||||
|
clientId,
|
||||||
|
clientSecret,
|
||||||
|
isActive,
|
||||||
|
orgSlug
|
||||||
|
});
|
||||||
|
|
||||||
|
return data;
|
||||||
|
},
|
||||||
|
onSuccess(_, dto) {
|
||||||
|
queryClient.invalidateQueries(oidcConfigKeys.getOIDCConfig(dto.orgSlug));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -0,0 +1,245 @@
|
|||||||
|
import { useEffect } from "react";
|
||||||
|
import { Controller, useForm } from "react-hook-form";
|
||||||
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { createNotification } from "@app/components/notifications";
|
||||||
|
import { Button, FormControl, Input, Modal, ModalContent } from "@app/components/v2";
|
||||||
|
import { useOrganization } from "@app/context";
|
||||||
|
import { useGetOIDCConfig } from "@app/hooks/api";
|
||||||
|
import { useCreateOIDCConfig, useUpdateOIDCConfig } from "@app/hooks/api/oidcConfig/mutations";
|
||||||
|
import { UsePopUpState } from "@app/hooks/usePopUp";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
popUp: UsePopUpState<["addOIDC"]>;
|
||||||
|
handlePopUpClose: (popUpName: keyof UsePopUpState<["addOIDC"]>) => void;
|
||||||
|
handlePopUpToggle: (popUpName: keyof UsePopUpState<["addOIDC"]>, state?: boolean) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
const schema = z.object({
|
||||||
|
issuer: z.string().min(1),
|
||||||
|
authorizationEndpoint: z.string().min(1),
|
||||||
|
jwksUri: z.string().min(1),
|
||||||
|
tokenEndpoint: z.string().min(1),
|
||||||
|
userinfoEndpoint: z.string().min(1),
|
||||||
|
clientId: z.string().min(1),
|
||||||
|
clientSecret: z.string().min(1)
|
||||||
|
});
|
||||||
|
|
||||||
|
export type OIDCFormData = z.infer<typeof schema>;
|
||||||
|
|
||||||
|
export const OIDCModal = ({ popUp, handlePopUpClose, handlePopUpToggle }: Props) => {
|
||||||
|
const { currentOrg } = useOrganization();
|
||||||
|
|
||||||
|
const { mutateAsync: createMutateAsync, isLoading: createIsLoading } = useCreateOIDCConfig();
|
||||||
|
const { mutateAsync: updateMutateAsync, isLoading: updateIsLoading } = useUpdateOIDCConfig();
|
||||||
|
const { data } = useGetOIDCConfig(currentOrg?.slug ?? "");
|
||||||
|
|
||||||
|
const { control, handleSubmit, reset, setValue } = useForm<OIDCFormData>({
|
||||||
|
resolver: zodResolver(schema)
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (data) {
|
||||||
|
setValue("issuer", data.issuer);
|
||||||
|
setValue("authorizationEndpoint", data.authorizationEndpoint);
|
||||||
|
setValue("jwksUri", data.jwksUri);
|
||||||
|
setValue("tokenEndpoint", data.tokenEndpoint);
|
||||||
|
setValue("userinfoEndpoint", data.userinfoEndpoint);
|
||||||
|
setValue("clientId", data.clientId);
|
||||||
|
setValue("clientSecret", data.clientSecret);
|
||||||
|
}
|
||||||
|
}, [data]);
|
||||||
|
|
||||||
|
const onOIDCModalSubmit = async ({
|
||||||
|
issuer,
|
||||||
|
authorizationEndpoint,
|
||||||
|
jwksUri,
|
||||||
|
tokenEndpoint,
|
||||||
|
userinfoEndpoint,
|
||||||
|
clientId,
|
||||||
|
clientSecret
|
||||||
|
}: OIDCFormData) => {
|
||||||
|
try {
|
||||||
|
if (!currentOrg) return;
|
||||||
|
|
||||||
|
if (!data) {
|
||||||
|
await createMutateAsync({
|
||||||
|
issuer,
|
||||||
|
authorizationEndpoint,
|
||||||
|
jwksUri,
|
||||||
|
tokenEndpoint,
|
||||||
|
userinfoEndpoint,
|
||||||
|
clientId,
|
||||||
|
clientSecret,
|
||||||
|
isActive: true,
|
||||||
|
orgSlug: currentOrg.slug
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
await updateMutateAsync({
|
||||||
|
issuer,
|
||||||
|
authorizationEndpoint,
|
||||||
|
jwksUri,
|
||||||
|
tokenEndpoint,
|
||||||
|
userinfoEndpoint,
|
||||||
|
clientId,
|
||||||
|
clientSecret,
|
||||||
|
isActive: true,
|
||||||
|
orgSlug: currentOrg.slug
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
handlePopUpClose("addOIDC");
|
||||||
|
|
||||||
|
createNotification({
|
||||||
|
text: `Successfully ${!data ? "added" : "updated"} OIDC SSO configuration`,
|
||||||
|
type: "success"
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
createNotification({
|
||||||
|
text: `Failed to ${!data ? "add" : "update"} OIDC SSO configuration`,
|
||||||
|
type: "error"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
isOpen={popUp?.addOIDC?.isOpen}
|
||||||
|
onOpenChange={(isOpen) => {
|
||||||
|
handlePopUpToggle("addOIDC", isOpen);
|
||||||
|
reset();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ModalContent title="Manage OIDC configuration">
|
||||||
|
<form onSubmit={handleSubmit(onOIDCModalSubmit)}>
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="issuer"
|
||||||
|
render={({ field, fieldState: { error } }) => (
|
||||||
|
<FormControl label="Issuer" errorText={error?.message} isError={Boolean(error)}>
|
||||||
|
<Input {...field} placeholder="https://accounts.google.com" autoComplete="off" />
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="authorizationEndpoint"
|
||||||
|
render={({ field, fieldState: { error } }) => (
|
||||||
|
<FormControl
|
||||||
|
label="Authorization Endpoint"
|
||||||
|
errorText={error?.message}
|
||||||
|
isError={Boolean(error)}
|
||||||
|
>
|
||||||
|
<Input
|
||||||
|
{...field}
|
||||||
|
placeholder="https://accounts.google.com/o/oauth2/v2/auth"
|
||||||
|
autoComplete="off"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="tokenEndpoint"
|
||||||
|
render={({ field, fieldState: { error } }) => (
|
||||||
|
<FormControl
|
||||||
|
label="Token Endpoint"
|
||||||
|
errorText={error?.message}
|
||||||
|
isError={Boolean(error)}
|
||||||
|
>
|
||||||
|
<Input
|
||||||
|
{...field}
|
||||||
|
placeholder="https://oauth2.googleapis.com/token"
|
||||||
|
autoComplete="off"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="userinfoEndpoint"
|
||||||
|
render={({ field, fieldState: { error } }) => (
|
||||||
|
<FormControl
|
||||||
|
label="User info endpoint"
|
||||||
|
errorText={error?.message}
|
||||||
|
isError={Boolean(error)}
|
||||||
|
>
|
||||||
|
<Input
|
||||||
|
{...field}
|
||||||
|
placeholder="https://openidconnect.googleapis.com/v1/userinfo"
|
||||||
|
autoComplete="off"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="jwksUri"
|
||||||
|
render={({ field, fieldState: { error } }) => (
|
||||||
|
<FormControl label="JWKS URI" errorText={error?.message} isError={Boolean(error)}>
|
||||||
|
<Input
|
||||||
|
{...field}
|
||||||
|
placeholder="https://www.googleapis.com/oauth2/v3/certs"
|
||||||
|
autoComplete="off"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="clientId"
|
||||||
|
render={({ field, fieldState: { error } }) => (
|
||||||
|
<FormControl label="Client ID" errorText={error?.message} isError={Boolean(error)}>
|
||||||
|
<Input
|
||||||
|
placeholder="Client ID"
|
||||||
|
type="password"
|
||||||
|
autoComplete="off"
|
||||||
|
{...field}
|
||||||
|
className="bg-mineshaft-800"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="clientSecret"
|
||||||
|
render={({ field, fieldState: { error } }) => (
|
||||||
|
<FormControl
|
||||||
|
label="Client Secret"
|
||||||
|
errorText={error?.message}
|
||||||
|
isError={Boolean(error)}
|
||||||
|
>
|
||||||
|
<Input
|
||||||
|
placeholder="Client Secret"
|
||||||
|
type="password"
|
||||||
|
autoComplete="off"
|
||||||
|
{...field}
|
||||||
|
className="bg-mineshaft-800"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<div className="mt-8 flex items-center">
|
||||||
|
<Button
|
||||||
|
className="mr-4"
|
||||||
|
size="sm"
|
||||||
|
type="submit"
|
||||||
|
isLoading={createIsLoading || updateIsLoading}
|
||||||
|
>
|
||||||
|
{!data ? "Add" : "Update"}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
colorSchema="secondary"
|
||||||
|
variant="plain"
|
||||||
|
onClick={() => handlePopUpClose("addOIDC")}
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</ModalContent>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -3,6 +3,7 @@ import { withPermission } from "@app/hoc";
|
|||||||
|
|
||||||
import { OrgGeneralAuthSection } from "./OrgGeneralAuthSection";
|
import { OrgGeneralAuthSection } from "./OrgGeneralAuthSection";
|
||||||
import { OrgLDAPSection } from "./OrgLDAPSection";
|
import { OrgLDAPSection } from "./OrgLDAPSection";
|
||||||
|
import { OrgOIDCSection } from "./OrgOIDCSection";
|
||||||
import { OrgScimSection } from "./OrgSCIMSection";
|
import { OrgScimSection } from "./OrgSCIMSection";
|
||||||
import { OrgSSOSection } from "./OrgSSOSection";
|
import { OrgSSOSection } from "./OrgSSOSection";
|
||||||
|
|
||||||
@@ -12,6 +13,7 @@ export const OrgAuthTab = withPermission(
|
|||||||
<div className="rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-6">
|
<div className="rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-6">
|
||||||
<OrgGeneralAuthSection />
|
<OrgGeneralAuthSection />
|
||||||
<OrgSSOSection />
|
<OrgSSOSection />
|
||||||
|
<OrgOIDCSection />
|
||||||
<OrgLDAPSection />
|
<OrgLDAPSection />
|
||||||
<OrgScimSection />
|
<OrgScimSection />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -0,0 +1,101 @@
|
|||||||
|
import { createNotification } from "@app/components/notifications";
|
||||||
|
import { OrgPermissionCan } from "@app/components/permissions";
|
||||||
|
import { Button, Switch } from "@app/components/v2";
|
||||||
|
import { OrgPermissionActions, OrgPermissionSubjects, useOrganization } from "@app/context";
|
||||||
|
import { useGetOIDCConfig } from "@app/hooks/api";
|
||||||
|
import { useUpdateOIDCConfig } from "@app/hooks/api/oidcConfig/mutations";
|
||||||
|
import { usePopUp } from "@app/hooks/usePopUp";
|
||||||
|
|
||||||
|
import { OIDCModal } from "./OIDCModal";
|
||||||
|
|
||||||
|
export const OrgOIDCSection = (): JSX.Element => {
|
||||||
|
const { currentOrg } = useOrganization();
|
||||||
|
|
||||||
|
const { data, isLoading } = useGetOIDCConfig(currentOrg?.slug ?? "");
|
||||||
|
const { mutateAsync } = useUpdateOIDCConfig();
|
||||||
|
const { popUp, handlePopUpOpen, handlePopUpClose, handlePopUpToggle } = usePopUp([
|
||||||
|
"addOIDC"
|
||||||
|
] as const);
|
||||||
|
|
||||||
|
const handleOIDCToggle = async (value: boolean) => {
|
||||||
|
try {
|
||||||
|
if (!currentOrg?.id) return;
|
||||||
|
|
||||||
|
await mutateAsync({
|
||||||
|
orgSlug: currentOrg?.slug,
|
||||||
|
isActive: value
|
||||||
|
});
|
||||||
|
|
||||||
|
createNotification({
|
||||||
|
text: `Successfully ${value ? "enabled" : "disabled"} OIDC SSO`,
|
||||||
|
type: "success"
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
createNotification({
|
||||||
|
text: `Failed to ${value ? "enable" : "disable"} OIDC SSO`,
|
||||||
|
type: "error"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const addOidcButtonClick = async () => {
|
||||||
|
try {
|
||||||
|
handlePopUpOpen("addOIDC");
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<hr className="border-mineshaft-600" />
|
||||||
|
<div className="py-4">
|
||||||
|
<div className="mb-2 flex items-center justify-between">
|
||||||
|
<h2 className="text-md text-mineshaft-100">OIDC</h2>
|
||||||
|
{!isLoading && (
|
||||||
|
<OrgPermissionCan I={OrgPermissionActions.Create} a={OrgPermissionSubjects.Sso}>
|
||||||
|
{(isAllowed) => (
|
||||||
|
<Button
|
||||||
|
onClick={addOidcButtonClick}
|
||||||
|
colorSchema="secondary"
|
||||||
|
isDisabled={!isAllowed}
|
||||||
|
>
|
||||||
|
Manage
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</OrgPermissionCan>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<p className="text-sm text-mineshaft-300">Manage OIDC authentication configuration</p>
|
||||||
|
</div>
|
||||||
|
{data && (
|
||||||
|
<div className="py-4">
|
||||||
|
<div className="mb-2 flex items-center justify-between">
|
||||||
|
<h2 className="text-md text-mineshaft-100">Enable OIDC</h2>
|
||||||
|
{!isLoading && (
|
||||||
|
<OrgPermissionCan I={OrgPermissionActions.Edit} a={OrgPermissionSubjects.Sso}>
|
||||||
|
{(isAllowed) => (
|
||||||
|
<Switch
|
||||||
|
id="enable-oidc-sso"
|
||||||
|
onCheckedChange={(value) => handleOIDCToggle(value)}
|
||||||
|
isChecked={data ? data.isActive : false}
|
||||||
|
isDisabled={!isAllowed}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</OrgPermissionCan>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<p className="text-sm text-mineshaft-300">
|
||||||
|
Allow members to authenticate into Infisical with OIDC
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<OIDCModal
|
||||||
|
popUp={popUp}
|
||||||
|
handlePopUpClose={handlePopUpClose}
|
||||||
|
handlePopUpToggle={handlePopUpToggle}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user