diff --git a/backend/src/ee/services/secret-rotation-v2/auth0-client-secret/auth0-client-secret-rotation-fns.ts b/backend/src/ee/services/secret-rotation-v2/auth0-client-secret/auth0-client-secret-rotation-fns.ts index 92c24145e..6debd2402 100644 --- a/backend/src/ee/services/secret-rotation-v2/auth0-client-secret/auth0-client-secret-rotation-fns.ts +++ b/backend/src/ee/services/secret-rotation-v2/auth0-client-secret/auth0-client-secret-rotation-fns.ts @@ -10,6 +10,7 @@ import { TRotationFactoryRotateCredentials } from "@app/ee/services/secret-rotation-v2/secret-rotation-v2-types"; import { request } from "@app/lib/config/request"; +import { blockLocalAndPrivateIpAddresses } from "@app/lib/validator"; import { getAuth0ConnectionAccessToken } from "@app/services/app-connection/auth0"; import { generatePassword } from "../shared/utils"; @@ -26,11 +27,13 @@ export const auth0ClientSecretRotationFactory: TRotationFactory< const $rotateClientSecret = async () => { const accessToken = await getAuth0ConnectionAccessToken(connection, appConnectionDAL, kmsService); + const { audience } = connection.credentials; + await blockLocalAndPrivateIpAddresses(audience); const clientSecret = generatePassword(); await request.request({ method: "PATCH", - url: `${connection.credentials.audience}clients/${clientId}`, + url: `${audience}clients/${clientId}`, headers: { authorization: `Bearer ${accessToken}` }, data: { client_secret: clientSecret @@ -53,11 +56,13 @@ export const auth0ClientSecretRotationFactory: TRotationFactory< callback ) => { const accessToken = await getAuth0ConnectionAccessToken(connection, appConnectionDAL, kmsService); + const { audience } = connection.credentials; + await blockLocalAndPrivateIpAddresses(audience); // we just trigger an auth0 rotation to negate our credentials await request.request({ method: "POST", - url: `${connection.credentials.audience}clients/${clientId}/rotate-secret`, + url: `${audience}clients/${clientId}/rotate-secret`, headers: { authorization: `Bearer ${accessToken}` } }); diff --git a/backend/src/ee/services/secret-rotation-v2/auth0-client-secret/auth0-client-secret-rotation-schemas.ts b/backend/src/ee/services/secret-rotation-v2/auth0-client-secret/auth0-client-secret-rotation-schemas.ts index 4d4ceb79e..3a0ba265b 100644 --- a/backend/src/ee/services/secret-rotation-v2/auth0-client-secret/auth0-client-secret-rotation-schemas.ts +++ b/backend/src/ee/services/secret-rotation-v2/auth0-client-secret/auth0-client-secret-rotation-schemas.ts @@ -28,7 +28,7 @@ const Auth0ClientSecretRotationParametersSchema = z.object({ }); const Auth0ClientSecretRotationSecretsMappingSchema = z.object({ - clientId: SecretNameSchema.describe(SecretRotations.SECRETS_MAPPING.AUTH0_CLIENT_SECRET.clientID), + clientId: SecretNameSchema.describe(SecretRotations.SECRETS_MAPPING.AUTH0_CLIENT_SECRET.clientId), clientSecret: SecretNameSchema.describe(SecretRotations.SECRETS_MAPPING.AUTH0_CLIENT_SECRET.clientSecret) }); diff --git a/backend/src/lib/api-docs/constants.ts b/backend/src/lib/api-docs/constants.ts index 99e34cf0f..20e6944e0 100644 --- a/backend/src/lib/api-docs/constants.ts +++ b/backend/src/lib/api-docs/constants.ts @@ -1979,7 +1979,7 @@ export const SecretRotations = { password: "The name of the secret that the generated password will be mapped to." }, AUTH0_CLIENT_SECRET: { - clientID: "The name of the secret that the client ID will be mapped to.", + clientId: "The name of the secret that the client ID will be mapped to.", clientSecret: "The name of the secret that the rotated client secret will be mapped to." } } diff --git a/backend/src/server/routes/v1/app-connection-routers/auth0-connection-router.ts b/backend/src/server/routes/v1/app-connection-routers/auth0-connection-router.ts index a940012fd..db17c8eac 100644 --- a/backend/src/server/routes/v1/app-connection-routers/auth0-connection-router.ts +++ b/backend/src/server/routes/v1/app-connection-routers/auth0-connection-router.ts @@ -1,9 +1,14 @@ +import { z } from "zod"; + +import { readLimit } from "@app/server/config/rateLimiter"; +import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; import { AppConnection } from "@app/services/app-connection/app-connection-enums"; import { CreateAuth0ConnectionSchema, SanitizedAuth0ConnectionSchema, UpdateAuth0ConnectionSchema } from "@app/services/app-connection/auth0"; +import { AuthMode } from "@app/services/auth/auth-type"; import { registerAppConnectionEndpoints } from "./app-connection-endpoints"; @@ -15,4 +20,32 @@ export const registerAuth0ConnectionRouter = async (server: FastifyZodProvider) createSchema: CreateAuth0ConnectionSchema, updateSchema: UpdateAuth0ConnectionSchema }); + + // The below endpoints are not exposed and for Infisical App use + + server.route({ + method: "GET", + url: `/:connectionId/clients`, + config: { + rateLimit: readLimit + }, + schema: { + params: z.object({ + connectionId: z.string().uuid() + }), + response: { + 200: z.object({ + clients: z.object({ name: z.string(), id: z.string() }).array() + }) + } + }, + onRequest: verifyAuth([AuthMode.JWT]), + handler: async (req) => { + const { connectionId } = req.params; + + const clients = await server.services.appConnection.auth0.listClients(connectionId, req.permission); + + return { clients }; + } + }); }; diff --git a/backend/src/services/app-connection/app-connection-service.ts b/backend/src/services/app-connection/app-connection-service.ts index 025f01734..4de9ee81e 100644 --- a/backend/src/services/app-connection/app-connection-service.ts +++ b/backend/src/services/app-connection/app-connection-service.ts @@ -14,6 +14,7 @@ import { TRANSITION_CONNECTION_CREDENTIALS_TO_PLATFORM, validateAppConnectionCredentials } from "@app/services/app-connection/app-connection-fns"; +import { auth0ConnectionService } from "@app/services/app-connection/auth0/auth0-connection-service"; import { TKmsServiceFactory } from "@app/services/kms/kms-service"; import { TAppConnectionDALFactory } from "./app-connection-dal"; @@ -440,6 +441,7 @@ export const appConnectionServiceFactory = ({ aws: awsConnectionService(connectAppConnectionById), humanitec: humanitecConnectionService(connectAppConnectionById), camunda: camundaConnectionService(connectAppConnectionById, appConnectionDAL, kmsService), - vercel: vercelConnectionService(connectAppConnectionById) + vercel: vercelConnectionService(connectAppConnectionById), + auth0: auth0ConnectionService(connectAppConnectionById, appConnectionDAL, kmsService) }; }; diff --git a/backend/src/services/app-connection/auth0/auth0-connection-fns.ts b/backend/src/services/app-connection/auth0/auth0-connection-fns.ts index 755df5c50..5a9989b43 100644 --- a/backend/src/services/app-connection/auth0/auth0-connection-fns.ts +++ b/backend/src/services/app-connection/auth0/auth0-connection-fns.ts @@ -1,5 +1,3 @@ -import { AxiosError } from "axios"; - import { request } from "@app/lib/config/request"; import { BadRequestError } from "@app/lib/errors"; import { removeTrailingSlash } from "@app/lib/fn"; @@ -45,7 +43,11 @@ const authorizeAuth0Connection = async ({ throw new Error(`Unhandled token type: ${data.token_type}`); } - return { accessToken: data.access_token, expiresAt: data.expires_in * 1000 + Date.now() }; + return { + accessToken: data.access_token, + // cap token lifespan to 10 minutes + expiresAt: Math.min(data.expires_in * 1000, 600000) + Date.now() + }; }; export const getAuth0ConnectionAccessToken = async ( @@ -53,7 +55,12 @@ export const getAuth0ConnectionAccessToken = async ( appConnectionDAL: Pick, kmsService: Pick ) => { - // just getting a new auth token every time because if permissions change the previous token doesn't have the changes + const { expiresAt, accessToken } = credentials; + + // get new token if expired or less than 5 minutes until expiry + if (Date.now() < expiresAt - 300000) { + return accessToken; + } const authData = await authorizeAuth0Connection(credentials); @@ -84,7 +91,7 @@ export const validateAuth0ConnectionCredentials = async ({ credentials }: TAuth0 }; } catch (e: unknown) { throw new BadRequestError({ - message: (e as AxiosError).message ?? `Unable to validate connection: verify credentials` + message: (e as Error).message ?? `Unable to validate connection: verify credentials` }); } }; diff --git a/backend/src/services/app-connection/auth0/auth0-connection-service.ts b/backend/src/services/app-connection/auth0/auth0-connection-service.ts new file mode 100644 index 000000000..693c55ea6 --- /dev/null +++ b/backend/src/services/app-connection/auth0/auth0-connection-service.ts @@ -0,0 +1,71 @@ +import { request } from "@app/lib/config/request"; +import { OrgServiceActor } from "@app/lib/types"; +import { blockLocalAndPrivateIpAddresses } from "@app/lib/validator"; +import { TAppConnectionDALFactory } from "@app/services/app-connection/app-connection-dal"; +import { AppConnection } from "@app/services/app-connection/app-connection-enums"; +import { getAuth0ConnectionAccessToken } from "@app/services/app-connection/auth0/auth0-connection-fns"; +import { TKmsServiceFactory } from "@app/services/kms/kms-service"; + +import { TAuth0Connection, TAuth0ListClient, TAuth0ListClientsResponse } from "./auth0-connection-types"; + +type TGetAppConnectionFunc = ( + app: AppConnection, + connectionId: string, + actor: OrgServiceActor +) => Promise; + +const listAuth0Clients = async ( + appConnection: TAuth0Connection, + appConnectionDAL: Pick, + kmsService: Pick +) => { + const accessToken = await getAuth0ConnectionAccessToken(appConnection, appConnectionDAL, kmsService); + + const { audience, clientId: connectionClientId } = appConnection.credentials; + await blockLocalAndPrivateIpAddresses(audience); + + const clients: TAuth0ListClient[] = []; + let hasMore = true; + let page = 0; + + while (hasMore) { + // eslint-disable-next-line no-await-in-loop + const { data: clientsPage } = await request.get(`${audience}clients`, { + headers: { + Authorization: `Bearer ${accessToken}`, + "Accept-Encoding": "application/json" + }, + params: { + include_totals: true, + per_page: 100, + page + } + }); + + clients.push(...clientsPage.clients); + page += 1; + hasMore = clientsPage.total > clients.length; + } + + return ( + clients.filter((client) => client.client_id !== connectionClientId && client.name !== "All Applications") ?? [] + ); +}; + +export const auth0ConnectionService = ( + getAppConnection: TGetAppConnectionFunc, + appConnectionDAL: Pick, + kmsService: Pick +) => { + const listClients = async (connectionId: string, actor: OrgServiceActor) => { + const appConnection = await getAppConnection(AppConnection.Auth0, connectionId, actor); + + const clients = await listAuth0Clients(appConnection, appConnectionDAL, kmsService); + + return clients.map((client) => ({ id: client.client_id, name: client.name })); + }; + + return { + listClients + }; +}; diff --git a/backend/src/services/app-connection/auth0/auth0-connection-types.ts b/backend/src/services/app-connection/auth0/auth0-connection-types.ts index a77bbe40c..ebb601946 100644 --- a/backend/src/services/app-connection/auth0/auth0-connection-types.ts +++ b/backend/src/services/app-connection/auth0/auth0-connection-types.ts @@ -27,3 +27,13 @@ export type TAuth0AccessTokenResponse = { scope: string; token_type: string; }; + +export type TAuth0ListClient = { + name: string; + client_id: string; +}; + +export type TAuth0ListClientsResponse = { + total: number; + clients: TAuth0ListClient[]; +}; diff --git a/docs/documentation/platform/secret-rotation/auth0-client-secret.mdx b/docs/documentation/platform/secret-rotation/auth0-client-secret.mdx index 362bc24b2..3845a3879 100644 --- a/docs/documentation/platform/secret-rotation/auth0-client-secret.mdx +++ b/docs/documentation/platform/secret-rotation/auth0-client-secret.mdx @@ -13,11 +13,7 @@ description: "Learn how to automatically rotate Auth0 Client Secrets." ## Prerequisites -1. Create an [Auth0 Connection](/integrations/app-connections/auth0) with the required **Secret Rotation** audience and permissions -2. Copy the **Client ID** of the application in Auth0 you want to rotate the Client Secret for: - - ![Auth0 Client ID](/images/secret-rotations-v2/auth0-client-secret/auth0-app-client-id.png) - +- Create an [Auth0 Connection](/integrations/app-connections/auth0) with the required **Secret Rotation** audience and permissions ## Create an Auth0 Client Secret Rotation in Infisical @@ -36,12 +32,14 @@ description: "Learn how to automatically rotate Auth0 Client Secrets." - **Rotation Interval** - the interval, in days, that once elapsed will trigger a rotation. - **Rotate At** - the local time of day when rotation should occur once the interval has elapsed. - **Auto-Rotation Enabled** - whether secrets should automatically be rotated once the rotation interval has elapsed. Disable this option to manually rotate secrets or pause secret rotation. + + Due to Auth0 Client Secret Rotations rotating a single credential set, auto-rotation may result in service interruptions. If you need to ensure service continuity, we recommend disabling this option. + - 4. Input the Client ID of the Auth0 application acquired above that will have its Client Secret rotated. Then click **Next**. + + 4. Select the Auth0 application whose Client Secret you want to rotate. Then click **Next**. ![Rotation Parameters](/images/secret-rotations-v2/auth0-client-secret/auth0-client-secret-parameters.png) - - **Client ID** - the Client ID of the application whose Client Secret will be rotated. - 5. Specify the secret names that the client credentials should be mapped to. Then click **Next**. ![Rotation Secrets Mapping](/images/secret-rotations-v2/auth0-client-secret/auth0-client-secret-secrets-mapping.png) @@ -64,6 +62,10 @@ description: "Learn how to automatically rotate Auth0 Client Secrets." To create an Auth0 Client Secret Rotation, make an API request to the [Create Auth0 Client Secret Rotation](/api-reference/endpoints/secret-rotations/auth0-client-secret/create) API endpoint. + You will first need the **Client ID** of the Auth0 application you want to rotate the secret for. This can be obtained from the Applications dashboard. + ![Auth0 Client ID](/images/secret-rotations-v2/auth0-client-secret/auth0-app-client-id.png) + + ### Sample request ```bash Request @@ -71,9 +73,9 @@ description: "Learn how to automatically rotate Auth0 Client Secrets." --url https://us.infisical.com/api/v2/secret-rotations/auth0-client-secret \ --header 'Content-Type: application/json' \ --data '{ - "name": "my-pg-rotation", + "name": "my-auth0-rotation", "projectId": "3c90c3cc-0d44-4b50-8888-8dd25736052a", - "description": "my database credentials rotation", + "description": "my client secret rotation", "connectionId": "3c90c3cc-0d44-4b50-8888-8dd25736052a", "environment": "dev", "secretPath": "/", @@ -93,14 +95,18 @@ description: "Learn how to automatically rotate Auth0 Client Secrets." }' ``` + + Due to Auth0 Client Secret Rotations rotating a single credential set, auto-rotation may result in service interruptions. If you need to ensure service continuity, we recommend disabling this option. + + ### Sample response ```bash Response { "secretRotation": { "id": "3c90c3cc-0d44-4b50-8888-8dd25736052a", - "name": "my-pg-rotation", - "description": "my database credentials rotation", + "name": "my-auth0-rotation", + "description": "my client secret rotation", "secretsMapping": { "clientId": "AUTH0_CLIENT_ID", "clientSecret": "AUTH0_CLIENT_SECRET" diff --git a/docs/images/app-connections/auth0/auth0-secret-rotation-api-selection.png b/docs/images/app-connections/auth0/auth0-secret-rotation-api-selection.png index 1653a498b..42f3ee1e4 100644 Binary files a/docs/images/app-connections/auth0/auth0-secret-rotation-api-selection.png and b/docs/images/app-connections/auth0/auth0-secret-rotation-api-selection.png differ diff --git a/docs/images/secret-rotations-v2/auth0-client-secret/auth0-client-secret-parameters.png b/docs/images/secret-rotations-v2/auth0-client-secret/auth0-client-secret-parameters.png index 19ed0e9e7..1f2fc64f9 100644 Binary files a/docs/images/secret-rotations-v2/auth0-client-secret/auth0-client-secret-parameters.png and b/docs/images/secret-rotations-v2/auth0-client-secret/auth0-client-secret-parameters.png differ diff --git a/docs/integrations/app-connections/auth0.mdx b/docs/integrations/app-connections/auth0.mdx index 9dd321d04..42e78cb66 100644 --- a/docs/integrations/app-connections/auth0.mdx +++ b/docs/integrations/app-connections/auth0.mdx @@ -1,6 +1,6 @@ --- title: "Auth0 Connection" -description: "Learn how to configure a Auth0 Connection for Infisical." +description: "Learn how to configure an Auth0 Connection for Infisical." --- Infisical supports the use of [Client Credentials](https://auth0.com/docs/get-started/authentication-and-authorization-flow/client-credentials-flow) to connect with your Auth0 applications. @@ -22,7 +22,7 @@ Infisical supports the use of [Client Credentials](https://auth0.com/docs/get-st - Select the **Auth0 Management API** option from the dropdown and grant the `update:client_keys` permission. + Select the **Auth0 Management API** option from the dropdown and grant the `update:client_keys` and `read:clients` permission. ![Secret Rotation Authorization](/images/app-connections/auth0/auth0-secret-rotation-api-selection.png) @@ -72,7 +72,7 @@ Infisical supports the use of [Client Credentials](https://auth0.com/docs/get-st "clientId": "...", "clientSecret": "...", "audience": "https://xxx-xxxxxxxxx.us.auth0.com/api/v2/" - }, + } }' ``` diff --git a/frontend/src/components/secret-rotations-v2/forms/SecretRotationV2ConfigurationFields.tsx b/frontend/src/components/secret-rotations-v2/forms/SecretRotationV2ConfigurationFields.tsx index d2ef136f3..d96c8b3d2 100644 --- a/frontend/src/components/secret-rotations-v2/forms/SecretRotationV2ConfigurationFields.tsx +++ b/frontend/src/components/secret-rotations-v2/forms/SecretRotationV2ConfigurationFields.tsx @@ -1,8 +1,14 @@ import { Controller, useFormContext } from "react-hook-form"; +import { faWarning } from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { format, setHours, setMinutes } from "date-fns"; import { FilterableSelect, FormControl, Input, Switch } from "@app/components/v2"; -import { getRotateAtLocal } from "@app/helpers/secretRotationsV2"; +import { + getRotateAtLocal, + IS_ROTATION_DUAL_CREDENTIALS, + SECRET_ROTATION_MAP +} from "@app/helpers/secretRotationsV2"; import { WorkspaceEnv } from "@app/hooks/api/workspace/types"; import { TSecretRotationV2Form } from "./schemas"; @@ -14,7 +20,9 @@ type Props = { }; export const SecretRotationV2ConfigurationFields = ({ isUpdate, environments }: Props) => { - const { control } = useFormContext(); + const { control, watch } = useFormContext(); + + const [type, isAutoRotationEnabled] = watch(["type", "isAutoRotationEnabled"]); return ( <> @@ -117,6 +125,14 @@ export const SecretRotationV2ConfigurationFields = ({ isUpdate, environments }: ); }} /> + {!IS_ROTATION_DUAL_CREDENTIALS[type] && isAutoRotationEnabled && ( +
+ Due to{" "} + {SECRET_ROTATION_MAP[type].name} Rotations rotating a single credential set, auto-rotation + may result in service interruptions. If you need to ensure service continuity, we + recommend disabling this option. +
+ )} ); }; diff --git a/frontend/src/components/secret-rotations-v2/forms/SecretRotationV2Form.tsx b/frontend/src/components/secret-rotations-v2/forms/SecretRotationV2Form.tsx index e5d56b314..e1c74b420 100644 --- a/frontend/src/components/secret-rotations-v2/forms/SecretRotationV2Form.tsx +++ b/frontend/src/components/secret-rotations-v2/forms/SecretRotationV2Form.tsx @@ -12,7 +12,7 @@ import { SecretRotationV2ReviewFields } from "@app/components/secret-rotations-v import { SecretRotationV2SecretsMappingFields } from "@app/components/secret-rotations-v2/forms/SecretRotationV2SecretsMappingFields"; import { Button } from "@app/components/v2"; import { useWorkspace } from "@app/context"; -import { SECRET_ROTATION_MAP } from "@app/helpers/secretRotationsV2"; +import { IS_ROTATION_DUAL_CREDENTIALS, SECRET_ROTATION_MAP } from "@app/helpers/secretRotationsV2"; import { SecretRotation, TSecretRotationV2, @@ -84,7 +84,7 @@ export const SecretRotationV2Form = ({ } : { type, - isAutoRotationEnabled: true, + isAutoRotationEnabled: IS_ROTATION_DUAL_CREDENTIALS[type], rotationInterval: DEFAULT_ROTATION_INTERVAL, rotateAtUtc: { hours: 0, diff --git a/frontend/src/components/secret-rotations-v2/forms/SecretRotationV2ParametersFields/Auth0ClientSecretRotationParametersFields.tsx b/frontend/src/components/secret-rotations-v2/forms/SecretRotationV2ParametersFields/Auth0ClientSecretRotationParametersFields.tsx index 3729f62fb..5a5a59343 100644 --- a/frontend/src/components/secret-rotations-v2/forms/SecretRotationV2ParametersFields/Auth0ClientSecretRotationParametersFields.tsx +++ b/frontend/src/components/secret-rotations-v2/forms/SecretRotationV2ParametersFields/Auth0ClientSecretRotationParametersFields.tsx @@ -1,30 +1,70 @@ import { Controller, useFormContext } from "react-hook-form"; +import { SingleValue } from "react-select"; +import { faCircleInfo } from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { TSecretRotationV2Form } from "@app/components/secret-rotations-v2/forms/schemas"; -import { FormControl, Input } from "@app/components/v2"; +import { FilterableSelect, FormControl, Tooltip } from "@app/components/v2"; +import { useAuth0ConnectionListClients } from "@app/hooks/api/appConnections/auth0"; +import { TAuth0Client } from "@app/hooks/api/appConnections/auth0/types"; import { SecretRotation } from "@app/hooks/api/secretRotationsV2"; export const Auth0ClientSecretRotationParametersFields = () => { - const { control } = useFormContext< + const { control, watch } = useFormContext< TSecretRotationV2Form & { type: SecretRotation.Auth0ClientSecret; } >(); + const connectionId = watch("connection.id"); + + const { data: clients, isPending: isClientsPending } = useAuth0ConnectionListClients( + connectionId, + { enabled: Boolean(connectionId) } + ); + return ( ( + Ensure that your connection has the{" "} + read_clients permission and the application + exists in the connection's audience. + + } + > +
+ Don't see the application you're looking for?{" "} + +
+ + } > - + client.id === value) ?? null} + onChange={(option) => { + onChange((option as SingleValue)?.id ?? null); + }} + options={clients} + placeholder="Select an application..." + getOptionLabel={(option) => option.name} + getOptionValue={(option) => option.id} + />
)} - control={control} - name="parameters.clientId" /> ); }; diff --git a/frontend/src/components/secret-rotations-v2/forms/SecretRotationV2ParametersFields/shared/SqlCredentialsRotationParametersFields.tsx b/frontend/src/components/secret-rotations-v2/forms/SecretRotationV2ParametersFields/shared/SqlCredentialsRotationParametersFields.tsx index 3e019d873..aa317d695 100644 --- a/frontend/src/components/secret-rotations-v2/forms/SecretRotationV2ParametersFields/shared/SqlCredentialsRotationParametersFields.tsx +++ b/frontend/src/components/secret-rotations-v2/forms/SecretRotationV2ParametersFields/shared/SqlCredentialsRotationParametersFields.tsx @@ -8,7 +8,7 @@ import { SecretRotation, useSecretRotationV2Option } from "@app/hooks/api/secret export const SqlCredentialsRotationParametersFields = () => { const { control, watch } = useFormContext< TSecretRotationV2Form & { - type: SecretRotation.PostgresCredentials; // all sql rotations share these fields + type: SecretRotation.PostgresCredentials | SecretRotation.MsSqlCredentials; } >(); diff --git a/frontend/src/components/secret-rotations-v2/forms/SecretRotationV2ReviewFields/shared/SqlCredentialsRotationReviewFields.tsx b/frontend/src/components/secret-rotations-v2/forms/SecretRotationV2ReviewFields/shared/SqlCredentialsRotationReviewFields.tsx index 231f0060c..d61055355 100644 --- a/frontend/src/components/secret-rotations-v2/forms/SecretRotationV2ReviewFields/shared/SqlCredentialsRotationReviewFields.tsx +++ b/frontend/src/components/secret-rotations-v2/forms/SecretRotationV2ReviewFields/shared/SqlCredentialsRotationReviewFields.tsx @@ -9,7 +9,7 @@ import { SecretRotationReviewSection } from "./SecretRotationReviewSection"; export const SqlCredentialsRotationReviewFields = () => { const { watch } = useFormContext< TSecretRotationV2Form & { - type: SecretRotation.PostgresCredentials; // all sql rotations share these fields + type: SecretRotation.PostgresCredentials | SecretRotation.MsSqlCredentials; } >(); diff --git a/frontend/src/components/secret-rotations-v2/forms/SecretRotationV2SecretsMappingFields/shared/SqlCredentialsRotationSecretsMappingFields.tsx b/frontend/src/components/secret-rotations-v2/forms/SecretRotationV2SecretsMappingFields/shared/SqlCredentialsRotationSecretsMappingFields.tsx index 953cb982c..67721de72 100644 --- a/frontend/src/components/secret-rotations-v2/forms/SecretRotationV2SecretsMappingFields/shared/SqlCredentialsRotationSecretsMappingFields.tsx +++ b/frontend/src/components/secret-rotations-v2/forms/SecretRotationV2SecretsMappingFields/shared/SqlCredentialsRotationSecretsMappingFields.tsx @@ -9,7 +9,7 @@ import { SecretsMappingTable } from "./SecretsMappingTable"; export const SqlCredentialsRotationSecretsMappingFields = () => { const { control, watch } = useFormContext< TSecretRotationV2Form & { - type: SecretRotation.PostgresCredentials; // all sql rotations share these fields + type: SecretRotation.PostgresCredentials | SecretRotation.MsSqlCredentials; } >(); diff --git a/frontend/src/helpers/secretRotationsV2.ts b/frontend/src/helpers/secretRotationsV2.ts index d61f67680..1a57d37cd 100644 --- a/frontend/src/helpers/secretRotationsV2.ts +++ b/frontend/src/helpers/secretRotationsV2.ts @@ -28,6 +28,13 @@ export const SECRET_ROTATION_CONNECTION_MAP: Record = { + [SecretRotation.PostgresCredentials]: true, + [SecretRotation.MsSqlCredentials]: true, + [SecretRotation.Auth0ClientSecret]: false +}; + export const getRotateAtLocal = ({ hours, minutes }: TSecretRotationV2["rotateAtUtc"]) => { const now = new Date(); diff --git a/frontend/src/hooks/api/appConnections/auth0/index.ts b/frontend/src/hooks/api/appConnections/auth0/index.ts new file mode 100644 index 000000000..b69c25120 --- /dev/null +++ b/frontend/src/hooks/api/appConnections/auth0/index.ts @@ -0,0 +1 @@ +export * from "./queries"; diff --git a/frontend/src/hooks/api/appConnections/auth0/queries.tsx b/frontend/src/hooks/api/appConnections/auth0/queries.tsx new file mode 100644 index 000000000..5f6d1f52c --- /dev/null +++ b/frontend/src/hooks/api/appConnections/auth0/queries.tsx @@ -0,0 +1,37 @@ +import { useQuery, UseQueryOptions } from "@tanstack/react-query"; + +import { apiRequest } from "@app/config/request"; + +import { appConnectionKeys } from "../queries"; +import { TAuth0Client } from "./types"; + +const auth0ConnectionKeys = { + all: [...appConnectionKeys.all, "auth0"] as const, + listClients: (connectionId: string) => + [...auth0ConnectionKeys.all, "clients", connectionId] as const +}; + +export const useAuth0ConnectionListClients = ( + connectionId: string, + options?: Omit< + UseQueryOptions< + TAuth0Client[], + unknown, + TAuth0Client[], + ReturnType + >, + "queryKey" | "queryFn" + > +) => { + return useQuery({ + queryKey: auth0ConnectionKeys.listClients(connectionId), + queryFn: async () => { + const { data } = await apiRequest.get<{ clients: TAuth0Client[] }>( + `/api/v1/app-connections/auth0/${connectionId}/clients` + ); + + return data.clients; + }, + ...options + }); +}; diff --git a/frontend/src/hooks/api/appConnections/auth0/types.ts b/frontend/src/hooks/api/appConnections/auth0/types.ts new file mode 100644 index 000000000..c96615e29 --- /dev/null +++ b/frontend/src/hooks/api/appConnections/auth0/types.ts @@ -0,0 +1,4 @@ +export type TAuth0Client = { + name: string; + id: string; +}; diff --git a/frontend/src/pages/organization/AppConnections/AppConnectionsPage/components/AppConnectionForm/Auth0ConnectionForm.tsx b/frontend/src/pages/organization/AppConnections/AppConnectionsPage/components/AppConnectionForm/Auth0ConnectionForm.tsx index 0e9134d09..98076b9e1 100644 --- a/frontend/src/pages/organization/AppConnections/AppConnectionsPage/components/AppConnectionForm/Auth0ConnectionForm.tsx +++ b/frontend/src/pages/organization/AppConnections/AppConnectionsPage/components/AppConnectionForm/Auth0ConnectionForm.tsx @@ -70,7 +70,7 @@ export const Auth0ConnectionForm = ({ appConnection, onSubmit }: Props) => { render={({ field: { value, onChange }, fieldState: { error } }) => (