Change Azure Client Secret Rotation to show app client id

This commit is contained in:
carlosmonastyrski
2025-04-30 15:17:24 -03:00
parent cf84dde0fa
commit d0a642a63a
9 changed files with 46 additions and 17 deletions
@@ -24,7 +24,7 @@ export const azureClientSecretRotationFactory: TRotationFactory<
> = (secretRotation, appConnectionDAL, kmsService) => { > = (secretRotation, appConnectionDAL, kmsService) => {
const { const {
connection, connection,
parameters: { appId }, parameters: { appId, clientId: clientIdParam },
secretsMapping secretsMapping
} = secretRotation; } = secretRotation;
@@ -64,7 +64,8 @@ export const azureClientSecretRotationFactory: TRotationFactory<
return { return {
clientSecret: data.secretText, clientSecret: data.secretText,
clientId: data.keyId keyId: data.keyId,
clientId: clientIdParam
}; };
} catch (error: unknown) { } catch (error: unknown) {
if (error instanceof AxiosError) { if (error instanceof AxiosError) {
@@ -81,14 +82,14 @@ export const azureClientSecretRotationFactory: TRotationFactory<
/** /**
* Revokes a client secret from the Azure app using its keyId. * 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 accessToken = await getAzureConnectionAccessToken(connection.id, appConnectionDAL, kmsService);
const endpoint = `${GRAPH_API_BASE}/applications/${appId}/removePassword`; const endpoint = `${GRAPH_API_BASE}/applications/${appId}/removePassword`;
try { try {
await request.post( await request.post(
endpoint, endpoint,
{ keyId: clientId }, { keyId },
{ {
headers: { headers: {
Authorization: `Bearer ${accessToken}`, Authorization: `Bearer ${accessToken}`,
@@ -99,7 +100,7 @@ export const azureClientSecretRotationFactory: TRotationFactory<
} catch (error: unknown) { } catch (error: unknown) {
if (error instanceof AxiosError) { if (error instanceof AxiosError) {
throw new BadRequestError({ 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" error.message || "Unknown error"
}` }`
}); });
@@ -129,7 +130,7 @@ export const azureClientSecretRotationFactory: TRotationFactory<
) => { ) => {
if (!credentials?.length) return callback(); if (!credentials?.length) return callback();
await Promise.all(credentials.map(({ clientId }) => revokeCredential(clientId))); await Promise.all(credentials.map(({ keyId }) => revokeCredential(keyId)));
return callback(); return callback();
}; };
@@ -141,9 +142,8 @@ export const azureClientSecretRotationFactory: TRotationFactory<
callback callback
) => { ) => {
const newCredentials = await $rotateClientSecret(); const newCredentials = await $rotateClientSecret();
if (oldCredentials?.keyId) {
if (oldCredentials?.clientId) { await revokeCredential(oldCredentials.keyId);
await revokeCredential(oldCredentials.clientId);
} }
return callback(newCredentials); return callback(newCredentials);
@@ -154,7 +154,10 @@ export const azureClientSecretRotationFactory: TRotationFactory<
*/ */
const getSecretsPayload: TRotationFactoryGetSecretsPayload<TAzureClientSecretRotationGeneratedCredentials> = ({ const getSecretsPayload: TRotationFactoryGetSecretsPayload<TAzureClientSecretRotationGeneratedCredentials> = ({
clientSecret clientSecret
}) => [{ key: secretsMapping.clientSecret, value: clientSecret }]; }) => [
{ key: secretsMapping.clientSecret, value: clientSecret },
{ key: secretsMapping.clientId, value: clientIdParam }
];
return { return {
issueCredentials, issueCredentials,
@@ -13,7 +13,8 @@ import { AppConnection } from "@app/services/app-connection/app-connection-enums
export const AzureClientSecretRotationGeneratedCredentialsSchema = z export const AzureClientSecretRotationGeneratedCredentialsSchema = z
.object({ .object({
clientId: z.string(), clientId: z.string(),
clientSecret: z.string() clientSecret: z.string(),
keyId: z.string()
}) })
.array() .array()
.min(1) .min(1)
@@ -21,7 +22,8 @@ export const AzureClientSecretRotationGeneratedCredentialsSchema = z
const AzureClientSecretRotationParametersSchema = z.object({ const AzureClientSecretRotationParametersSchema = z.object({
appId: z.string().trim().min(1, "App ID Required").describe(SecretRotations.PARAMETERS.AZURE_CLIENT_SECRET.appId), 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({ const AzureClientSecretRotationSecretsMappingSchema = z.object({
+2 -1
View File
@@ -2089,7 +2089,8 @@ export const SecretRotations = {
}, },
AZURE_CLIENT_SECRET: { AZURE_CLIENT_SECRET: {
appId: "The ID of the Azure Application to rotate the client secret for.", 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: { LDAP_PASSWORD: {
dn: "The Distinguished Name (DN) of the principal to rotate the password for." dn: "The Distinguished Name (DN) of the principal to rotate the password for."
@@ -75,6 +75,7 @@ description: "Learn how to automatically rotate Azure Client Secrets."
}, },
"parameters": { "parameters": {
"appId": "...", "appId": "...",
"clientId": "...",
"appName": "..." "appName": "..."
}, },
"secretsMapping": { "secretsMapping": {
@@ -131,7 +132,8 @@ description: "Learn how to automatically rotate Azure Client Secrets."
"type": "azure-client-secret", "type": "azure-client-secret",
"parameters": { "parameters": {
"appId": "...", "appId": "...",
"appName": "..." "appName": "...",
"clientId": "..."
} }
} }
} }
@@ -19,7 +19,7 @@ export const ViewAzureClientSecretRotationGeneratedCredentials = ({
<ViewRotationGeneratedCredentialsDisplay <ViewRotationGeneratedCredentialsDisplay
activeCredentials={ activeCredentials={
<> <>
<CredentialDisplay label="Secret ID">{activeCredentials?.clientId}</CredentialDisplay> <CredentialDisplay label="Client ID">{activeCredentials?.clientId}</CredentialDisplay>
<CredentialDisplay isSensitive label="Client Secret"> <CredentialDisplay isSensitive label="Client Secret">
{activeCredentials?.clientSecret} {activeCredentials?.clientSecret}
</CredentialDisplay> </CredentialDisplay>
@@ -27,7 +27,7 @@ export const ViewAzureClientSecretRotationGeneratedCredentials = ({
} }
inactiveCredentials={ inactiveCredentials={
<> <>
<CredentialDisplay label="Secret ID">{inactiveCredentials?.clientId}</CredentialDisplay> <CredentialDisplay label="Client ID">{inactiveCredentials?.clientId}</CredentialDisplay>
<CredentialDisplay isSensitive label="Client Secret"> <CredentialDisplay isSensitive label="Client Secret">
{inactiveCredentials?.clientSecret} {inactiveCredentials?.clientSecret}
</CredentialDisplay> </CredentialDisplay>
@@ -61,6 +61,7 @@ export const AzureClientSecretRotationParametersFields = () => {
onChange={(option) => { onChange={(option) => {
onChange((option as SingleValue<TAzureClient>)?.id ?? null); onChange((option as SingleValue<TAzureClient>)?.id ?? null);
setValue("parameters.appName", (option as SingleValue<TAzureClient>)?.name ?? ""); setValue("parameters.appName", (option as SingleValue<TAzureClient>)?.name ?? "");
setValue("parameters.clientId", (option as SingleValue<TAzureClient>)?.appId ?? "");
}} }}
options={clients} options={clients}
placeholder="Select an application..." placeholder="Select an application..."
@@ -16,6 +16,24 @@ export const AzureClientSecretRotationSecretsMappingFields = () => {
const { rotationOption } = useSecretRotationV2Option(SecretRotation.AzureClientSecret); const { rotationOption } = useSecretRotationV2Option(SecretRotation.AzureClientSecret);
const items = [ const items = [
{
name: "Client ID",
input: (
<Controller
render={({ field: { value, onChange }, fieldState: { error } }) => (
<FormControl isError={Boolean(error)} errorText={error?.message}>
<Input
value={value}
onChange={onChange}
placeholder={rotationOption?.template.secretsMapping.clientId}
/>
</FormControl>
)}
control={control}
name="secretsMapping.clientId"
/>
)
},
{ {
name: "Client Secret", name: "Client Secret",
input: ( input: (
@@ -8,7 +8,8 @@ export const AzureClientSecretRotationSchema = z
type: z.literal(SecretRotation.AzureClientSecret), type: z.literal(SecretRotation.AzureClientSecret),
parameters: z.object({ parameters: z.object({
appId: z.string().trim().min(1, "App ID required"), 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({ secretsMapping: z.object({
clientId: z.string().trim().min(1, "Client ID required"), clientId: z.string().trim().min(1, "Client ID required"),
@@ -1,4 +1,5 @@
export type TAzureClient = { export type TAzureClient = {
name: string; name: string;
appId: string;
id: string; id: string;
}; };