Merge pull request #3436 from Infisical/misc/made-jwt-signature-alg-configurable-for-oidc

misc: made jwt signature alg configurable for oidc
This commit is contained in:
Sheen
2025-04-17 00:47:16 +08:00
committed by GitHub
12 changed files with 108 additions and 22 deletions

View File

@@ -0,0 +1,21 @@
import { Knex } from "knex";
import { OIDCJWTSignatureAlgorithm } from "@app/ee/services/oidc/oidc-config-types";
import { TableName } from "../schemas";
export async function up(knex: Knex): Promise<void> {
if (!(await knex.schema.hasColumn(TableName.OidcConfig, "jwtSignatureAlgorithm"))) {
await knex.schema.alterTable(TableName.OidcConfig, (t) => {
t.string("jwtSignatureAlgorithm").defaultTo(OIDCJWTSignatureAlgorithm.RS256).notNullable();
});
}
}
export async function down(knex: Knex): Promise<void> {
if (await knex.schema.hasColumn(TableName.OidcConfig, "jwtSignatureAlgorithm")) {
await knex.schema.alterTable(TableName.OidcConfig, (t) => {
t.dropColumn("jwtSignatureAlgorithm");
});
}
}

View File

@@ -30,9 +30,10 @@ export const OidcConfigsSchema = z.object({
updatedAt: z.date(),
orgId: z.string().uuid(),
lastUsed: z.date().nullable().optional(),
manageGroupMemberships: z.boolean().default(false),
encryptedOidcClientId: zodBuffer,
encryptedOidcClientSecret: zodBuffer
encryptedOidcClientSecret: zodBuffer,
manageGroupMemberships: z.boolean().default(false),
jwtSignatureAlgorithm: z.string().default("RS256")
});
export type TOidcConfigs = z.infer<typeof OidcConfigsSchema>;

View File

