diff --git a/backend/src/ee/services/secret-rotation-v2/azure-client-secret/azure-client-secret-rotation-fns.ts b/backend/src/ee/services/secret-rotation-v2/azure-client-secret/azure-client-secret-rotation-fns.ts index ea24c669d..bf134ecf3 100644 --- a/backend/src/ee/services/secret-rotation-v2/azure-client-secret/azure-client-secret-rotation-fns.ts +++ b/backend/src/ee/services/secret-rotation-v2/azure-client-secret/azure-client-secret-rotation-fns.ts @@ -24,7 +24,7 @@ export const azureClientSecretRotationFactory: TRotationFactory< > = (secretRotation, appConnectionDAL, kmsService) => { const { connection, - parameters: { appId }, + parameters: { appId, clientId: clientIdParam }, secretsMapping } = secretRotation; @@ -64,7 +64,8 @@ export const azureClientSecretRotationFactory: TRotationFactory< return { clientSecret: data.secretText, - clientId: data.keyId + keyId: data.keyId, + clientId: clientIdParam }; } catch (error: unknown) { if (error instanceof AxiosError) { @@ -81,14 +82,14 @@ export const azureClientSecretRotationFactory: TRotationFactory< /** * Revokes a client secret from the Azure app using its keyId. */ - const revokeCredential = async (clientId: string) => { + const revokeCredential = async (keyId: string) => { const accessToken = await getAzureConnectionAccessToken(connection.id, appConnectionDAL, kmsService); const endpoint = `${GRAPH_API_BASE}/applications/${appId}/removePassword`; try { await request.post( endpoint, - { keyId: clientId }, + { keyId }, { headers: { Authorization: `Bearer ${accessToken}`, @@ -99,7 +100,7 @@ export const azureClientSecretRotationFactory: TRotationFactory< } catch (error: unknown) { if (error instanceof AxiosError) { throw new BadRequestError({ - message: `Failed to remove client secret with keyId ${clientId} from app ${appId}: ${ + message: `Failed to remove client secret with keyId ${keyId} from app ${appId}: ${ error.message || "Unknown error" }` }); @@ -129,7 +130,7 @@ export const azureClientSecretRotationFactory: TRotationFactory< ) => { if (!credentials?.length) return callback(); - await Promise.all(credentials.map(({ clientId }) => revokeCredential(clientId))); + await Promise.all(credentials.map(({ keyId }) => revokeCredential(keyId))); return callback(); }; @@ -141,9 +142,8 @@ export const azureClientSecretRotationFactory: TRotationFactory< callback ) => { const newCredentials = await $rotateClientSecret(); - - if (oldCredentials?.clientId) { - await revokeCredential(oldCredentials.clientId); + if (oldCredentials?.keyId) { + await revokeCredential(oldCredentials.keyId); } return callback(newCredentials); @@ -154,7 +154,10 @@ export const azureClientSecretRotationFactory: TRotationFactory< */ const getSecretsPayload: TRotationFactoryGetSecretsPayload = ({ clientSecret - }) => [{ key: secretsMapping.clientSecret, value: clientSecret }]; + }) => [ + { key: secretsMapping.clientSecret, value: clientSecret }, + { key: secretsMapping.clientId, value: clientIdParam } + ]; return { issueCredentials, diff --git a/backend/src/ee/services/secret-rotation-v2/azure-client-secret/azure-client-secret-rotation-schemas.ts b/backend/src/ee/services/secret-rotation-v2/azure-client-secret/azure-client-secret-rotation-schemas.ts index b8a313a86..888dece54 100644 --- a/backend/src/ee/services/secret-rotation-v2/azure-client-secret/azure-client-secret-rotation-schemas.ts +++ b/backend/src/ee/services/secret-rotation-v2/azure-client-secret/azure-client-secret-rotation-schemas.ts @@ -13,7 +13,8 @@ import { AppConnection } from "@app/services/app-connection/app-connection-enums export const AzureClientSecretRotationGeneratedCredentialsSchema = z .object({ clientId: z.string(), - clientSecret: z.string() + clientSecret: z.string(), + keyId: z.string() }) .array() .min(1) @@ -21,7 +22,8 @@ export const AzureClientSecretRotationGeneratedCredentialsSchema = z const AzureClientSecretRotationParametersSchema = z.object({ appId: z.string().trim().min(1, "App ID Required").describe(SecretRotations.PARAMETERS.AZURE_CLIENT_SECRET.appId), - appName: z.string().trim().describe(SecretRotations.PARAMETERS.AZURE_CLIENT_SECRET.appName).optional() + appName: z.string().trim().describe(SecretRotations.PARAMETERS.AZURE_CLIENT_SECRET.appName).optional(), + clientId: z.string().trim().describe(SecretRotations.PARAMETERS.AZURE_CLIENT_SECRET.clientId) }); const AzureClientSecretRotationSecretsMappingSchema = z.object({ diff --git a/backend/src/lib/api-docs/constants.ts b/backend/src/lib/api-docs/constants.ts index 9a5f1a48f..f75e13df4 100644 --- a/backend/src/lib/api-docs/constants.ts +++ b/backend/src/lib/api-docs/constants.ts @@ -2089,7 +2089,8 @@ export const SecretRotations = { }, AZURE_CLIENT_SECRET: { appId: "The ID of the Azure Application to rotate the client secret for.", - appName: "The name of the Azure Application to rotate the client secret for." + appName: "The name of the Azure Application to rotate the client secret for.", + clientId: "The client ID of the Azure Application to rotate the client secret for." }, LDAP_PASSWORD: { dn: "The Distinguished Name (DN) of the principal to rotate the password for." diff --git a/docs/documentation/platform/secret-rotation/azure-client-secret.mdx b/docs/documentation/platform/secret-rotation/azure-client-secret.mdx index d377e0667..6ae7e89e0 100644 --- a/docs/documentation/platform/secret-rotation/azure-client-secret.mdx +++ b/docs/documentation/platform/secret-rotation/azure-client-secret.mdx @@ -75,6 +75,7 @@ description: "Learn how to automatically rotate Azure Client Secrets." }, "parameters": { "appId": "...", + "clientId": "...", "appName": "..." }, "secretsMapping": { @@ -131,7 +132,8 @@ description: "Learn how to automatically rotate Azure Client Secrets." "type": "azure-client-secret", "parameters": { "appId": "...", - "appName": "..." + "appName": "...", + "clientId": "..." } } } diff --git a/frontend/src/components/secret-rotations-v2/ViewSecretRotationV2GeneratedCredentials/ViewAzureClientSecretRotationGeneratedCredentials.tsx b/frontend/src/components/secret-rotations-v2/ViewSecretRotationV2GeneratedCredentials/ViewAzureClientSecretRotationGeneratedCredentials.tsx index 4e6aeeed1..7dd94b6c5 100644 --- a/frontend/src/components/secret-rotations-v2/ViewSecretRotationV2GeneratedCredentials/ViewAzureClientSecretRotationGeneratedCredentials.tsx +++ b/frontend/src/components/secret-rotations-v2/ViewSecretRotationV2GeneratedCredentials/ViewAzureClientSecretRotationGeneratedCredentials.tsx @@ -19,7 +19,7 @@ export const ViewAzureClientSecretRotationGeneratedCredentials = ({ - {activeCredentials?.clientId} + {activeCredentials?.clientId} {activeCredentials?.clientSecret} @@ -27,7 +27,7 @@ export const ViewAzureClientSecretRotationGeneratedCredentials = ({ } inactiveCredentials={ <> - {inactiveCredentials?.clientId} + {inactiveCredentials?.clientId} {inactiveCredentials?.clientSecret} diff --git a/frontend/src/components/secret-rotations-v2/forms/SecretRotationV2ParametersFields/AzureClientSecretRotationParametersFields.tsx b/frontend/src/components/secret-rotations-v2/forms/SecretRotationV2ParametersFields/AzureClientSecretRotationParametersFields.tsx index 6b7ca957e..ff77dc1f7 100644 --- a/frontend/src/components/secret-rotations-v2/forms/SecretRotationV2ParametersFields/AzureClientSecretRotationParametersFields.tsx +++ b/frontend/src/components/secret-rotations-v2/forms/SecretRotationV2ParametersFields/AzureClientSecretRotationParametersFields.tsx @@ -61,6 +61,7 @@ export const AzureClientSecretRotationParametersFields = () => { onChange={(option) => { onChange((option as SingleValue)?.id ?? null); setValue("parameters.appName", (option as SingleValue)?.name ?? ""); + setValue("parameters.clientId", (option as SingleValue)?.appId ?? ""); }} options={clients} placeholder="Select an application..." diff --git a/frontend/src/components/secret-rotations-v2/forms/SecretRotationV2SecretsMappingFields/AzureClientSecretRotationSecretsMappingFields.tsx b/frontend/src/components/secret-rotations-v2/forms/SecretRotationV2SecretsMappingFields/AzureClientSecretRotationSecretsMappingFields.tsx index 7c99e0687..77a34d083 100644 --- a/frontend/src/components/secret-rotations-v2/forms/SecretRotationV2SecretsMappingFields/AzureClientSecretRotationSecretsMappingFields.tsx +++ b/frontend/src/components/secret-rotations-v2/forms/SecretRotationV2SecretsMappingFields/AzureClientSecretRotationSecretsMappingFields.tsx @@ -16,6 +16,24 @@ export const AzureClientSecretRotationSecretsMappingFields = () => { const { rotationOption } = useSecretRotationV2Option(SecretRotation.AzureClientSecret); const items = [ + { + name: "Client ID", + input: ( + ( + + + + )} + control={control} + name="secretsMapping.clientId" + /> + ) + }, { name: "Client Secret", input: ( diff --git a/frontend/src/components/secret-rotations-v2/forms/schemas/azure-client-secret-rotation-schema.ts b/frontend/src/components/secret-rotations-v2/forms/schemas/azure-client-secret-rotation-schema.ts index fbceaad13..c68afb0ed 100644 --- a/frontend/src/components/secret-rotations-v2/forms/schemas/azure-client-secret-rotation-schema.ts +++ b/frontend/src/components/secret-rotations-v2/forms/schemas/azure-client-secret-rotation-schema.ts @@ -8,7 +8,8 @@ export const AzureClientSecretRotationSchema = z type: z.literal(SecretRotation.AzureClientSecret), parameters: z.object({ appId: z.string().trim().min(1, "App ID required"), - appName: z.string().trim().min(1, "App Name required") + appName: z.string().trim().min(1, "App Name required"), + clientId: z.string().trim().min(1, "Client ID required") }), secretsMapping: z.object({ clientId: z.string().trim().min(1, "Client ID required"), diff --git a/frontend/src/hooks/api/appConnections/azure/types.ts b/frontend/src/hooks/api/appConnections/azure/types.ts index ce74879f0..29da637f0 100644 --- a/frontend/src/hooks/api/appConnections/azure/types.ts +++ b/frontend/src/hooks/api/appConnections/azure/types.ts @@ -1,4 +1,5 @@ export type TAzureClient = { name: string; + appId: string; id: string; };