diff --git a/backend/src/services/identity-ua/identity-ua-service.ts b/backend/src/services/identity-ua/identity-ua-service.ts index 53404788e..dec172e4e 100644 --- a/backend/src/services/identity-ua/identity-ua-service.ts +++ b/backend/src/services/identity-ua/identity-ua-service.ts @@ -122,9 +122,9 @@ export const identityUaServiceFactory = ({ } : { accessTokenTTL: identityUa.accessTokenPeriod, - // Setting Max TTL to 2 × period ensures that clients can always renew their token - // at least once, and matches client logic that checks if renewing would exceed Max TTL. - accessTokenMaxTTL: 2 * identityUa.accessTokenPeriod + // We set a very large Max TTL for periodic tokens to ensure that clients (even outdated ones) can always renew their token + // without them having to update their SDKs, CLIs, etc. This workaround sets it to 30 years to emulate "forever" + accessTokenMaxTTL: 1000000000 }; const identityAccessToken = await identityUaDAL.transaction(async (tx) => { diff --git a/cli/packages/cmd/agent.go b/cli/packages/cmd/agent.go index 445941674..cb10050dd 100644 --- a/cli/packages/cmd/agent.go +++ b/cli/packages/cmd/agent.go @@ -576,7 +576,7 @@ func (tm *AgentManager) FetchUniversalAuthAccessToken() (credential infisicalSdk } tm.cachedUniversalAuthClientSecret = clientSecret - if tm.removeUniversalAuthClientSecretOnRead { + if universalAuthConfig.RemoveClientSecretOnRead { defer os.Remove(universalAuthConfig.ClientSecretPath) } @@ -718,7 +718,7 @@ func (tm *AgentManager) FetchNewAccessToken() error { } // Refreshes the existing access token -func (tm *AgentManager) RefreshAccessToken() error { +func (tm *AgentManager) RefreshAccessToken(accessToken string) error { httpClient, err := util.GetRestyClientWithCustomHeaders() if err != nil { return err @@ -728,7 +728,6 @@ func (tm *AgentManager) RefreshAccessToken() error { SetRetryMaxWaitTime(20 * time.Second). SetRetryWaitTime(5 * time.Second) - accessToken := tm.GetToken() response, err := api.CallMachineIdentityRefreshAccessToken(httpClient, api.UniversalAuthRefreshRequest{AccessToken: accessToken}) if err != nil { return err @@ -752,18 +751,37 @@ func (tm *AgentManager) ManageTokenLifecycle() { accessTokenRefreshedTime = tm.accessTokenFetchedTime } - nextAccessTokenExpiresInTime := accessTokenRefreshedTime.Add(tm.accessTokenTTL - (5 * time.Second)) + // Calculate next expiry time at 2/3 of the TTL + nextAccessTokenExpiresInTime := accessTokenRefreshedTime.Add(tm.accessTokenTTL * 2 / 3) if tm.accessTokenFetchedTime.IsZero() && tm.accessTokenRefreshedTime.IsZero() { - // case: init login to get access token - log.Info().Msg("attempting to authenticate...") - err := tm.FetchNewAccessToken() - if err != nil { - log.Error().Msgf("unable to authenticate because %v. Will retry in 30 seconds", err) + // try to fetch token from sink files first + // if token is found, refresh the token right away and continue from there + isSavedTokenValid := false + token := tm.FetchTokenFromFiles() + if token != "" { + log.Info().Msg("found existing token in file, attempting to refresh...") + err := tm.RefreshAccessToken(token) + isSavedTokenValid = err == nil + if isSavedTokenValid { + log.Info().Msg("token refreshed successfully from saved file") + tm.accessTokenFetchedTime = time.Now() + } else { + log.Error().Msg("unable to refresh token from saved file") + } + } - // wait a bit before trying again - time.Sleep((30 * time.Second)) - continue + if !isSavedTokenValid { + // case: init login to get access token + log.Info().Msg("attempting to authenticate...") + err := tm.FetchNewAccessToken() + if err != nil { + log.Error().Msgf("unable to authenticate because %v. Will retry in 30 seconds", err) + + // wait a bit before trying again + time.Sleep((30 * time.Second)) + continue + } } } else if time.Now().After(accessTokenMaxTTLExpiresInTime) { // case: token has reached max ttl and we should re-authenticate entirely (cannot refresh) @@ -779,7 +797,7 @@ func (tm *AgentManager) ManageTokenLifecycle() { } else { // case: token ttl has expired, but the token is still within max ttl, so we can refresh log.Info().Msgf("attempting to refresh existing token...") - err := tm.RefreshAccessToken() + err := tm.RefreshAccessToken(tm.GetToken()) if err != nil { log.Error().Msgf("unable to refresh token because %v. Will retry in 30 seconds", err) @@ -800,15 +818,18 @@ func (tm *AgentManager) ManageTokenLifecycle() { accessTokenRefreshedTime = tm.accessTokenRefreshedTime } - nextAccessTokenExpiresInTime = accessTokenRefreshedTime.Add(tm.accessTokenTTL - (5 * time.Second)) + // Recalculate next expiry time at 2/3 of the TTL + nextAccessTokenExpiresInTime = accessTokenRefreshedTime.Add(tm.accessTokenTTL * 2 / 3) accessTokenMaxTTLExpiresInTime = tm.accessTokenFetchedTime.Add(tm.accessTokenMaxTTL - (5 * time.Second)) if nextAccessTokenExpiresInTime.After(accessTokenMaxTTLExpiresInTime) { - // case: Refreshed so close that the next refresh would occur beyond max ttl (this is because currently, token renew tries to add +access-token-ttl amount of time) - // example: access token ttl is 11 sec and max ttl is 30 sec. So it will start with 11 seconds, then 22 seconds but the next time you call refresh it would try to extend it to 33 but max ttl only allows 30, so the token will be valid until 30 before we need to reauth - time.Sleep(tm.accessTokenTTL - nextAccessTokenExpiresInTime.Sub(accessTokenMaxTTLExpiresInTime)) + // case: Refreshed so close that the next refresh would occur beyond max ttl + // Sleep until we're at 2/3 of the remaining time to max TTL + remainingTime := accessTokenMaxTTLExpiresInTime.Sub(time.Now()) + time.Sleep(remainingTime * 2 / 3) } else { - time.Sleep(tm.accessTokenTTL - (5 * time.Second)) + // Sleep until we're at 2/3 of the TTL + time.Sleep(tm.accessTokenTTL * 2 / 3) } } } @@ -830,6 +851,24 @@ func (tm *AgentManager) WriteTokenToFiles() { } } +func (tm *AgentManager) FetchTokenFromFiles() string { + for _, sinkFile := range tm.filePaths { + if sinkFile.Type == "file" { + tokenBytes, err := ioutil.ReadFile(sinkFile.Config.Path) + if err != nil { + log.Debug().Msgf("unable to read token from file '%s' because %v", sinkFile.Config.Path, err) + continue + } + + token := string(tokenBytes) + if token != "" { + return token + } + } + } + return "" +} + func (tm *AgentManager) WriteTemplateToFile(bytes *bytes.Buffer, template *Template) { if err := WriteBytesToFile(bytes, template.DestinationPath); err != nil { log.Error().Msgf("template engine: unable to write secrets to path because %s. Will try again on next cycle", err) diff --git a/cli/packages/cmd/dynamic_secrets.go b/cli/packages/cmd/dynamic_secrets.go index 45bc323d8..0443e7714 100644 --- a/cli/packages/cmd/dynamic_secrets.go +++ b/cli/packages/cmd/dynamic_secrets.go @@ -49,6 +49,11 @@ func getDynamicSecretList(cmd *cobra.Command, args []string) { util.HandleError(err, "Unable to parse flag") } + projectSlug, err := cmd.Flags().GetString("project-slug") + if err != nil { + util.HandleError(err, "Unable to parse flag") + } + secretsPath, err := cmd.Flags().GetString("path") if err != nil { util.HandleError(err, "Unable to parse path flag") @@ -60,10 +65,10 @@ func getDynamicSecretList(cmd *cobra.Command, args []string) { util.HandleError(err, "Unable to get resty client with custom headers") } - if projectId == "" { + if projectId == "" && projectSlug == "" { workspaceFile, err := util.GetWorkSpaceFromFile() if err != nil { - util.PrintErrorMessageAndExit("Please either run infisical init to connect to a project or pass in project id with --projectId flag") + util.PrintErrorMessageAndExit("Please either run infisical init to connect to a project, pass in project slug with --project-slug flag, or pass in project id with --projectId flag") } projectId = workspaceFile.WorkspaceId } @@ -100,13 +105,16 @@ func getDynamicSecretList(cmd *cobra.Command, args []string) { }) infisicalClient.Auth().SetAccessToken(infisicalToken) - projectDetails, err := api.CallGetProjectById(httpClient, projectId) - if err != nil { - util.HandleError(err, "To fetch project details") + if projectSlug == "" { + projectDetails, err := api.CallGetProjectById(httpClient, projectId) + if err != nil { + util.HandleError(err, "To fetch project details") + } + projectSlug = projectDetails.Slug } dynamicSecretRootCredentials, err := infisicalClient.DynamicSecrets().List(infisicalSdk.ListDynamicSecretsRootCredentialsOptions{ - ProjectSlug: projectDetails.Slug, + ProjectSlug: projectSlug, SecretPath: secretsPath, EnvironmentSlug: environmentName, }) @@ -156,6 +164,11 @@ func createDynamicSecretLeaseByName(cmd *cobra.Command, args []string) { util.HandleError(err, "Unable to parse flag") } + projectSlug, err := cmd.Flags().GetString("project-slug") + if err != nil { + util.HandleError(err, "Unable to parse flag") + } + ttl, err := cmd.Flags().GetString("ttl") if err != nil { util.HandleError(err, "Unable to parse flag") @@ -177,10 +190,10 @@ func createDynamicSecretLeaseByName(cmd *cobra.Command, args []string) { util.HandleError(err, "Unable to get resty client with custom headers") } - if projectId == "" { + if projectId == "" && projectSlug == "" { workspaceFile, err := util.GetWorkSpaceFromFile() if err != nil { - util.PrintErrorMessageAndExit("Please either run infisical init to connect to a project or pass in project id with --projectId flag") + util.PrintErrorMessageAndExit("Please either run infisical init to connect to a project, pass in project id with --projectId flag, or pass in project slug with --project-slug flag") } projectId = workspaceFile.WorkspaceId } @@ -216,14 +229,17 @@ func createDynamicSecretLeaseByName(cmd *cobra.Command, args []string) { }) infisicalClient.Auth().SetAccessToken(infisicalToken) - projectDetails, err := api.CallGetProjectById(httpClient, projectId) - if err != nil { - util.HandleError(err, "To fetch project details") + if projectSlug == "" { + projectDetails, err := api.CallGetProjectById(httpClient, projectId) + if err != nil { + util.HandleError(err, "To fetch project details") + } + projectSlug = projectDetails.Slug } dynamicSecretRootCredential, err := infisicalClient.DynamicSecrets().GetByName(infisicalSdk.GetDynamicSecretRootCredentialByNameOptions{ DynamicSecretName: dynamicSecretRootCredentialName, - ProjectSlug: projectDetails.Slug, + ProjectSlug: projectSlug, SecretPath: secretsPath, EnvironmentSlug: environmentName, }) @@ -233,7 +249,7 @@ func createDynamicSecretLeaseByName(cmd *cobra.Command, args []string) { } // for Kubernetes dynamic secrets only - kubernetesNamespace, err := cmd.Flags().GetString("kubernetesNamespace") + kubernetesNamespace, err := cmd.Flags().GetString("kubernetes-namespace") if err != nil { util.HandleError(err, "Unable to parse flag") } @@ -245,7 +261,7 @@ func createDynamicSecretLeaseByName(cmd *cobra.Command, args []string) { leaseCredentials, _, leaseDetails, err := infisicalClient.DynamicSecrets().Leases().Create(infisicalSdk.CreateDynamicSecretLeaseOptions{ DynamicSecretName: dynamicSecretRootCredential.Name, - ProjectSlug: projectDetails.Slug, + ProjectSlug: projectSlug, TTL: ttl, SecretPath: secretsPath, EnvironmentSlug: environmentName, @@ -304,6 +320,11 @@ func renewDynamicSecretLeaseByName(cmd *cobra.Command, args []string) { util.HandleError(err, "Unable to parse flag") } + projectSlug, err := cmd.Flags().GetString("project-slug") + if err != nil { + util.HandleError(err, "Unable to parse flag") + } + ttl, err := cmd.Flags().GetString("ttl") if err != nil { util.HandleError(err, "Unable to parse flag") @@ -320,10 +341,10 @@ func renewDynamicSecretLeaseByName(cmd *cobra.Command, args []string) { util.HandleError(err, "Unable to get resty client with custom headers") } - if projectId == "" { + if projectId == "" && projectSlug == "" { workspaceFile, err := util.GetWorkSpaceFromFile() if err != nil { - util.PrintErrorMessageAndExit("Please either run infisical init to connect to a project or pass in project id with --projectId flag") + util.PrintErrorMessageAndExit("Please either run infisical init to connect to a project, pass in project slug with --project-slug flag, or pass in project id with --projectId flag") } projectId = workspaceFile.WorkspaceId } @@ -360,9 +381,12 @@ func renewDynamicSecretLeaseByName(cmd *cobra.Command, args []string) { }) infisicalClient.Auth().SetAccessToken(infisicalToken) - projectDetails, err := api.CallGetProjectById(httpClient, projectId) - if err != nil { - util.HandleError(err, "To fetch project details") + if projectSlug == "" { + projectDetails, err := api.CallGetProjectById(httpClient, projectId) + if err != nil { + util.HandleError(err, "To fetch project details") + } + projectSlug = projectDetails.Slug } if err != nil { @@ -370,7 +394,7 @@ func renewDynamicSecretLeaseByName(cmd *cobra.Command, args []string) { } leaseDetails, err := infisicalClient.DynamicSecrets().Leases().RenewById(infisicalSdk.RenewDynamicSecretLeaseOptions{ - ProjectSlug: projectDetails.Slug, + ProjectSlug: projectSlug, TTL: ttl, SecretPath: secretsPath, EnvironmentSlug: environmentName, @@ -416,6 +440,11 @@ func revokeDynamicSecretLeaseByName(cmd *cobra.Command, args []string) { util.HandleError(err, "Unable to parse flag") } + projectSlug, err := cmd.Flags().GetString("project-slug") + if err != nil { + util.HandleError(err, "Unable to parse flag") + } + secretsPath, err := cmd.Flags().GetString("path") if err != nil { util.HandleError(err, "Unable to parse path flag") @@ -427,10 +456,10 @@ func revokeDynamicSecretLeaseByName(cmd *cobra.Command, args []string) { util.HandleError(err, "Unable to get resty client with custom headers") } - if projectId == "" { + if projectId == "" && projectSlug == "" { workspaceFile, err := util.GetWorkSpaceFromFile() if err != nil { - util.PrintErrorMessageAndExit("Please either run infisical init to connect to a project or pass in project id with --projectId flag") + util.PrintErrorMessageAndExit("Please either run infisical init to connect to a project, pass in project slug with --project-slug flag, or pass in project id with --projectId flag") } projectId = workspaceFile.WorkspaceId } @@ -467,9 +496,12 @@ func revokeDynamicSecretLeaseByName(cmd *cobra.Command, args []string) { }) infisicalClient.Auth().SetAccessToken(infisicalToken) - projectDetails, err := api.CallGetProjectById(httpClient, projectId) - if err != nil { - util.HandleError(err, "To fetch project details") + if projectSlug == "" { + projectDetails, err := api.CallGetProjectById(httpClient, projectId) + if err != nil { + util.HandleError(err, "To fetch project details") + } + projectSlug = projectDetails.Slug } if err != nil { @@ -477,11 +509,12 @@ func revokeDynamicSecretLeaseByName(cmd *cobra.Command, args []string) { } leaseDetails, err := infisicalClient.DynamicSecrets().Leases().DeleteById(infisicalSdk.DeleteDynamicSecretLeaseOptions{ - ProjectSlug: projectDetails.Slug, + ProjectSlug: projectSlug, SecretPath: secretsPath, EnvironmentSlug: environmentName, LeaseId: dynamicSecretLeaseId, }) + if err != nil { util.HandleError(err, "To revoke dynamic secret lease") } @@ -522,6 +555,11 @@ func listDynamicSecretLeaseByName(cmd *cobra.Command, args []string) { util.HandleError(err, "Unable to parse flag") } + projectSlug, err := cmd.Flags().GetString("project-slug") + if err != nil { + util.HandleError(err, "Unable to parse flag") + } + secretsPath, err := cmd.Flags().GetString("path") if err != nil { util.HandleError(err, "Unable to parse path flag") @@ -533,10 +571,10 @@ func listDynamicSecretLeaseByName(cmd *cobra.Command, args []string) { util.HandleError(err, "Unable to get resty client with custom headers") } - if projectId == "" { + if projectId == "" && projectSlug == "" { workspaceFile, err := util.GetWorkSpaceFromFile() if err != nil { - util.PrintErrorMessageAndExit("Please either run infisical init to connect to a project or pass in project id with --projectId flag") + util.PrintErrorMessageAndExit("Please either run infisical init to connect to a project, pass in project slug with --project-slug flag, or pass in project id with --projectId flag") } projectId = workspaceFile.WorkspaceId } @@ -572,14 +610,17 @@ func listDynamicSecretLeaseByName(cmd *cobra.Command, args []string) { }) infisicalClient.Auth().SetAccessToken(infisicalToken) - projectDetails, err := api.CallGetProjectById(httpClient, projectId) - if err != nil { - util.HandleError(err, "To fetch project details") + if projectSlug == "" { + projectDetails, err := api.CallGetProjectById(httpClient, projectId) + if err != nil { + util.HandleError(err, "To fetch project details") + } + projectSlug = projectDetails.Slug } dynamicSecretLeases, err := infisicalClient.DynamicSecrets().Leases().List(infisicalSdk.ListDynamicSecretLeasesOptions{ DynamicSecretName: dynamicSecretRootCredentialName, - ProjectSlug: projectDetails.Slug, + ProjectSlug: projectSlug, SecretPath: secretsPath, EnvironmentSlug: environmentName, }) @@ -596,34 +637,39 @@ func init() { dynamicSecretLeaseCreateCmd.Flags().StringP("path", "p", "/", "The path from where dynamic secret should be leased from") dynamicSecretLeaseCreateCmd.Flags().String("token", "", "Create dynamic secret leases using machine identity access token") dynamicSecretLeaseCreateCmd.Flags().String("projectId", "", "Manually set the projectId to fetch leased from when using machine identity based auth") + dynamicSecretLeaseCreateCmd.Flags().String("project-slug", "", "Manually set the project-slug to create lease in") dynamicSecretLeaseCreateCmd.Flags().String("ttl", "", "The lease lifetime TTL. If not provided the default TTL of dynamic secret will be used.") dynamicSecretLeaseCreateCmd.Flags().Bool("plain", false, "Print leased credentials without formatting, one per line") // Kubernetes specific flags - dynamicSecretLeaseCreateCmd.Flags().String("kubernetesNamespace", "", "The namespace to create the lease in. Only used for Kubernetes dynamic secrets.") + dynamicSecretLeaseCreateCmd.Flags().String("kubernetes-namespace", "", "The namespace to create the lease in. Only used for Kubernetes dynamic secrets.") dynamicSecretLeaseCmd.AddCommand(dynamicSecretLeaseCreateCmd) dynamicSecretLeaseListCmd.Flags().StringP("path", "p", "/", "The path from where dynamic secret should be leased from") dynamicSecretLeaseListCmd.Flags().String("token", "", "Fetch dynamic secret leases machine identity access token") dynamicSecretLeaseListCmd.Flags().String("projectId", "", "Manually set the projectId to fetch leased from when using machine identity based auth") + dynamicSecretLeaseListCmd.Flags().String("project-slug", "", "Manually set the project-slug to list leases from") dynamicSecretLeaseCmd.AddCommand(dynamicSecretLeaseListCmd) dynamicSecretLeaseRenewCmd.Flags().StringP("path", "p", "/", "The path from where dynamic secret should be leased from") dynamicSecretLeaseRenewCmd.Flags().String("token", "", "Renew dynamic secrets machine identity access token") dynamicSecretLeaseRenewCmd.Flags().String("projectId", "", "Manually set the projectId to fetch leased from when using machine identity based auth") + dynamicSecretLeaseRenewCmd.Flags().String("project-slug", "", "Manually set the project-slug to renew lease in") dynamicSecretLeaseRenewCmd.Flags().String("ttl", "", "The lease lifetime TTL. If not provided the default TTL of dynamic secret will be used.") dynamicSecretLeaseCmd.AddCommand(dynamicSecretLeaseRenewCmd) dynamicSecretLeaseRevokeCmd.Flags().StringP("path", "p", "/", "The path from where dynamic secret should be leased from") dynamicSecretLeaseRevokeCmd.Flags().String("token", "", "Delete dynamic secrets using machine identity access token") dynamicSecretLeaseRevokeCmd.Flags().String("projectId", "", "Manually set the projectId to fetch leased from when using machine identity based auth") + dynamicSecretLeaseRevokeCmd.Flags().String("project-slug", "", "Manually set the project-slug to revoke lease from") dynamicSecretLeaseCmd.AddCommand(dynamicSecretLeaseRevokeCmd) dynamicSecretCmd.AddCommand(dynamicSecretLeaseCmd) dynamicSecretCmd.Flags().String("token", "", "Fetch secrets using service token or machine identity access token") dynamicSecretCmd.Flags().String("projectId", "", "Manually set the projectId to fetch dynamic-secret when using machine identity based auth") + dynamicSecretCmd.Flags().String("project-slug", "", "Manually set the project-slug to fetch dynamic-secret from") dynamicSecretCmd.PersistentFlags().String("env", "dev", "Used to select the environment name on which actions should be taken on") dynamicSecretCmd.Flags().String("path", "/", "get dynamic secret within a folder path") rootCmd.AddCommand(dynamicSecretCmd) diff --git a/docs/cli/commands/dynamic-secrets.mdx b/docs/cli/commands/dynamic-secrets.mdx index 5db90b564..76ea96cf5 100644 --- a/docs/cli/commands/dynamic-secrets.mdx +++ b/docs/cli/commands/dynamic-secrets.mdx @@ -49,7 +49,7 @@ export INFISICAL_DISABLE_UPDATE_CHECK=true ### Flags - The project ID to fetch dynamic secrets from. This is required when using a machine identity to authenticate. + The project ID to fetch dynamic secrets from. ```bash # Example @@ -58,6 +58,16 @@ infisical dynamic-secrets --projectId= + + The project slug to fetch dynamic secrets from. + +```bash +# Example +infisical dynamic-secrets --project-slug= +``` + + + The authenticated token to fetch dynamic secrets from. This is required when using a machine identity to authenticate. @@ -119,7 +129,7 @@ infisical dynamic-secrets lease create --path="/" --env=de - The project ID of the dynamic secrets to lease from. This is required when using a machine identity to authenticate. + The project ID of the dynamic secrets to lease from. ```bash # Example @@ -128,6 +138,16 @@ infisical dynamic-secrets lease create --projectId= + + The project slug of the dynamic secrets to lease from. + +```bash +# Example +infisical dynamic-secrets lease create --project-slug= +``` + + + The authenticated token to create dynamic secret leases. This is required when using a machine identity to authenticate. @@ -153,12 +173,12 @@ infisical dynamic-secrets lease create --ttl= The following flags are specific to certain providers or integrations: - + The namespace to create the lease in. Only used for Kubernetes dynamic secrets. ```bash # Example - infisical dynamic-secrets lease create --kubernetesNamespace= + infisical dynamic-secrets lease create --kubernetes-namespace= ``` @@ -190,7 +210,7 @@ infisical dynamic-secrets lease list --path="/" --env=dev - The project ID of the dynamic secrets to list leases from. This is required when using a machine identity to authenticate. + The project ID of the dynamic secrets to list leases from. ```bash # Example @@ -199,6 +219,16 @@ infisical dynamic-secrets lease list --projectId= + + The project slug of the dynamic secrets to list leases from. + +```bash +# Example +infisical dynamic-secrets lease list --project-slug= +``` + + + The authenticated token to list dynamic secret leases. This is required when using a machine identity to authenticate. @@ -235,7 +265,7 @@ infisical dynamic-secrets lease renew --path="/" --env=dev - The project ID of the dynamic secret's lease from. This is required when using a machine identity to authenticate. + The project ID of the dynamic secret to lease from. ```bash # Example @@ -244,6 +274,16 @@ infisical dynamic-secrets lease renew --projectId= + + The project slug of the dynamic secret to lease from. + +```bash +# Example +infisical dynamic-secrets lease renew --project-slug= +``` + + + The authenticated token to create dynamic secret leases. This is required when using a machine identity to authenticate. @@ -290,7 +330,7 @@ infisical dynamic-secrets lease delete --path="/" --env=dev - The project ID of the dynamic secret's lease from. This is required when using a machine identity to authenticate. + The project ID of the dynamic secret to delete lease from. ```bash # Example @@ -299,6 +339,16 @@ infisical dynamic-secrets lease delete --projectId= + + The project slug of the dynamic secret to delete lease from. + +```bash +# Example +infisical dynamic-secrets lease delete --project-slug= +``` + + + The authenticated token to delete dynamic secret leases. This is required when using a machine identity to authenticate.