@@ -12,7 +12,7 @@ import RedisStore from "connect-redis";
import { z } from "zod";
import { OidcConfigsSchema } from "@app/db/schemas";
import { OIDCConfigurationType } from "@app/ee/services/oidc/oidc-config-types";
import { OIDCConfigurationType, OIDCJWTSignatureAlgorithm } from "@app/ee/services/oidc/oidc-config-types";
import { getConfig } from "@app/lib/config/env";
import { authRateLimit, readLimit, writeLimit } from "@app/server/config/rateLimiter";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
@@ -30,7 +30,8 @@ const SanitizedOidcConfigSchema = OidcConfigsSchema.pick({
orgId: true,
isActive: true,
allowedEmailDomains: true,
manageGroupMemberships: true
manageGroupMemberships: true,
jwtSignatureAlgorithm: true
});
export const registerOidcRouter = async (server: FastifyZodProvider) => {
@@ -170,7 +171,8 @@ export const registerOidcRouter = async (server: FastifyZodProvider) => {
isActive: true,
orgId: true,
allowedEmailDomains: true,
manageGroupMemberships: true
manageGroupMemberships: true,
jwtSignatureAlgorithm: true
}).extend({
clientId: z.string(),
clientSecret: z.string()
@@ -225,7 +227,8 @@ export const registerOidcRouter = async (server: FastifyZodProvider) => {
clientId: z.string().trim(),
clientSecret: z.string().trim(),
isActive: z.boolean(),
manageGroupMemberships: z.boolean().optional()
manageGroupMemberships: z.boolean().optional(),
jwtSignatureAlgorithm: z.nativeEnum(OIDCJWTSignatureAlgorithm).optional()
})
.partial()
.merge(z.object({ orgSlug: z.string() })),
@@ -292,7 +295,11 @@ export const registerOidcRouter = async (server: FastifyZodProvider) => {
clientSecret: z.string().trim(),
isActive: z.boolean(),
orgSlug: z.string().trim(),
manageGroupMemberships: z.boolean().optional().default(false)
manageGroupMemberships: z.boolean().optional().default(false),
jwtSignatureAlgorithm: z
.nativeEnum(OIDCJWTSignatureAlgorithm)
.optional()
.default(OIDCJWTSignatureAlgorithm.RS256)
})
.superRefine((data, ctx) => {
if (data.configurationType === OIDCConfigurationType.CUSTOM) {

View File

@@ -165,7 +165,8 @@ export const oidcConfigServiceFactory = ({
allowedEmailDomains: oidcCfg.allowedEmailDomains,
clientId,
clientSecret,
manageGroupMemberships: oidcCfg.manageGroupMemberships
manageGroupMemberships: oidcCfg.manageGroupMemberships,
jwtSignatureAlgorithm: oidcCfg.jwtSignatureAlgorithm
};
};
@@ -481,7 +482,8 @@ export const oidcConfigServiceFactory = ({
userinfoEndpoint,
clientId,
clientSecret,
manageGroupMemberships
manageGroupMemberships,
jwtSignatureAlgorithm
}: TUpdateOidcCfgDTO) => {
const org = await orgDAL.findOne({
slug: orgSlug
@@ -536,7 +538,8 @@ export const oidcConfigServiceFactory = ({
jwksUri,
isActive,
lastUsed: null,
manageGroupMemberships
manageGroupMemberships,
jwtSignatureAlgorithm
};
if (clientId !== undefined) {
@@ -569,7 +572,8 @@ export const oidcConfigServiceFactory = ({
userinfoEndpoint,
clientId,
clientSecret,
manageGroupMemberships
manageGroupMemberships,
jwtSignatureAlgorithm
}: TCreateOidcCfgDTO) => {
const org = await orgDAL.findOne({
slug: orgSlug
@@ -613,6 +617,7 @@ export const oidcConfigServiceFactory = ({
userinfoEndpoint,
orgId: org.id,
manageGroupMemberships,
jwtSignatureAlgorithm,
encryptedOidcClientId: encryptor({ plainText: Buffer.from(clientId) }).cipherTextBlob,
encryptedOidcClientSecret: encryptor({ plainText: Buffer.from(clientSecret) }).cipherTextBlob
});
@@ -676,7 +681,8 @@ export const oidcConfigServiceFactory = ({
const client = new issuer.Client({
client_id: oidcCfg.clientId,
client_secret: oidcCfg.clientSecret,
redirect_uris: [`${appCfg.SITE_URL}/api/v1/sso/oidc/callback`]
redirect_uris: [`${appCfg.SITE_URL}/api/v1/sso/oidc/callback`],
id_token_signed_response_alg: oidcCfg.jwtSignatureAlgorithm
});
const strategy = new OpenIdStrategy(

View File

@@ -5,6 +5,12 @@ export enum OIDCConfigurationType {
DISCOVERY_URL = "discoveryURL"
}
export enum OIDCJWTSignatureAlgorithm {
RS256 = "RS256",
HS256 = "HS256",
RS512 = "RS512"
}
export type TOidcLoginDTO = {
externalId: string;
email: string;
@@ -40,6 +46,7 @@ export type TCreateOidcCfgDTO = {
isActive: boolean;
orgSlug: string;
manageGroupMemberships: boolean;
jwtSignatureAlgorithm: OIDCJWTSignatureAlgorithm;
} & TGenericPermission;
export type TUpdateOidcCfgDTO = Partial<{
@@ -56,5 +63,6 @@ export type TUpdateOidcCfgDTO = Partial<{
isActive: boolean;
orgSlug: string;
manageGroupMemberships: boolean;
jwtSignatureAlgorithm: OIDCJWTSignatureAlgorithm;
}> &
TGenericPermission;

View File

@@ -42,7 +42,7 @@ description: "Learn how to configure Auth0 OIDC for Infisical SSO."
3.1. Back in Infisical, in the Organization settings > Security > OIDC, click **Connect**.
![OIDC auth0 manage org Infisical](../../../images/sso/auth0-oidc/org-oidc-overview.png)
3.2. For configuration type, select **Discovery URL**. Then, set **Discovery Document URL**, **Client ID**, and **Client Secret** from step 2.1 and 2.2.
3.2. For configuration type, select **Discovery URL**. Then, set **Discovery Document URL**, **JWT Signature Algorithm**, **Client ID**, and **Client Secret** from step 2.1 and 2.2.
![OIDC auth0 paste values into Infisical](../../../images/sso/auth0-oidc/org-update-oidc.png)
Once you've done that, press **Update** to complete the required configuration.

View File

@@ -69,7 +69,7 @@ description: "Learn how to configure Keycloak OIDC for Infisical SSO."
3.1. Back in Infisical, in the Organization settings > Security > OIDC, click Connect.
![OIDC keycloak manage org Infisical](/images/sso/keycloak-oidc/manage-org-oidc.png)
3.2. For configuration type, select Discovery URL. Then, set the appropriate values for **Discovery Document URL**, **Client ID**, and **Client Secret**.
3.2. For configuration type, select Discovery URL. Then, set the appropriate values for **Discovery Document URL**, **JWT Signature Algorithm**, **Client ID**, and **Client Secret**.
![OIDC keycloak paste values into Infisical](/images/sso/keycloak-oidc/create-oidc.png)
Once you've done that, press **Update** to complete the required configuration.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 474 KiB

After

Width:  |  Height:  |  Size: 588 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 470 KiB

After

Width:  |  Height:  |  Size: 588 KiB

View File

@@ -4,6 +4,7 @@ import { apiRequest } from "@app/config/request";
import { organizationKeys } from "../organization/queries";
import { oidcConfigKeys } from "./queries";
import { OIDCJWTSignatureAlgorithm } from "./types";
export const useUpdateOIDCConfig = () => {
const queryClient = useQueryClient();
@@ -21,7 +22,8 @@ export const useUpdateOIDCConfig = () => {
clientSecret,
isActive,
orgSlug,
manageGroupMemberships
manageGroupMemberships,
jwtSignatureAlgorithm
}: {
allowedEmailDomains?: string;
issuer?: string;
@@ -36,6 +38,7 @@ export const useUpdateOIDCConfig = () => {
configurationType?: string;
orgSlug: string;
manageGroupMemberships?: boolean;
jwtSignatureAlgorithm?: OIDCJWTSignatureAlgorithm;
}) => {
const { data } = await apiRequest.patch("/api/v1/sso/oidc/config", {
issuer,
@@ -50,7 +53,8 @@ export const useUpdateOIDCConfig = () => {
orgSlug,
clientSecret,
isActive,
manageGroupMemberships
manageGroupMemberships,
jwtSignatureAlgorithm
});
return data;
@@ -78,7 +82,8 @@ export const useCreateOIDCConfig = () => {
clientSecret,
isActive,
orgSlug,
manageGroupMemberships
manageGroupMemberships,
jwtSignatureAlgorithm
}: {
issuer?: string;
configurationType: string;
@@ -93,6 +98,7 @@ export const useCreateOIDCConfig = () => {
orgSlug: string;
allowedEmailDomains?: string;
manageGroupMemberships?: boolean;
jwtSignatureAlgorithm?: OIDCJWTSignatureAlgorithm;
}) => {
const { data } = await apiRequest.post("/api/v1/sso/oidc/config", {
issuer,
@@ -107,7 +113,8 @@ export const useCreateOIDCConfig = () => {
clientSecret,
isActive,
orgSlug,
manageGroupMemberships
manageGroupMemberships,
jwtSignatureAlgorithm
});
return data;

View File

@@ -13,4 +13,11 @@ export type OIDCConfigData = {
clientSecret: string;
allowedEmailDomains?: string;
manageGroupMemberships: boolean;
jwtSignatureAlgorithm: OIDCJWTSignatureAlgorithm;
};
export enum OIDCJWTSignatureAlgorithm {
RS256 = "RS256",
HS256 = "HS256",
RS512 = "RS512"
}

View File

@@ -18,6 +18,7 @@ import { useOrganization } from "@app/context";
import { useToggle } from "@app/hooks";
import { useGetOIDCConfig } from "@app/hooks/api";
import { useCreateOIDCConfig, useUpdateOIDCConfig } from "@app/hooks/api/oidcConfig/mutations";
import { OIDCJWTSignatureAlgorithm } from "@app/hooks/api/oidcConfig/types";
import { UsePopUpState } from "@app/hooks/usePopUp";
enum ConfigurationType {
@@ -43,7 +44,8 @@ const schema = z
userinfoEndpoint: z.string().optional(),
clientId: z.string().min(1),
clientSecret: z.string().min(1),
allowedEmailDomains: z.string().optional()
allowedEmailDomains: z.string().optional(),
jwtSignatureAlgorithm: z.nativeEnum(OIDCJWTSignatureAlgorithm).optional()
})
.superRefine((data, ctx) => {
if (data.configurationType === ConfigurationType.CUSTOM) {
@@ -159,6 +161,7 @@ export const OIDCModal = ({ popUp, handlePopUpClose, handlePopUpToggle, hideDele
setValue("clientSecret", data.clientSecret);
setValue("allowedEmailDomains", data.allowedEmailDomains);
setValue("configurationType", data.configurationType);
setValue("jwtSignatureAlgorithm", data.jwtSignatureAlgorithm);
}
}, [data]);
@@ -172,7 +175,8 @@ export const OIDCModal = ({ popUp, handlePopUpClose, handlePopUpToggle, hideDele
configurationType,
discoveryURL,
clientId,
clientSecret
clientSecret,
jwtSignatureAlgorithm
}: OIDCFormData) => {
try {
if (!currentOrg) {
@@ -192,7 +196,8 @@ export const OIDCModal = ({ popUp, handlePopUpClose, handlePopUpToggle, hideDele
clientId,
clientSecret,
isActive: true,
orgSlug: currentOrg.slug
orgSlug: currentOrg.slug,
jwtSignatureAlgorithm
});
} else {
await updateMutateAsync({
@@ -207,7 +212,8 @@ export const OIDCModal = ({ popUp, handlePopUpClose, handlePopUpToggle, hideDele
clientId,
clientSecret,
isActive: true,
orgSlug: currentOrg.slug
orgSlug: currentOrg.slug,
jwtSignatureAlgorithm
});
}
@@ -362,6 +368,29 @@ export const OIDCModal = ({ popUp, handlePopUpClose, handlePopUpToggle, hideDele
/>
</>
)}
<Controller
control={control}
defaultValue={OIDCJWTSignatureAlgorithm.RS256}
name="jwtSignatureAlgorithm"
render={({ field: { onChange, ...field }, fieldState: { error } }) => (
<FormControl
className="w-full"
label="JWT Signature Algorithm"
errorText={error?.message}
isError={Boolean(error)}
>
<Select
defaultValue={field.value}
onValueChange={(e) => onChange(e)}
className="w-full"
>
<SelectItem value={OIDCJWTSignatureAlgorithm.RS256}>RS256</SelectItem>
<SelectItem value={OIDCJWTSignatureAlgorithm.RS512}>RS512</SelectItem>
<SelectItem value={OIDCJWTSignatureAlgorithm.HS256}>HS256</SelectItem>
</Select>
</FormControl>
)}
/>
<Controller
control={control}
name="allowedEmailDomains"