Browse Source

feat: Add BeyondTrust WorkloadCredentials provider (#5999)

* feat: add BeyondTrust Workload Credentials provider

Add support for BeyondTrust Workload Credentials as a new provider for
External Secrets Operator. This provider enables:

- Reading static secrets from BeyondTrust Workload Credentials
- Generating dynamic credentials (e.g., temporary AWS credentials)
- SecretStore and ClusterSecretStore support
- Dynamic secret generation via BeyondtrustWorkloadCredentialsDynamicSecret

The provider includes:
- Full API type definitions with proper validation
- HTTP client with custom CA bundle support
- Comprehensive test coverage
- Documentation and usage examples
- Support for metadata and client-side tag filtering

Signed-off-by: smritidahal <sdahal@beyondtrust.com>

* fix: address CodeRabbit review suggestions

- Fix APIURL example to use BeyondTrust domain instead of CyberArk
- Update stale kind references from BeyondtrustSecretsDynamicSecret to BeyondtrustWorkloadCredentialsDynamicSecret
- Fix namespace consistency in documentation examples
- Add colon separators to error messages for consistency
- Implement HTTP response size limits (10 MiB) to prevent unbounded memory allocation
- Normalize baseURL to prevent double-slash issues in URL construction
- Clone default HTTP transport to preserve proxy and connection settings
- Fix nil pointer validation in fake client argument checks
- Fix GetAllSecrets to return per-property entries instead of marshaled objects

Signed-off-by: smritidahal <sdahal@beyondtrust.com>

* chore: update BeyondTrust Secrets API version to 2026-04-28

Signed-off-by: smritidahal <sdahal@beyondtrust.com>

* chore: address code review feedback

   - Fix RBAC resource ordering: move beyondtrustworkloadcredentialsdynamicsecrets
     to correct alphabetical position before cloudsmithaccesstokens
   - Add API documentation reference constant and include in error messages
     to help users troubleshoot API issues
   - Define validationTimeout constant with explanation (15s for balancing
     API response time vs failing fast on connectivity issues)
   - Enforce UUID v4 validation for siteId (per API requirements) with
     accurate comment explaining RFC 4122 variant bits

Signed-off-by: smritidahal <sdahal@beyondtrust.com>

* update license headers

Signed-off-by: smritidahal <sdahal@beyondtrust.com>

* fix lint errors

Signed-off-by: smritidahal <sdahal@beyondtrust.com>

* address PR review feedback

Signed-off-by: smritidahal <sdahal@beyondtrust.com>

* fix GetAllSecrets to namespace by path to prevent collisions

Signed-off-by: smritidahal <sdahal@beyondtrust.com>

* lint and generate

Signed-off-by: smritidahal <sdahal@beyondtrust.com>

* chore: update go version to 1.26.4 for BeyondTrust Workload Credentials modules

Signed-off-by: smritidahal <sdahal@beyondtrust.com>

---------

Signed-off-by: smritidahal <sdahal@beyondtrust.com>
Smriti Dahal 1 month ago
parent
commit
999a26d9be
50 changed files with 6344 additions and 23 deletions
  1. 1 1
      apis/externalsecrets/v1/externalsecret_types.go
  2. 98 0
      apis/externalsecrets/v1/secretstore_beyondtrustworkloadcredentials_types.go
  3. 4 0
      apis/externalsecrets/v1/secretstore_types.go
  4. 87 0
      apis/externalsecrets/v1/zz_generated.deepcopy.go
  5. 3 0
      apis/generators/v1alpha1/register.go
  6. 71 0
      apis/generators/v1alpha1/types_beyondtrustworkloadcredentials.go
  7. 19 16
      apis/generators/v1alpha1/types_cluster.go
  8. 88 0
      apis/generators/v1alpha1/zz_generated.deepcopy.go
  9. 2 0
      config/crds/bases/external-secrets.io_clusterexternalsecrets.yaml
  10. 1 0
      config/crds/bases/external-secrets.io_clusterpushsecrets.yaml
  11. 135 0
      config/crds/bases/external-secrets.io_clustersecretstores.yaml
  12. 2 0
      config/crds/bases/external-secrets.io_externalsecrets.yaml
  13. 1 0
      config/crds/bases/external-secrets.io_pushsecrets.yaml
  14. 135 0
      config/crds/bases/external-secrets.io_secretstores.yaml
  15. 216 0
      config/crds/bases/generators.external-secrets.io_beyondtrustworkloadcredentialsdynamicsecrets.yaml
  16. 166 0
      config/crds/bases/generators.external-secrets.io_clustergenerators.yaml
  17. 1 0
      config/crds/bases/kustomization.yaml
  18. 3 0
      deploy/charts/external-secrets/templates/rbac.yaml
  19. 641 0
      deploy/crds/bundle.yaml
  20. 293 0
      docs/api/generator/beyondtrustworkloadcredentials.md
  21. 412 0
      docs/api/spec.md
  22. 384 0
      docs/provider/beyondtrustworkloadcredentials.md
  23. 16 0
      docs/snippets/beyondtrustworkloadcredentials-dynamic-external-secret.yaml
  24. 16 0
      docs/snippets/beyondtrustworkloadcredentials-dynamic-secret.yaml
  25. 17 0
      docs/snippets/beyondtrustworkloadcredentials-external-secret.yaml
  26. 17 0
      docs/snippets/beyondtrustworkloadcredentials-secret-store.yaml
  27. 182 0
      generators/v1/beyondtrustworkloadcredentials/beyondtrustworkloadcredentials.go
  28. 406 0
      generators/v1/beyondtrustworkloadcredentials/beyondtrustworkloadcredentials_test.go
  29. 120 0
      generators/v1/beyondtrustworkloadcredentials/fixtures_test.go
  30. 100 0
      generators/v1/beyondtrustworkloadcredentials/go.mod
  31. 238 0
      generators/v1/beyondtrustworkloadcredentials/go.sum
  32. 4 0
      go.mod
  33. 34 0
      pkg/register/beyondtrustworkloadcredentials.go
  34. 2 0
      pkg/register/generators.go
  35. 320 0
      providers/v1/beyondtrustworkloadcredentials/client.go
  36. 176 0
      providers/v1/beyondtrustworkloadcredentials/fake/fake.go
  37. 97 0
      providers/v1/beyondtrustworkloadcredentials/go.mod
  38. 238 0
      providers/v1/beyondtrustworkloadcredentials/go.sum
  39. 409 0
      providers/v1/beyondtrustworkloadcredentials/httpclient/client.go
  40. 43 0
      providers/v1/beyondtrustworkloadcredentials/httpclient/types.go
  41. 335 0
      providers/v1/beyondtrustworkloadcredentials/provider.go
  42. 662 0
      providers/v1/beyondtrustworkloadcredentials/provider_test.go
  43. 76 0
      providers/v1/beyondtrustworkloadcredentials/util/beyondtrustworkloadcredentials.go
  44. 11 0
      runtime/esutils/resolvers/generator.go
  45. 2 2
      tests/__snapshot__/clusterexternalsecret-v1.yaml
  46. 23 1
      tests/__snapshot__/clustergenerator-v1alpha1.yaml
  47. 17 0
      tests/__snapshot__/clustersecretstore-v1.yaml
  48. 2 2
      tests/__snapshot__/externalsecret-v1.yaml
  49. 1 1
      tests/__snapshot__/pushsecret-v1alpha1.yaml
  50. 17 0
      tests/__snapshot__/secretstore-v1.yaml

+ 1 - 1
apis/externalsecrets/v1/externalsecret_types.go

@@ -593,7 +593,7 @@ type GeneratorRef struct {
 	APIVersion string `json:"apiVersion,omitempty"`
 
 	// Specify the Kind of the generator resource
-	// +kubebuilder:validation:Enum=ACRAccessToken;ClusterGenerator;CloudsmithAccessToken;ECRAuthorizationToken;Fake;GCRAccessToken;GithubAccessToken;QuayAccessToken;Password;SSHKey;STSSessionToken;UUID;VaultDynamicSecret;Webhook;Grafana;MFA
+	// +kubebuilder:validation:Enum=ACRAccessToken;BeyondtrustWorkloadCredentialsDynamicSecret;ClusterGenerator;CloudsmithAccessToken;ECRAuthorizationToken;Fake;GCRAccessToken;GithubAccessToken;QuayAccessToken;Password;SSHKey;STSSessionToken;UUID;VaultDynamicSecret;Webhook;Grafana;MFA
 	Kind string `json:"kind"`
 
 	// Specify the name of the generator resource

+ 98 - 0
apis/externalsecrets/v1/secretstore_beyondtrustworkloadcredentials_types.go

@@ -0,0 +1,98 @@
+/*
+Copyright © The ESO Authors
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    https://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package v1
+
+import (
+	esmeta "github.com/external-secrets/external-secrets/apis/meta/v1"
+)
+
+// BeyondtrustWorkloadCredentialsAuthSecretRef defines a reference to a secret containing credentials for the BeyondTrust Workload Credentials provider.
+// The nested structure supports multiple authentication methods (currently only API token is supported).
+// For more information on authentication, see: https://docs.beyondtrust.com/bt-docs/docs/secrets-api#authentication
+type BeyondtrustWorkloadCredentialsAuthSecretRef struct {
+	// Token references the Kubernetes secret containing the BeyondTrust Workload Credentials API token.
+	// The secret should contain the API key used to authenticate with BeyondTrust Workload Credentials.
+	// Create an API token in your BeyondTrust Workload Credentials console and store it in a Kubernetes secret.
+	// For details on creating API tokens, see: https://docs.beyondtrust.com/bt-docs/docs/secrets-api#authentication
+	Token esmeta.SecretKeySelector `json:"token"`
+}
+
+// BeyondtrustWorkloadCredentialsAuth defines the authentication method for the BeyondTrust Workload Credentials provider.
+// Currently supports API key authentication via Kubernetes secret reference.
+// For authentication documentation, see: https://docs.beyondtrust.com/bt-docs/docs/secrets-api#authentication
+type BeyondtrustWorkloadCredentialsAuth struct {
+	// APIKey configures API token authentication for BeyondTrust Workload Credentials.
+	// The token is retrieved from a Kubernetes secret and used as a Bearer token for API requests.
+	APIKey BeyondtrustWorkloadCredentialsAuthSecretRef `json:"apikey"`
+}
+
+// BeyondtrustWorkloadCredentialsServer defines connection configuration for BeyondTrust Workload Credentials.
+// For API reference documentation, see: https://docs.beyondtrust.com/bt-docs/docs/secrets-api
+type BeyondtrustWorkloadCredentialsServer struct {
+	// APIURL is the base URL of your BeyondTrust Workload Credentials API server.
+	// This should be the full URL to your BeyondTrust instance.
+	// Example: https://api.beyondtrust.io/siie
+	// For more information, see: https://docs.beyondtrust.com/bt-docs/docs/secrets-api#base-url
+	// +required
+	APIURL string `json:"apiUrl"`
+
+	// SiteID is your BeyondTrust Workload Credentials site identifier (UUID format).
+	// This identifier is unique to your BeyondTrust Workload Credentials instance.
+	// You can find your Site ID in the BeyondTrust Workload Credentials admin console.
+	// Example: a1b2c3d4-e5f6-4890-abcd-ef1234567890
+	// For more information, see: https://docs.beyondtrust.com/bt-docs/docs/secrets-api
+	// +required
+	SiteID string `json:"siteId"`
+}
+
+// BeyondtrustWorkloadCredentialsProvider configures a store to sync secrets using the BeyondTrust Workload Credentials provider.
+// BeyondTrust Workload Credentials provides secure storage for static secrets and dynamic credential generation.
+// This provider supports reading secrets and generating dynamic credentials (e.g., temporary AWS credentials).
+// For complete documentation, see: https://docs.beyondtrust.com/bt-docs/docs/secrets-api
+type BeyondtrustWorkloadCredentialsProvider struct {
+	// Auth configures how the Operator authenticates with the BeyondTrust Workload Credentials API.
+	// Currently supports API key authentication via Kubernetes secret reference.
+	// For authentication setup, see: https://docs.beyondtrust.com/bt-docs/docs/secrets-api#authentication
+	// +required
+	Auth *BeyondtrustWorkloadCredentialsAuth `json:"auth"`
+
+	// Server configures the BeyondTrust Workload Credentials server connection details.
+	// Includes the API URL and Site ID for your BeyondTrust instance.
+	// For API reference, see: https://docs.beyondtrust.com/bt-docs/docs/secrets-api
+	// +required
+	Server *BeyondtrustWorkloadCredentialsServer `json:"server"`
+
+	// FolderPath specifies the default folder path for secret retrieval.
+	// Secrets will be fetched from this folder unless overridden in the ExternalSecret spec.
+	// Example: "production/database" or "dev/api-keys"
+	// Leave empty to retrieve secrets from the root folder.
+	// For folder organization, see: https://docs.beyondtrust.com/bt-docs/docs/secrets-api#folders
+	// +optional
+	FolderPath string `json:"folderPath,omitempty"`
+
+	// CABundle is a base64-encoded CA certificate used to validate the BeyondTrust Workload Credentials API TLS certificate.
+	// Use this when your BeyondTrust instance uses a self-signed certificate or internal CA.
+	// If not set, the system's trusted root certificates are used.
+	// +optional
+	CABundle []byte `json:"caBundle,omitempty"`
+
+	// CAProvider points to a Secret or ConfigMap containing a PEM-encoded CA certificate.
+	// This is used to validate the BeyondTrust Workload Credentials API TLS certificate.
+	// Use this as an alternative to CABundle when you want to reference an existing Kubernetes resource.
+	// +optional
+	CAProvider *CAProvider `json:"caProvider,omitempty"`
+}

+ 4 - 0
apis/externalsecrets/v1/secretstore_types.go

@@ -208,6 +208,10 @@ type SecretStoreProvider struct {
 	// +optional
 	Beyondtrust *BeyondtrustProvider `json:"beyondtrust,omitempty"`
 
+	// BeyondtrustWorkloadCredentials configures this store to sync secrets using the BeyondTrust Workload Credentials provider.
+	// +optional
+	BeyondtrustWorkloadCredentials *BeyondtrustWorkloadCredentialsProvider `json:"beyondtrustworkloadcredentials,omitempty"`
+
 	// CloudruSM configures this store to sync secrets using the Cloud.ru Secret Manager provider
 	// +optional
 	CloudruSM *CloudruSMProvider `json:"cloudrusm,omitempty"`

+ 87 - 0
apis/externalsecrets/v1/zz_generated.deepcopy.go

@@ -607,6 +607,88 @@ func (in *BeyondtrustServer) DeepCopy() *BeyondtrustServer {
 	return out
 }
 
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *BeyondtrustWorkloadCredentialsAuth) DeepCopyInto(out *BeyondtrustWorkloadCredentialsAuth) {
+	*out = *in
+	in.APIKey.DeepCopyInto(&out.APIKey)
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BeyondtrustWorkloadCredentialsAuth.
+func (in *BeyondtrustWorkloadCredentialsAuth) DeepCopy() *BeyondtrustWorkloadCredentialsAuth {
+	if in == nil {
+		return nil
+	}
+	out := new(BeyondtrustWorkloadCredentialsAuth)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *BeyondtrustWorkloadCredentialsAuthSecretRef) DeepCopyInto(out *BeyondtrustWorkloadCredentialsAuthSecretRef) {
+	*out = *in
+	in.Token.DeepCopyInto(&out.Token)
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BeyondtrustWorkloadCredentialsAuthSecretRef.
+func (in *BeyondtrustWorkloadCredentialsAuthSecretRef) DeepCopy() *BeyondtrustWorkloadCredentialsAuthSecretRef {
+	if in == nil {
+		return nil
+	}
+	out := new(BeyondtrustWorkloadCredentialsAuthSecretRef)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *BeyondtrustWorkloadCredentialsProvider) DeepCopyInto(out *BeyondtrustWorkloadCredentialsProvider) {
+	*out = *in
+	if in.Auth != nil {
+		in, out := &in.Auth, &out.Auth
+		*out = new(BeyondtrustWorkloadCredentialsAuth)
+		(*in).DeepCopyInto(*out)
+	}
+	if in.Server != nil {
+		in, out := &in.Server, &out.Server
+		*out = new(BeyondtrustWorkloadCredentialsServer)
+		**out = **in
+	}
+	if in.CABundle != nil {
+		in, out := &in.CABundle, &out.CABundle
+		*out = make([]byte, len(*in))
+		copy(*out, *in)
+	}
+	if in.CAProvider != nil {
+		in, out := &in.CAProvider, &out.CAProvider
+		*out = new(CAProvider)
+		(*in).DeepCopyInto(*out)
+	}
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BeyondtrustWorkloadCredentialsProvider.
+func (in *BeyondtrustWorkloadCredentialsProvider) DeepCopy() *BeyondtrustWorkloadCredentialsProvider {
+	if in == nil {
+		return nil
+	}
+	out := new(BeyondtrustWorkloadCredentialsProvider)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *BeyondtrustWorkloadCredentialsServer) DeepCopyInto(out *BeyondtrustWorkloadCredentialsServer) {
+	*out = *in
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BeyondtrustWorkloadCredentialsServer.
+func (in *BeyondtrustWorkloadCredentialsServer) DeepCopy() *BeyondtrustWorkloadCredentialsServer {
+	if in == nil {
+		return nil
+	}
+	out := new(BeyondtrustWorkloadCredentialsServer)
+	in.DeepCopyInto(out)
+	return out
+}
+
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *BitwardenSecretsManagerAuth) DeepCopyInto(out *BitwardenSecretsManagerAuth) {
 	*out = *in
@@ -3739,6 +3821,11 @@ func (in *SecretStoreProvider) DeepCopyInto(out *SecretStoreProvider) {
 		*out = new(BeyondtrustProvider)
 		(*in).DeepCopyInto(*out)
 	}
+	if in.BeyondtrustWorkloadCredentials != nil {
+		in, out := &in.BeyondtrustWorkloadCredentials, &out.BeyondtrustWorkloadCredentials
+		*out = new(BeyondtrustWorkloadCredentialsProvider)
+		(*in).DeepCopyInto(*out)
+	}
 	if in.CloudruSM != nil {
 		in, out := &in.CloudruSM, &out.CloudruSM
 		*out = new(CloudruSMProvider)

+ 3 - 0
apis/generators/v1alpha1/register.go

@@ -73,6 +73,8 @@ var (
 	ClusterGeneratorKind = reflect.TypeFor[ClusterGenerator]().Name()
 	// CloudsmithAccessTokenKind is the kind name for CloudsmithAccessToken resource.
 	CloudsmithAccessTokenKind = reflect.TypeFor[CloudsmithAccessToken]().Name()
+	// BeyondtrustWorkloadCredentialsDynamicSecretKind is the kind name for BeyondtrustWorkloadCredentialsDynamicSecret resource.
+	BeyondtrustWorkloadCredentialsDynamicSecretKind = reflect.TypeFor[BeyondtrustWorkloadCredentialsDynamicSecret]().Name()
 )
 
 func init() {
@@ -94,6 +96,7 @@ func init() {
 	*/
 
 	SchemeBuilder.Register(&ACRAccessToken{}, &ACRAccessTokenList{})
+	SchemeBuilder.Register(&BeyondtrustWorkloadCredentialsDynamicSecret{}, &BeyondtrustWorkloadCredentialsDynamicSecretList{})
 	SchemeBuilder.Register(&ClusterGenerator{}, &ClusterGeneratorList{})
 	SchemeBuilder.Register(&CloudsmithAccessToken{}, &CloudsmithAccessTokenList{})
 	SchemeBuilder.Register(&ECRAuthorizationToken{}, &ECRAuthorizationTokenList{})

+ 71 - 0
apis/generators/v1alpha1/types_beyondtrustworkloadcredentials.go

@@ -0,0 +1,71 @@
+/*
+Copyright © The ESO Authors
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    https://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package v1alpha1
+
+import (
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+
+	esv1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1"
+)
+
+// BeyondtrustWorkloadCredentialsDynamicSecretSpec defines the desired spec for BeyondtrustWorkloadCredentials dynamic generator.
+// This generator enables obtaining temporary, short-lived credentials from BeyondTrust Workload Credentials.
+// For more information, see: https://docs.beyondtrust.com/bt-docs/docs/secrets-api
+type BeyondtrustWorkloadCredentialsDynamicSecretSpec struct {
+	// Controller selects the controller that should handle this generator.
+	// Leave empty to use the default controller.
+	// +optional
+	Controller string `json:"controller,omitempty"`
+
+	// Provider contains the BeyondtrustWorkloadCredentials provider configuration including authentication,
+	// server connection details, and the folder path to the dynamic secret definition.
+	// The folderPath should point to a dynamic secret definition that has been created in
+	// BeyondTrust Workload Credentials (e.g., "production/aws-temp").
+	// For setup details, see: https://docs.beyondtrust.com/bt-docs/docs/secrets-api
+	// +required
+	Provider *esv1.BeyondtrustWorkloadCredentialsProvider `json:"provider"`
+
+	// RetrySettings configures exponential backoff for failed API requests.
+	// If not specified, uses the default retry settings.
+	// +optional
+	RetrySettings *esv1.SecretStoreRetrySettings `json:"retrySettings,omitempty"`
+}
+
+// BeyondtrustWorkloadCredentialsDynamicSecret represents a generator that requests dynamic credentials from BeyondTrust Workload Credentials.
+// This generator calls the BeyondTrust Workload Credentials API to generate fresh, temporary credentials
+// (such as AWS STS credentials) each time an ExternalSecret is refreshed.
+// Dynamic secret definitions must be created in BeyondTrust Workload Credentials before they can be referenced.
+// For complete documentation, see: https://docs.beyondtrust.com/bt-docs/docs/secrets-api
+// +kubebuilder:object:root=true
+// +kubebuilder:storageversion
+// +kubebuilder:subresource:status
+// +kubebuilder:metadata:labels="external-secrets.io/component=controller"
+// +kubebuilder:resource:scope=Namespaced,categories={external-secrets, external-secrets-generators}
+type BeyondtrustWorkloadCredentialsDynamicSecret struct {
+	metav1.TypeMeta   `json:",inline"`
+	metav1.ObjectMeta `json:"metadata,omitempty"`
+
+	Spec BeyondtrustWorkloadCredentialsDynamicSecretSpec `json:"spec,omitempty"`
+}
+
+// BeyondtrustWorkloadCredentialsDynamicSecretList contains a list of BeyondtrustWorkloadCredentialsDynamicSecret resources.
+// +kubebuilder:object:root=true
+type BeyondtrustWorkloadCredentialsDynamicSecretList struct {
+	metav1.TypeMeta `json:",inline"`
+	metav1.ListMeta `json:"metadata,omitempty"`
+	Items           []BeyondtrustWorkloadCredentialsDynamicSecret `json:"items"`
+}

+ 19 - 16
apis/generators/v1alpha1/types_cluster.go

@@ -30,7 +30,7 @@ type ClusterGeneratorSpec struct {
 }
 
 // GeneratorKind represents a kind of generator.
-// +kubebuilder:validation:Enum=ACRAccessToken;CloudsmithAccessToken;ECRAuthorizationToken;Fake;GCRAccessToken;GithubAccessToken;QuayAccessToken;Password;SSHKey;STSSessionToken;UUID;VaultDynamicSecret;Webhook;Grafana
+// +kubebuilder:validation:Enum=ACRAccessToken;BeyondtrustWorkloadCredentialsDynamicSecret;CloudsmithAccessToken;ECRAuthorizationToken;Fake;GCRAccessToken;GithubAccessToken;QuayAccessToken;Password;SSHKey;STSSessionToken;UUID;VaultDynamicSecret;Webhook;Grafana;MFA
 type GeneratorKind string
 
 const (
@@ -64,27 +64,30 @@ const (
 	GeneratorKindMFA GeneratorKind = "MFA"
 	// GeneratorKindCloudsmithAccessToken represents a Cloudsmith access token generator.
 	GeneratorKindCloudsmithAccessToken GeneratorKind = "CloudsmithAccessToken"
+	// GeneratorKindBeyondtrustWorkloadCredentialsDynamicSecret represents a BeyondTrust Workload Credentials dynamic secret generator.
+	GeneratorKindBeyondtrustWorkloadCredentialsDynamicSecret GeneratorKind = "BeyondtrustWorkloadCredentialsDynamicSecret"
 )
 
 // GeneratorSpec defines the configuration for various supported generator types.
 // +kubebuilder:validation:MaxProperties=1
 // +kubebuilder:validation:MinProperties=1
 type GeneratorSpec struct {
-	ACRAccessTokenSpec        *ACRAccessTokenSpec        `json:"acrAccessTokenSpec,omitempty"`
-	CloudsmithAccessTokenSpec *CloudsmithAccessTokenSpec `json:"cloudsmithAccessTokenSpec,omitempty"`
-	ECRAuthorizationTokenSpec *ECRAuthorizationTokenSpec `json:"ecrAuthorizationTokenSpec,omitempty"`
-	FakeSpec                  *FakeSpec                  `json:"fakeSpec,omitempty"`
-	GCRAccessTokenSpec        *GCRAccessTokenSpec        `json:"gcrAccessTokenSpec,omitempty"`
-	GithubAccessTokenSpec     *GithubAccessTokenSpec     `json:"githubAccessTokenSpec,omitempty"`
-	QuayAccessTokenSpec       *QuayAccessTokenSpec       `json:"quayAccessTokenSpec,omitempty"`
-	PasswordSpec              *PasswordSpec              `json:"passwordSpec,omitempty"`
-	SSHKeySpec                *SSHKeySpec                `json:"sshKeySpec,omitempty"`
-	STSSessionTokenSpec       *STSSessionTokenSpec       `json:"stsSessionTokenSpec,omitempty"`
-	UUIDSpec                  *UUIDSpec                  `json:"uuidSpec,omitempty"`
-	VaultDynamicSecretSpec    *VaultDynamicSecretSpec    `json:"vaultDynamicSecretSpec,omitempty"`
-	WebhookSpec               *WebhookSpec               `json:"webhookSpec,omitempty"`
-	GrafanaSpec               *GrafanaSpec               `json:"grafanaSpec,omitempty"`
-	MFASpec                   *MFASpec                   `json:"mfaSpec,omitempty"`
+	ACRAccessTokenSpec                              *ACRAccessTokenSpec                              `json:"acrAccessTokenSpec,omitempty"`
+	BeyondtrustWorkloadCredentialsDynamicSecretSpec *BeyondtrustWorkloadCredentialsDynamicSecretSpec `json:"beyondtrustWorkloadCredentialsDynamicSecretSpec,omitempty"`
+	CloudsmithAccessTokenSpec                       *CloudsmithAccessTokenSpec                       `json:"cloudsmithAccessTokenSpec,omitempty"`
+	ECRAuthorizationTokenSpec                       *ECRAuthorizationTokenSpec                       `json:"ecrAuthorizationTokenSpec,omitempty"`
+	FakeSpec                                        *FakeSpec                                        `json:"fakeSpec,omitempty"`
+	GCRAccessTokenSpec                              *GCRAccessTokenSpec                              `json:"gcrAccessTokenSpec,omitempty"`
+	GithubAccessTokenSpec                           *GithubAccessTokenSpec                           `json:"githubAccessTokenSpec,omitempty"`
+	QuayAccessTokenSpec                             *QuayAccessTokenSpec                             `json:"quayAccessTokenSpec,omitempty"`
+	PasswordSpec                                    *PasswordSpec                                    `json:"passwordSpec,omitempty"`
+	SSHKeySpec                                      *SSHKeySpec                                      `json:"sshKeySpec,omitempty"`
+	STSSessionTokenSpec                             *STSSessionTokenSpec                             `json:"stsSessionTokenSpec,omitempty"`
+	UUIDSpec                                        *UUIDSpec                                        `json:"uuidSpec,omitempty"`
+	VaultDynamicSecretSpec                          *VaultDynamicSecretSpec                          `json:"vaultDynamicSecretSpec,omitempty"`
+	WebhookSpec                                     *WebhookSpec                                     `json:"webhookSpec,omitempty"`
+	GrafanaSpec                                     *GrafanaSpec                                     `json:"grafanaSpec,omitempty"`
+	MFASpec                                         *MFASpec                                         `json:"mfaSpec,omitempty"`
 }
 
 // ClusterGenerator represents a cluster-wide generator which can be referenced as part of `generatorRef` fields.

+ 88 - 0
apis/generators/v1alpha1/zz_generated.deepcopy.go

@@ -287,6 +287,89 @@ func (in *AzureACRWorkloadIdentityAuth) DeepCopy() *AzureACRWorkloadIdentityAuth
 	return out
 }
 
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *BeyondtrustWorkloadCredentialsDynamicSecret) DeepCopyInto(out *BeyondtrustWorkloadCredentialsDynamicSecret) {
+	*out = *in
+	out.TypeMeta = in.TypeMeta
+	in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
+	in.Spec.DeepCopyInto(&out.Spec)
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BeyondtrustWorkloadCredentialsDynamicSecret.
+func (in *BeyondtrustWorkloadCredentialsDynamicSecret) DeepCopy() *BeyondtrustWorkloadCredentialsDynamicSecret {
+	if in == nil {
+		return nil
+	}
+	out := new(BeyondtrustWorkloadCredentialsDynamicSecret)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
+func (in *BeyondtrustWorkloadCredentialsDynamicSecret) DeepCopyObject() runtime.Object {
+	if c := in.DeepCopy(); c != nil {
+		return c
+	}
+	return nil
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *BeyondtrustWorkloadCredentialsDynamicSecretList) DeepCopyInto(out *BeyondtrustWorkloadCredentialsDynamicSecretList) {
+	*out = *in
+	out.TypeMeta = in.TypeMeta
+	in.ListMeta.DeepCopyInto(&out.ListMeta)
+	if in.Items != nil {
+		in, out := &in.Items, &out.Items
+		*out = make([]BeyondtrustWorkloadCredentialsDynamicSecret, len(*in))
+		for i := range *in {
+			(*in)[i].DeepCopyInto(&(*out)[i])
+		}
+	}
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BeyondtrustWorkloadCredentialsDynamicSecretList.
+func (in *BeyondtrustWorkloadCredentialsDynamicSecretList) DeepCopy() *BeyondtrustWorkloadCredentialsDynamicSecretList {
+	if in == nil {
+		return nil
+	}
+	out := new(BeyondtrustWorkloadCredentialsDynamicSecretList)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
+func (in *BeyondtrustWorkloadCredentialsDynamicSecretList) DeepCopyObject() runtime.Object {
+	if c := in.DeepCopy(); c != nil {
+		return c
+	}
+	return nil
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *BeyondtrustWorkloadCredentialsDynamicSecretSpec) DeepCopyInto(out *BeyondtrustWorkloadCredentialsDynamicSecretSpec) {
+	*out = *in
+	if in.Provider != nil {
+		in, out := &in.Provider, &out.Provider
+		*out = new(externalsecretsv1.BeyondtrustWorkloadCredentialsProvider)
+		(*in).DeepCopyInto(*out)
+	}
+	if in.RetrySettings != nil {
+		in, out := &in.RetrySettings, &out.RetrySettings
+		*out = new(externalsecretsv1.SecretStoreRetrySettings)
+		(*in).DeepCopyInto(*out)
+	}
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BeyondtrustWorkloadCredentialsDynamicSecretSpec.
+func (in *BeyondtrustWorkloadCredentialsDynamicSecretSpec) DeepCopy() *BeyondtrustWorkloadCredentialsDynamicSecretSpec {
+	if in == nil {
+		return nil
+	}
+	out := new(BeyondtrustWorkloadCredentialsDynamicSecretSpec)
+	in.DeepCopyInto(out)
+	return out
+}
+
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *CloudsmithAccessToken) DeepCopyInto(out *CloudsmithAccessToken) {
 	*out = *in
@@ -749,6 +832,11 @@ func (in *GeneratorSpec) DeepCopyInto(out *GeneratorSpec) {
 		*out = new(ACRAccessTokenSpec)
 		(*in).DeepCopyInto(*out)
 	}
+	if in.BeyondtrustWorkloadCredentialsDynamicSecretSpec != nil {
+		in, out := &in.BeyondtrustWorkloadCredentialsDynamicSecretSpec, &out.BeyondtrustWorkloadCredentialsDynamicSecretSpec
+		*out = new(BeyondtrustWorkloadCredentialsDynamicSecretSpec)
+		(*in).DeepCopyInto(*out)
+	}
 	if in.CloudsmithAccessTokenSpec != nil {
 		in, out := &in.CloudsmithAccessTokenSpec, &out.CloudsmithAccessTokenSpec
 		*out = new(CloudsmithAccessTokenSpec)

+ 2 - 0
config/crds/bases/external-secrets.io_clusterexternalsecrets.yaml

@@ -168,6 +168,7 @@ spec:
                                   description: Specify the Kind of the generator resource
                                   enum:
                                   - ACRAccessToken
+                                  - BeyondtrustWorkloadCredentialsDynamicSecret
                                   - ClusterGenerator
                                   - CloudsmithAccessToken
                                   - ECRAuthorizationToken
@@ -434,6 +435,7 @@ spec:
                                   description: Specify the Kind of the generator resource
                                   enum:
                                   - ACRAccessToken
+                                  - BeyondtrustWorkloadCredentialsDynamicSecret
                                   - ClusterGenerator
                                   - CloudsmithAccessToken
                                   - ECRAuthorizationToken

+ 1 - 0
config/crds/bases/external-secrets.io_clusterpushsecrets.yaml

@@ -428,6 +428,7 @@ spec:
                             description: Specify the Kind of the generator resource
                             enum:
                             - ACRAccessToken
+                            - BeyondtrustWorkloadCredentialsDynamicSecret
                             - ClusterGenerator
                             - CloudsmithAccessToken
                             - ECRAuthorizationToken

+ 135 - 0
config/crds/bases/external-secrets.io_clustersecretstores.yaml

@@ -1164,6 +1164,141 @@ spec:
                     - auth
                     - server
                     type: object
+                  beyondtrustworkloadcredentials:
+                    description: BeyondtrustWorkloadCredentials configures this store
+                      to sync secrets using the BeyondTrust Workload Credentials provider.
+                    properties:
+                      auth:
+                        description: |-
+                          Auth configures how the Operator authenticates with the BeyondTrust Workload Credentials API.
+                          Currently supports API key authentication via Kubernetes secret reference.
+                          For authentication setup, see: https://docs.beyondtrust.com/bt-docs/docs/secrets-api#authentication
+                        properties:
+                          apikey:
+                            description: |-
+                              APIKey configures API token authentication for BeyondTrust Workload Credentials.
+                              The token is retrieved from a Kubernetes secret and used as a Bearer token for API requests.
+                            properties:
+                              token:
+                                description: |-
+                                  Token references the Kubernetes secret containing the BeyondTrust Workload Credentials API token.
+                                  The secret should contain the API key used to authenticate with BeyondTrust Workload Credentials.
+                                  Create an API token in your BeyondTrust Workload Credentials console and store it in a Kubernetes secret.
+                                  For details on creating API tokens, see: https://docs.beyondtrust.com/bt-docs/docs/secrets-api#authentication
+                                properties:
+                                  key:
+                                    description: |-
+                                      A key in the referenced Secret.
+                                      Some instances of this field may be defaulted, in others it may be required.
+                                    maxLength: 253
+                                    minLength: 1
+                                    pattern: ^[-._a-zA-Z0-9]+$
+                                    type: string
+                                  name:
+                                    description: The name of the Secret resource being
+                                      referred to.
+                                    maxLength: 253
+                                    minLength: 1
+                                    pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+                                    type: string
+                                  namespace:
+                                    description: |-
+                                      The namespace of the Secret resource being referred to.
+                                      Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+                                    maxLength: 63
+                                    minLength: 1
+                                    pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+                                    type: string
+                                type: object
+                            required:
+                            - token
+                            type: object
+                        required:
+                        - apikey
+                        type: object
+                      caBundle:
+                        description: |-
+                          CABundle is a base64-encoded CA certificate used to validate the BeyondTrust Workload Credentials API TLS certificate.
+                          Use this when your BeyondTrust instance uses a self-signed certificate or internal CA.
+                          If not set, the system's trusted root certificates are used.
+                        format: byte
+                        type: string
+                      caProvider:
+                        description: |-
+                          CAProvider points to a Secret or ConfigMap containing a PEM-encoded CA certificate.
+                          This is used to validate the BeyondTrust Workload Credentials API TLS certificate.
+                          Use this as an alternative to CABundle when you want to reference an existing Kubernetes resource.
+                        properties:
+                          key:
+                            description: The key where the CA certificate can be found
+                              in the Secret or ConfigMap.
+                            maxLength: 253
+                            minLength: 1
+                            pattern: ^[-._a-zA-Z0-9]+$
+                            type: string
+                          name:
+                            description: The name of the object located at the provider
+                              type.
+                            maxLength: 253
+                            minLength: 1
+                            pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+                            type: string
+                          namespace:
+                            description: |-
+                              The namespace the Provider type is in.
+                              Can only be defined when used in a ClusterSecretStore.
+                            maxLength: 63
+                            minLength: 1
+                            pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+                            type: string
+                          type:
+                            description: The type of provider to use such as "Secret",
+                              or "ConfigMap".
+                            enum:
+                            - Secret
+                            - ConfigMap
+                            type: string
+                        required:
+                        - name
+                        - type
+                        type: object
+                      folderPath:
+                        description: |-
+                          FolderPath specifies the default folder path for secret retrieval.
+                          Secrets will be fetched from this folder unless overridden in the ExternalSecret spec.
+                          Example: "production/database" or "dev/api-keys"
+                          Leave empty to retrieve secrets from the root folder.
+                          For folder organization, see: https://docs.beyondtrust.com/bt-docs/docs/secrets-api#folders
+                        type: string
+                      server:
+                        description: |-
+                          Server configures the BeyondTrust Workload Credentials server connection details.
+                          Includes the API URL and Site ID for your BeyondTrust instance.
+                          For API reference, see: https://docs.beyondtrust.com/bt-docs/docs/secrets-api
+                        properties:
+                          apiUrl:
+                            description: |-
+                              APIURL is the base URL of your BeyondTrust Workload Credentials API server.
+                              This should be the full URL to your BeyondTrust instance.
+                              Example: https://api.beyondtrust.io/siie
+                              For more information, see: https://docs.beyondtrust.com/bt-docs/docs/secrets-api#base-url
+                            type: string
+                          siteId:
+                            description: |-
+                              SiteID is your BeyondTrust Workload Credentials site identifier (UUID format).
+                              This identifier is unique to your BeyondTrust Workload Credentials instance.
+                              You can find your Site ID in the BeyondTrust Workload Credentials admin console.
+                              Example: a1b2c3d4-e5f6-4890-abcd-ef1234567890
+                              For more information, see: https://docs.beyondtrust.com/bt-docs/docs/secrets-api
+                            type: string
+                        required:
+                        - apiUrl
+                        - siteId
+                        type: object
+                    required:
+                    - auth
+                    - server
+                    type: object
                   bitwardensecretsmanager:
                     description: BitwardenSecretsManager configures this store to
                       sync secrets using BitwardenSecretsManager provider

+ 2 - 0
config/crds/bases/external-secrets.io_externalsecrets.yaml

@@ -153,6 +153,7 @@ spec:
                               description: Specify the Kind of the generator resource
                               enum:
                               - ACRAccessToken
+                              - BeyondtrustWorkloadCredentialsDynamicSecret
                               - ClusterGenerator
                               - CloudsmithAccessToken
                               - ECRAuthorizationToken
@@ -417,6 +418,7 @@ spec:
                               description: Specify the Kind of the generator resource
                               enum:
                               - ACRAccessToken
+                              - BeyondtrustWorkloadCredentialsDynamicSecret
                               - ClusterGenerator
                               - CloudsmithAccessToken
                               - ECRAuthorizationToken

+ 1 - 0
config/crds/bases/external-secrets.io_pushsecrets.yaml

@@ -351,6 +351,7 @@ spec:
                         description: Specify the Kind of the generator resource
                         enum:
                         - ACRAccessToken
+                        - BeyondtrustWorkloadCredentialsDynamicSecret
                         - ClusterGenerator
                         - CloudsmithAccessToken
                         - ECRAuthorizationToken

+ 135 - 0
config/crds/bases/external-secrets.io_secretstores.yaml

@@ -1164,6 +1164,141 @@ spec:
                     - auth
                     - server
                     type: object
+                  beyondtrustworkloadcredentials:
+                    description: BeyondtrustWorkloadCredentials configures this store
+                      to sync secrets using the BeyondTrust Workload Credentials provider.
+                    properties:
+                      auth:
+                        description: |-
+                          Auth configures how the Operator authenticates with the BeyondTrust Workload Credentials API.
+                          Currently supports API key authentication via Kubernetes secret reference.
+                          For authentication setup, see: https://docs.beyondtrust.com/bt-docs/docs/secrets-api#authentication
+                        properties:
+                          apikey:
+                            description: |-
+                              APIKey configures API token authentication for BeyondTrust Workload Credentials.
+                              The token is retrieved from a Kubernetes secret and used as a Bearer token for API requests.
+                            properties:
+                              token:
+                                description: |-
+                                  Token references the Kubernetes secret containing the BeyondTrust Workload Credentials API token.
+                                  The secret should contain the API key used to authenticate with BeyondTrust Workload Credentials.
+                                  Create an API token in your BeyondTrust Workload Credentials console and store it in a Kubernetes secret.
+                                  For details on creating API tokens, see: https://docs.beyondtrust.com/bt-docs/docs/secrets-api#authentication
+                                properties:
+                                  key:
+                                    description: |-
+                                      A key in the referenced Secret.
+                                      Some instances of this field may be defaulted, in others it may be required.
+                                    maxLength: 253
+                                    minLength: 1
+                                    pattern: ^[-._a-zA-Z0-9]+$
+                                    type: string
+                                  name:
+                                    description: The name of the Secret resource being
+                                      referred to.
+                                    maxLength: 253
+                                    minLength: 1
+                                    pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+                                    type: string
+                                  namespace:
+                                    description: |-
+                                      The namespace of the Secret resource being referred to.
+                                      Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+                                    maxLength: 63
+                                    minLength: 1
+                                    pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+                                    type: string
+                                type: object
+                            required:
+                            - token
+                            type: object
+                        required:
+                        - apikey
+                        type: object
+                      caBundle:
+                        description: |-
+                          CABundle is a base64-encoded CA certificate used to validate the BeyondTrust Workload Credentials API TLS certificate.
+                          Use this when your BeyondTrust instance uses a self-signed certificate or internal CA.
+                          If not set, the system's trusted root certificates are used.
+                        format: byte
+                        type: string
+                      caProvider:
+                        description: |-
+                          CAProvider points to a Secret or ConfigMap containing a PEM-encoded CA certificate.
+                          This is used to validate the BeyondTrust Workload Credentials API TLS certificate.
+                          Use this as an alternative to CABundle when you want to reference an existing Kubernetes resource.
+                        properties:
+                          key:
+                            description: The key where the CA certificate can be found
+                              in the Secret or ConfigMap.
+                            maxLength: 253
+                            minLength: 1
+                            pattern: ^[-._a-zA-Z0-9]+$
+                            type: string
+                          name:
+                            description: The name of the object located at the provider
+                              type.
+                            maxLength: 253
+                            minLength: 1
+                            pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+                            type: string
+                          namespace:
+                            description: |-
+                              The namespace the Provider type is in.
+                              Can only be defined when used in a ClusterSecretStore.
+                            maxLength: 63
+                            minLength: 1
+                            pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+                            type: string
+                          type:
+                            description: The type of provider to use such as "Secret",
+                              or "ConfigMap".
+                            enum:
+                            - Secret
+                            - ConfigMap
+                            type: string
+                        required:
+                        - name
+                        - type
+                        type: object
+                      folderPath:
+                        description: |-
+                          FolderPath specifies the default folder path for secret retrieval.
+                          Secrets will be fetched from this folder unless overridden in the ExternalSecret spec.
+                          Example: "production/database" or "dev/api-keys"
+                          Leave empty to retrieve secrets from the root folder.
+                          For folder organization, see: https://docs.beyondtrust.com/bt-docs/docs/secrets-api#folders
+                        type: string
+                      server:
+                        description: |-
+                          Server configures the BeyondTrust Workload Credentials server connection details.
+                          Includes the API URL and Site ID for your BeyondTrust instance.
+                          For API reference, see: https://docs.beyondtrust.com/bt-docs/docs/secrets-api
+                        properties:
+                          apiUrl:
+                            description: |-
+                              APIURL is the base URL of your BeyondTrust Workload Credentials API server.
+                              This should be the full URL to your BeyondTrust instance.
+                              Example: https://api.beyondtrust.io/siie
+                              For more information, see: https://docs.beyondtrust.com/bt-docs/docs/secrets-api#base-url
+                            type: string
+                          siteId:
+                            description: |-
+                              SiteID is your BeyondTrust Workload Credentials site identifier (UUID format).
+                              This identifier is unique to your BeyondTrust Workload Credentials instance.
+                              You can find your Site ID in the BeyondTrust Workload Credentials admin console.
+                              Example: a1b2c3d4-e5f6-4890-abcd-ef1234567890
+                              For more information, see: https://docs.beyondtrust.com/bt-docs/docs/secrets-api
+                            type: string
+                        required:
+                        - apiUrl
+                        - siteId
+                        type: object
+                    required:
+                    - auth
+                    - server
+                    type: object
                   bitwardensecretsmanager:
                     description: BitwardenSecretsManager configures this store to
                       sync secrets using BitwardenSecretsManager provider

+ 216 - 0
config/crds/bases/generators.external-secrets.io_beyondtrustworkloadcredentialsdynamicsecrets.yaml

@@ -0,0 +1,216 @@
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
+metadata:
+  annotations:
+    controller-gen.kubebuilder.io/version: v0.19.0
+  labels:
+    external-secrets.io/component: controller
+  name: beyondtrustworkloadcredentialsdynamicsecrets.generators.external-secrets.io
+spec:
+  group: generators.external-secrets.io
+  names:
+    categories:
+    - external-secrets
+    - external-secrets-generators
+    kind: BeyondtrustWorkloadCredentialsDynamicSecret
+    listKind: BeyondtrustWorkloadCredentialsDynamicSecretList
+    plural: beyondtrustworkloadcredentialsdynamicsecrets
+    singular: beyondtrustworkloadcredentialsdynamicsecret
+  scope: Namespaced
+  versions:
+  - name: v1alpha1
+    schema:
+      openAPIV3Schema:
+        description: |-
+          BeyondtrustWorkloadCredentialsDynamicSecret represents a generator that requests dynamic credentials from BeyondTrust Workload Credentials.
+          This generator calls the BeyondTrust Workload Credentials API to generate fresh, temporary credentials
+          (such as AWS STS credentials) each time an ExternalSecret is refreshed.
+          Dynamic secret definitions must be created in BeyondTrust Workload Credentials before they can be referenced.
+          For complete documentation, see: https://docs.beyondtrust.com/bt-docs/docs/secrets-api
+        properties:
+          apiVersion:
+            description: |-
+              APIVersion defines the versioned schema of this representation of an object.
+              Servers should convert recognized schemas to the latest internal value, and
+              may reject unrecognized values.
+              More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
+            type: string
+          kind:
+            description: |-
+              Kind is a string value representing the REST resource this object represents.
+              Servers may infer this from the endpoint the client submits requests to.
+              Cannot be updated.
+              In CamelCase.
+              More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
+            type: string
+          metadata:
+            type: object
+          spec:
+            description: |-
+              BeyondtrustWorkloadCredentialsDynamicSecretSpec defines the desired spec for BeyondtrustWorkloadCredentials dynamic generator.
+              This generator enables obtaining temporary, short-lived credentials from BeyondTrust Workload Credentials.
+              For more information, see: https://docs.beyondtrust.com/bt-docs/docs/secrets-api
+            properties:
+              controller:
+                description: |-
+                  Controller selects the controller that should handle this generator.
+                  Leave empty to use the default controller.
+                type: string
+              provider:
+                description: |-
+                  Provider contains the BeyondtrustWorkloadCredentials provider configuration including authentication,
+                  server connection details, and the folder path to the dynamic secret definition.
+                  The folderPath should point to a dynamic secret definition that has been created in
+                  BeyondTrust Workload Credentials (e.g., "production/aws-temp").
+                  For setup details, see: https://docs.beyondtrust.com/bt-docs/docs/secrets-api
+                properties:
+                  auth:
+                    description: |-
+                      Auth configures how the Operator authenticates with the BeyondTrust Workload Credentials API.
+                      Currently supports API key authentication via Kubernetes secret reference.
+                      For authentication setup, see: https://docs.beyondtrust.com/bt-docs/docs/secrets-api#authentication
+                    properties:
+                      apikey:
+                        description: |-
+                          APIKey configures API token authentication for BeyondTrust Workload Credentials.
+                          The token is retrieved from a Kubernetes secret and used as a Bearer token for API requests.
+                        properties:
+                          token:
+                            description: |-
+                              Token references the Kubernetes secret containing the BeyondTrust Workload Credentials API token.
+                              The secret should contain the API key used to authenticate with BeyondTrust Workload Credentials.
+                              Create an API token in your BeyondTrust Workload Credentials console and store it in a Kubernetes secret.
+                              For details on creating API tokens, see: https://docs.beyondtrust.com/bt-docs/docs/secrets-api#authentication
+                            properties:
+                              key:
+                                description: |-
+                                  A key in the referenced Secret.
+                                  Some instances of this field may be defaulted, in others it may be required.
+                                maxLength: 253
+                                minLength: 1
+                                pattern: ^[-._a-zA-Z0-9]+$
+                                type: string
+                              name:
+                                description: The name of the Secret resource being
+                                  referred to.
+                                maxLength: 253
+                                minLength: 1
+                                pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+                                type: string
+                              namespace:
+                                description: |-
+                                  The namespace of the Secret resource being referred to.
+                                  Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+                                maxLength: 63
+                                minLength: 1
+                                pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+                                type: string
+                            type: object
+                        required:
+                        - token
+                        type: object
+                    required:
+                    - apikey
+                    type: object
+                  caBundle:
+                    description: |-
+                      CABundle is a base64-encoded CA certificate used to validate the BeyondTrust Workload Credentials API TLS certificate.
+                      Use this when your BeyondTrust instance uses a self-signed certificate or internal CA.
+                      If not set, the system's trusted root certificates are used.
+                    format: byte
+                    type: string
+                  caProvider:
+                    description: |-
+                      CAProvider points to a Secret or ConfigMap containing a PEM-encoded CA certificate.
+                      This is used to validate the BeyondTrust Workload Credentials API TLS certificate.
+                      Use this as an alternative to CABundle when you want to reference an existing Kubernetes resource.
+                    properties:
+                      key:
+                        description: The key where the CA certificate can be found
+                          in the Secret or ConfigMap.
+                        maxLength: 253
+                        minLength: 1
+                        pattern: ^[-._a-zA-Z0-9]+$
+                        type: string
+                      name:
+                        description: The name of the object located at the provider
+                          type.
+                        maxLength: 253
+                        minLength: 1
+                        pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+                        type: string
+                      namespace:
+                        description: |-
+                          The namespace the Provider type is in.
+                          Can only be defined when used in a ClusterSecretStore.
+                        maxLength: 63
+                        minLength: 1
+                        pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+                        type: string
+                      type:
+                        description: The type of provider to use such as "Secret",
+                          or "ConfigMap".
+                        enum:
+                        - Secret
+                        - ConfigMap
+                        type: string
+                    required:
+                    - name
+                    - type
+                    type: object
+                  folderPath:
+                    description: |-
+                      FolderPath specifies the default folder path for secret retrieval.
+                      Secrets will be fetched from this folder unless overridden in the ExternalSecret spec.
+                      Example: "production/database" or "dev/api-keys"
+                      Leave empty to retrieve secrets from the root folder.
+                      For folder organization, see: https://docs.beyondtrust.com/bt-docs/docs/secrets-api#folders
+                    type: string
+                  server:
+                    description: |-
+                      Server configures the BeyondTrust Workload Credentials server connection details.
+                      Includes the API URL and Site ID for your BeyondTrust instance.
+                      For API reference, see: https://docs.beyondtrust.com/bt-docs/docs/secrets-api
+                    properties:
+                      apiUrl:
+                        description: |-
+                          APIURL is the base URL of your BeyondTrust Workload Credentials API server.
+                          This should be the full URL to your BeyondTrust instance.
+                          Example: https://api.beyondtrust.io/siie
+                          For more information, see: https://docs.beyondtrust.com/bt-docs/docs/secrets-api#base-url
+                        type: string
+                      siteId:
+                        description: |-
+                          SiteID is your BeyondTrust Workload Credentials site identifier (UUID format).
+                          This identifier is unique to your BeyondTrust Workload Credentials instance.
+                          You can find your Site ID in the BeyondTrust Workload Credentials admin console.
+                          Example: a1b2c3d4-e5f6-4890-abcd-ef1234567890
+                          For more information, see: https://docs.beyondtrust.com/bt-docs/docs/secrets-api
+                        type: string
+                    required:
+                    - apiUrl
+                    - siteId
+                    type: object
+                required:
+                - auth
+                - server
+                type: object
+              retrySettings:
+                description: |-
+                  RetrySettings configures exponential backoff for failed API requests.
+                  If not specified, uses the default retry settings.
+                properties:
+                  maxRetries:
+                    format: int32
+                    type: integer
+                  retryInterval:
+                    type: string
+                type: object
+            required:
+            - provider
+            type: object
+        type: object
+    served: true
+    storage: true
+    subresources:
+      status: {}

+ 166 - 0
config/crds/bases/generators.external-secrets.io_clustergenerators.yaml

@@ -214,6 +214,170 @@ spec:
                     - auth
                     - registry
                     type: object
+                  beyondtrustWorkloadCredentialsDynamicSecretSpec:
+                    description: |-
+                      BeyondtrustWorkloadCredentialsDynamicSecretSpec defines the desired spec for BeyondtrustWorkloadCredentials dynamic generator.
+                      This generator enables obtaining temporary, short-lived credentials from BeyondTrust Workload Credentials.
+                      For more information, see: https://docs.beyondtrust.com/bt-docs/docs/secrets-api
+                    properties:
+                      controller:
+                        description: |-
+                          Controller selects the controller that should handle this generator.
+                          Leave empty to use the default controller.
+                        type: string
+                      provider:
+                        description: |-
+                          Provider contains the BeyondtrustWorkloadCredentials provider configuration including authentication,
+                          server connection details, and the folder path to the dynamic secret definition.
+                          The folderPath should point to a dynamic secret definition that has been created in
+                          BeyondTrust Workload Credentials (e.g., "production/aws-temp").
+                          For setup details, see: https://docs.beyondtrust.com/bt-docs/docs/secrets-api
+                        properties:
+                          auth:
+                            description: |-
+                              Auth configures how the Operator authenticates with the BeyondTrust Workload Credentials API.
+                              Currently supports API key authentication via Kubernetes secret reference.
+                              For authentication setup, see: https://docs.beyondtrust.com/bt-docs/docs/secrets-api#authentication
+                            properties:
+                              apikey:
+                                description: |-
+                                  APIKey configures API token authentication for BeyondTrust Workload Credentials.
+                                  The token is retrieved from a Kubernetes secret and used as a Bearer token for API requests.
+                                properties:
+                                  token:
+                                    description: |-
+                                      Token references the Kubernetes secret containing the BeyondTrust Workload Credentials API token.
+                                      The secret should contain the API key used to authenticate with BeyondTrust Workload Credentials.
+                                      Create an API token in your BeyondTrust Workload Credentials console and store it in a Kubernetes secret.
+                                      For details on creating API tokens, see: https://docs.beyondtrust.com/bt-docs/docs/secrets-api#authentication
+                                    properties:
+                                      key:
+                                        description: |-
+                                          A key in the referenced Secret.
+                                          Some instances of this field may be defaulted, in others it may be required.
+                                        maxLength: 253
+                                        minLength: 1
+                                        pattern: ^[-._a-zA-Z0-9]+$
+                                        type: string
+                                      name:
+                                        description: The name of the Secret resource
+                                          being referred to.
+                                        maxLength: 253
+                                        minLength: 1
+                                        pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+                                        type: string
+                                      namespace:
+                                        description: |-
+                                          The namespace of the Secret resource being referred to.
+                                          Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+                                        maxLength: 63
+                                        minLength: 1
+                                        pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+                                        type: string
+                                    type: object
+                                required:
+                                - token
+                                type: object
+                            required:
+                            - apikey
+                            type: object
+                          caBundle:
+                            description: |-
+                              CABundle is a base64-encoded CA certificate used to validate the BeyondTrust Workload Credentials API TLS certificate.
+                              Use this when your BeyondTrust instance uses a self-signed certificate or internal CA.
+                              If not set, the system's trusted root certificates are used.
+                            format: byte
+                            type: string
+                          caProvider:
+                            description: |-
+                              CAProvider points to a Secret or ConfigMap containing a PEM-encoded CA certificate.
+                              This is used to validate the BeyondTrust Workload Credentials API TLS certificate.
+                              Use this as an alternative to CABundle when you want to reference an existing Kubernetes resource.
+                            properties:
+                              key:
+                                description: The key where the CA certificate can
+                                  be found in the Secret or ConfigMap.
+                                maxLength: 253
+                                minLength: 1
+                                pattern: ^[-._a-zA-Z0-9]+$
+                                type: string
+                              name:
+                                description: The name of the object located at the
+                                  provider type.
+                                maxLength: 253
+                                minLength: 1
+                                pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+                                type: string
+                              namespace:
+                                description: |-
+                                  The namespace the Provider type is in.
+                                  Can only be defined when used in a ClusterSecretStore.
+                                maxLength: 63
+                                minLength: 1
+                                pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+                                type: string
+                              type:
+                                description: The type of provider to use such as "Secret",
+                                  or "ConfigMap".
+                                enum:
+                                - Secret
+                                - ConfigMap
+                                type: string
+                            required:
+                            - name
+                            - type
+                            type: object
+                          folderPath:
+                            description: |-
+                              FolderPath specifies the default folder path for secret retrieval.
+                              Secrets will be fetched from this folder unless overridden in the ExternalSecret spec.
+                              Example: "production/database" or "dev/api-keys"
+                              Leave empty to retrieve secrets from the root folder.
+                              For folder organization, see: https://docs.beyondtrust.com/bt-docs/docs/secrets-api#folders
+                            type: string
+                          server:
+                            description: |-
+                              Server configures the BeyondTrust Workload Credentials server connection details.
+                              Includes the API URL and Site ID for your BeyondTrust instance.
+                              For API reference, see: https://docs.beyondtrust.com/bt-docs/docs/secrets-api
+                            properties:
+                              apiUrl:
+                                description: |-
+                                  APIURL is the base URL of your BeyondTrust Workload Credentials API server.
+                                  This should be the full URL to your BeyondTrust instance.
+                                  Example: https://api.beyondtrust.io/siie
+                                  For more information, see: https://docs.beyondtrust.com/bt-docs/docs/secrets-api#base-url
+                                type: string
+                              siteId:
+                                description: |-
+                                  SiteID is your BeyondTrust Workload Credentials site identifier (UUID format).
+                                  This identifier is unique to your BeyondTrust Workload Credentials instance.
+                                  You can find your Site ID in the BeyondTrust Workload Credentials admin console.
+                                  Example: a1b2c3d4-e5f6-4890-abcd-ef1234567890
+                                  For more information, see: https://docs.beyondtrust.com/bt-docs/docs/secrets-api
+                                type: string
+                            required:
+                            - apiUrl
+                            - siteId
+                            type: object
+                        required:
+                        - auth
+                        - server
+                        type: object
+                      retrySettings:
+                        description: |-
+                          RetrySettings configures exponential backoff for failed API requests.
+                          If not specified, uses the default retry settings.
+                        properties:
+                          maxRetries:
+                            format: int32
+                            type: integer
+                          retryInterval:
+                            type: string
+                        type: object
+                    required:
+                    - provider
+                    type: object
                   cloudsmithAccessTokenSpec:
                     description: CloudsmithAccessTokenSpec defines the configuration
                       for generating a Cloudsmith access token using OIDC authentication.
@@ -2383,6 +2547,7 @@ spec:
                 description: Kind the kind of this generator.
                 enum:
                 - ACRAccessToken
+                - BeyondtrustWorkloadCredentialsDynamicSecret
                 - CloudsmithAccessToken
                 - ECRAuthorizationToken
                 - Fake
@@ -2396,6 +2561,7 @@ spec:
                 - VaultDynamicSecret
                 - Webhook
                 - Grafana
+                - MFA
                 type: string
             required:
             - generator

+ 1 - 0
config/crds/bases/kustomization.yaml

@@ -9,6 +9,7 @@ resources:
   - external-secrets.io_pushsecrets.yaml
   - external-secrets.io_secretstores.yaml
   - generators.external-secrets.io_acraccesstokens.yaml
+  - generators.external-secrets.io_beyondtrustworkloadcredentialsdynamicsecrets.yaml
   - generators.external-secrets.io_cloudsmithaccesstokens.yaml
   - generators.external-secrets.io_clustergenerators.yaml
   - generators.external-secrets.io_ecrauthorizationtokens.yaml

+ 3 - 0
deploy/charts/external-secrets/templates/rbac.yaml

@@ -113,6 +113,7 @@ rules:
     - "webhooks"
     - "grafanas"
     - "mfas"
+    - "beyondtrustworkloadcredentialsdynamicsecrets"
     verbs:
     - "get"
     - "list"
@@ -273,6 +274,7 @@ rules:
     - "generators.external-secrets.io"
     resources:
     - "acraccesstokens"
+    - "beyondtrustworkloadcredentialsdynamicsecrets"
     - "cloudsmithaccesstokens"
     {{- if .Values.processClusterGenerator }}
     - "clustergenerators"
@@ -353,6 +355,7 @@ rules:
     - "grafanas"
     - "generatorstates"
     - "mfas"
+    - "beyondtrustworkloadcredentialsdynamicsecrets"
     - "uuids"
     verbs:
       - "create"

+ 641 - 0
deploy/crds/bundle.yaml

@@ -157,6 +157,7 @@ spec:
                                     description: Specify the Kind of the generator resource
                                     enum:
                                       - ACRAccessToken
+                                      - BeyondtrustWorkloadCredentialsDynamicSecret
                                       - ClusterGenerator
                                       - CloudsmithAccessToken
                                       - ECRAuthorizationToken
@@ -406,6 +407,7 @@ spec:
                                     description: Specify the Kind of the generator resource
                                     enum:
                                       - ACRAccessToken
+                                      - BeyondtrustWorkloadCredentialsDynamicSecret
                                       - ClusterGenerator
                                       - CloudsmithAccessToken
                                       - ECRAuthorizationToken
@@ -1976,6 +1978,7 @@ spec:
                               description: Specify the Kind of the generator resource
                               enum:
                                 - ACRAccessToken
+                                - BeyondtrustWorkloadCredentialsDynamicSecret
                                 - ClusterGenerator
                                 - CloudsmithAccessToken
                                 - ECRAuthorizationToken
@@ -3340,6 +3343,136 @@ spec:
                         - auth
                         - server
                       type: object
+                    beyondtrustworkloadcredentials:
+                      description: BeyondtrustWorkloadCredentials configures this store to sync secrets using the BeyondTrust Workload Credentials provider.
+                      properties:
+                        auth:
+                          description: |-
+                            Auth configures how the Operator authenticates with the BeyondTrust Workload Credentials API.
+                            Currently supports API key authentication via Kubernetes secret reference.
+                            For authentication setup, see: https://docs.beyondtrust.com/bt-docs/docs/secrets-api#authentication
+                          properties:
+                            apikey:
+                              description: |-
+                                APIKey configures API token authentication for BeyondTrust Workload Credentials.
+                                The token is retrieved from a Kubernetes secret and used as a Bearer token for API requests.
+                              properties:
+                                token:
+                                  description: |-
+                                    Token references the Kubernetes secret containing the BeyondTrust Workload Credentials API token.
+                                    The secret should contain the API key used to authenticate with BeyondTrust Workload Credentials.
+                                    Create an API token in your BeyondTrust Workload Credentials console and store it in a Kubernetes secret.
+                                    For details on creating API tokens, see: https://docs.beyondtrust.com/bt-docs/docs/secrets-api#authentication
+                                  properties:
+                                    key:
+                                      description: |-
+                                        A key in the referenced Secret.
+                                        Some instances of this field may be defaulted, in others it may be required.
+                                      maxLength: 253
+                                      minLength: 1
+                                      pattern: ^[-._a-zA-Z0-9]+$
+                                      type: string
+                                    name:
+                                      description: The name of the Secret resource being referred to.
+                                      maxLength: 253
+                                      minLength: 1
+                                      pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+                                      type: string
+                                    namespace:
+                                      description: |-
+                                        The namespace of the Secret resource being referred to.
+                                        Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+                                      maxLength: 63
+                                      minLength: 1
+                                      pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+                                      type: string
+                                  type: object
+                              required:
+                                - token
+                              type: object
+                          required:
+                            - apikey
+                          type: object
+                        caBundle:
+                          description: |-
+                            CABundle is a base64-encoded CA certificate used to validate the BeyondTrust Workload Credentials API TLS certificate.
+                            Use this when your BeyondTrust instance uses a self-signed certificate or internal CA.
+                            If not set, the system's trusted root certificates are used.
+                          format: byte
+                          type: string
+                        caProvider:
+                          description: |-
+                            CAProvider points to a Secret or ConfigMap containing a PEM-encoded CA certificate.
+                            This is used to validate the BeyondTrust Workload Credentials API TLS certificate.
+                            Use this as an alternative to CABundle when you want to reference an existing Kubernetes resource.
+                          properties:
+                            key:
+                              description: The key where the CA certificate can be found in the Secret or ConfigMap.
+                              maxLength: 253
+                              minLength: 1
+                              pattern: ^[-._a-zA-Z0-9]+$
+                              type: string
+                            name:
+                              description: The name of the object located at the provider type.
+                              maxLength: 253
+                              minLength: 1
+                              pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+                              type: string
+                            namespace:
+                              description: |-
+                                The namespace the Provider type is in.
+                                Can only be defined when used in a ClusterSecretStore.
+                              maxLength: 63
+                              minLength: 1
+                              pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+                              type: string
+                            type:
+                              description: The type of provider to use such as "Secret", or "ConfigMap".
+                              enum:
+                                - Secret
+                                - ConfigMap
+                              type: string
+                          required:
+                            - name
+                            - type
+                          type: object
+                        folderPath:
+                          description: |-
+                            FolderPath specifies the default folder path for secret retrieval.
+                            Secrets will be fetched from this folder unless overridden in the ExternalSecret spec.
+                            Example: "production/database" or "dev/api-keys"
+                            Leave empty to retrieve secrets from the root folder.
+                            For folder organization, see: https://docs.beyondtrust.com/bt-docs/docs/secrets-api#folders
+                          type: string
+                        server:
+                          description: |-
+                            Server configures the BeyondTrust Workload Credentials server connection details.
+                            Includes the API URL and Site ID for your BeyondTrust instance.
+                            For API reference, see: https://docs.beyondtrust.com/bt-docs/docs/secrets-api
+                          properties:
+                            apiUrl:
+                              description: |-
+                                APIURL is the base URL of your BeyondTrust Workload Credentials API server.
+                                This should be the full URL to your BeyondTrust instance.
+                                Example: https://api.beyondtrust.io/siie
+                                For more information, see: https://docs.beyondtrust.com/bt-docs/docs/secrets-api#base-url
+                              type: string
+                            siteId:
+                              description: |-
+                                SiteID is your BeyondTrust Workload Credentials site identifier (UUID format).
+                                This identifier is unique to your BeyondTrust Workload Credentials instance.
+                                You can find your Site ID in the BeyondTrust Workload Credentials admin console.
+                                Example: a1b2c3d4-e5f6-4890-abcd-ef1234567890
+                                For more information, see: https://docs.beyondtrust.com/bt-docs/docs/secrets-api
+                              type: string
+                          required:
+                            - apiUrl
+                            - siteId
+                          type: object
+                      required:
+                        - auth
+                        - server
+                      type: object
                     bitwardensecretsmanager:
                       description: BitwardenSecretsManager configures this store to sync secrets using BitwardenSecretsManager provider
                       properties:
@@ -12747,6 +12880,7 @@ spec:
                                 description: Specify the Kind of the generator resource
                                 enum:
                                   - ACRAccessToken
+                                  - BeyondtrustWorkloadCredentialsDynamicSecret
                                   - ClusterGenerator
                                   - CloudsmithAccessToken
                                   - ECRAuthorizationToken
@@ -12996,6 +13130,7 @@ spec:
                                 description: Specify the Kind of the generator resource
                                 enum:
                                   - ACRAccessToken
+                                  - BeyondtrustWorkloadCredentialsDynamicSecret
                                   - ClusterGenerator
                                   - CloudsmithAccessToken
                                   - ECRAuthorizationToken
@@ -14279,6 +14414,7 @@ spec:
                           description: Specify the Kind of the generator resource
                           enum:
                             - ACRAccessToken
+                            - BeyondtrustWorkloadCredentialsDynamicSecret
                             - ClusterGenerator
                             - CloudsmithAccessToken
                             - ECRAuthorizationToken
@@ -15671,6 +15807,136 @@ spec:
                         - auth
                         - server
                       type: object
+                    beyondtrustworkloadcredentials:
+                      description: BeyondtrustWorkloadCredentials configures this store to sync secrets using the BeyondTrust Workload Credentials provider.
+                      properties:
+                        auth:
+                          description: |-
+                            Auth configures how the Operator authenticates with the BeyondTrust Workload Credentials API.
+                            Currently supports API key authentication via Kubernetes secret reference.
+                            For authentication setup, see: https://docs.beyondtrust.com/bt-docs/docs/secrets-api#authentication
+                          properties:
+                            apikey:
+                              description: |-
+                                APIKey configures API token authentication for BeyondTrust Workload Credentials.
+                                The token is retrieved from a Kubernetes secret and used as a Bearer token for API requests.
+                              properties:
+                                token:
+                                  description: |-
+                                    Token references the Kubernetes secret containing the BeyondTrust Workload Credentials API token.
+                                    The secret should contain the API key used to authenticate with BeyondTrust Workload Credentials.
+                                    Create an API token in your BeyondTrust Workload Credentials console and store it in a Kubernetes secret.
+                                    For details on creating API tokens, see: https://docs.beyondtrust.com/bt-docs/docs/secrets-api#authentication
+                                  properties:
+                                    key:
+                                      description: |-
+                                        A key in the referenced Secret.
+                                        Some instances of this field may be defaulted, in others it may be required.
+                                      maxLength: 253
+                                      minLength: 1
+                                      pattern: ^[-._a-zA-Z0-9]+$
+                                      type: string
+                                    name:
+                                      description: The name of the Secret resource being referred to.
+                                      maxLength: 253
+                                      minLength: 1
+                                      pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+                                      type: string
+                                    namespace:
+                                      description: |-
+                                        The namespace of the Secret resource being referred to.
+                                        Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+                                      maxLength: 63
+                                      minLength: 1
+                                      pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+                                      type: string
+                                  type: object
+                              required:
+                                - token
+                              type: object
+                          required:
+                            - apikey
+                          type: object
+                        caBundle:
+                          description: |-
+                            CABundle is a base64-encoded CA certificate used to validate the BeyondTrust Workload Credentials API TLS certificate.
+                            Use this when your BeyondTrust instance uses a self-signed certificate or internal CA.
+                            If not set, the system's trusted root certificates are used.
+                          format: byte
+                          type: string
+                        caProvider:
+                          description: |-
+                            CAProvider points to a Secret or ConfigMap containing a PEM-encoded CA certificate.
+                            This is used to validate the BeyondTrust Workload Credentials API TLS certificate.
+                            Use this as an alternative to CABundle when you want to reference an existing Kubernetes resource.
+                          properties:
+                            key:
+                              description: The key where the CA certificate can be found in the Secret or ConfigMap.
+                              maxLength: 253
+                              minLength: 1
+                              pattern: ^[-._a-zA-Z0-9]+$
+                              type: string
+                            name:
+                              description: The name of the object located at the provider type.
+                              maxLength: 253
+                              minLength: 1
+                              pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+                              type: string
+                            namespace:
+                              description: |-
+                                The namespace the Provider type is in.
+                                Can only be defined when used in a ClusterSecretStore.
+                              maxLength: 63
+                              minLength: 1
+                              pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+                              type: string
+                            type:
+                              description: The type of provider to use such as "Secret", or "ConfigMap".
+                              enum:
+                                - Secret
+                                - ConfigMap
+                              type: string
+                          required:
+                            - name
+                            - type
+                          type: object
+                        folderPath:
+                          description: |-
+                            FolderPath specifies the default folder path for secret retrieval.
+                            Secrets will be fetched from this folder unless overridden in the ExternalSecret spec.
+                            Example: "production/database" or "dev/api-keys"
+                            Leave empty to retrieve secrets from the root folder.
+                            For folder organization, see: https://docs.beyondtrust.com/bt-docs/docs/secrets-api#folders
+                          type: string
+                        server:
+                          description: |-
+                            Server configures the BeyondTrust Workload Credentials server connection details.
+                            Includes the API URL and Site ID for your BeyondTrust instance.
+                            For API reference, see: https://docs.beyondtrust.com/bt-docs/docs/secrets-api
+                          properties:
+                            apiUrl:
+                              description: |-
+                                APIURL is the base URL of your BeyondTrust Workload Credentials API server.
+                                This should be the full URL to your BeyondTrust instance.
+                                Example: https://api.beyondtrust.io/siie
+                                For more information, see: https://docs.beyondtrust.com/bt-docs/docs/secrets-api#base-url
+                              type: string
+                            siteId:
+                              description: |-
+                                SiteID is your BeyondTrust Workload Credentials site identifier (UUID format).
+                                This identifier is unique to your BeyondTrust Workload Credentials instance.
+                                You can find your Site ID in the BeyondTrust Workload Credentials admin console.
+                                Example: a1b2c3d4-e5f6-4890-abcd-ef1234567890
+                                For more information, see: https://docs.beyondtrust.com/bt-docs/docs/secrets-api
+                              type: string
+                          required:
+                            - apiUrl
+                            - siteId
+                          type: object
+                      required:
+                        - auth
+                        - server
+                      type: object
                     bitwardensecretsmanager:
                       description: BitwardenSecretsManager configures this store to sync secrets using BitwardenSecretsManager provider
                       properties:
@@ -25142,6 +25408,219 @@ spec:
 ---
 apiVersion: apiextensions.k8s.io/v1
 kind: CustomResourceDefinition
+metadata:
+  annotations:
+    controller-gen.kubebuilder.io/version: v0.19.0
+  labels:
+    external-secrets.io/component: controller
+  name: beyondtrustworkloadcredentialsdynamicsecrets.generators.external-secrets.io
+spec:
+  group: generators.external-secrets.io
+  names:
+    categories:
+      - external-secrets
+      - external-secrets-generators
+    kind: BeyondtrustWorkloadCredentialsDynamicSecret
+    listKind: BeyondtrustWorkloadCredentialsDynamicSecretList
+    plural: beyondtrustworkloadcredentialsdynamicsecrets
+    singular: beyondtrustworkloadcredentialsdynamicsecret
+  scope: Namespaced
+  versions:
+    - name: v1alpha1
+      schema:
+        openAPIV3Schema:
+          description: |-
+            BeyondtrustWorkloadCredentialsDynamicSecret represents a generator that requests dynamic credentials from BeyondTrust Workload Credentials.
+            This generator calls the BeyondTrust Workload Credentials API to generate fresh, temporary credentials
+            (such as AWS STS credentials) each time an ExternalSecret is refreshed.
+            Dynamic secret definitions must be created in BeyondTrust Workload Credentials before they can be referenced.
+            For complete documentation, see: https://docs.beyondtrust.com/bt-docs/docs/secrets-api
+          properties:
+            apiVersion:
+              description: |-
+                APIVersion defines the versioned schema of this representation of an object.
+                Servers should convert recognized schemas to the latest internal value, and
+                may reject unrecognized values.
+                More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
+              type: string
+            kind:
+              description: |-
+                Kind is a string value representing the REST resource this object represents.
+                Servers may infer this from the endpoint the client submits requests to.
+                Cannot be updated.
+                In CamelCase.
+                More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
+              type: string
+            metadata:
+              type: object
+            spec:
+              description: |-
+                BeyondtrustWorkloadCredentialsDynamicSecretSpec defines the desired spec for BeyondtrustWorkloadCredentials dynamic generator.
+                This generator enables obtaining temporary, short-lived credentials from BeyondTrust Workload Credentials.
+                For more information, see: https://docs.beyondtrust.com/bt-docs/docs/secrets-api
+              properties:
+                controller:
+                  description: |-
+                    Controller selects the controller that should handle this generator.
+                    Leave empty to use the default controller.
+                  type: string
+                provider:
+                  description: |-
+                    Provider contains the BeyondtrustWorkloadCredentials provider configuration including authentication,
+                    server connection details, and the folder path to the dynamic secret definition.
+                    The folderPath should point to a dynamic secret definition that has been created in
+                    BeyondTrust Workload Credentials (e.g., "production/aws-temp").
+                    For setup details, see: https://docs.beyondtrust.com/bt-docs/docs/secrets-api
+                  properties:
+                    auth:
+                      description: |-
+                        Auth configures how the Operator authenticates with the BeyondTrust Workload Credentials API.
+                        Currently supports API key authentication via Kubernetes secret reference.
+                        For authentication setup, see: https://docs.beyondtrust.com/bt-docs/docs/secrets-api#authentication
+                      properties:
+                        apikey:
+                          description: |-
+                            APIKey configures API token authentication for BeyondTrust Workload Credentials.
+                            The token is retrieved from a Kubernetes secret and used as a Bearer token for API requests.
+                          properties:
+                            token:
+                              description: |-
+                                Token references the Kubernetes secret containing the BeyondTrust Workload Credentials API token.
+                                The secret should contain the API key used to authenticate with BeyondTrust Workload Credentials.
+                                Create an API token in your BeyondTrust Workload Credentials console and store it in a Kubernetes secret.
+                                For details on creating API tokens, see: https://docs.beyondtrust.com/bt-docs/docs/secrets-api#authentication
+                              properties:
+                                key:
+                                  description: |-
+                                    A key in the referenced Secret.
+                                    Some instances of this field may be defaulted, in others it may be required.
+                                  maxLength: 253
+                                  minLength: 1
+                                  pattern: ^[-._a-zA-Z0-9]+$
+                                  type: string
+                                name:
+                                  description: The name of the Secret resource being referred to.
+                                  maxLength: 253
+                                  minLength: 1
+                                  pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+                                  type: string
+                                namespace:
+                                  description: |-
+                                    The namespace of the Secret resource being referred to.
+                                    Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+                                  maxLength: 63
+                                  minLength: 1
+                                  pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+                                  type: string
+                              type: object
+                          required:
+                            - token
+                          type: object
+                      required:
+                        - apikey
+                      type: object
+                    caBundle:
+                      description: |-
+                        CABundle is a base64-encoded CA certificate used to validate the BeyondTrust Workload Credentials API TLS certificate.
+                        Use this when your BeyondTrust instance uses a self-signed certificate or internal CA.
+                        If not set, the system's trusted root certificates are used.
+                      format: byte
+                      type: string
+                    caProvider:
+                      description: |-
+                        CAProvider points to a Secret or ConfigMap containing a PEM-encoded CA certificate.
+                        This is used to validate the BeyondTrust Workload Credentials API TLS certificate.
+                        Use this as an alternative to CABundle when you want to reference an existing Kubernetes resource.
+                      properties:
+                        key:
+                          description: The key where the CA certificate can be found in the Secret or ConfigMap.
+                          maxLength: 253
+                          minLength: 1
+                          pattern: ^[-._a-zA-Z0-9]+$
+                          type: string
+                        name:
+                          description: The name of the object located at the provider type.
+                          maxLength: 253
+                          minLength: 1
+                          pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+                          type: string
+                        namespace:
+                          description: |-
+                            The namespace the Provider type is in.
+                            Can only be defined when used in a ClusterSecretStore.
+                          maxLength: 63
+                          minLength: 1
+                          pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+                          type: string
+                        type:
+                          description: The type of provider to use such as "Secret", or "ConfigMap".
+                          enum:
+                            - Secret
+                            - ConfigMap
+                          type: string
+                      required:
+                        - name
+                        - type
+                      type: object
+                    folderPath:
+                      description: |-
+                        FolderPath specifies the default folder path for secret retrieval.
+                        Secrets will be fetched from this folder unless overridden in the ExternalSecret spec.
+                        Example: "production/database" or "dev/api-keys"
+                        Leave empty to retrieve secrets from the root folder.
+                        For folder organization, see: https://docs.beyondtrust.com/bt-docs/docs/secrets-api#folders
+                      type: string
+                    server:
+                      description: |-
+                        Server configures the BeyondTrust Workload Credentials server connection details.
+                        Includes the API URL and Site ID for your BeyondTrust instance.
+                        For API reference, see: https://docs.beyondtrust.com/bt-docs/docs/secrets-api
+                      properties:
+                        apiUrl:
+                          description: |-
+                            APIURL is the base URL of your BeyondTrust Workload Credentials API server.
+                            This should be the full URL to your BeyondTrust instance.
+                            Example: https://api.beyondtrust.io/siie
+                            For more information, see: https://docs.beyondtrust.com/bt-docs/docs/secrets-api#base-url
+                          type: string
+                        siteId:
+                          description: |-
+                            SiteID is your BeyondTrust Workload Credentials site identifier (UUID format).
+                            This identifier is unique to your BeyondTrust Workload Credentials instance.
+                            You can find your Site ID in the BeyondTrust Workload Credentials admin console.
+                            Example: a1b2c3d4-e5f6-4890-abcd-ef1234567890
+                            For more information, see: https://docs.beyondtrust.com/bt-docs/docs/secrets-api
+                          type: string
+                      required:
+                        - apiUrl
+                        - siteId
+                      type: object
+                  required:
+                    - auth
+                    - server
+                  type: object
+                retrySettings:
+                  description: |-
+                    RetrySettings configures exponential backoff for failed API requests.
+                    If not specified, uses the default retry settings.
+                  properties:
+                    maxRetries:
+                      format: int32
+                      type: integer
+                    retryInterval:
+                      type: string
+                  type: object
+              required:
+                - provider
+              type: object
+          type: object
+      served: true
+      storage: true
+      subresources:
+        status: {}
+---
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
 metadata:
   annotations:
     controller-gen.kubebuilder.io/version: v0.19.0
@@ -25436,6 +25915,166 @@ spec:
                         - auth
                         - registry
                       type: object
+                    beyondtrustWorkloadCredentialsDynamicSecretSpec:
+                      description: |-
+                        BeyondtrustWorkloadCredentialsDynamicSecretSpec defines the desired spec for BeyondtrustWorkloadCredentials dynamic generator.
+                        This generator enables obtaining temporary, short-lived credentials from BeyondTrust Workload Credentials.
+                        For more information, see: https://docs.beyondtrust.com/bt-docs/docs/secrets-api
+                      properties:
+                        controller:
+                          description: |-
+                            Controller selects the controller that should handle this generator.
+                            Leave empty to use the default controller.
+                          type: string
+                        provider:
+                          description: |-
+                            Provider contains the BeyondtrustWorkloadCredentials provider configuration including authentication,
+                            server connection details, and the folder path to the dynamic secret definition.
+                            The folderPath should point to a dynamic secret definition that has been created in
+                            BeyondTrust Workload Credentials (e.g., "production/aws-temp").
+                            For setup details, see: https://docs.beyondtrust.com/bt-docs/docs/secrets-api
+                          properties:
+                            auth:
+                              description: |-
+                                Auth configures how the Operator authenticates with the BeyondTrust Workload Credentials API.
+                                Currently supports API key authentication via Kubernetes secret reference.
+                                For authentication setup, see: https://docs.beyondtrust.com/bt-docs/docs/secrets-api#authentication
+                              properties:
+                                apikey:
+                                  description: |-
+                                    APIKey configures API token authentication for BeyondTrust Workload Credentials.
+                                    The token is retrieved from a Kubernetes secret and used as a Bearer token for API requests.
+                                  properties:
+                                    token:
+                                      description: |-
+                                        Token references the Kubernetes secret containing the BeyondTrust Workload Credentials API token.
+                                        The secret should contain the API key used to authenticate with BeyondTrust Workload Credentials.
+                                        Create an API token in your BeyondTrust Workload Credentials console and store it in a Kubernetes secret.
+                                        For details on creating API tokens, see: https://docs.beyondtrust.com/bt-docs/docs/secrets-api#authentication
+                                      properties:
+                                        key:
+                                          description: |-
+                                            A key in the referenced Secret.
+                                            Some instances of this field may be defaulted, in others it may be required.
+                                          maxLength: 253
+                                          minLength: 1
+                                          pattern: ^[-._a-zA-Z0-9]+$
+                                          type: string
+                                        name:
+                                          description: The name of the Secret resource being referred to.
+                                          maxLength: 253
+                                          minLength: 1
+                                          pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+                                          type: string
+                                        namespace:
+                                          description: |-
+                                            The namespace of the Secret resource being referred to.
+                                            Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+                                          maxLength: 63
+                                          minLength: 1
+                                          pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+                                          type: string
+                                      type: object
+                                  required:
+                                    - token
+                                  type: object
+                              required:
+                                - apikey
+                              type: object
+                            caBundle:
+                              description: |-
+                                CABundle is a base64-encoded CA certificate used to validate the BeyondTrust Workload Credentials API TLS certificate.
+                                Use this when your BeyondTrust instance uses a self-signed certificate or internal CA.
+                                If not set, the system's trusted root certificates are used.
+                              format: byte
+                              type: string
+                            caProvider:
+                              description: |-
+                                CAProvider points to a Secret or ConfigMap containing a PEM-encoded CA certificate.
+                                This is used to validate the BeyondTrust Workload Credentials API TLS certificate.
+                                Use this as an alternative to CABundle when you want to reference an existing Kubernetes resource.
+                              properties:
+                                key:
+                                  description: The key where the CA certificate can be found in the Secret or ConfigMap.
+                                  maxLength: 253
+                                  minLength: 1
+                                  pattern: ^[-._a-zA-Z0-9]+$
+                                  type: string
+                                name:
+                                  description: The name of the object located at the provider type.
+                                  maxLength: 253
+                                  minLength: 1
+                                  pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+                                  type: string
+                                namespace:
+                                  description: |-
+                                    The namespace the Provider type is in.
+                                    Can only be defined when used in a ClusterSecretStore.
+                                  maxLength: 63
+                                  minLength: 1
+                                  pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+                                  type: string
+                                type:
+                                  description: The type of provider to use such as "Secret", or "ConfigMap".
+                                  enum:
+                                    - Secret
+                                    - ConfigMap
+                                  type: string
+                              required:
+                                - name
+                                - type
+                              type: object
+                            folderPath:
+                              description: |-
+                                FolderPath specifies the default folder path for secret retrieval.
+                                Secrets will be fetched from this folder unless overridden in the ExternalSecret spec.
+                                Example: "production/database" or "dev/api-keys"
+                                Leave empty to retrieve secrets from the root folder.
+                                For folder organization, see: https://docs.beyondtrust.com/bt-docs/docs/secrets-api#folders
+                              type: string
+                            server:
+                              description: |-
+                                Server configures the BeyondTrust Workload Credentials server connection details.
+                                Includes the API URL and Site ID for your BeyondTrust instance.
+                                For API reference, see: https://docs.beyondtrust.com/bt-docs/docs/secrets-api
+                              properties:
+                                apiUrl:
+                                  description: |-
+                                    APIURL is the base URL of your BeyondTrust Workload Credentials API server.
+                                    This should be the full URL to your BeyondTrust instance.
+                                    Example: https://api.beyondtrust.io/siie
+                                    For more information, see: https://docs.beyondtrust.com/bt-docs/docs/secrets-api#base-url
+                                  type: string
+                                siteId:
+                                  description: |-
+                                    SiteID is your BeyondTrust Workload Credentials site identifier (UUID format).
+                                    This identifier is unique to your BeyondTrust Workload Credentials instance.
+                                    You can find your Site ID in the BeyondTrust Workload Credentials admin console.
+                                    Example: a1b2c3d4-e5f6-4890-abcd-ef1234567890
+                                    For more information, see: https://docs.beyondtrust.com/bt-docs/docs/secrets-api
+                                  type: string
+                              required:
+                                - apiUrl
+                                - siteId
+                              type: object
+                          required:
+                            - auth
+                            - server
+                          type: object
+                        retrySettings:
+                          description: |-
+                            RetrySettings configures exponential backoff for failed API requests.
+                            If not specified, uses the default retry settings.
+                          properties:
+                            maxRetries:
+                              format: int32
+                              type: integer
+                            retryInterval:
+                              type: string
+                          type: object
+                      required:
+                        - provider
+                      type: object
                     cloudsmithAccessTokenSpec:
                       description: CloudsmithAccessTokenSpec defines the configuration for generating a Cloudsmith access token using OIDC authentication.
                       properties:
@@ -27475,6 +28114,7 @@ spec:
                   description: Kind the kind of this generator.
                   enum:
                     - ACRAccessToken
+                    - BeyondtrustWorkloadCredentialsDynamicSecret
                     - CloudsmithAccessToken
                     - ECRAuthorizationToken
                     - Fake
@@ -27488,6 +28128,7 @@ spec:
                     - VaultDynamicSecret
                     - Webhook
                     - Grafana
+                    - MFA
                   type: string
               required:
                 - generator

+ 293 - 0
docs/api/generator/beyondtrustworkloadcredentials.md

@@ -0,0 +1,293 @@
+The `BeyondtrustWorkloadCredentialsDynamicSecret` Generator provides an interface to BeyondTrust Workload Credentials's
+dynamic secret generation capabilities. This enables obtaining temporary, short-lived credentials.
+
+Dynamic secret definitions must be created in BeyondTrust Workload Credentials before they can be
+referenced by the generator. The generator calls the generation endpoint to produce fresh credentials
+each time it is invoked.
+
+For complete BeyondTrust Workload Credentials API documentation, see: [https://docs.beyondtrust.com/bt-docs/docs/secrets-api](https://docs.beyondtrust.com/bt-docs/docs/secrets-api)
+
+Any authentication method supported by the BeyondTrust Workload Credentials provider can be used here
+(`provider` block of the spec).
+
+## Example manifest
+
+```yaml
+{% include 'beyondtrustworkloadcredentials-dynamic-secret.yaml' %}
+```
+
+Example `ExternalSecret` that references the BeyondTrust Workload Credentials generator:
+```yaml
+{% include 'beyondtrustworkloadcredentials-dynamic-external-secret.yaml' %}
+```
+
+## Configuration
+
+### Folder Path
+
+The `folderPath` in the generator spec uses the format `{folder}/{secretName}`:
+- `folder`: The folder containing the dynamic secret definition (e.g., `eso`)
+- `secretName`: The name of the dynamic secret definition (e.g., `dynamic`)
+
+For example, if your dynamic secret is stored at path `my/dynamic` in BeyondTrust Workload Credentials:
+
+```yaml
+spec:
+  provider:
+    folderPath: "my/dynamic"
+```
+### Generated Secret Fields
+The generator returns different fields depending on the type of dynamic secret:
+#### AWS Dynamic Secrets
+```yaml
+stringData:
+  accessKeyId: ASIAIOSFODNN7EXAMPLE
+  secretAccessKey: wJal...YEKY
+  sessionToken: IQoJ...Ek8=
+  leaseId: 84038398-ec0f-417d-9a0f-02494fd7d22c
+  expiration: 2025-12-29T22:35:29Z
+```
+All fields are automatically populated in the target Kubernetes secret.
+### Credential Refresh and Expiration
+**Important:** External Secrets Operator does NOT automatically handle credential expiration/TTL from BeyondTrust Workload Credentials. The refresh is controlled solely by the `refreshInterval` specified in the ExternalSecret spec.
+
+#### Setting Refresh Interval
+
+You should set `refreshInterval` to **less than** the credential lifetime to ensure credentials are refreshed before expiration:
+
+```yaml
+apiVersion: external-secrets.io/v1
+kind: ExternalSecret
+metadata:
+  name: aws-credentials
+spec:
+  refreshInterval: 45m  # If credentials expire in 1 hour
+  target:
+    name: aws-temp-creds
+  dataFrom:
+    - sourceRef:
+        generatorRef:
+          apiVersion: generators.external-secrets.io/v1alpha1
+          kind: BeyondtrustWorkloadCredentialsDynamicSecret
+          name: beyondtrustworkloadcredentials-ds
+```
+
+#### What happens if refreshInterval > credential expiration?
+
+Credentials will expire before being refreshed. Users will see:
+- ExternalSecret status: `SecretSyncError`
+- Logs/events: Authorization errors when the application tries to use expired credentials
+- The application will fail to authenticate with the target service
+
+#### What happens if refreshInterval << credential expiration?
+
+For example, if credentials expire in 1 hour but `refreshInterval: 1m`:
+- New credentials are generated every minute
+- Old credentials remain valid until their expiration time
+- Multiple valid credential sets may exist simultaneously
+- **These credentials expire automatically at their TTL in AWS** (for AssumeRole credentials).
+
+**Recommendation:** Set `refreshInterval` to 75-80% of the credential lifetime. For example:
+- 1-hour credentials → `refreshInterval: 45m`
+- 12-hour credentials → `refreshInterval: 9h`
+- 24-hour credentials → `refreshInterval: 18h`
+
+### Generator Reusability
+
+Generators are reusable Custom Resources. You can reference the same generator from multiple ExternalSecrets:
+
+```yaml
+---
+apiVersion: external-secrets.io/v1
+kind: ExternalSecret
+metadata:
+  name: app-1-aws-creds
+spec:
+  refreshInterval: 45m
+  target:
+    name: app-1-aws-credentials
+  dataFrom:
+    - sourceRef:
+        generatorRef:
+          kind: BeyondtrustWorkloadCredentialsDynamicSecret
+          name: beyondtrustworkloadcredentials-ds
+---
+apiVersion: external-secrets.io/v1
+kind: ExternalSecret
+metadata:
+  name: app-2-aws-creds
+spec:
+  refreshInterval: 45m
+  target:
+    name: app-2-aws-credentials
+  dataFrom:
+    - sourceRef:
+        generatorRef:
+          kind: BeyondtrustWorkloadCredentialsDynamicSecret
+          name: beyondtrustworkloadcredentials-ds
+```
+
+**Important:** Each reference triggers a **new credential generation**. In the example above, `app-1` and `app-2` will receive different, independent sets of credentials.
+
+### Authentication
+
+The generator uses the same authentication mechanism as the BeyondTrust Workload Credentials provider (API key authentication):
+
+```yaml
+apiVersion: generators.external-secrets.io/v1alpha1
+kind: BeyondtrustWorkloadCredentialsDynamicSecret
+metadata:
+  name: beyondtrustworkloadcredentials-ds
+  namespace: external-secrets
+spec:
+  provider:
+    auth:
+      apikey:
+        token:
+          name: api-token
+          key: token
+```
+
+Create the API token secret:
+```bash
+kubectl create secret generic api-token \
+  --from-literal=token=<YOUR_API_TOKEN> \
+  -n external-secrets
+```
+
+### Certificate Trust
+
+If using self-signed certificates, configure trust using `caProvider`:
+
+```yaml
+spec:
+  provider:
+    # ... other config ...
+    caProvider:
+      type: Secret
+      name: my-ca-bundle
+      key: ca.crt
+```
+
+Create the CA bundle secret:
+```bash
+kubectl create secret generic my-ca-bundle \
+  --from-file=ca.crt="/path/to/ca.crt" \
+  -n external-secrets
+```
+
+### Server Configuration
+
+Configure the BeyondTrust Workload Credentials API endpoint:
+
+```yaml
+spec:
+  provider:
+    server:
+      apiUrl: "https://api.beyondtrust.io/site"
+      siteId: "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
+```
+
+- `apiUrl`: The base URL of your BeyondTrust Workload Credentials API
+- `siteId`: Your BeyondTrust site identifier (UUID format)
+
+### Complete Example
+
+Here's a complete example for AWS dynamic credentials:
+
+1. Create the API token and CA bundle secrets:
+```bash
+kubectl create secret generic api-token \
+  --from-literal=token=<YOUR_API_TOKEN> \
+  -n external-secrets
+kubectl create secret generic my-ca-bundle \
+  --from-file=ca.crt="/path/to/ca.crt" \
+  -n external-secrets
+```
+
+2. Create the generator:
+```yaml
+apiVersion: generators.external-secrets.io/v1alpha1
+kind: BeyondtrustWorkloadCredentialsDynamicSecret
+metadata:
+  name: aws-dynamic-generator
+  namespace: external-secrets
+spec:
+  provider:
+    auth:
+      apikey:
+        token:
+          name: api-token
+          key: token
+    server:
+      apiUrl: "https://api.beyondtrust.io/site"
+      siteId: "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
+    folderPath: "production/aws-temp"
+```
+
+3. Create an ExternalSecret that uses the generator:
+```yaml
+apiVersion: external-secrets.io/v1
+kind: ExternalSecret
+metadata:
+  name: app-aws-credentials
+  namespace: external-secrets
+spec:
+  refreshInterval: 45m  # Refresh before 1-hour expiration
+  target:
+    name: aws-temp-credentials
+    creationPolicy: Owner
+  dataFrom:
+    - sourceRef:
+        generatorRef:
+          apiVersion: generators.external-secrets.io/v1alpha1
+          kind: BeyondtrustWorkloadCredentialsDynamicSecret
+          name: aws-dynamic-generator
+```
+
+4. The resulting Kubernetes secret will contain:
+```yaml
+apiVersion: v1
+kind: Secret
+metadata:
+  name: aws-temp-credentials
+  namespace: external-secrets
+data:
+  accessKeyId: QVNJ...R04=
+  secretAccessKey: Z3dk...WFk=
+  sessionToken: SVFv...Ek8=
+  leaseId: NTdk...Nm1j
+  expiration: MjAy...OVo=
+```
+
+### Troubleshooting
+
+#### Empty Credential Fields
+
+If the generated secret has empty values:
+1. Verify the dynamic secret exists in BeyondTrust Workload Credentials at the specified path
+2. Check the API token has permissions to generate credentials
+3. Verify the `folderPath` format is correct (`folder/secretName`)
+4. Check controller logs: `kubectl logs -l app.kubernetes.io/name=external-secrets -n external-secrets`
+
+#### Authentication Errors
+
+If you see 403/401 errors:
+1. Verify the API token is valid and not expired
+2. Check the token has `generate` permissions for the dynamic secret
+3. Ensure the `caProvider` or `caBundle` is configured correctly if using self-signed certificates
+
+#### Timeout Errors
+
+If credential generation times out:
+1. Check network connectivity from the cluster to BeyondTrust Workload Credentials API
+2. Verify the API endpoint is responsive
+3. Check if there are firewall rules blocking the connection
+
+#### Credential Expiration Issues
+
+If applications report authentication failures:
+1. Check if `refreshInterval` is greater than credential lifetime
+2. Review the `expiration` field in the secret to see when credentials expire
+3. Adjust `refreshInterval` to be 75-80% of the credential lifetime
+4. Check ExternalSecret status: `kubectl describe externalsecret <name> -n <namespace>`

+ 412 - 0
docs/api/spec.md

@@ -1581,6 +1581,226 @@ int
 </tr>
 </tbody>
 </table>
+<h3 id="external-secrets.io/v1.BeyondtrustWorkloadCredentialsAuth">BeyondtrustWorkloadCredentialsAuth
+</h3>
+<p>
+(<em>Appears on:</em>
+<a href="#external-secrets.io/v1.BeyondtrustWorkloadCredentialsProvider">BeyondtrustWorkloadCredentialsProvider</a>)
+</p>
+<p>
+<p>BeyondtrustWorkloadCredentialsAuth defines the authentication method for the BeyondTrust Workload Credentials provider.
+Currently supports API key authentication via Kubernetes secret reference.
+For authentication documentation, see: <a href="https://docs.beyondtrust.com/bt-docs/docs/secrets-api#authentication">https://docs.beyondtrust.com/bt-docs/docs/secrets-api#authentication</a></p>
+</p>
+<table>
+<thead>
+<tr>
+<th>Field</th>
+<th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td>
+<code>apikey</code></br>
+<em>
+<a href="#external-secrets.io/v1.BeyondtrustWorkloadCredentialsAuthSecretRef">
+BeyondtrustWorkloadCredentialsAuthSecretRef
+</a>
+</em>
+</td>
+<td>
+<p>APIKey configures API token authentication for BeyondTrust Workload Credentials.
+The token is retrieved from a Kubernetes secret and used as a Bearer token for API requests.</p>
+</td>
+</tr>
+</tbody>
+</table>
+<h3 id="external-secrets.io/v1.BeyondtrustWorkloadCredentialsAuthSecretRef">BeyondtrustWorkloadCredentialsAuthSecretRef
+</h3>
+<p>
+(<em>Appears on:</em>
+<a href="#external-secrets.io/v1.BeyondtrustWorkloadCredentialsAuth">BeyondtrustWorkloadCredentialsAuth</a>)
+</p>
+<p>
+<p>BeyondtrustWorkloadCredentialsAuthSecretRef defines a reference to a secret containing credentials for the BeyondTrust Workload Credentials provider.
+The nested structure supports multiple authentication methods (currently only API token is supported).
+For more information on authentication, see: <a href="https://docs.beyondtrust.com/bt-docs/docs/secrets-api#authentication">https://docs.beyondtrust.com/bt-docs/docs/secrets-api#authentication</a></p>
+</p>
+<table>
+<thead>
+<tr>
+<th>Field</th>
+<th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td>
+<code>token</code></br>
+<em>
+<a href="https://pkg.go.dev/github.com/external-secrets/external-secrets/apis/meta/v1#SecretKeySelector">
+External Secrets meta/v1.SecretKeySelector
+</a>
+</em>
+</td>
+<td>
+<p>Token references the Kubernetes secret containing the BeyondTrust Workload Credentials API token.
+The secret should contain the API key used to authenticate with BeyondTrust Workload Credentials.
+Create an API token in your BeyondTrust Workload Credentials console and store it in a Kubernetes secret.
+For details on creating API tokens, see: <a href="https://docs.beyondtrust.com/bt-docs/docs/secrets-api#authentication">https://docs.beyondtrust.com/bt-docs/docs/secrets-api#authentication</a></p>
+</td>
+</tr>
+</tbody>
+</table>
+<h3 id="external-secrets.io/v1.BeyondtrustWorkloadCredentialsProvider">BeyondtrustWorkloadCredentialsProvider
+</h3>
+<p>
+(<em>Appears on:</em>
+<a href="#external-secrets.io/v1.SecretStoreProvider">SecretStoreProvider</a>, 
+<a href="#generators.external-secrets.io/v1alpha1.BeyondtrustWorkloadCredentialsDynamicSecretSpec">BeyondtrustWorkloadCredentialsDynamicSecretSpec</a>)
+</p>
+<p>
+<p>BeyondtrustWorkloadCredentialsProvider configures a store to sync secrets using the BeyondTrust Workload Credentials provider.
+BeyondTrust Workload Credentials provides secure storage for static secrets and dynamic credential generation.
+This provider supports reading secrets and generating dynamic credentials (e.g., temporary AWS credentials).
+For complete documentation, see: <a href="https://docs.beyondtrust.com/bt-docs/docs/secrets-api">https://docs.beyondtrust.com/bt-docs/docs/secrets-api</a></p>
+</p>
+<table>
+<thead>
+<tr>
+<th>Field</th>
+<th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td>
+<code>auth</code></br>
+<em>
+<a href="#external-secrets.io/v1.BeyondtrustWorkloadCredentialsAuth">
+BeyondtrustWorkloadCredentialsAuth
+</a>
+</em>
+</td>
+<td>
+<p>Auth configures how the Operator authenticates with the BeyondTrust Workload Credentials API.
+Currently supports API key authentication via Kubernetes secret reference.
+For authentication setup, see: <a href="https://docs.beyondtrust.com/bt-docs/docs/secrets-api#authentication">https://docs.beyondtrust.com/bt-docs/docs/secrets-api#authentication</a></p>
+</td>
+</tr>
+<tr>
+<td>
+<code>server</code></br>
+<em>
+<a href="#external-secrets.io/v1.BeyondtrustWorkloadCredentialsServer">
+BeyondtrustWorkloadCredentialsServer
+</a>
+</em>
+</td>
+<td>
+<p>Server configures the BeyondTrust Workload Credentials server connection details.
+Includes the API URL and Site ID for your BeyondTrust instance.
+For API reference, see: <a href="https://docs.beyondtrust.com/bt-docs/docs/secrets-api">https://docs.beyondtrust.com/bt-docs/docs/secrets-api</a></p>
+</td>
+</tr>
+<tr>
+<td>
+<code>folderPath</code></br>
+<em>
+string
+</em>
+</td>
+<td>
+<em>(Optional)</em>
+<p>FolderPath specifies the default folder path for secret retrieval.
+Secrets will be fetched from this folder unless overridden in the ExternalSecret spec.
+Example: &ldquo;production/database&rdquo; or &ldquo;dev/api-keys&rdquo;
+Leave empty to retrieve secrets from the root folder.
+For folder organization, see: <a href="https://docs.beyondtrust.com/bt-docs/docs/secrets-api#folders">https://docs.beyondtrust.com/bt-docs/docs/secrets-api#folders</a></p>
+</td>
+</tr>
+<tr>
+<td>
+<code>caBundle</code></br>
+<em>
+[]byte
+</em>
+</td>
+<td>
+<em>(Optional)</em>
+<p>CABundle is a base64-encoded CA certificate used to validate the BeyondTrust Workload Credentials API TLS certificate.
+Use this when your BeyondTrust instance uses a self-signed certificate or internal CA.
+If not set, the system&rsquo;s trusted root certificates are used.</p>
+</td>
+</tr>
+<tr>
+<td>
+<code>caProvider</code></br>
+<em>
+<a href="#external-secrets.io/v1.CAProvider">
+CAProvider
+</a>
+</em>
+</td>
+<td>
+<em>(Optional)</em>
+<p>CAProvider points to a Secret or ConfigMap containing a PEM-encoded CA certificate.
+This is used to validate the BeyondTrust Workload Credentials API TLS certificate.
+Use this as an alternative to CABundle when you want to reference an existing Kubernetes resource.</p>
+</td>
+</tr>
+</tbody>
+</table>
+<h3 id="external-secrets.io/v1.BeyondtrustWorkloadCredentialsServer">BeyondtrustWorkloadCredentialsServer
+</h3>
+<p>
+(<em>Appears on:</em>
+<a href="#external-secrets.io/v1.BeyondtrustWorkloadCredentialsProvider">BeyondtrustWorkloadCredentialsProvider</a>)
+</p>
+<p>
+<p>BeyondtrustWorkloadCredentialsServer defines connection configuration for BeyondTrust Workload Credentials.
+For API reference documentation, see: <a href="https://docs.beyondtrust.com/bt-docs/docs/secrets-api">https://docs.beyondtrust.com/bt-docs/docs/secrets-api</a></p>
+</p>
+<table>
+<thead>
+<tr>
+<th>Field</th>
+<th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td>
+<code>apiUrl</code></br>
+<em>
+string
+</em>
+</td>
+<td>
+<p>APIURL is the base URL of your BeyondTrust Workload Credentials API server.
+This should be the full URL to your BeyondTrust instance.
+Example: <a href="https://api.beyondtrust.io/siie">https://api.beyondtrust.io/siie</a>
+For more information, see: <a href="https://docs.beyondtrust.com/bt-docs/docs/secrets-api#base-url">https://docs.beyondtrust.com/bt-docs/docs/secrets-api#base-url</a></p>
+</td>
+</tr>
+<tr>
+<td>
+<code>siteId</code></br>
+<em>
+string
+</em>
+</td>
+<td>
+<p>SiteID is your BeyondTrust Workload Credentials site identifier (UUID format).
+This identifier is unique to your BeyondTrust Workload Credentials instance.
+You can find your Site ID in the BeyondTrust Workload Credentials admin console.
+Example: a1b2c3d4-e5f6-4890-abcd-ef1234567890
+For more information, see: <a href="https://docs.beyondtrust.com/bt-docs/docs/secrets-api">https://docs.beyondtrust.com/bt-docs/docs/secrets-api</a></p>
+</td>
+</tr>
+</tbody>
+</table>
 <h3 id="external-secrets.io/v1.BitwardenSecretsManagerAuth">BitwardenSecretsManagerAuth
 </h3>
 <p>
@@ -1800,6 +2020,7 @@ string
 <p>
 (<em>Appears on:</em>
 <a href="#external-secrets.io/v1.AkeylessProvider">AkeylessProvider</a>, 
+<a href="#external-secrets.io/v1.BeyondtrustWorkloadCredentialsProvider">BeyondtrustWorkloadCredentialsProvider</a>, 
 <a href="#external-secrets.io/v1.BitwardenSecretsManagerProvider">BitwardenSecretsManagerProvider</a>, 
 <a href="#external-secrets.io/v1.ConjurProvider">ConjurProvider</a>, 
 <a href="#external-secrets.io/v1.GitlabProvider">GitlabProvider</a>, 
@@ -10245,6 +10466,20 @@ BeyondtrustProvider
 </tr>
 <tr>
 <td>
+<code>beyondtrustworkloadcredentials</code></br>
+<em>
+<a href="#external-secrets.io/v1.BeyondtrustWorkloadCredentialsProvider">
+BeyondtrustWorkloadCredentialsProvider
+</a>
+</em>
+</td>
+<td>
+<em>(Optional)</em>
+<p>BeyondtrustWorkloadCredentials configures this store to sync secrets using the BeyondTrust Workload Credentials provider.</p>
+</td>
+</tr>
+<tr>
+<td>
 <code>cloudrusm</code></br>
 <em>
 <a href="#external-secrets.io/v1.CloudruSMProvider">
@@ -10379,6 +10614,7 @@ Defaults to <code>SecretStore</code></p>
 <p>
 (<em>Appears on:</em>
 <a href="#external-secrets.io/v1.SecretStoreSpec">SecretStoreSpec</a>, 
+<a href="#generators.external-secrets.io/v1alpha1.BeyondtrustWorkloadCredentialsDynamicSecretSpec">BeyondtrustWorkloadCredentialsDynamicSecretSpec</a>, 
 <a href="#generators.external-secrets.io/v1alpha1.VaultDynamicSecretSpec">VaultDynamicSecretSpec</a>)
 </p>
 <p>
@@ -25699,6 +25935,167 @@ that should be used when authenticating with WorkloadIdentity.</p>
 </tr>
 </tbody>
 </table>
+<h3 id="generators.external-secrets.io/v1alpha1.BeyondtrustWorkloadCredentialsDynamicSecret">BeyondtrustWorkloadCredentialsDynamicSecret
+</h3>
+<p>
+<p>BeyondtrustWorkloadCredentialsDynamicSecret represents a generator that requests dynamic credentials from BeyondTrust Workload Credentials.
+This generator calls the BeyondTrust Workload Credentials API to generate fresh, temporary credentials
+(such as AWS STS credentials) each time an ExternalSecret is refreshed.
+Dynamic secret definitions must be created in BeyondTrust Workload Credentials before they can be referenced.
+For complete documentation, see: <a href="https://docs.beyondtrust.com/bt-docs/docs/secrets-api">https://docs.beyondtrust.com/bt-docs/docs/secrets-api</a></p>
+</p>
+<table>
+<thead>
+<tr>
+<th>Field</th>
+<th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td>
+<code>metadata</code></br>
+<em>
+<a href="https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.25/#objectmeta-v1-meta">
+Kubernetes meta/v1.ObjectMeta
+</a>
+</em>
+</td>
+<td>
+Refer to the Kubernetes API documentation for the fields of the
+<code>metadata</code> field.
+</td>
+</tr>
+<tr>
+<td>
+<code>spec</code></br>
+<em>
+<a href="#generators.external-secrets.io/v1alpha1.BeyondtrustWorkloadCredentialsDynamicSecretSpec">
+BeyondtrustWorkloadCredentialsDynamicSecretSpec
+</a>
+</em>
+</td>
+<td>
+<br/>
+<br/>
+<table>
+<tr>
+<td>
+<code>controller</code></br>
+<em>
+string
+</em>
+</td>
+<td>
+<em>(Optional)</em>
+<p>Controller selects the controller that should handle this generator.
+Leave empty to use the default controller.</p>
+</td>
+</tr>
+<tr>
+<td>
+<code>provider</code></br>
+<em>
+<a href="#external-secrets.io/v1.BeyondtrustWorkloadCredentialsProvider">
+BeyondtrustWorkloadCredentialsProvider
+</a>
+</em>
+</td>
+<td>
+<p>Provider contains the BeyondtrustWorkloadCredentials provider configuration including authentication,
+server connection details, and the folder path to the dynamic secret definition.
+The folderPath should point to a dynamic secret definition that has been created in
+BeyondTrust Workload Credentials (e.g., &ldquo;production/aws-temp&rdquo;).
+For setup details, see: <a href="https://docs.beyondtrust.com/bt-docs/docs/secrets-api">https://docs.beyondtrust.com/bt-docs/docs/secrets-api</a></p>
+</td>
+</tr>
+<tr>
+<td>
+<code>retrySettings</code></br>
+<em>
+<a href="#external-secrets.io/v1.SecretStoreRetrySettings">
+SecretStoreRetrySettings
+</a>
+</em>
+</td>
+<td>
+<em>(Optional)</em>
+<p>RetrySettings configures exponential backoff for failed API requests.
+If not specified, uses the default retry settings.</p>
+</td>
+</tr>
+</table>
+</td>
+</tr>
+</tbody>
+</table>
+<h3 id="generators.external-secrets.io/v1alpha1.BeyondtrustWorkloadCredentialsDynamicSecretSpec">BeyondtrustWorkloadCredentialsDynamicSecretSpec
+</h3>
+<p>
+(<em>Appears on:</em>
+<a href="#generators.external-secrets.io/v1alpha1.BeyondtrustWorkloadCredentialsDynamicSecret">BeyondtrustWorkloadCredentialsDynamicSecret</a>, 
+<a href="#generators.external-secrets.io/v1alpha1.GeneratorSpec">GeneratorSpec</a>)
+</p>
+<p>
+<p>BeyondtrustWorkloadCredentialsDynamicSecretSpec defines the desired spec for BeyondtrustWorkloadCredentials dynamic generator.
+This generator enables obtaining temporary, short-lived credentials from BeyondTrust Workload Credentials.
+For more information, see: <a href="https://docs.beyondtrust.com/bt-docs/docs/secrets-api">https://docs.beyondtrust.com/bt-docs/docs/secrets-api</a></p>
+</p>
+<table>
+<thead>
+<tr>
+<th>Field</th>
+<th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td>
+<code>controller</code></br>
+<em>
+string
+</em>
+</td>
+<td>
+<em>(Optional)</em>
+<p>Controller selects the controller that should handle this generator.
+Leave empty to use the default controller.</p>
+</td>
+</tr>
+<tr>
+<td>
+<code>provider</code></br>
+<em>
+<a href="#external-secrets.io/v1.BeyondtrustWorkloadCredentialsProvider">
+BeyondtrustWorkloadCredentialsProvider
+</a>
+</em>
+</td>
+<td>
+<p>Provider contains the BeyondtrustWorkloadCredentials provider configuration including authentication,
+server connection details, and the folder path to the dynamic secret definition.
+The folderPath should point to a dynamic secret definition that has been created in
+BeyondTrust Workload Credentials (e.g., &ldquo;production/aws-temp&rdquo;).
+For setup details, see: <a href="https://docs.beyondtrust.com/bt-docs/docs/secrets-api">https://docs.beyondtrust.com/bt-docs/docs/secrets-api</a></p>
+</td>
+</tr>
+<tr>
+<td>
+<code>retrySettings</code></br>
+<em>
+<a href="#external-secrets.io/v1.SecretStoreRetrySettings">
+SecretStoreRetrySettings
+</a>
+</em>
+</td>
+<td>
+<em>(Optional)</em>
+<p>RetrySettings configures exponential backoff for failed API requests.
+If not specified, uses the default retry settings.</p>
+</td>
+</tr>
+</tbody>
+</table>
 <h3 id="generators.external-secrets.io/v1alpha1.CloudsmithAccessToken">CloudsmithAccessToken
 </h3>
 <p>
@@ -26588,6 +26985,9 @@ string
 <tbody><tr><td><p>&#34;ACRAccessToken&#34;</p></td>
 <td><p>GeneratorKindACRAccessToken represents an Azure Container Registry access token generator.</p>
 </td>
+</tr><tr><td><p>&#34;BeyondtrustWorkloadCredentialsDynamicSecret&#34;</p></td>
+<td><p>GeneratorKindBeyondtrustWorkloadCredentialsDynamicSecret represents a BeyondTrust Workload Credentials dynamic secret generator.</p>
+</td>
 </tr><tr><td><p>&#34;CloudsmithAccessToken&#34;</p></td>
 <td><p>GeneratorKindCloudsmithAccessToken represents a Cloudsmith access token generator.</p>
 </td>
@@ -26668,6 +27068,18 @@ ACRAccessTokenSpec
 </tr>
 <tr>
 <td>
+<code>beyondtrustWorkloadCredentialsDynamicSecretSpec</code></br>
+<em>
+<a href="#generators.external-secrets.io/v1alpha1.BeyondtrustWorkloadCredentialsDynamicSecretSpec">
+BeyondtrustWorkloadCredentialsDynamicSecretSpec
+</a>
+</em>
+</td>
+<td>
+</td>
+</tr>
+<tr>
+<td>
 <code>cloudsmithAccessTokenSpec</code></br>
 <em>
 <a href="#generators.external-secrets.io/v1alpha1.CloudsmithAccessTokenSpec">

+ 384 - 0
docs/provider/beyondtrustworkloadcredentials.md

@@ -0,0 +1,384 @@
+## BeyondTrust Workload Credentials
+
+External Secrets Operator integrates with [BeyondTrust Workload Credentials](https://docs.beyondtrust.com/bt-docs/docs/secrets-api) for secret management.
+
+The provider supports static key-value secrets stored in folders. For dynamic secret generation (e.g., temporary AWS credentials), refer to the [BeyondTrust Workload Credentials Generator](../api/generator/beyondtrustworkloadcredentials.md).
+
+For complete BeyondTrust Workload Credentials API documentation, see: [https://docs.beyondtrust.com/bt-docs/docs/secrets-api](https://docs.beyondtrust.com/bt-docs/docs/secrets-api)
+
+### Example
+
+First, create a SecretStore with a BeyondTrust Workload Credentials backend. You'll need an API token and the server configuration:
+
+```yaml
+{% include 'beyondtrustworkloadcredentials-secret-store.yaml' %}
+```
+
+Create the API token secret:
+```bash
+kubectl create secret generic api-token \
+  --from-literal=token=<YOUR_API_TOKEN> \
+  -n external-secrets
+```
+
+If using self-signed certificates, create a CA bundle secret:
+```bash
+kubectl create secret generic my-ca-bundle \
+  --from-file=ca.crt="/path/to/root.crt" \
+  -n external-secrets
+```
+
+Now create an ExternalSecret that uses the above SecretStore:
+
+```yaml
+{% include 'beyondtrustworkloadcredentials-external-secret.yaml' %}
+```
+
+This will automatically create a Kubernetes Secret with the synced data.
+
+### Fetching Secret Properties
+
+#### Single Property Retrieval
+
+You can fetch a specific property from a secret by specifying the `property` field:
+
+```yaml
+apiVersion: external-secrets.io/v1
+kind: ExternalSecret
+metadata:
+  name: postgres-credentials
+  namespace: external-secrets
+spec:
+  refreshInterval: 1m
+  secretStoreRef:
+    name: beyondtrustworkloadcredentials-ss
+    kind: SecretStore
+  target:
+    name: postgres-creds
+  data:
+    - secretKey: username
+      remoteRef:
+        key: postgresCreds
+        property: username
+    - secretKey: password
+      remoteRef:
+        key: postgresCreds
+        property: password
+```
+This creates a secret with individual keys for `username` and `password`.
+
+#### Fetching All Properties as JSON
+
+If you omit the `property` field, you'll get all key-value pairs as a single JSON string:
+
+```yaml
+apiVersion: external-secrets.io/v1
+kind: ExternalSecret
+metadata:
+  name: postgres-credentials-json
+  namespace: external-secrets
+spec:
+  refreshInterval: 1m
+  secretStoreRef:
+    name: beyondtrustworkloadcredentials-ss
+    kind: SecretStore
+  target:
+    name: postgres-creds-json
+  data:
+    - secretKey: credentials
+      remoteRef:
+        key: postgresCreds
+```
+
+This returns: `{"username":"user","password":"pass"}` as the value of `credentials`.
+
+#### Extracting All Properties as Separate Keys
+
+To sync all properties of a secret as individual keys in the target Kubernetes secret, use `dataFrom` with `extract`:
+
+```yaml
+apiVersion: external-secrets.io/v1
+kind: ExternalSecret
+metadata:
+  name: postgres-credentials-extracted
+  namespace: external-secrets
+spec:
+  refreshInterval: 1m
+  secretStoreRef:
+    name: beyondtrustworkloadcredentials-ss
+    kind: SecretStore
+  target:
+    name: postgres-creds-extracted
+  dataFrom:
+    - extract:
+        key: postgresCreds
+```
+
+This creates a secret with:
+```yaml
+data:
+  username: dXNlcg==     # base64("user")
+  password: cGFzcw==     # base64("pass")
+```
+
+### Getting Multiple Secrets
+
+You can extract multiple secrets from a folder by using `dataFrom.find`.
+
+Given a folder `eso/static` with these secrets:
+- `anotherSecret`: `{"someKey": "value1"}`
+- `mySecret`: `{"myKey": "value2", "someKey": "value3"}`
+- `postgresCreds`: `{"username": "user", "password": "pass"}`
+
+#### Fetch All Secrets in Folder
+
+```yaml
+apiVersion: external-secrets.io/v1
+kind: ExternalSecret
+metadata:
+  name: all-secrets
+  namespace: external-secrets
+spec:
+  refreshInterval: 1m
+  secretStoreRef:
+    name: beyondtrustworkloadcredentials-ss
+    kind: SecretStore
+  target:
+    name: all-folder-secrets
+  dataFrom:
+    - find:
+        name:
+          regexp: ".*"
+```
+
+This merges all key-value pairs from all secrets in the folder into a single Kubernetes secret.
+
+#### Regex Pattern Filtering
+
+To sync only secrets matching a specific pattern:
+
+```yaml
+apiVersion: external-secrets.io/v1
+kind: ExternalSecret
+metadata:
+  name: filtered-secrets
+  namespace: external-secrets
+spec:
+  refreshInterval: 1m
+  secretStoreRef:
+    name: beyondtrustworkloadcredentials-ss
+    kind: SecretStore
+  target:
+    name: filtered-folder-secrets
+  dataFrom:
+    - find:
+        name:
+          regexp: "Secret$"
+```
+
+This will only sync secrets whose names end with "Secret" (e.g., `anotherSecret`, `mySecret`).
+
+#### Specifying a Different Folder
+
+By default, `find` uses the `folderPath` from the SecretStore. To search a different folder, use the `path` field:
+
+```yaml
+apiVersion: external-secrets.io/v1
+kind: ExternalSecret
+metadata:
+  name: subfolder-secrets
+  namespace: external-secrets
+spec:
+  refreshInterval: 1m
+  secretStoreRef:
+    name: beyondtrustworkloadcredentials-ss
+    kind: SecretStore
+  target:
+    name: subfolder-data
+  dataFrom:
+    - find:
+        path: "eso/production"  # Override folder path
+        name:
+          regexp: ".*"           # Get all secrets in this folder
+```
+
+This will list all secrets in the `eso/production` folder, regardless of the `folderPath` configured in the SecretStore.
+
+**Note:** The `path` field specifies a folder path, not a path to a specific secret. To fetch a single secret, use `data` with `extract` or individual `remoteRef` entries.
+
+### Handling Source Secret Deletion
+
+By default, when a source secret is deleted from BeyondTrust Workload Credentials, the managed Kubernetes secret is retained. You can change this behavior using `deletionPolicy`:
+
+```yaml
+apiVersion: external-secrets.io/v1
+kind: ExternalSecret
+metadata:
+  name: secret-with-deletion
+  namespace: external-secrets
+spec:
+  refreshInterval: 1m
+  secretStoreRef:
+    name: beyondtrustworkloadcredentials-ss
+    kind: SecretStore
+  target:
+    name: managed-secret
+    deletionPolicy: Delete  # Delete the Kubernetes secret when source is removed
+  data:
+    - secretKey: myKey
+      remoteRef:
+        key: mySecret
+```
+
+Valid values:
+- `Retain` (default): Keep the Kubernetes secret even if the source is deleted
+- `Delete`: Remove the Kubernetes secret when the source is deleted
+
+### Authentication
+
+BeyondTrust Workload Credentials uses API key authentication. The API key is stored in a Kubernetes Secret and referenced in the SecretStore:
+
+```yaml
+apiVersion: external-secrets.io/v1
+kind: SecretStore
+metadata:
+  name: beyondtrustworkloadcredentials-ss
+  namespace: external-secrets
+spec:
+  provider:
+    beyondtrustworkloadcredentials:
+      auth:
+        apikey:
+          token:
+            name: api-token  # Name of the Kubernetes Secret
+            key: token            # Key within the Secret
+      server:
+        apiUrl: "https://api.beyondtrust.io/site"
+        siteId: "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
+      folderPath: "eso/static"
+```
+
+```yaml
+auth:
+  apikey:
+    token:
+      name: api-token
+      key: token
+```
+
+### Server Configuration
+
+The server configuration consists of:
+- `apiUrl`: The base URL of your BeyondTrust Workload Credentials API
+- `siteId`: Your BeyondTrust site identifier (UUID format)
+
+The provider automatically constructs the full API endpoint as: `{apiUrl}/{siteId}/secrets`
+
+### Certificate Trust
+
+BeyondTrust Workload Credentials typically uses certificates signed by public CAs, requiring no additional configuration.
+
+If using self-signed certificates, configure trust using either `caBundle` or `caProvider`:
+
+#### Using caProvider (Recommended)
+
+```yaml
+spec:
+  provider:
+    beyondtrustworkloadcredentials:
+      # ... other config ...
+      caProvider:
+        type: Secret
+        name: my-ca-bundle
+        key: ca.crt
+        namespace: external-secrets  # Required for ClusterSecretStore
+```
+
+First create the CA bundle secret:
+```bash
+kubectl create secret generic my-ca-bundle \
+  --from-file=ca.crt="/path/to/ca.crt" \
+  -n external-secrets
+```
+
+#### Using caBundle
+
+Alternatively, embed the base64-encoded PEM certificate directly:
+
+```yaml
+spec:
+  provider:
+    beyondtrustworkloadcredentials:
+      # ... other config ...
+      server:
+        apiUrl: "https://api.beyondtrust.io/site"
+        siteId: "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
+      caBundle: "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0t..."  # base64-encoded PEM
+```
+
+To generate the base64 string:
+```bash
+cat /path/to/ca.crt | base64 -w 0
+```
+
+### Folder Path
+
+The `folderPath` specifies the default folder containing your secrets. This should be a folder path, not the full path to a specific secret.
+
+For example, if your secrets are stored at:
+- `eso/static/secret1`
+- `eso/static/secret2`
+- `eso/static/secret3`
+
+Set `folderPath: "eso/static"` in your SecretStore.
+
+When using `data` or `dataFrom.extract`, secret names are relative to this folder. When using `dataFrom.find`, this folder is searched by default (unless overridden with the `path` field).
+### Refresh Interval
+The `refreshInterval` controls how often the ExternalSecret checks for updates:
+
+```yaml
+spec:
+  refreshInterval: 5m  # Check every 5 minutes
+```
+
+Supported units: `s` (seconds), `m` (minutes), `h` (hours).
+
+**Best Practice:** Balance between keeping secrets up-to-date and minimizing API calls. For most use cases, `1m` to `15m` is appropriate.
+
+### ClusterSecretStore
+
+To use a ClusterSecretStore (accessible across all namespaces):
+
+```yaml
+apiVersion: external-secrets.io/v1
+kind: ClusterSecretStore
+metadata:
+  name: beyondtrustworkloadcredentials-css
+spec:
+  provider:
+    beyondtrustworkloadcredentials:
+      auth:
+        apikey:
+          token:
+            name: api-token
+            key: token
+            namespace: external-secrets  # Required: specify where the token secret lives
+      server:
+        apiUrl: "https://api.beyondtrust.io/site"
+        siteId: "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
+      folderPath: "eso/static"
+```
+
+Reference it in an ExternalSecret:
+```yaml
+apiVersion: external-secrets.io/v1
+kind: ExternalSecret
+metadata:
+  name: my-secret
+  namespace: my-app
+spec:
+  secretStoreRef:
+    name: beyondtrustworkloadcredentials-css
+    kind: ClusterSecretStore  # Specify ClusterSecretStore
+  # ... rest of spec
+```

+ 16 - 0
docs/snippets/beyondtrustworkloadcredentials-dynamic-external-secret.yaml

@@ -0,0 +1,16 @@
+apiVersion: external-secrets.io/v1
+kind: ExternalSecret
+metadata:
+  name: app-aws-credentials
+  namespace: external-secrets
+spec:
+  refreshInterval: 5m
+  refreshPolicy: Periodic
+  target:
+    name: app-aws-credentials
+  dataFrom:
+    - sourceRef:
+        generatorRef:
+          apiVersion: generators.external-secrets.io/v1alpha1
+          kind: BeyondtrustWorkloadCredentialsDynamicSecret
+          name: aws-dynamic-generator

+ 16 - 0
docs/snippets/beyondtrustworkloadcredentials-dynamic-secret.yaml

@@ -0,0 +1,16 @@
+apiVersion: generators.external-secrets.io/v1alpha1
+kind: BeyondtrustWorkloadCredentialsDynamicSecret
+metadata:
+  name: aws-dynamic-generator
+  namespace: external-secrets
+spec:
+  provider:
+    auth:
+      apikey:
+        token:
+          name: bts-api-token
+          key: token
+    server:
+      apiUrl: "https://api.beyondtrust.io/site"
+      siteId: <SITE_ID>
+    folderPath: <FOLDER_PATH>

+ 17 - 0
docs/snippets/beyondtrustworkloadcredentials-external-secret.yaml

@@ -0,0 +1,17 @@
+apiVersion: external-secrets.io/v1
+kind: ExternalSecret
+metadata:
+  name: beyondtrustsecret
+  namespace: external-secrets
+spec:
+  refreshInterval: 1m
+  refreshPolicy: Periodic
+  secretStoreRef:
+    name: beyondtrustworkloadcredentials-ss
+    kind: SecretStore
+  target:
+    name: my-beyondtrust-secret
+  dataFrom:
+    - find:
+        name:
+          regexp: ".*"

+ 17 - 0
docs/snippets/beyondtrustworkloadcredentials-secret-store.yaml

@@ -0,0 +1,17 @@
+apiVersion: external-secrets.io/v1
+kind: SecretStore
+metadata:
+  name: beyondtrustworkloadcredentials-ss
+  namespace: external-secrets
+spec:
+  provider:
+    beyondtrustworkloadcredentials:
+      auth:
+        apikey:
+          token:
+            name: bts-api-token
+            key: token
+      server:
+        apiUrl: "https://api.beyondtrust.io/site"
+        siteId: <SITE_ID>
+      folderPath: <FOLDER_PATH>

+ 182 - 0
generators/v1/beyondtrustworkloadcredentials/beyondtrustworkloadcredentials.go

@@ -0,0 +1,182 @@
+/*
+Copyright © The ESO Authors
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+	https://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Package beyondtrustworkloadcredentialsdynamic provides a generator for BeyondTrust Workload Credentials dynamic credentials.
+package beyondtrustworkloadcredentialsdynamic
+
+import (
+	"context"
+	"encoding/json"
+	"errors"
+	"fmt"
+	"strings"
+
+	apiextensions "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
+	"sigs.k8s.io/controller-runtime/pkg/client"
+	"sigs.k8s.io/yaml"
+
+	genv1alpha1 "github.com/external-secrets/external-secrets/apis/generators/v1alpha1"
+	beyondtrustworkloadcredentialsprovider "github.com/external-secrets/external-secrets/providers/v1/beyondtrustworkloadcredentials"
+	"github.com/external-secrets/external-secrets/providers/v1/beyondtrustworkloadcredentials/httpclient"
+	btwcutil "github.com/external-secrets/external-secrets/providers/v1/beyondtrustworkloadcredentials/util"
+)
+
+// Generator implements BeyondtrustWorkloadCredentials dynamic generator.
+type Generator struct {
+	// NewBeyondtrustWorkloadCredentialsClient is a factory function to create a BeyondtrustWorkloadCredentials client.
+	// If nil, defaults to httpclient.NewBeyondtrustWorkloadCredentialsClient.
+	NewBeyondtrustWorkloadCredentialsClient func(server, token string) (btwcutil.Client, error)
+}
+
+const (
+	errNoSpec        = "no config spec provided"
+	errParseSpec     = "unable to parse spec: %w"
+	errMissingConfig = "no beyondtrustworkloadcredentials provider config in spec"
+	errNoPath        = "path is required in spec"
+	errGetSecret     = "unable to generate dynamic secret: %w"
+)
+
+// Generate creates the dynamic credentials by calling BeyondtrustWorkloadCredentials generate endpoint.
+func (g *Generator) Generate(ctx context.Context, jsonSpec *apiextensions.JSON, kube client.Client, namespace string) (map[string][]byte, genv1alpha1.GeneratorProviderState, error) {
+	spec, err := getDynamicSecretSpec(jsonSpec)
+	if err != nil {
+		return nil, nil, err
+	}
+	provider := spec.Spec.Provider
+
+	// parse and validate folder path and secret name early
+	fullPath := spec.Spec.Provider.FolderPath
+	folderPath, secretName := parsePath(fullPath)
+	if secretName == "" {
+		return nil, nil, fmt.Errorf("invalid path: missing secret name in %q", fullPath)
+	}
+
+	// create BeyondtrustWorkloadCredentials provider and initialize a client for generator controller
+	clientFactory := g.NewBeyondtrustWorkloadCredentialsClient
+	if clientFactory == nil {
+		clientFactory = httpclient.NewBeyondtrustWorkloadCredentialsClient
+	}
+	prov := beyondtrustworkloadcredentialsprovider.Provider{
+		NewBeyondtrustWorkloadCredentialsClient: clientFactory,
+	}
+	cl, err := prov.NewGeneratorClient(ctx, kube, provider, namespace)
+	if err != nil {
+		return nil, nil, fmt.Errorf("failed to create BeyondtrustWorkloadCredentials client: %w", err)
+	}
+
+	// call generate
+	generatedSecret, err := cl.GenerateDynamicSecret(ctx, secretName, folderPath)
+	if err != nil {
+		return nil, nil, fmt.Errorf(errGetSecret, err)
+	}
+	if generatedSecret == nil {
+		return nil, nil, fmt.Errorf("generated secret is nil")
+	}
+
+	out := convertToByteMap(generatedSecret)
+
+	// prepare provider state
+	state := &struct {
+		Path string `json:"path,omitempty"`
+	}{Path: spec.Spec.Provider.FolderPath}
+
+	stateJSON, _ := json.Marshal(state)
+	gpState := genv1alpha1.GeneratorProviderState(&apiextensions.JSON{Raw: stateJSON})
+
+	return out, gpState, nil
+}
+
+// Cleanup is a no-op for BeyondtrustWorkloadCredentials dynamic generator.
+func (g *Generator) Cleanup(_ context.Context, _ *apiextensions.JSON, _ genv1alpha1.GeneratorProviderState, _ client.Client, _ string) error {
+	return nil
+}
+
+// getDynamicSecretSpec checks if the provided spec is valid.
+func getDynamicSecretSpec(jsonSpec *apiextensions.JSON) (*genv1alpha1.BeyondtrustWorkloadCredentialsDynamicSecret, error) {
+	if jsonSpec == nil {
+		return nil, errors.New(errNoSpec)
+	}
+
+	spec, err := parseSpec(jsonSpec.Raw)
+	if err != nil {
+		return nil, fmt.Errorf(errParseSpec, err)
+	}
+	if spec == nil || spec.Spec.Provider == nil {
+		return nil, errors.New(errMissingConfig)
+	}
+	if spec.Spec.Provider.FolderPath == "" {
+		return nil, errors.New(errNoPath)
+	}
+
+	return spec, nil
+}
+
+// parseSpec unmarshals the JSON spec into a BeyondtrustWorkloadCredentialsDynamicSecret struct.
+func parseSpec(data []byte) (*genv1alpha1.BeyondtrustWorkloadCredentialsDynamicSecret, error) {
+	var spec genv1alpha1.BeyondtrustWorkloadCredentialsDynamicSecret
+	if err := yaml.Unmarshal(data, &spec); err != nil {
+		return nil, err
+	}
+	return &spec, nil
+}
+
+// Parse the path to extract folder and secret name.
+// Path format: "folder/subfolder/secretname" or just "secretname".
+func parsePath(fullPath string) (*string, string) {
+	var folderPath *string
+	var secretName string
+
+	lastSlash := strings.LastIndex(fullPath, "/")
+	if lastSlash >= 0 {
+		folder := fullPath[:lastSlash]
+		folderPath = &folder
+		secretName = fullPath[lastSlash+1:]
+	} else {
+		secretName = fullPath
+	}
+	return folderPath, secretName
+}
+
+// Convert generatedSecret to map[string][]byte.
+func convertToByteMap(generatedSecret *btwcutil.GeneratedSecret) map[string][]byte {
+	out := make(map[string][]byte)
+
+	// Defensive nil check
+	if generatedSecret == nil {
+		return out
+	}
+
+	out["accessKeyId"] = []byte(generatedSecret.AccessKeyID)
+	out["secretAccessKey"] = []byte(generatedSecret.SecretAccessKey)
+	out["leaseId"] = []byte(generatedSecret.LeaseID)
+	out["expiration"] = []byte(generatedSecret.Expiration)
+
+	if generatedSecret.SessionToken != "" {
+		out["sessionToken"] = []byte(generatedSecret.SessionToken)
+	}
+
+	return out
+}
+
+// NewGenerator creates a new BeyondtrustWorkloadCredentials generator instance.
+func NewGenerator() genv1alpha1.Generator {
+	return &Generator{}
+}
+
+// Kind returns the generator kind string.
+func Kind() string {
+	return string(genv1alpha1.GeneratorKindBeyondtrustWorkloadCredentialsDynamicSecret)
+}

+ 406 - 0
generators/v1/beyondtrustworkloadcredentials/beyondtrustworkloadcredentials_test.go

@@ -0,0 +1,406 @@
+/*
+Copyright © The ESO Authors
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+	https://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package beyondtrustworkloadcredentialsdynamic
+
+import (
+	"context"
+	"errors"
+	"testing"
+
+	"github.com/google/go-cmp/cmp"
+	corev1 "k8s.io/api/core/v1"
+	apiextensions "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	kclient "sigs.k8s.io/controller-runtime/pkg/client"
+	clientfake "sigs.k8s.io/controller-runtime/pkg/client/fake"
+
+	"github.com/external-secrets/external-secrets/providers/v1/beyondtrustworkloadcredentials/fake"
+	btwcutil "github.com/external-secrets/external-secrets/providers/v1/beyondtrustworkloadcredentials/util"
+)
+
+type args struct {
+	jsonSpec     *apiextensions.JSON
+	kube         kclient.Client
+	btsClientFn  func(server, token string) (btwcutil.Client, error)
+	generateMock func(ctx context.Context, name string, folderPath *string) (*btwcutil.GeneratedSecret, error)
+}
+
+type want struct {
+	val        map[string][]byte
+	partialVal map[string][]byte
+	err        error
+}
+
+type testCase struct {
+	reason string
+	args   args
+	want   want
+}
+
+func TestBeyondtrustWorkloadCredentialsDynamicSecretGenerator(t *testing.T) {
+	namespace := "test-namespace"
+
+	cases := map[string]testCase{
+		"NilSpec": {
+			reason: "Raise an error with empty spec.",
+			args: args{
+				jsonSpec: nil,
+			},
+			want: want{
+				err: errors.New("no config spec provided"),
+			},
+		},
+		"InvalidSpec": {
+			reason: "Raise an error with invalid spec.",
+			args: args{
+				jsonSpec: &apiextensions.JSON{
+					Raw: []byte(``),
+				},
+			},
+			want: want{
+				err: errors.New("no beyondtrustworkloadcredentials provider config in spec"),
+			},
+		},
+		"MissingFolderPath": {
+			reason: "Raise error if path is not provided.",
+			args: args{
+				jsonSpec: &apiextensions.JSON{
+					Raw: []byte(specMissingFolderPath),
+				},
+				kube: clientfake.NewClientBuilder().Build(),
+			},
+			want: want{
+				err: errors.New("path is required in spec"),
+			},
+		},
+		"EmptySecretName": {
+			reason: "Raise error if path ends with slash resulting in empty secret name.",
+			args: args{
+				jsonSpec: &apiextensions.JSON{
+					Raw: []byte(specEmptySecretName),
+				},
+				kube: clientfake.NewClientBuilder().Build(),
+			},
+			want: want{
+				err: errors.New("invalid path: missing secret name in \"test/folder/\""),
+			},
+		},
+		"MissingAuth": {
+			reason: "Raise error if auth is not provided.",
+			args: args{
+				jsonSpec: &apiextensions.JSON{
+					Raw: []byte(specMissingAuth),
+				},
+				kube: clientfake.NewClientBuilder().Build(),
+			},
+			want: want{
+				err: errors.New(
+					"failed to create BeyondtrustWorkloadCredentials client: failed to load credentials: missing or invalid BeyondtrustWorkloadCredentials API Token in BeyondtrustWorkloadCredentials SecretStore",
+				),
+			},
+		},
+		"SecretNotFound": {
+			reason: "Raise error if secret containing api token does not exist.",
+			args: args{
+				jsonSpec: &apiextensions.JSON{
+					Raw: []byte(specSecretNotFound),
+				},
+				kube: clientfake.NewClientBuilder().Build(),
+			},
+			want: want{
+				err: errors.New(
+					"failed to create BeyondtrustWorkloadCredentials client: failed to load credentials: " +
+						"cannot get Kubernetes secret \"nonexistent-secret\" from namespace \"test-namespace\": " +
+						"secrets \"nonexistent-secret\" not found",
+				),
+			},
+		},
+		"SuccessfulGeneration": {
+			reason: "Successfully generate dynamic secret.",
+			args: args{
+				jsonSpec: &apiextensions.JSON{
+					Raw: []byte(validDynamicSecretSpec),
+				},
+				kube: clientfake.NewClientBuilder().WithObjects(&corev1.Secret{
+					ObjectMeta: metav1.ObjectMeta{
+						Name:      "beyondtrustworkloadcredentials-token",
+						Namespace: namespace,
+					},
+					Data: map[string][]byte{
+						"token": []byte("test-token"),
+					},
+				}).Build(),
+				btsClientFn: func(server, token string) (btwcutil.Client, error) {
+					client := &fake.BeyondtrustWorkloadCredentialsClient{}
+					client.WithValues(context.Background(), nil, nil, nil, nil, nil, nil)
+					return client, nil
+				},
+				generateMock: func(ctx context.Context, name string, folderPath *string) (*btwcutil.GeneratedSecret, error) {
+					return &btwcutil.GeneratedSecret{
+						AccessKeyID:     "AKIAIOSFODNN7EXAMPLE",
+						SecretAccessKey: "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
+						SessionToken:    "FwoGZXIvYXdzEBYaD...",
+						Expiration:      "2025-12-08T12:00:00Z",
+						LeaseID:         "aws/creds/example/abc123",
+					}, nil
+				},
+			},
+			want: want{
+				val: map[string][]byte{
+					"accessKeyId":     []byte("AKIAIOSFODNN7EXAMPLE"),
+					"secretAccessKey": []byte("wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"),
+					"sessionToken":    []byte("FwoGZXIvYXdzEBYaD..."),
+					"expiration":      []byte("2025-12-08T12:00:00Z"),
+					"leaseId":         []byte("aws/creds/example/abc123"),
+				},
+			},
+		},
+		"SuccessfulGenerationAtRootFolder": {
+			reason: "Successfully generate dynamic secret at root folder path.",
+			args: args{
+				jsonSpec: &apiextensions.JSON{
+					Raw: []byte(validDynamicSecretSpecNoFolder),
+				},
+				kube: clientfake.NewClientBuilder().WithObjects(&corev1.Secret{
+					ObjectMeta: metav1.ObjectMeta{
+						Name:      "beyondtrustworkloadcredentials-token",
+						Namespace: namespace,
+					},
+					Data: map[string][]byte{
+						"token": []byte("test-token"),
+					},
+				}).Build(),
+				btsClientFn: func(server, token string) (btwcutil.Client, error) {
+					client := &fake.BeyondtrustWorkloadCredentialsClient{}
+					client.WithValues(context.Background(), nil, nil, nil, nil, nil, nil)
+					return client, nil
+				},
+				generateMock: func(ctx context.Context, name string, folderPath *string) (*btwcutil.GeneratedSecret, error) {
+					if folderPath != nil {
+						return nil, errors.New("expected nil folder path")
+					}
+					// For generic secrets, we'll just return empty fields
+					return &btwcutil.GeneratedSecret{
+						AccessKeyID:     "admin",
+						SecretAccessKey: "secret123",
+					}, nil
+				},
+			},
+			want: want{
+				partialVal: map[string][]byte{
+					"accessKeyId":     []byte("admin"),
+					"secretAccessKey": []byte("secret123"),
+				},
+			},
+		},
+		"GenerationError": {
+			reason: "Raise error when generation fails.",
+			args: args{
+				jsonSpec: &apiextensions.JSON{
+					Raw: []byte(validDynamicSecretSpecWithFolder),
+				},
+				kube: clientfake.NewClientBuilder().WithObjects(&corev1.Secret{
+					ObjectMeta: metav1.ObjectMeta{
+						Name:      "beyondtrustworkloadcredentials-token",
+						Namespace: namespace,
+					},
+					Data: map[string][]byte{
+						"token": []byte("test-token"),
+					},
+				}).Build(),
+				btsClientFn: func(server, token string) (btwcutil.Client, error) {
+					client := &fake.BeyondtrustWorkloadCredentialsClient{}
+					client.WithValues(context.Background(), nil, nil, nil, nil, nil, nil)
+					return client, nil
+				},
+				generateMock: func(ctx context.Context, name string, folderPath *string) (*btwcutil.GeneratedSecret, error) {
+					return nil, errors.New("API error: insufficient permissions")
+				},
+			},
+			want: want{
+				err: errors.New("unable to generate dynamic secret: API error: insufficient permissions"),
+			},
+		},
+	}
+	for name, tc := range cases {
+		t.Run(name, func(t *testing.T) {
+			// Create client factory function with mock setup
+			newClientFn := func(server, token string) (btwcutil.Client, error) {
+				client := &fake.BeyondtrustWorkloadCredentialsClient{}
+				client.WithValues(context.Background(), nil, nil, nil, nil, nil, nil)
+
+				// Set up the generate mock if provided
+				if tc.args.generateMock != nil {
+					client.WithGenerateDynamicSecret(tc.args.generateMock)
+				}
+
+				return client, nil
+			}
+
+			if tc.args.btsClientFn != nil {
+				// If a custom client function is provided, wrap it to add the generate mock
+				customFn := tc.args.btsClientFn
+				newClientFn = func(server, token string) (btwcutil.Client, error) {
+					client, err := customFn(server, token)
+					if err != nil {
+						return nil, err
+					}
+
+					// If it's a fake client, set up the generate mock
+					if fakeClient, ok := client.(*fake.BeyondtrustWorkloadCredentialsClient); ok && tc.args.generateMock != nil {
+						fakeClient.WithGenerateDynamicSecret(tc.args.generateMock)
+					}
+
+					return client, nil
+				}
+			}
+
+			// Create generator with injected client factory
+			gen := &Generator{
+				NewBeyondtrustWorkloadCredentialsClient: newClientFn,
+			}
+
+			// Call gen.Generate() for all test cases
+			val, _, err := gen.Generate(context.Background(), tc.args.jsonSpec, tc.args.kube, namespace)
+
+			// Assertions
+			if tc.want.err != nil {
+				if err != nil {
+					if diff := cmp.Diff(tc.want.err.Error(), err.Error()); diff != "" {
+						t.Errorf("\n%s\nbeyondtrustworkloadcredentials.Generate(...): -want error, +got error:\n%s", tc.reason, diff)
+					}
+				} else {
+					t.Errorf("\n%s\nbeyondtrustworkloadcredentials.Generate(...): -want error, +got val:\n%v", tc.reason, val)
+				}
+			} else if tc.want.partialVal != nil {
+				// Success case: expect no error
+				if err != nil {
+					t.Fatalf("\n%s\nbeyondtrustworkloadcredentials.Generate(...): unexpected error: expected nil, got %v", tc.reason, err)
+				}
+				for k, v := range tc.want.partialVal {
+					if diff := cmp.Diff(v, val[k]); diff != "" {
+						t.Errorf("\n%s\nbeyondtrustworkloadcredentials.Generate(...) -> %s: -want partial, +got partial:\n%s", tc.reason, k, diff)
+					}
+				}
+			} else {
+				// Success case: expect no error
+				if err != nil {
+					t.Fatalf("\n%s\nbeyondtrustworkloadcredentials.Generate(...): unexpected error: expected nil, got %v", tc.reason, err)
+				}
+				if diff := cmp.Diff(tc.want.val, val); diff != "" {
+					t.Errorf("\n%s\nbeyondtrustworkloadcredentials.Generate(...): -want val, +got val:\n%s", tc.reason, diff)
+				}
+			}
+		})
+	}
+}
+
+func TestPathParsing(t *testing.T) {
+	tests := []struct {
+		name           string
+		input          string
+		wantFolder     *string
+		wantSecretName string
+	}{
+		{
+			name:           "Path with folder",
+			input:          "test/subfolder/secret-name",
+			wantFolder:     new("test/subfolder"),
+			wantSecretName: "secret-name",
+		},
+		{
+			name:           "Path without folder",
+			input:          "secret-name",
+			wantFolder:     nil,
+			wantSecretName: "secret-name",
+		},
+		{
+			name:           "Path with single folder",
+			input:          "folder/secret",
+			wantFolder:     new("folder"),
+			wantSecretName: "secret",
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			gotFolder, gotSecretName := parsePath(tt.input)
+
+			if tt.wantFolder == nil && gotFolder != nil {
+				t.Errorf("parsePath() gotFolder = %v, want nil", *gotFolder)
+			} else if tt.wantFolder != nil && gotFolder == nil {
+				t.Errorf("parsePath() gotFolder = nil, want %v", *tt.wantFolder)
+			} else if tt.wantFolder != nil && gotFolder != nil && *gotFolder != *tt.wantFolder {
+				t.Errorf("parsePath() gotFolder = %v, want %v", *gotFolder, *tt.wantFolder)
+			}
+
+			if gotSecretName != tt.wantSecretName {
+				t.Errorf("parsePath() gotSecretName = %v, want %v", gotSecretName, tt.wantSecretName)
+			}
+		})
+	}
+}
+
+func TestConvertToByteMap(t *testing.T) {
+	tests := []struct {
+		name  string
+		input *btwcutil.GeneratedSecret
+		want  map[string][]byte
+	}{
+		{
+			name: "Valid string values",
+			input: &btwcutil.GeneratedSecret{
+				AccessKeyID:     "AKIAIOSFODNN7EXAMPLE",
+				SecretAccessKey: "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
+				SessionToken:    "FwoGZXIvYXdzEBYaD...",
+				LeaseID:         "aws/creds/example/abc123",
+				Expiration:      "2025-12-08T12:00:00Z",
+			},
+			want: map[string][]byte{
+				"accessKeyId":     []byte("AKIAIOSFODNN7EXAMPLE"),
+				"secretAccessKey": []byte("wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"),
+				"sessionToken":    []byte("FwoGZXIvYXdzEBYaD..."),
+				"leaseId":         []byte("aws/creds/example/abc123"),
+				"expiration":      []byte("2025-12-08T12:00:00Z"),
+			},
+		},
+		{
+			name: "Empty session token",
+			input: &btwcutil.GeneratedSecret{
+				AccessKeyID:     "AKIAIOSFODNN7EXAMPLE",
+				SecretAccessKey: "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
+				LeaseID:         "aws/creds/example/abc123",
+				Expiration:      "2025-12-08T12:00:00Z",
+			},
+			want: map[string][]byte{
+				"accessKeyId":     []byte("AKIAIOSFODNN7EXAMPLE"),
+				"secretAccessKey": []byte("wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"),
+				"leaseId":         []byte("aws/creds/example/abc123"),
+				"expiration":      []byte("2025-12-08T12:00:00Z"),
+			},
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			got := convertToByteMap(tt.input)
+			if diff := cmp.Diff(tt.want, got); diff != "" {
+				t.Errorf("convertToByteMap() mismatch (-want +got):\n%s", diff)
+			}
+		})
+	}
+}

+ 120 - 0
generators/v1/beyondtrustworkloadcredentials/fixtures_test.go

@@ -0,0 +1,120 @@
+/*
+Copyright © The ESO Authors
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+	https://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package beyondtrustworkloadcredentialsdynamic
+
+// Test fixtures containing JSON spec strings for BeyondtrustWorkloadCredentials dynamic secret generator tests.
+
+const (
+	// validDynamicSecretSpec is a valid BeyondtrustWorkloadCredentialsDynamicSecret spec with all required fields.
+	validDynamicSecretSpec = `apiVersion: generators.external-secrets.io/v1alpha1
+kind: BeyondtrustWorkloadCredentialsDynamicSecret
+spec:
+  provider:
+    folderPath: "test/aws-dynamic"
+    auth:
+      apikey:
+        token:
+          name: "beyondtrustworkloadcredentials-token"
+          key: "token"
+    server:
+      apiUrl: "https://example.com"
+      siteId: "12345678-1234-4234-8234-123456789012"`
+
+	// validDynamicSecretSpecNoFolder is a spec with a secret at root level (no folder prefix).
+	validDynamicSecretSpecNoFolder = `apiVersion: generators.external-secrets.io/v1alpha1
+kind: BeyondtrustWorkloadCredentialsDynamicSecret
+spec:
+  provider:
+    folderPath: "mysecret"
+    auth:
+      apikey:
+        token:
+          name: "beyondtrustworkloadcredentials-token"
+          key: "token"
+    server:
+      apiUrl: "https://example.com"
+      siteId: "12345678-1234-4234-8234-123456789012"`
+
+	// specMissingFolderPath has no folderPath field.
+	specMissingFolderPath = `apiVersion: generators.external-secrets.io/v1alpha1
+kind: BeyondtrustWorkloadCredentialsDynamicSecret
+spec:
+  provider:
+    auth:
+      apikey:
+        token:
+          name: "beyondtrustworkloadcredentials-token"
+          key: "token"
+    server:
+      apiUrl: "https://example.com"
+      siteId: "12345678-1234-4234-8234-123456789012"`
+
+	// specMissingAuth has no auth field.
+	specMissingAuth = `apiVersion: generators.external-secrets.io/v1alpha1
+kind: BeyondtrustWorkloadCredentialsDynamicSecret
+spec:
+  provider:
+    folderPath: "test/dynamic-secret"
+    server:
+      apiUrl: "https://example.com"
+      siteId: "12345678-1234-4234-8234-123456789012"`
+
+	// specSecretNotFound references a non-existent secret.
+	specSecretNotFound = `apiVersion: generators.external-secrets.io/v1alpha1
+kind: BeyondtrustWorkloadCredentialsDynamicSecret
+spec:
+  provider:
+    folderPath: "test/dynamic-secret"
+    auth:
+      apikey:
+        token:
+          name: "nonexistent-secret"
+          key: "token"
+    server:
+      apiUrl: "https://example.com"
+      siteId: "12345678-1234-4234-8234-123456789012"`
+
+	// validDynamicSecretSpecWithFolder is used for error and non-string value tests.
+	validDynamicSecretSpecWithFolder = `apiVersion: generators.external-secrets.io/v1alpha1
+kind: BeyondtrustWorkloadCredentialsDynamicSecret
+spec:
+  provider:
+    folderPath: "test/dynamic-secret"
+    auth:
+      apikey:
+        token:
+          name: "beyondtrustworkloadcredentials-token"
+          key: "token"
+    server:
+      apiUrl: "https://example.com"
+      siteId: "12345678-1234-4234-8234-123456789012"`
+
+	// specEmptySecretName has a folderPath ending with "/" resulting in empty secret name.
+	specEmptySecretName = `apiVersion: generators.external-secrets.io/v1alpha1
+kind: BeyondtrustWorkloadCredentialsDynamicSecret
+spec:
+  provider:
+    folderPath: "test/folder/"
+    auth:
+      apikey:
+        token:
+          name: "beyondtrustworkloadcredentials-token"
+          key: "token"
+    server:
+      apiUrl: "https://example.com"
+      siteId: "12345678-1234-4234-8234-123456789012"`
+)

+ 100 - 0
generators/v1/beyondtrustworkloadcredentials/go.mod

@@ -0,0 +1,100 @@
+module github.com/external-secrets/external-secrets/generators/v1/beyondtrustworkloadcredentials
+
+go 1.26.4
+
+require (
+	github.com/external-secrets/external-secrets/apis v0.0.0
+	github.com/external-secrets/external-secrets/providers/v1/beyondtrustworkloadcredentials v0.0.0-00010101000000-000000000000
+	github.com/google/go-cmp v0.7.0
+	k8s.io/api v0.35.2
+	k8s.io/apiextensions-apiserver v0.35.2
+	k8s.io/apimachinery v0.35.2
+	sigs.k8s.io/controller-runtime v0.23.3
+	sigs.k8s.io/yaml v1.6.0
+)
+
+require (
+	dario.cat/mergo v1.0.2 // indirect
+	github.com/Masterminds/goutils v1.1.1 // indirect
+	github.com/Masterminds/semver/v3 v3.4.0 // indirect
+	github.com/beorn7/perks v1.0.1 // indirect
+	github.com/cespare/xxhash/v2 v2.3.0 // indirect
+	github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
+	github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.1 // indirect
+	github.com/emicklei/go-restful/v3 v3.13.0 // indirect
+	github.com/evanphx/json-patch/v5 v5.9.11 // indirect
+	github.com/external-secrets/external-secrets/runtime v0.0.0 // indirect
+	github.com/fsnotify/fsnotify v1.9.0 // indirect
+	github.com/fxamacker/cbor/v2 v2.9.0 // indirect
+	github.com/go-logr/logr v1.4.3 // indirect
+	github.com/go-openapi/jsonpointer v0.22.5 // indirect
+	github.com/go-openapi/jsonreference v0.21.5 // indirect
+	github.com/go-openapi/swag v0.25.5 // indirect
+	github.com/go-openapi/swag/cmdutils v0.25.5 // indirect
+	github.com/go-openapi/swag/conv v0.25.5 // indirect
+	github.com/go-openapi/swag/fileutils v0.25.5 // indirect
+	github.com/go-openapi/swag/jsonname v0.25.5 // indirect
+	github.com/go-openapi/swag/jsonutils v0.25.5 // indirect
+	github.com/go-openapi/swag/loading v0.25.5 // indirect
+	github.com/go-openapi/swag/mangling v0.25.5 // indirect
+	github.com/go-openapi/swag/netutils v0.25.5 // indirect
+	github.com/go-openapi/swag/stringutils v0.25.5 // indirect
+	github.com/go-openapi/swag/typeutils v0.25.5 // indirect
+	github.com/go-openapi/swag/yamlutils v0.25.5 // indirect
+	github.com/goccy/go-json v0.10.5 // indirect
+	github.com/google/btree v1.1.3 // indirect
+	github.com/google/gnostic-models v0.7.1 // indirect
+	github.com/google/uuid v1.6.0 // indirect
+	github.com/huandu/xstrings v1.5.0 // indirect
+	github.com/json-iterator/go v1.1.12 // indirect
+	github.com/lestrrat-go/blackmagic v1.0.4 // indirect
+	github.com/lestrrat-go/httpcc v1.0.1 // indirect
+	github.com/lestrrat-go/httprc v1.0.6 // indirect
+	github.com/lestrrat-go/iter v1.0.2 // indirect
+	github.com/lestrrat-go/jwx/v2 v2.1.6 // indirect
+	github.com/lestrrat-go/option v1.0.1 // indirect
+	github.com/mitchellh/copystructure v1.2.0 // indirect
+	github.com/mitchellh/reflectwalk v1.0.2 // indirect
+	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
+	github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect
+	github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
+	github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
+	github.com/prometheus/client_golang v1.23.2 // indirect
+	github.com/prometheus/client_model v0.6.2 // indirect
+	github.com/prometheus/common v0.67.5 // indirect
+	github.com/prometheus/procfs v0.20.1 // indirect
+	github.com/segmentio/asm v1.2.1 // indirect
+	github.com/shopspring/decimal v1.4.0 // indirect
+	github.com/spf13/cast v1.10.0 // indirect
+	github.com/spf13/pflag v1.0.10 // indirect
+	github.com/x448/float16 v0.8.4 // indirect
+	go.yaml.in/yaml/v2 v2.4.4 // indirect
+	go.yaml.in/yaml/v3 v3.0.4 // indirect
+	golang.org/x/crypto v0.51.0 // indirect
+	golang.org/x/net v0.54.0 // indirect
+	golang.org/x/oauth2 v0.36.0 // indirect
+	golang.org/x/sync v0.20.0 // indirect
+	golang.org/x/sys v0.44.0 // indirect
+	golang.org/x/term v0.43.0 // indirect
+	golang.org/x/text v0.37.0 // indirect
+	golang.org/x/time v0.15.0 // indirect
+	gomodules.xyz/jsonpatch/v2 v2.5.0 // indirect
+	google.golang.org/protobuf v1.36.11 // indirect
+	gopkg.in/evanphx/json-patch.v4 v4.13.0 // indirect
+	gopkg.in/inf.v0 v0.9.1 // indirect
+	k8s.io/client-go v0.35.2 // indirect
+	k8s.io/klog/v2 v2.140.0 // indirect
+	k8s.io/kube-openapi v0.0.0-20260304202019-5b3e3fdb0acf // indirect
+	k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2 // indirect
+	sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect
+	sigs.k8s.io/randfill v1.0.0 // indirect
+	sigs.k8s.io/structured-merge-diff/v6 v6.3.2 // indirect
+	software.sslmate.com/src/go-pkcs12 v0.7.0 // indirect
+)
+
+replace (
+	github.com/external-secrets/external-secrets/apis => ../../../apis
+	github.com/external-secrets/external-secrets/runtime => ../../../runtime
+)
+
+replace github.com/external-secrets/external-secrets/providers/v1/beyondtrustworkloadcredentials => ../../../providers/v1/beyondtrustworkloadcredentials

+ 238 - 0
generators/v1/beyondtrustworkloadcredentials/go.sum

@@ -0,0 +1,238 @@
+dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8=
+dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA=
+github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
+github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
+github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0=
+github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
+github.com/aws/aws-sdk-go-v2 v1.41.6 h1:1AX0AthnBQzMx1vbmir3Y4WsnJgiydmnJjiLu+LvXOg=
+github.com/aws/aws-sdk-go-v2 v1.41.6/go.mod h1:dy0UzBIfwSeot4grGvY1AqFWN5zgziMmWGzysDnHFcQ=
+github.com/aws/smithy-go v1.25.1 h1:J8ERsGSU7d+aCmdQur5Txg6bVoYelvQJgtZehD12GkI=
+github.com/aws/smithy-go v1.25.1/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc=
+github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
+github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
+github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
+github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
+github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.1 h1:5RVFMOWjMyRy8cARdy79nAmgYw3hK/4HUq48LQ6Wwqo=
+github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.1/go.mod h1:ZXNYxsqcloTdSy/rNShjYzMhyjf0LaoftYK0p+A3h40=
+github.com/emicklei/go-restful/v3 v3.13.0 h1:C4Bl2xDndpU6nJ4bc1jXd+uTmYPVUwkD6bFY/oTyCes=
+github.com/emicklei/go-restful/v3 v3.13.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
+github.com/evanphx/json-patch v0.5.2 h1:xVCHIVMUu1wtM/VkR9jVZ45N3FhZfYMMYGorLCR8P3k=
+github.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ=
+github.com/evanphx/json-patch/v5 v5.9.11 h1:/8HVnzMq13/3x9TPvjG08wUGqBTmZBsCWzjTM0wiaDU=
+github.com/evanphx/json-patch/v5 v5.9.11/go.mod h1:3j+LviiESTElxA4p3EMKAB9HXj3/XEtnUf6OZxqIQTM=
+github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
+github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
+github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
+github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
+github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM=
+github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ=
+github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
+github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
+github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ=
+github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg=
+github.com/go-openapi/jsonpointer v0.22.5 h1:8on/0Yp4uTb9f4XvTrM2+1CPrV05QPZXu+rvu2o9jcA=
+github.com/go-openapi/jsonpointer v0.22.5/go.mod h1:gyUR3sCvGSWchA2sUBJGluYMbe1zazrYWIkWPjjMUY0=
+github.com/go-openapi/jsonreference v0.21.5 h1:6uCGVXU/aNF13AQNggxfysJ+5ZcU4nEAe+pJyVWRdiE=
+github.com/go-openapi/jsonreference v0.21.5/go.mod h1:u25Bw85sX4E2jzFodh1FOKMTZLcfifd1Q+iKKOUxExw=
+github.com/go-openapi/swag v0.25.5 h1:pNkwbUEeGwMtcgxDr+2GBPAk4kT+kJ+AaB+TMKAg+TU=
+github.com/go-openapi/swag v0.25.5/go.mod h1:B3RT6l8q7X803JRxa2e59tHOiZlX1t8viplOcs9CwTA=
+github.com/go-openapi/swag/cmdutils v0.25.5 h1:yh5hHrpgsw4NwM9KAEtaDTXILYzdXh/I8Whhx9hKj7c=
+github.com/go-openapi/swag/cmdutils v0.25.5/go.mod h1:pdae/AFo6WxLl5L0rq87eRzVPm/XRHM3MoYgRMvG4A0=
+github.com/go-openapi/swag/conv v0.25.5 h1:wAXBYEXJjoKwE5+vc9YHhpQOFj2JYBMF2DUi+tGu97g=
+github.com/go-openapi/swag/conv v0.25.5/go.mod h1:CuJ1eWvh1c4ORKx7unQnFGyvBbNlRKbnRyAvDvzWA4k=
+github.com/go-openapi/swag/fileutils v0.25.5 h1:B6JTdOcs2c0dBIs9HnkyTW+5gC+8NIhVBUwERkFhMWk=
+github.com/go-openapi/swag/fileutils v0.25.5/go.mod h1:V3cT9UdMQIaH4WiTrUc9EPtVA4txS0TOmRURmhGF4kc=
+github.com/go-openapi/swag/jsonname v0.25.5 h1:8p150i44rv/Drip4vWI3kGi9+4W9TdI3US3uUYSFhSo=
+github.com/go-openapi/swag/jsonname v0.25.5/go.mod h1:jNqqikyiAK56uS7n8sLkdaNY/uq6+D2m2LANat09pKU=
+github.com/go-openapi/swag/jsonutils v0.25.5 h1:XUZF8awQr75MXeC+/iaw5usY/iM7nXPDwdG3Jbl9vYo=
+github.com/go-openapi/swag/jsonutils v0.25.5/go.mod h1:48FXUaz8YsDAA9s5AnaUvAmry1UcLcNVWUjY42XkrN4=
+github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.5 h1:SX6sE4FrGb4sEnnxbFL/25yZBb5Hcg1inLeErd86Y1U=
+github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.5/go.mod h1:/2KvOTrKWjVA5Xli3DZWdMCZDzz3uV/T7bXwrKWPquo=
+github.com/go-openapi/swag/loading v0.25.5 h1:odQ/umlIZ1ZVRteI6ckSrvP6e2w9UTF5qgNdemJHjuU=
+github.com/go-openapi/swag/loading v0.25.5/go.mod h1:I8A8RaaQ4DApxhPSWLNYWh9NvmX2YKMoB9nwvv6oW6g=
+github.com/go-openapi/swag/mangling v0.25.5 h1:hyrnvbQRS7vKePQPHHDso+k6CGn5ZBs5232UqWZmJZw=
+github.com/go-openapi/swag/mangling v0.25.5/go.mod h1:6hadXM/o312N/h98RwByLg088U61TPGiltQn71Iw0NY=
+github.com/go-openapi/swag/netutils v0.25.5 h1:LZq2Xc2QI8+7838elRAaPCeqJnHODfSyOa7ZGfxDKlU=
+github.com/go-openapi/swag/netutils v0.25.5/go.mod h1:lHbtmj4m57APG/8H7ZcMMSWzNqIQcu0RFiXrPUara14=
+github.com/go-openapi/swag/stringutils v0.25.5 h1:NVkoDOA8YBgtAR/zvCx5rhJKtZF3IzXcDdwOsYzrB6M=
+github.com/go-openapi/swag/stringutils v0.25.5/go.mod h1:PKK8EZdu4QJq8iezt17HM8RXnLAzY7gW0O1KKarrZII=
+github.com/go-openapi/swag/typeutils v0.25.5 h1:EFJ+PCga2HfHGdo8s8VJXEVbeXRCYwzzr9u4rJk7L7E=
+github.com/go-openapi/swag/typeutils v0.25.5/go.mod h1:itmFmScAYE1bSD8C4rS0W+0InZUBrB2xSPbWt6DLGuc=
+github.com/go-openapi/swag/yamlutils v0.25.5 h1:kASCIS+oIeoc55j28T4o8KwlV2S4ZLPT6G0iq2SSbVQ=
+github.com/go-openapi/swag/yamlutils v0.25.5/go.mod h1:Gek1/SjjfbYvM+Iq4QGwa/2lEXde9n2j4a3wI3pNuOQ=
+github.com/go-openapi/testify/enable/yaml/v2 v2.4.0 h1:7SgOMTvJkM8yWrQlU8Jm18VeDPuAvB/xWrdxFJkoFag=
+github.com/go-openapi/testify/enable/yaml/v2 v2.4.0/go.mod h1:14iV8jyyQlinc9StD7w1xVPW3CO3q1Gj04Jy//Kw4VM=
+github.com/go-openapi/testify/v2 v2.4.0 h1:8nsPrHVCWkQ4p8h1EsRVymA2XABB4OT40gcvAu+voFM=
+github.com/go-openapi/testify/v2 v2.4.0/go.mod h1:HCPmvFFnheKK2BuwSA0TbbdxJ3I16pjwMkYkP4Ywn54=
+github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
+github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
+github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
+github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
+github.com/gofrs/flock v0.13.0 h1:95JolYOvGMqeH31+FC7D2+uULf6mG61mEZ/A8dRYMzw=
+github.com/gofrs/flock v0.13.0/go.mod h1:jxeyy9R1auM5S6JYDBhDt+E2TCo7DkratH4Pgi8P+Z0=
+github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg=
+github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
+github.com/google/gnostic-models v0.7.1 h1:SisTfuFKJSKM5CPZkffwi6coztzzeYUhc3v4yxLWH8c=
+github.com/google/gnostic-models v0.7.1/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7OUGxBlw57miDrQ=
+github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
+github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
+github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
+github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/google/pprof v0.0.0-20260115054156-294ebfa9ad83 h1:z2ogiKUYzX5Is6zr/vP9vJGqPwcdqsWjOt+V8J7+bTc=
+github.com/google/pprof v0.0.0-20260115054156-294ebfa9ad83/go.mod h1:MxpfABSjhmINe3F1It9d+8exIHFvUqtLIRCdOGNXqiI=
+github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
+github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI=
+github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
+github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
+github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
+github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
+github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
+github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
+github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
+github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
+github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
+github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
+github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
+github.com/lestrrat-go/blackmagic v1.0.4 h1:IwQibdnf8l2KoO+qC3uT4OaTWsW7tuRQXy9TRN9QanA=
+github.com/lestrrat-go/blackmagic v1.0.4/go.mod h1:6AWFyKNNj0zEXQYfTMPfZrAXUWUfTIZ5ECEUEJaijtw=
+github.com/lestrrat-go/httpcc v1.0.1 h1:ydWCStUeJLkpYyjLDHihupbn2tYmZ7m22BGkcvZZrIE=
+github.com/lestrrat-go/httpcc v1.0.1/go.mod h1:qiltp3Mt56+55GPVCbTdM9MlqhvzyuL6W/NMDA8vA5E=
+github.com/lestrrat-go/httprc v1.0.6 h1:qgmgIRhpvBqexMJjA/PmwSvhNk679oqD1RbovdCGW8k=
+github.com/lestrrat-go/httprc v1.0.6/go.mod h1:mwwz3JMTPBjHUkkDv/IGJ39aALInZLrhBp0X7KGUZlo=
+github.com/lestrrat-go/iter v1.0.2 h1:gMXo1q4c2pHmC3dn8LzRhJfP1ceCbgSiT9lUydIzltI=
+github.com/lestrrat-go/iter v1.0.2/go.mod h1:Momfcq3AnRlRjI5b5O8/G5/BvpzrhoFTZcn06fEOPt4=
+github.com/lestrrat-go/jwx/v2 v2.1.6 h1:hxM1gfDILk/l5ylers6BX/Eq1m/pnxe9NBwW6lVfecA=
+github.com/lestrrat-go/jwx/v2 v2.1.6/go.mod h1:Y722kU5r/8mV7fYDifjug0r8FK8mZdw0K0GpJw/l8pU=
+github.com/lestrrat-go/option v1.0.1 h1:oAzP2fvZGQKWkvHa1/SAcFolBEca1oN+mQ7eooNBEYU=
+github.com/lestrrat-go/option v1.0.1/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I=
+github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
+github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
+github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
+github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
+github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
+github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8=
+github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
+github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
+github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
+github.com/onsi/ginkgo/v2 v2.28.0 h1:Rrf+lVLmtlBIKv6KrIGJCjyY8N36vDVcutbGJkyqjJc=
+github.com/onsi/ginkgo/v2 v2.28.0/go.mod h1:ArE1D/XhNXBXCBkKOLkbsb2c81dQHCRcF5zwn/ykDRo=
+github.com/onsi/gomega v1.39.1 h1:1IJLAad4zjPn2PsnhH70V4DKRFlrCzGBNrNaru+Vf28=
+github.com/onsi/gomega v1.39.1/go.mod h1:hL6yVALoTOxeWudERyfppUcZXjMwIMLnuSfruD2lcfg=
+github.com/oracle/oci-go-sdk/v65 v65.103.0 h1:HfyZx+JefCPK3At0Xt45q+wr914jDXuoyzOFX3XCbno=
+github.com/oracle/oci-go-sdk/v65 v65.103.0/go.mod h1:oB8jFGVc/7/zJ+DbleE8MzGHjhs2ioCz5stRTdZdIcY=
+github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
+github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
+github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o=
+github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg=
+github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
+github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
+github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4=
+github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw=
+github.com/prometheus/procfs v0.20.1 h1:XwbrGOIplXW/AU3YhIhLODXMJYyC1isLFfYCsTEycfc=
+github.com/prometheus/procfs v0.20.1/go.mod h1:o9EMBZGRyvDrSPH1RqdxhojkuXstoe4UlK79eF5TGGo=
+github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
+github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
+github.com/segmentio/asm v1.2.1 h1:DTNbBqs57ioxAD4PrArqftgypG4/qNpXoJx8TVXxPR0=
+github.com/segmentio/asm v1.2.1/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs=
+github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k=
+github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=
+github.com/sony/gobreaker v1.0.0 h1:feX5fGGXSl3dYd4aHZItw+FpHLvvoaqkawKjVNiFMNQ=
+github.com/sony/gobreaker v1.0.0/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY=
+github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY=
+github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo=
+github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
+github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
+github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
+github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
+github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
+github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
+github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM=
+github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78/go.mod h1:aL8wCCfTfSfmXjznFBSZNN13rSJjlIOI1fUNAtF7rmI=
+go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
+go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
+go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
+go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
+go.uber.org/zap v1.28.0 h1:IZzaP1Fv73/T/pBMLk4VutPl36uNC+OSUh3JLG3FIjo=
+go.uber.org/zap v1.28.0/go.mod h1:rDLpOi171uODNm/mxFcuYWxDsqWSAVkFdX4XojSKg/Q=
+go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ=
+go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ=
+go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
+go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
+golang.org/x/crypto v0.51.0 h1:IBPXwPfKxY7cWQZ38ZCIRPI50YLeevDLlLnyC5wRGTI=
+golang.org/x/crypto v0.51.0/go.mod h1:8AdwkbraGNABw2kOX6YFPs3WM22XqI4EXEd8g+x7Oc8=
+golang.org/x/mod v0.35.0 h1:Ww1D637e6Pg+Zb2KrWfHQUnH2dQRLBQyAtpr/haaJeM=
+golang.org/x/mod v0.35.0/go.mod h1:+GwiRhIInF8wPm+4AoT6L0FA1QWAad3OMdTRx4tFYlU=
+golang.org/x/net v0.54.0 h1:2zJIZAxAHV/OHCDTCOHAYehQzLfSXuf/5SoL/Dv6w/w=
+golang.org/x/net v0.54.0/go.mod h1:Sj4oj8jK6XmHpBZU/zWHw3BV3abl4Kvi+Ut7cQcY+cQ=
+golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs=
+golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q=
+golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
+golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
+golang.org/x/sys v0.44.0 h1:ildZl3J4uzeKP07r2F++Op7E9B29JRUy+a27EibtBTQ=
+golang.org/x/sys v0.44.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
+golang.org/x/term v0.43.0 h1:S4RLU2sB31O/NCl+zFN9Aru9A/Cq2aqKpTZJ6B+DwT4=
+golang.org/x/term v0.43.0/go.mod h1:lrhlHNdQJHO+1qVYiHfFKVuVioJIheAc3fBSMFYEIsk=
+golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc=
+golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38=
+golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U=
+golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno=
+golang.org/x/tools v0.44.0 h1:UP4ajHPIcuMjT1GqzDWRlalUEoY+uzoZKnhOjbIPD2c=
+golang.org/x/tools v0.44.0/go.mod h1:KA0AfVErSdxRZIsOVipbv3rQhVXTnlU6UhKxHd1seDI=
+gomodules.xyz/jsonpatch/v2 v2.5.0 h1:JELs8RLM12qJGXU4u/TO3V25KW8GreMKl9pdkk14RM0=
+gomodules.xyz/jsonpatch/v2 v2.5.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY=
+google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
+google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
+gopkg.in/evanphx/json-patch.v4 v4.13.0 h1:czT3CmqEaQ1aanPc5SdlgQrrEIb8w/wwCvWWnfEbYzo=
+gopkg.in/evanphx/json-patch.v4 v4.13.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M=
+gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
+gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+k8s.io/api v0.35.2 h1:tW7mWc2RpxW7HS4CoRXhtYHSzme1PN1UjGHJ1bdrtdw=
+k8s.io/api v0.35.2/go.mod h1:7AJfqGoAZcwSFhOjcGM7WV05QxMMgUaChNfLTXDRE60=
+k8s.io/apiextensions-apiserver v0.35.2 h1:iyStXHoJZsUXPh/nFAsjC29rjJWdSgUmG1XpApE29c0=
+k8s.io/apiextensions-apiserver v0.35.2/go.mod h1:OdyGvcO1FtMDWQ+rRh/Ei3b6X3g2+ZDHd0MSRGeS8rU=
+k8s.io/apimachinery v0.35.2 h1:NqsM/mmZA7sHW02JZ9RTtk3wInRgbVxL8MPfzSANAK8=
+k8s.io/apimachinery v0.35.2/go.mod h1:jQCgFZFR1F4Ik7hvr2g84RTJSZegBc8yHgFWKn//hns=
+k8s.io/client-go v0.35.2 h1:YUfPefdGJA4aljDdayAXkc98DnPkIetMl4PrKX97W9o=
+k8s.io/client-go v0.35.2/go.mod h1:4QqEwh4oQpeK8AaefZ0jwTFJw/9kIjdQi0jpKeYvz7g=
+k8s.io/klog/v2 v2.140.0 h1:Tf+J3AH7xnUzZyVVXhTgGhEKnFqye14aadWv7bzXdzc=
+k8s.io/klog/v2 v2.140.0/go.mod h1:o+/RWfJ6PwpnFn7OyAG3QnO47BFsymfEfrz6XyYSSp0=
+k8s.io/kube-openapi v0.0.0-20260304202019-5b3e3fdb0acf h1:btPscg4cMql0XdYK2jLsJcNEKmACJz8l+U7geC06FiM=
+k8s.io/kube-openapi v0.0.0-20260304202019-5b3e3fdb0acf/go.mod h1:kdmbQkyfwUagLfXIad1y2TdrjPFWp2Q89B3qkRwf/pQ=
+k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2 h1:AZYQSJemyQB5eRxqcPky+/7EdBj0xi3g0ZcxxJ7vbWU=
+k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2/go.mod h1:xDxuJ0whA3d0I4mf/C4ppKHxXynQ+fxnkmQH0vTHnuk=
+sigs.k8s.io/controller-runtime v0.23.3 h1:VjB/vhoPoA9l1kEKZHBMnQF33tdCLQKJtydy4iqwZ80=
+sigs.k8s.io/controller-runtime v0.23.3/go.mod h1:B6COOxKptp+YaUT5q4l6LqUJTRpizbgf9KSRNdQGns0=
+sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 h1:IpInykpT6ceI+QxKBbEflcR5EXP7sU1kvOlxwZh5txg=
+sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg=
+sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU=
+sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
+sigs.k8s.io/structured-merge-diff/v6 v6.3.2 h1:kwVWMx5yS1CrnFWA/2QHyRVJ8jM6dBA80uLmm0wJkk8=
+sigs.k8s.io/structured-merge-diff/v6 v6.3.2/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE=
+sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=
+sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=
+software.sslmate.com/src/go-pkcs12 v0.7.0 h1:Db8W44cB54TWD7stUFFSWxdfpdn6fZVcDl0w3R4RVM0=
+software.sslmate.com/src/go-pkcs12 v0.7.0/go.mod h1:Qiz0EyvDRJjjxGyUQa2cCNZn/wMyzrRJ/qcDXOQazLI=

+ 4 - 0
go.mod

@@ -5,6 +5,7 @@ go 1.26.4
 replace (
 	github.com/external-secrets/external-secrets/apis => ./apis
 	github.com/external-secrets/external-secrets/generators/v1/acr => ./generators/v1/acr
+	github.com/external-secrets/external-secrets/generators/v1/beyondtrustworkloadcredentials => ./generators/v1/beyondtrustworkloadcredentials
 	github.com/external-secrets/external-secrets/generators/v1/cloudsmith => ./generators/v1/cloudsmith
 	github.com/external-secrets/external-secrets/generators/v1/ecr => ./generators/v1/ecr
 	github.com/external-secrets/external-secrets/generators/v1/fake => ./generators/v1/fake
@@ -24,6 +25,7 @@ replace (
 	github.com/external-secrets/external-secrets/providers/v1/azure => ./providers/v1/azure
 	github.com/external-secrets/external-secrets/providers/v1/barbican => ./providers/v1/barbican
 	github.com/external-secrets/external-secrets/providers/v1/beyondtrust => ./providers/v1/beyondtrust
+	github.com/external-secrets/external-secrets/providers/v1/beyondtrustworkloadcredentials => ./providers/v1/beyondtrustworkloadcredentials
 	github.com/external-secrets/external-secrets/providers/v1/bitwarden => ./providers/v1/bitwarden
 	github.com/external-secrets/external-secrets/providers/v1/chef => ./providers/v1/chef
 	github.com/external-secrets/external-secrets/providers/v1/cloudru => ./providers/v1/cloudru
@@ -119,6 +121,7 @@ require github.com/1Password/connect-sdk-go v1.5.3 // indirect
 require (
 	github.com/external-secrets/external-secrets/apis v0.0.0
 	github.com/external-secrets/external-secrets/generators/v1/acr v0.0.0-00010101000000-000000000000
+	github.com/external-secrets/external-secrets/generators/v1/beyondtrustworkloadcredentials v0.0.0-00010101000000-000000000000
 	github.com/external-secrets/external-secrets/generators/v1/cloudsmith v0.0.0-00010101000000-000000000000
 	github.com/external-secrets/external-secrets/generators/v1/ecr v0.0.0-00010101000000-000000000000
 	github.com/external-secrets/external-secrets/generators/v1/fake v0.0.0-00010101000000-000000000000
@@ -138,6 +141,7 @@ require (
 	github.com/external-secrets/external-secrets/providers/v1/azure v0.0.0-20251103072335-a9b233b6936f
 	github.com/external-secrets/external-secrets/providers/v1/barbican v0.0.0-00010101000000-000000000000
 	github.com/external-secrets/external-secrets/providers/v1/beyondtrust v0.0.0-00010101000000-000000000000
+	github.com/external-secrets/external-secrets/providers/v1/beyondtrustworkloadcredentials v0.0.0-00010101000000-000000000000
 	github.com/external-secrets/external-secrets/providers/v1/bitwarden v0.0.0-00010101000000-000000000000
 	github.com/external-secrets/external-secrets/providers/v1/chef v0.0.0-00010101000000-000000000000
 	github.com/external-secrets/external-secrets/providers/v1/cloudru v0.0.0-00010101000000-000000000000

+ 34 - 0
pkg/register/beyondtrustworkloadcredentials.go

@@ -0,0 +1,34 @@
+//go:build beyondtrust || all_providers
+
+/*
+Copyright © The ESO Authors
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    https://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Package register provides explicit registration of all providers and generators.
+package register
+
+import (
+	esv1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1"
+	beyondtrustworkloadcredentials "github.com/external-secrets/external-secrets/providers/v1/beyondtrustworkloadcredentials"
+)
+
+func init() {
+	// Register beyondtrustworkloadcredentials provider
+	esv1.Register(
+		beyondtrustworkloadcredentials.NewProvider(),
+		beyondtrustworkloadcredentials.ProviderSpec(),
+		beyondtrustworkloadcredentials.MaintenanceStatus(),
+	)
+}

+ 2 - 0
pkg/register/generators.go

@@ -19,6 +19,7 @@ package register
 import (
 	genv1alpha1 "github.com/external-secrets/external-secrets/apis/generators/v1alpha1"
 	acr "github.com/external-secrets/external-secrets/generators/v1/acr"
+	beyondtrustworkloadcredentials "github.com/external-secrets/external-secrets/generators/v1/beyondtrustworkloadcredentials"
 	cloudsmith "github.com/external-secrets/external-secrets/generators/v1/cloudsmith"
 	ecr "github.com/external-secrets/external-secrets/generators/v1/ecr"
 	fakegen "github.com/external-secrets/external-secrets/generators/v1/fake"
@@ -38,6 +39,7 @@ import (
 func init() {
 	// Register all generators
 	genv1alpha1.Register(acr.Kind(), acr.NewGenerator())
+	genv1alpha1.Register(beyondtrustworkloadcredentials.Kind(), beyondtrustworkloadcredentials.NewGenerator())
 	genv1alpha1.Register(cloudsmith.Kind(), cloudsmith.NewGenerator())
 	genv1alpha1.Register(ecr.Kind(), ecr.NewGenerator())
 	genv1alpha1.Register(fakegen.Kind(), fakegen.NewGenerator())

+ 320 - 0
providers/v1/beyondtrustworkloadcredentials/client.go

@@ -0,0 +1,320 @@
+/*
+Copyright © The ESO Authors
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+	https://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Package beyondtrustworkloadcredentials provides a client for BeyondTrust Workload Credentials.
+package beyondtrustworkloadcredentials
+
+import (
+	"context"
+	"encoding/json"
+	"errors"
+	"fmt"
+	"path"
+	"regexp"
+	"strings"
+	"time"
+
+	corev1 "k8s.io/api/core/v1"
+
+	esv1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1"
+	"github.com/external-secrets/external-secrets/providers/v1/beyondtrustworkloadcredentials/httpclient"
+	btwcutil "github.com/external-secrets/external-secrets/providers/v1/beyondtrustworkloadcredentials/util"
+	"github.com/external-secrets/external-secrets/runtime/esutils"
+)
+
+const (
+	// ErrMsgNotImplemented is the error message for unimplemented methods.
+	ErrMsgNotImplemented = "not implemented: %s"
+
+	// validationTimeout is the timeout for SecretStore validation operations (network check and session validation).
+	// Set to 15 seconds to balance between allowing sufficient time for API responses and failing fast on connectivity issues.
+	validationTimeout = 15 * time.Second
+)
+
+// Client implements the SecretsClient interface for BeyondTrust Secrets.
+type Client struct {
+	beyondtrustWorkloadCredentialsClient btwcutil.Client
+	store                                *esv1.BeyondtrustWorkloadCredentialsProvider
+}
+
+// Validate checks if the client is configured correctly
+// and is able to retrieve secrets from the BeyondTrust Secrets provider.
+// If the validation result is unknown it will be ignored.
+func (c *Client) Validate() (esv1.ValidationResult, error) {
+	// Check for nil receiver
+	if c == nil {
+		return esv1.ValidationResultError, fmt.Errorf("client is nil")
+	}
+
+	// Check for nil beyondtrustWorkloadCredentialsClient
+	if c.beyondtrustWorkloadCredentialsClient == nil {
+		return esv1.ValidationResultError, fmt.Errorf("beyondtrustWorkloadCredentialsClient is not initialized")
+	}
+
+	// Check for nil BaseURL
+	baseURL := c.beyondtrustWorkloadCredentialsClient.BaseURL()
+	if baseURL == nil {
+		return esv1.ValidationResultError, fmt.Errorf("base URL is not configured")
+	}
+
+	clientURL := baseURL.String()
+	if err := esutils.NetworkValidate(clientURL, validationTimeout); err != nil {
+		return esv1.ValidationResultError, err
+	}
+
+	// Validate authentication by checking session
+	ctx, cancel := context.WithTimeout(context.Background(), validationTimeout)
+	defer cancel()
+
+	if err := c.beyondtrustWorkloadCredentialsClient.CheckSession(ctx); err != nil {
+		return esv1.ValidationResultError, fmt.Errorf("authentication validation failed: %w", err)
+	}
+
+	return esv1.ValidationResultReady, nil
+}
+
+// GetSecret returns a single secret from the BeyondTrust Secrets provider
+//
+//	if GetSecret returns an error with type NoSecretError
+//	then the secret entry will be deleted depending on the deletionPolicy.
+func (c *Client) GetSecret(ctx context.Context, ref esv1.ExternalSecretDataRemoteRef) ([]byte, error) {
+	folderPath := c.store.FolderPath
+
+	secret, err := c.beyondtrustWorkloadCredentialsClient.GetSecret(ctx, ref.Key, &folderPath)
+	if err != nil {
+		// Wrap 404s as NoSecretError to allow ESO deletionPolicy handling
+		var apiErr *httpclient.APIError
+		if errors.As(err, &apiErr) && apiErr.StatusCode == 404 {
+			return nil, esv1.NoSecretError{}
+		}
+		return nil, fmt.Errorf("failed to get secret: %w", err)
+	}
+
+	// Extract value from map
+	if secret.Secret == nil {
+		return nil, fmt.Errorf("secret value is nil")
+	}
+
+	// If there's a property key in the remote reference, use it
+	if ref.Property != "" {
+		value, ok := secret.Secret[ref.Property]
+		if !ok {
+			return nil, fmt.Errorf("property %s not found in secret", ref.Property)
+		}
+
+		// Handle different value types to preserve binary and object data
+		switch val := value.(type) {
+		case string:
+			return []byte(val), nil
+		case []byte:
+			return val, nil
+		default:
+			// non-string: marshal to JSON to preserve structure
+			b, err := json.Marshal(val)
+			if err != nil {
+				return nil, fmt.Errorf("failed to marshal secret value for property %q: %w", ref.Property, err)
+			}
+			return b, nil
+		}
+	}
+
+	// If no property specified, return the entire secret as JSON
+	secretBytes, err := json.Marshal(secret.Secret)
+	if err != nil {
+		return nil, fmt.Errorf("failed to marshal secret: %w", err)
+	}
+
+	return secretBytes, nil
+}
+
+// GetAllSecrets retrieves all secrets from BeyondTrust Secrets that match the given criteria.
+func (c *Client) GetAllSecrets(ctx context.Context, ref esv1.ExternalSecretFind) (map[string][]byte, error) {
+	// Determine folder path: use ref.Path as folder scope if provided, otherwise use store default
+	folderPath := c.store.FolderPath
+	if ref.Path != nil {
+		folderPath = strings.TrimSuffix(*ref.Path, "/")
+	}
+
+	result := map[string][]byte{}
+
+	// List all secrets in the folder (recursive to get all secrets under the path)
+	secretsList, err := c.beyondtrustWorkloadCredentialsClient.GetSecrets(ctx, &folderPath, true)
+	if err != nil {
+		// Treat 404 from listing API as NoSecretError (folder not found or empty)
+		var apiErr *httpclient.APIError
+		if errors.As(err, &apiErr) && apiErr.StatusCode == 404 {
+			return nil, esv1.NoSecretError{}
+		}
+		return nil, fmt.Errorf("failed to list secrets: %w", err)
+	}
+
+	// If no regexp provided, include everything. If regexp provided, filter names.
+	var nameRe *regexp.Regexp
+	if ref.Name != nil && ref.Name.RegExp != "" {
+		nameRe, err = regexp.Compile(ref.Name.RegExp)
+		if err != nil {
+			return nil, fmt.Errorf("invalid name regexp %q: %w", ref.Name.RegExp, err)
+		}
+	}
+
+	for _, item := range secretsList {
+		// item.Path may be a full path; split to derive folder/name as the API expects
+		dir, itemName := path.Split(item.Path)
+		dir = strings.TrimSuffix(dir, "/")
+		itemFolderPath := folderPath
+		if dir != "" {
+			itemFolderPath = dir
+		}
+
+		if nameRe != nil {
+			if !nameRe.MatchString(itemName) {
+				continue
+			}
+		}
+
+		// Fetch the full secret for this matched item
+		fullSecret, err := c.beyondtrustWorkloadCredentialsClient.GetSecret(ctx, itemName, &itemFolderPath)
+		if err != nil {
+			// In name-regex listing, skip missing items instead of failing the entire operation
+			var apiErr *httpclient.APIError
+			if errors.As(err, &apiErr) && apiErr.StatusCode == 404 {
+				continue
+			}
+			return nil, fmt.Errorf("failed to get secret at path %q: %w", path.Join(itemFolderPath, itemName), err)
+		}
+		if fullSecret == nil || fullSecret.Secret == nil {
+			// Skip empty/missing entries in list mode
+			continue
+		}
+
+		// Filter by tags if specified (all tags must match)
+		if ref.Tags != nil && len(ref.Tags) > 0 {
+			if fullSecret.Metadata == nil || !matchTags(fullSecret.Metadata.Tags, ref.Tags) {
+				continue
+			}
+		}
+
+		// Add each property in the secret namespaced by the secret path to avoid key collisions
+		// across secrets that share inner property names.
+		for k, v := range fullSecret.Secret {
+			key := item.Path + "/" + k
+			switch val := v.(type) {
+			case string:
+				result[key] = []byte(val)
+			case []byte:
+				result[key] = val
+			default:
+				// non-string: marshal to JSON to preserve structure
+				b, err := json.Marshal(val)
+				if err != nil {
+					return nil, fmt.Errorf("failed to marshal secret value for key %q: %w", key, err)
+				}
+				result[key] = b
+			}
+		}
+	}
+
+	// If no secrets matched the criteria, return NoSecretError
+	if len(result) == 0 {
+		return nil, esv1.NoSecretError{}
+	}
+
+	return result, nil
+}
+
+// GetSecretMap returns multiple k/v pairs from the BeyondTrust Secrets provider as separate keys.
+// Each key-value pair in the secret becomes a separate entry in the returned map.
+func (c *Client) GetSecretMap(ctx context.Context, ref esv1.ExternalSecretDataRemoteRef) (map[string][]byte, error) {
+	folderPath := c.store.FolderPath
+
+	secret, err := c.beyondtrustWorkloadCredentialsClient.GetSecret(ctx, ref.Key, &folderPath)
+	if err != nil {
+		// Wrap 404s as NoSecretError to allow ESO deletionPolicy handling
+		var apiErr *httpclient.APIError
+		if errors.As(err, &apiErr) && apiErr.StatusCode == 404 {
+			return nil, esv1.NoSecretError{}
+		}
+		return nil, fmt.Errorf("failed to get secret: %w", err)
+	}
+
+	if secret == nil || secret.Secret == nil {
+		return nil, fmt.Errorf("secret value is nil")
+	}
+
+	// Convert all k/v pairs to []byte, preserving structure for non-string values
+	result := make(map[string][]byte)
+	for k, v := range secret.Secret {
+		switch val := v.(type) {
+		case string:
+			result[k] = []byte(val)
+		case []byte:
+			result[k] = val
+		default:
+			// non-string: marshal to JSON to preserve structure
+			b, err := json.Marshal(val)
+			if err != nil {
+				return nil, fmt.Errorf("failed to marshal secret value for key %q: %w", k, err)
+			}
+			result[k] = b
+		}
+	}
+
+	return result, nil
+}
+
+// matchTags checks if all required tags (filter) are present in the secret's tags
+// with matching values. Returns true if all filter tags match, false otherwise.
+func matchTags(secretTags, filterTags map[string]string) bool {
+	if len(filterTags) == 0 {
+		return true
+	}
+	if len(secretTags) == 0 {
+		return false
+	}
+
+	// All filter tags must be present and match
+	for filterKey, filterValue := range filterTags {
+		secretValue, exists := secretTags[filterKey]
+		if !exists || secretValue != filterValue {
+			return false
+		}
+	}
+	return true
+}
+
+/////////////////////////
+// NOT YET IMPLEMENTED //
+/////////////////////////
+
+// PushSecret will write a single secret into the BeyondTrust Secrets provider.
+func (c *Client) PushSecret(_ context.Context, _ *corev1.Secret, _ esv1.PushSecretData) error {
+	return fmt.Errorf(ErrMsgNotImplemented, "PushSecret")
+}
+
+// DeleteSecret will delete the secret from the BeyondTrust Secrets provider.
+func (c *Client) DeleteSecret(_ context.Context, _ esv1.PushSecretRemoteRef) error {
+	return fmt.Errorf(ErrMsgNotImplemented, "DeleteSecret")
+}
+
+// SecretExists checks if a secret is already present in the BeyondTrust Secrets provider at the given location.
+func (c *Client) SecretExists(_ context.Context, _ esv1.PushSecretRemoteRef) (bool, error) {
+	return false, fmt.Errorf(ErrMsgNotImplemented, "SecretExists")
+}
+
+// Close implements cleanup operations for the BeyondTrust Secrets client.
+func (c *Client) Close(_ context.Context) error {
+	return nil
+}

+ 176 - 0
providers/v1/beyondtrustworkloadcredentials/fake/fake.go

@@ -0,0 +1,176 @@
+/*
+Copyright © The ESO Authors
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+	https://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package fake
+
+import (
+	"context"
+	"errors"
+	"net/url"
+
+	btwcutil "github.com/external-secrets/external-secrets/providers/v1/beyondtrustworkloadcredentials/util"
+)
+
+type BeyondtrustWorkloadCredentialsClient struct {
+	getSecret             func(ctx context.Context, name string, folderPath *string) (*btwcutil.KV, error)
+	getSecrets            func(ctx context.Context, folderPath *string, recursive bool) ([]btwcutil.KVListItem, error)
+	generateDynamicSecret func(ctx context.Context, name string, folderPath *string) (*btwcutil.GeneratedSecret, error)
+	getCalls              int
+}
+
+func (c *BeyondtrustWorkloadCredentialsClient) BaseURL() *url.URL {
+	return &url.URL{Scheme: "", Host: ""}
+}
+
+func (c *BeyondtrustWorkloadCredentialsClient) SetBaseURL(urlStr string) error {
+	return nil
+}
+
+func (c *BeyondtrustWorkloadCredentialsClient) CheckSession(ctx context.Context) error {
+	// By default, fake client returns success for session check
+	return nil
+}
+
+func (c *BeyondtrustWorkloadCredentialsClient) Authenticate() error {
+	return nil
+}
+
+func (c *BeyondtrustWorkloadCredentialsClient) GetSecret(ctx context.Context, name string, folderPath *string) (*btwcutil.KV, error) {
+	if c.getSecret == nil {
+		return nil, errors.New("GetSecret not configured in fake client")
+	}
+	return c.getSecret(ctx, name, folderPath)
+}
+
+func (c *BeyondtrustWorkloadCredentialsClient) GetSecrets(ctx context.Context, folderPath *string, recursive bool) ([]btwcutil.KVListItem, error) {
+	if c.getSecrets == nil {
+		return nil, errors.New("GetSecrets not configured in fake client")
+	}
+	return c.getSecrets(ctx, folderPath, recursive)
+}
+
+func (c *BeyondtrustWorkloadCredentialsClient) GenerateDynamicSecret(ctx context.Context, name string, folderPath *string) (*btwcutil.GeneratedSecret, error) {
+	if c.generateDynamicSecret == nil {
+		return nil, errors.New("GenerateDynamicSecret not implemented in fake")
+	}
+	return c.generateDynamicSecret(ctx, name, folderPath)
+}
+
+// WithValues sets up the fake client to return specific values or errors for GetSecret and GetSecrets calls.
+func (c *BeyondtrustWorkloadCredentialsClient) WithValues(
+	ctx context.Context,
+	name, folderPath *string,
+	getResponse *btwcutil.KV,
+	getAllResponse []btwcutil.KVListItem,
+	getErrMsg, listErrMsg *string,
+) {
+	if c == nil {
+		return
+	}
+
+	c.getSecret = func(ctxIn context.Context, nameIn string, folderPathIn *string) (*btwcutil.KV, error) {
+		// Check for nil/non-nil mismatches and value mismatches
+		if ctxIn != ctx ||
+			(name != nil && nameIn != *name) ||
+			(folderPathIn == nil) != (folderPath == nil) ||
+			(folderPathIn != nil && folderPath != nil && *folderPathIn != *folderPath) {
+			return nil, errors.New("unexpected test argument getSecret")
+		}
+
+		if getErrMsg == nil {
+			return getResponse, nil
+		}
+
+		return nil, errors.New(*getErrMsg)
+	}
+
+	c.getSecrets = func(ctxIn context.Context, folderPathIn *string, recursive bool) ([]btwcutil.KVListItem, error) {
+		// Check for nil/non-nil mismatches and value mismatches
+		if ctxIn != ctx ||
+			(folderPathIn == nil) != (folderPath == nil) ||
+			(folderPathIn != nil && folderPath != nil && *folderPathIn != *folderPath) {
+			return nil, errors.New("unexpected test argument getSecrets")
+		}
+
+		if listErrMsg == nil {
+			return getAllResponse, nil
+		}
+
+		return nil, errors.New(*listErrMsg)
+	}
+}
+
+// WithMultiValues sets up the fake client to return multiple responses for GetSecret calls in sequence, or errors for GetSecret and GetSecrets calls.
+func (c *BeyondtrustWorkloadCredentialsClient) WithMultiValues(
+	ctx context.Context,
+	names []string,
+	folderPath *string,
+	getResponses []btwcutil.KV,
+	getAllResponse []btwcutil.KVListItem,
+	getErrMsg, listErrMsg *string,
+) {
+	if c == nil {
+		return
+	}
+
+	c.getSecret = func(ctxIn context.Context, nameIn string, folderPathIn *string) (*btwcutil.KV, error) {
+		// Bounds check for names slice access
+		if len(names) > 0 && c.getCalls >= len(names) {
+			return nil, errors.New("getSecret called more times than configured responses")
+		}
+
+		// Check for nil/non-nil mismatches and value mismatches
+		if ctxIn != ctx ||
+			(len(names) > 0 && names[c.getCalls] != nameIn) ||
+			(folderPathIn == nil) != (folderPath == nil) ||
+			(folderPathIn != nil && folderPath != nil && *folderPathIn != *folderPath) {
+			return nil, errors.New("unexpected test argument in getSecret")
+		}
+
+		if getErrMsg == nil {
+			// Bounds check for getResponses slice access
+			if c.getCalls >= len(getResponses) {
+				return nil, errors.New("getSecret called more times than configured responses")
+			}
+			c.getCalls++
+			return &getResponses[c.getCalls-1], nil
+		}
+
+		return nil, errors.New(*getErrMsg)
+	}
+
+	c.getSecrets = func(ctxIn context.Context, folderPathIn *string, recursive bool) ([]btwcutil.KVListItem, error) {
+		// Check for nil/non-nil mismatches and value mismatches
+		if ctxIn != ctx ||
+			(folderPathIn == nil) != (folderPath == nil) ||
+			(folderPathIn != nil && folderPath != nil && *folderPathIn != *folderPath) {
+			return nil, errors.New("unexpected test argument in getSecrets")
+		}
+
+		if listErrMsg == nil {
+			return getAllResponse, nil
+		}
+
+		return nil, errors.New(*listErrMsg)
+	}
+}
+
+func (c *BeyondtrustWorkloadCredentialsClient) WithGenerateDynamicSecret(fn func(ctx context.Context, name string, folderPath *string) (*btwcutil.GeneratedSecret, error)) {
+	if c == nil {
+		return
+	}
+	c.generateDynamicSecret = fn
+}

+ 97 - 0
providers/v1/beyondtrustworkloadcredentials/go.mod

@@ -0,0 +1,97 @@
+module github.com/external-secrets/external-secrets/providers/v1/beyondtrustworkloadcredentials
+
+go 1.26.4
+
+require (
+	github.com/external-secrets/external-secrets/apis v0.0.0
+	github.com/external-secrets/external-secrets/runtime v0.0.0
+	github.com/google/go-cmp v0.7.0
+	k8s.io/api v0.35.2
+	sigs.k8s.io/controller-runtime v0.23.3
+)
+
+require (
+	dario.cat/mergo v1.0.2 // indirect
+	github.com/Masterminds/goutils v1.1.1 // indirect
+	github.com/Masterminds/semver/v3 v3.4.0 // indirect
+	github.com/beorn7/perks v1.0.1 // indirect
+	github.com/cespare/xxhash/v2 v2.3.0 // indirect
+	github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
+	github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.1 // indirect
+	github.com/emicklei/go-restful/v3 v3.13.0 // indirect
+	github.com/evanphx/json-patch/v5 v5.9.11 // indirect
+	github.com/fsnotify/fsnotify v1.9.0 // indirect
+	github.com/fxamacker/cbor/v2 v2.9.0 // indirect
+	github.com/go-logr/logr v1.4.3 // indirect
+	github.com/go-openapi/jsonpointer v0.22.5 // indirect
+	github.com/go-openapi/jsonreference v0.21.5 // indirect
+	github.com/go-openapi/swag v0.25.5 // indirect
+	github.com/go-openapi/swag/cmdutils v0.25.5 // indirect
+	github.com/go-openapi/swag/conv v0.25.5 // indirect
+	github.com/go-openapi/swag/fileutils v0.25.5 // indirect
+	github.com/go-openapi/swag/jsonname v0.25.5 // indirect
+	github.com/go-openapi/swag/jsonutils v0.25.5 // indirect
+	github.com/go-openapi/swag/loading v0.25.5 // indirect
+	github.com/go-openapi/swag/mangling v0.25.5 // indirect
+	github.com/go-openapi/swag/netutils v0.25.5 // indirect
+	github.com/go-openapi/swag/stringutils v0.25.5 // indirect
+	github.com/go-openapi/swag/typeutils v0.25.5 // indirect
+	github.com/go-openapi/swag/yamlutils v0.25.5 // indirect
+	github.com/goccy/go-json v0.10.5 // indirect
+	github.com/google/btree v1.1.3 // indirect
+	github.com/google/gnostic-models v0.7.1 // indirect
+	github.com/google/uuid v1.6.0 // indirect
+	github.com/huandu/xstrings v1.5.0 // indirect
+	github.com/json-iterator/go v1.1.12 // indirect
+	github.com/lestrrat-go/blackmagic v1.0.4 // indirect
+	github.com/lestrrat-go/httpcc v1.0.1 // indirect
+	github.com/lestrrat-go/httprc v1.0.6 // indirect
+	github.com/lestrrat-go/iter v1.0.2 // indirect
+	github.com/lestrrat-go/jwx/v2 v2.1.6 // indirect
+	github.com/lestrrat-go/option v1.0.1 // indirect
+	github.com/mitchellh/copystructure v1.2.0 // indirect
+	github.com/mitchellh/reflectwalk v1.0.2 // indirect
+	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
+	github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect
+	github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
+	github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
+	github.com/prometheus/client_golang v1.23.2 // indirect
+	github.com/prometheus/client_model v0.6.2 // indirect
+	github.com/prometheus/common v0.67.5 // indirect
+	github.com/prometheus/procfs v0.20.1 // indirect
+	github.com/segmentio/asm v1.2.1 // indirect
+	github.com/shopspring/decimal v1.4.0 // indirect
+	github.com/spf13/cast v1.10.0 // indirect
+	github.com/spf13/pflag v1.0.10 // indirect
+	github.com/x448/float16 v0.8.4 // indirect
+	go.yaml.in/yaml/v2 v2.4.4 // indirect
+	go.yaml.in/yaml/v3 v3.0.4 // indirect
+	golang.org/x/crypto v0.51.0 // indirect
+	golang.org/x/net v0.54.0 // indirect
+	golang.org/x/oauth2 v0.36.0 // indirect
+	golang.org/x/sync v0.20.0 // indirect
+	golang.org/x/sys v0.44.0 // indirect
+	golang.org/x/term v0.43.0 // indirect
+	golang.org/x/text v0.37.0 // indirect
+	golang.org/x/time v0.15.0 // indirect
+	gomodules.xyz/jsonpatch/v2 v2.5.0 // indirect
+	google.golang.org/protobuf v1.36.11 // indirect
+	gopkg.in/evanphx/json-patch.v4 v4.13.0 // indirect
+	gopkg.in/inf.v0 v0.9.1 // indirect
+	k8s.io/apiextensions-apiserver v0.35.2 // indirect
+	k8s.io/apimachinery v0.35.2 // indirect
+	k8s.io/client-go v0.35.2 // indirect
+	k8s.io/klog/v2 v2.140.0 // indirect
+	k8s.io/kube-openapi v0.0.0-20260304202019-5b3e3fdb0acf // indirect
+	k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2 // indirect
+	sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect
+	sigs.k8s.io/randfill v1.0.0 // indirect
+	sigs.k8s.io/structured-merge-diff/v6 v6.3.2 // indirect
+	sigs.k8s.io/yaml v1.6.0 // indirect
+	software.sslmate.com/src/go-pkcs12 v0.7.0 // indirect
+)
+
+replace (
+	github.com/external-secrets/external-secrets/apis => ../../../apis
+	github.com/external-secrets/external-secrets/runtime => ../../../runtime
+)

+ 238 - 0
providers/v1/beyondtrustworkloadcredentials/go.sum

@@ -0,0 +1,238 @@
+dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8=
+dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA=
+github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
+github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
+github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0=
+github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
+github.com/aws/aws-sdk-go-v2 v1.41.6 h1:1AX0AthnBQzMx1vbmir3Y4WsnJgiydmnJjiLu+LvXOg=
+github.com/aws/aws-sdk-go-v2 v1.41.6/go.mod h1:dy0UzBIfwSeot4grGvY1AqFWN5zgziMmWGzysDnHFcQ=
+github.com/aws/smithy-go v1.25.1 h1:J8ERsGSU7d+aCmdQur5Txg6bVoYelvQJgtZehD12GkI=
+github.com/aws/smithy-go v1.25.1/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc=
+github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
+github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
+github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
+github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
+github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.1 h1:5RVFMOWjMyRy8cARdy79nAmgYw3hK/4HUq48LQ6Wwqo=
+github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.1/go.mod h1:ZXNYxsqcloTdSy/rNShjYzMhyjf0LaoftYK0p+A3h40=
+github.com/emicklei/go-restful/v3 v3.13.0 h1:C4Bl2xDndpU6nJ4bc1jXd+uTmYPVUwkD6bFY/oTyCes=
+github.com/emicklei/go-restful/v3 v3.13.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
+github.com/evanphx/json-patch v0.5.2 h1:xVCHIVMUu1wtM/VkR9jVZ45N3FhZfYMMYGorLCR8P3k=
+github.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ=
+github.com/evanphx/json-patch/v5 v5.9.11 h1:/8HVnzMq13/3x9TPvjG08wUGqBTmZBsCWzjTM0wiaDU=
+github.com/evanphx/json-patch/v5 v5.9.11/go.mod h1:3j+LviiESTElxA4p3EMKAB9HXj3/XEtnUf6OZxqIQTM=
+github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
+github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
+github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
+github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
+github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM=
+github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ=
+github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
+github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
+github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ=
+github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg=
+github.com/go-openapi/jsonpointer v0.22.5 h1:8on/0Yp4uTb9f4XvTrM2+1CPrV05QPZXu+rvu2o9jcA=
+github.com/go-openapi/jsonpointer v0.22.5/go.mod h1:gyUR3sCvGSWchA2sUBJGluYMbe1zazrYWIkWPjjMUY0=
+github.com/go-openapi/jsonreference v0.21.5 h1:6uCGVXU/aNF13AQNggxfysJ+5ZcU4nEAe+pJyVWRdiE=
+github.com/go-openapi/jsonreference v0.21.5/go.mod h1:u25Bw85sX4E2jzFodh1FOKMTZLcfifd1Q+iKKOUxExw=
+github.com/go-openapi/swag v0.25.5 h1:pNkwbUEeGwMtcgxDr+2GBPAk4kT+kJ+AaB+TMKAg+TU=
+github.com/go-openapi/swag v0.25.5/go.mod h1:B3RT6l8q7X803JRxa2e59tHOiZlX1t8viplOcs9CwTA=
+github.com/go-openapi/swag/cmdutils v0.25.5 h1:yh5hHrpgsw4NwM9KAEtaDTXILYzdXh/I8Whhx9hKj7c=
+github.com/go-openapi/swag/cmdutils v0.25.5/go.mod h1:pdae/AFo6WxLl5L0rq87eRzVPm/XRHM3MoYgRMvG4A0=
+github.com/go-openapi/swag/conv v0.25.5 h1:wAXBYEXJjoKwE5+vc9YHhpQOFj2JYBMF2DUi+tGu97g=
+github.com/go-openapi/swag/conv v0.25.5/go.mod h1:CuJ1eWvh1c4ORKx7unQnFGyvBbNlRKbnRyAvDvzWA4k=
+github.com/go-openapi/swag/fileutils v0.25.5 h1:B6JTdOcs2c0dBIs9HnkyTW+5gC+8NIhVBUwERkFhMWk=
+github.com/go-openapi/swag/fileutils v0.25.5/go.mod h1:V3cT9UdMQIaH4WiTrUc9EPtVA4txS0TOmRURmhGF4kc=
+github.com/go-openapi/swag/jsonname v0.25.5 h1:8p150i44rv/Drip4vWI3kGi9+4W9TdI3US3uUYSFhSo=
+github.com/go-openapi/swag/jsonname v0.25.5/go.mod h1:jNqqikyiAK56uS7n8sLkdaNY/uq6+D2m2LANat09pKU=
+github.com/go-openapi/swag/jsonutils v0.25.5 h1:XUZF8awQr75MXeC+/iaw5usY/iM7nXPDwdG3Jbl9vYo=
+github.com/go-openapi/swag/jsonutils v0.25.5/go.mod h1:48FXUaz8YsDAA9s5AnaUvAmry1UcLcNVWUjY42XkrN4=
+github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.5 h1:SX6sE4FrGb4sEnnxbFL/25yZBb5Hcg1inLeErd86Y1U=
+github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.5/go.mod h1:/2KvOTrKWjVA5Xli3DZWdMCZDzz3uV/T7bXwrKWPquo=
+github.com/go-openapi/swag/loading v0.25.5 h1:odQ/umlIZ1ZVRteI6ckSrvP6e2w9UTF5qgNdemJHjuU=
+github.com/go-openapi/swag/loading v0.25.5/go.mod h1:I8A8RaaQ4DApxhPSWLNYWh9NvmX2YKMoB9nwvv6oW6g=
+github.com/go-openapi/swag/mangling v0.25.5 h1:hyrnvbQRS7vKePQPHHDso+k6CGn5ZBs5232UqWZmJZw=
+github.com/go-openapi/swag/mangling v0.25.5/go.mod h1:6hadXM/o312N/h98RwByLg088U61TPGiltQn71Iw0NY=
+github.com/go-openapi/swag/netutils v0.25.5 h1:LZq2Xc2QI8+7838elRAaPCeqJnHODfSyOa7ZGfxDKlU=
+github.com/go-openapi/swag/netutils v0.25.5/go.mod h1:lHbtmj4m57APG/8H7ZcMMSWzNqIQcu0RFiXrPUara14=
+github.com/go-openapi/swag/stringutils v0.25.5 h1:NVkoDOA8YBgtAR/zvCx5rhJKtZF3IzXcDdwOsYzrB6M=
+github.com/go-openapi/swag/stringutils v0.25.5/go.mod h1:PKK8EZdu4QJq8iezt17HM8RXnLAzY7gW0O1KKarrZII=
+github.com/go-openapi/swag/typeutils v0.25.5 h1:EFJ+PCga2HfHGdo8s8VJXEVbeXRCYwzzr9u4rJk7L7E=
+github.com/go-openapi/swag/typeutils v0.25.5/go.mod h1:itmFmScAYE1bSD8C4rS0W+0InZUBrB2xSPbWt6DLGuc=
+github.com/go-openapi/swag/yamlutils v0.25.5 h1:kASCIS+oIeoc55j28T4o8KwlV2S4ZLPT6G0iq2SSbVQ=
+github.com/go-openapi/swag/yamlutils v0.25.5/go.mod h1:Gek1/SjjfbYvM+Iq4QGwa/2lEXde9n2j4a3wI3pNuOQ=
+github.com/go-openapi/testify/enable/yaml/v2 v2.4.0 h1:7SgOMTvJkM8yWrQlU8Jm18VeDPuAvB/xWrdxFJkoFag=
+github.com/go-openapi/testify/enable/yaml/v2 v2.4.0/go.mod h1:14iV8jyyQlinc9StD7w1xVPW3CO3q1Gj04Jy//Kw4VM=
+github.com/go-openapi/testify/v2 v2.4.0 h1:8nsPrHVCWkQ4p8h1EsRVymA2XABB4OT40gcvAu+voFM=
+github.com/go-openapi/testify/v2 v2.4.0/go.mod h1:HCPmvFFnheKK2BuwSA0TbbdxJ3I16pjwMkYkP4Ywn54=
+github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
+github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
+github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
+github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
+github.com/gofrs/flock v0.13.0 h1:95JolYOvGMqeH31+FC7D2+uULf6mG61mEZ/A8dRYMzw=
+github.com/gofrs/flock v0.13.0/go.mod h1:jxeyy9R1auM5S6JYDBhDt+E2TCo7DkratH4Pgi8P+Z0=
+github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg=
+github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
+github.com/google/gnostic-models v0.7.1 h1:SisTfuFKJSKM5CPZkffwi6coztzzeYUhc3v4yxLWH8c=
+github.com/google/gnostic-models v0.7.1/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7OUGxBlw57miDrQ=
+github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
+github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
+github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
+github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/google/pprof v0.0.0-20260115054156-294ebfa9ad83 h1:z2ogiKUYzX5Is6zr/vP9vJGqPwcdqsWjOt+V8J7+bTc=
+github.com/google/pprof v0.0.0-20260115054156-294ebfa9ad83/go.mod h1:MxpfABSjhmINe3F1It9d+8exIHFvUqtLIRCdOGNXqiI=
+github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
+github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI=
+github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
+github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
+github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
+github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
+github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
+github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
+github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
+github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
+github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
+github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
+github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
+github.com/lestrrat-go/blackmagic v1.0.4 h1:IwQibdnf8l2KoO+qC3uT4OaTWsW7tuRQXy9TRN9QanA=
+github.com/lestrrat-go/blackmagic v1.0.4/go.mod h1:6AWFyKNNj0zEXQYfTMPfZrAXUWUfTIZ5ECEUEJaijtw=
+github.com/lestrrat-go/httpcc v1.0.1 h1:ydWCStUeJLkpYyjLDHihupbn2tYmZ7m22BGkcvZZrIE=
+github.com/lestrrat-go/httpcc v1.0.1/go.mod h1:qiltp3Mt56+55GPVCbTdM9MlqhvzyuL6W/NMDA8vA5E=
+github.com/lestrrat-go/httprc v1.0.6 h1:qgmgIRhpvBqexMJjA/PmwSvhNk679oqD1RbovdCGW8k=
+github.com/lestrrat-go/httprc v1.0.6/go.mod h1:mwwz3JMTPBjHUkkDv/IGJ39aALInZLrhBp0X7KGUZlo=
+github.com/lestrrat-go/iter v1.0.2 h1:gMXo1q4c2pHmC3dn8LzRhJfP1ceCbgSiT9lUydIzltI=
+github.com/lestrrat-go/iter v1.0.2/go.mod h1:Momfcq3AnRlRjI5b5O8/G5/BvpzrhoFTZcn06fEOPt4=
+github.com/lestrrat-go/jwx/v2 v2.1.6 h1:hxM1gfDILk/l5ylers6BX/Eq1m/pnxe9NBwW6lVfecA=
+github.com/lestrrat-go/jwx/v2 v2.1.6/go.mod h1:Y722kU5r/8mV7fYDifjug0r8FK8mZdw0K0GpJw/l8pU=
+github.com/lestrrat-go/option v1.0.1 h1:oAzP2fvZGQKWkvHa1/SAcFolBEca1oN+mQ7eooNBEYU=
+github.com/lestrrat-go/option v1.0.1/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I=
+github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
+github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
+github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
+github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
+github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
+github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8=
+github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
+github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
+github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
+github.com/onsi/ginkgo/v2 v2.28.0 h1:Rrf+lVLmtlBIKv6KrIGJCjyY8N36vDVcutbGJkyqjJc=
+github.com/onsi/ginkgo/v2 v2.28.0/go.mod h1:ArE1D/XhNXBXCBkKOLkbsb2c81dQHCRcF5zwn/ykDRo=
+github.com/onsi/gomega v1.39.1 h1:1IJLAad4zjPn2PsnhH70V4DKRFlrCzGBNrNaru+Vf28=
+github.com/onsi/gomega v1.39.1/go.mod h1:hL6yVALoTOxeWudERyfppUcZXjMwIMLnuSfruD2lcfg=
+github.com/oracle/oci-go-sdk/v65 v65.103.0 h1:HfyZx+JefCPK3At0Xt45q+wr914jDXuoyzOFX3XCbno=
+github.com/oracle/oci-go-sdk/v65 v65.103.0/go.mod h1:oB8jFGVc/7/zJ+DbleE8MzGHjhs2ioCz5stRTdZdIcY=
+github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
+github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
+github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o=
+github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg=
+github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
+github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
+github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4=
+github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw=
+github.com/prometheus/procfs v0.20.1 h1:XwbrGOIplXW/AU3YhIhLODXMJYyC1isLFfYCsTEycfc=
+github.com/prometheus/procfs v0.20.1/go.mod h1:o9EMBZGRyvDrSPH1RqdxhojkuXstoe4UlK79eF5TGGo=
+github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
+github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
+github.com/segmentio/asm v1.2.1 h1:DTNbBqs57ioxAD4PrArqftgypG4/qNpXoJx8TVXxPR0=
+github.com/segmentio/asm v1.2.1/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs=
+github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k=
+github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=
+github.com/sony/gobreaker v1.0.0 h1:feX5fGGXSl3dYd4aHZItw+FpHLvvoaqkawKjVNiFMNQ=
+github.com/sony/gobreaker v1.0.0/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY=
+github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY=
+github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo=
+github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
+github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
+github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
+github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
+github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
+github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
+github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM=
+github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78/go.mod h1:aL8wCCfTfSfmXjznFBSZNN13rSJjlIOI1fUNAtF7rmI=
+go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
+go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
+go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
+go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
+go.uber.org/zap v1.28.0 h1:IZzaP1Fv73/T/pBMLk4VutPl36uNC+OSUh3JLG3FIjo=
+go.uber.org/zap v1.28.0/go.mod h1:rDLpOi171uODNm/mxFcuYWxDsqWSAVkFdX4XojSKg/Q=
+go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ=
+go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ=
+go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
+go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
+golang.org/x/crypto v0.51.0 h1:IBPXwPfKxY7cWQZ38ZCIRPI50YLeevDLlLnyC5wRGTI=
+golang.org/x/crypto v0.51.0/go.mod h1:8AdwkbraGNABw2kOX6YFPs3WM22XqI4EXEd8g+x7Oc8=
+golang.org/x/mod v0.35.0 h1:Ww1D637e6Pg+Zb2KrWfHQUnH2dQRLBQyAtpr/haaJeM=
+golang.org/x/mod v0.35.0/go.mod h1:+GwiRhIInF8wPm+4AoT6L0FA1QWAad3OMdTRx4tFYlU=
+golang.org/x/net v0.54.0 h1:2zJIZAxAHV/OHCDTCOHAYehQzLfSXuf/5SoL/Dv6w/w=
+golang.org/x/net v0.54.0/go.mod h1:Sj4oj8jK6XmHpBZU/zWHw3BV3abl4Kvi+Ut7cQcY+cQ=
+golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs=
+golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q=
+golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
+golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
+golang.org/x/sys v0.44.0 h1:ildZl3J4uzeKP07r2F++Op7E9B29JRUy+a27EibtBTQ=
+golang.org/x/sys v0.44.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
+golang.org/x/term v0.43.0 h1:S4RLU2sB31O/NCl+zFN9Aru9A/Cq2aqKpTZJ6B+DwT4=
+golang.org/x/term v0.43.0/go.mod h1:lrhlHNdQJHO+1qVYiHfFKVuVioJIheAc3fBSMFYEIsk=
+golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc=
+golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38=
+golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U=
+golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno=
+golang.org/x/tools v0.44.0 h1:UP4ajHPIcuMjT1GqzDWRlalUEoY+uzoZKnhOjbIPD2c=
+golang.org/x/tools v0.44.0/go.mod h1:KA0AfVErSdxRZIsOVipbv3rQhVXTnlU6UhKxHd1seDI=
+gomodules.xyz/jsonpatch/v2 v2.5.0 h1:JELs8RLM12qJGXU4u/TO3V25KW8GreMKl9pdkk14RM0=
+gomodules.xyz/jsonpatch/v2 v2.5.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY=
+google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
+google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
+gopkg.in/evanphx/json-patch.v4 v4.13.0 h1:czT3CmqEaQ1aanPc5SdlgQrrEIb8w/wwCvWWnfEbYzo=
+gopkg.in/evanphx/json-patch.v4 v4.13.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M=
+gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
+gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+k8s.io/api v0.35.2 h1:tW7mWc2RpxW7HS4CoRXhtYHSzme1PN1UjGHJ1bdrtdw=
+k8s.io/api v0.35.2/go.mod h1:7AJfqGoAZcwSFhOjcGM7WV05QxMMgUaChNfLTXDRE60=
+k8s.io/apiextensions-apiserver v0.35.2 h1:iyStXHoJZsUXPh/nFAsjC29rjJWdSgUmG1XpApE29c0=
+k8s.io/apiextensions-apiserver v0.35.2/go.mod h1:OdyGvcO1FtMDWQ+rRh/Ei3b6X3g2+ZDHd0MSRGeS8rU=
+k8s.io/apimachinery v0.35.2 h1:NqsM/mmZA7sHW02JZ9RTtk3wInRgbVxL8MPfzSANAK8=
+k8s.io/apimachinery v0.35.2/go.mod h1:jQCgFZFR1F4Ik7hvr2g84RTJSZegBc8yHgFWKn//hns=
+k8s.io/client-go v0.35.2 h1:YUfPefdGJA4aljDdayAXkc98DnPkIetMl4PrKX97W9o=
+k8s.io/client-go v0.35.2/go.mod h1:4QqEwh4oQpeK8AaefZ0jwTFJw/9kIjdQi0jpKeYvz7g=
+k8s.io/klog/v2 v2.140.0 h1:Tf+J3AH7xnUzZyVVXhTgGhEKnFqye14aadWv7bzXdzc=
+k8s.io/klog/v2 v2.140.0/go.mod h1:o+/RWfJ6PwpnFn7OyAG3QnO47BFsymfEfrz6XyYSSp0=
+k8s.io/kube-openapi v0.0.0-20260304202019-5b3e3fdb0acf h1:btPscg4cMql0XdYK2jLsJcNEKmACJz8l+U7geC06FiM=
+k8s.io/kube-openapi v0.0.0-20260304202019-5b3e3fdb0acf/go.mod h1:kdmbQkyfwUagLfXIad1y2TdrjPFWp2Q89B3qkRwf/pQ=
+k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2 h1:AZYQSJemyQB5eRxqcPky+/7EdBj0xi3g0ZcxxJ7vbWU=
+k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2/go.mod h1:xDxuJ0whA3d0I4mf/C4ppKHxXynQ+fxnkmQH0vTHnuk=
+sigs.k8s.io/controller-runtime v0.23.3 h1:VjB/vhoPoA9l1kEKZHBMnQF33tdCLQKJtydy4iqwZ80=
+sigs.k8s.io/controller-runtime v0.23.3/go.mod h1:B6COOxKptp+YaUT5q4l6LqUJTRpizbgf9KSRNdQGns0=
+sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 h1:IpInykpT6ceI+QxKBbEflcR5EXP7sU1kvOlxwZh5txg=
+sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg=
+sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU=
+sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
+sigs.k8s.io/structured-merge-diff/v6 v6.3.2 h1:kwVWMx5yS1CrnFWA/2QHyRVJ8jM6dBA80uLmm0wJkk8=
+sigs.k8s.io/structured-merge-diff/v6 v6.3.2/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE=
+sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=
+sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=
+software.sslmate.com/src/go-pkcs12 v0.7.0 h1:Db8W44cB54TWD7stUFFSWxdfpdn6fZVcDl0w3R4RVM0=
+software.sslmate.com/src/go-pkcs12 v0.7.0/go.mod h1:Qiz0EyvDRJjjxGyUQa2cCNZn/wMyzrRJ/qcDXOQazLI=

+ 409 - 0
providers/v1/beyondtrustworkloadcredentials/httpclient/client.go

@@ -0,0 +1,409 @@
+/*
+Copyright © The ESO Authors
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+	https://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Package httpclient provides an HTTP client for interacting with BeyondTrust Workload Credentials API.
+// API Documentation: https://docs.beyondtrust.com/bt-docs/docs/secrets-api
+package httpclient
+
+import (
+	"context"
+	"crypto/tls"
+	"crypto/x509"
+	"encoding/json"
+	"fmt"
+	"io"
+	"net/http"
+	"net/url"
+	"strings"
+	"time"
+
+	btwcutil "github.com/external-secrets/external-secrets/providers/v1/beyondtrustworkloadcredentials/util"
+)
+
+const (
+	// API documentation URL.
+	apiDocsURL = "https://docs.beyondtrust.com/bt-docs/docs/secrets-api"
+
+	// API version header for BeyondTrust Workload Credentials.
+	apiVersionHeader = "bt-secrets-api-version"
+	apiVersion       = "2026-04-28"
+
+	// Default timeout for HTTP requests.
+	defaultTimeout = 30 * time.Second
+
+	// Maximum response body size to prevent unbounded memory allocation.
+	maxResponseBytes = 10 << 20 // 10 MiB
+)
+
+// Client represents a client for interacting with BeyondTrust Workload Credentials API.
+type Client struct {
+	httpClient *http.Client
+	baseURL    *url.URL
+	token      string
+}
+
+// NewClient creates a new BeyondTrust Workload Credentials HTTP client.
+func NewClient(serverURL, token string) (*Client, error) {
+	if err := validateServerURL(serverURL); err != nil {
+		return nil, err
+	}
+
+	parsedURL, err := url.Parse(strings.TrimSuffix(serverURL, "/"))
+	if err != nil {
+		return nil, fmt.Errorf("failed to parse server URL %q: %w", serverURL, err)
+	}
+
+	return &Client{
+		httpClient: &http.Client{
+			Timeout: defaultTimeout,
+		},
+		baseURL: parsedURL,
+		token:   token,
+	}, nil
+}
+
+// NewClientWithCustomCA creates a client using the provided PEM-encoded CA bundle.
+func NewClientWithCustomCA(serverURL, token string, caBundlePEM []byte) (*Client, error) {
+	if err := validateServerURL(serverURL); err != nil {
+		return nil, err
+	}
+
+	parsedURL, err := url.Parse(strings.TrimSuffix(serverURL, "/"))
+	if err != nil {
+		return nil, fmt.Errorf("failed to parse server URL %q: %w", serverURL, err)
+	}
+
+	httpClient := &http.Client{
+		Timeout: defaultTimeout,
+	}
+
+	if len(caBundlePEM) > 0 {
+		roots := x509.NewCertPool()
+		if !roots.AppendCertsFromPEM(caBundlePEM) {
+			return nil, fmt.Errorf("failed to parse CA bundle PEM")
+		}
+		// Clone the default transport to preserve default settings like ProxyFromEnvironment
+		transport := http.DefaultTransport.(*http.Transport).Clone()
+		transport.TLSClientConfig = &tls.Config{
+			RootCAs:    roots,
+			MinVersion: tls.VersionTLS12,
+		}
+		httpClient.Transport = transport
+	}
+
+	return &Client{
+		httpClient: httpClient,
+		baseURL:    parsedURL,
+		token:      token,
+	}, nil
+}
+
+// BaseURL returns the base URL of the API.
+func (c *Client) BaseURL() *url.URL {
+	if c.baseURL == nil {
+		return nil
+	}
+	u := *c.baseURL
+	return &u
+}
+
+// SetBaseURL sets the base URL for the API.
+func (c *Client) SetBaseURL(urlStr string) error {
+	if err := validateServerURL(urlStr); err != nil {
+		return err
+	}
+
+	baseURL, err := url.Parse(strings.TrimSuffix(urlStr, "/"))
+	if err != nil {
+		return fmt.Errorf("failed to parse base URL %q: %w", urlStr, err)
+	}
+
+	c.baseURL = baseURL
+	return nil
+}
+
+// CheckSession verifies if the current authentication session is valid.
+func (c *Client) CheckSession(ctx context.Context) error {
+	endpoint := fmt.Sprintf("%s/session", c.baseURL.String())
+
+	req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint, http.NoBody)
+	if err != nil {
+		return fmt.Errorf("failed to create session check request: %w", err)
+	}
+
+	c.setHeaders(req)
+
+	resp, err := c.httpClient.Do(req)
+	if err != nil {
+		return fmt.Errorf("failed to check session: %w", err)
+	}
+	defer func() { _ = resp.Body.Close() }()
+
+	body, err := io.ReadAll(io.LimitReader(resp.Body, maxResponseBytes))
+	if err != nil {
+		return fmt.Errorf("failed to read session check response: %w", err)
+	}
+
+	if resp.StatusCode == http.StatusOK {
+		// HTTP 200 indicates a valid session
+		return nil
+	}
+
+	return parseError(body, resp.StatusCode, "/session")
+}
+
+// GetSecret fetches a single secret by name from the specified folder path.
+func (c *Client) GetSecret(ctx context.Context, name string, folderPath *string) (*btwcutil.KV, error) {
+	path := formatPath(folderPath)
+
+	endpoint := fmt.Sprintf("%s/static/%s", c.baseURL.String(), url.PathEscape(name))
+
+	// The single-secret endpoint uses "folder" while the list endpoint uses "path". These are intentionally different parameter names.
+	if folderPath != nil && *folderPath != "" {
+		endpoint += fmt.Sprintf("?folder=%s", url.QueryEscape(*folderPath))
+	}
+
+	req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint, http.NoBody)
+	if err != nil {
+		return nil, fmt.Errorf("failed to create request: %w", err)
+	}
+
+	c.setHeaders(req)
+
+	resp, err := c.httpClient.Do(req)
+	if err != nil {
+		return nil, fmt.Errorf("failed to fetch secret %q at %q: %w", name, path, err)
+	}
+	defer func() { _ = resp.Body.Close() }()
+
+	body, err := io.ReadAll(io.LimitReader(resp.Body, maxResponseBytes))
+	if err != nil {
+		return nil, fmt.Errorf("failed to read response body: %w", err)
+	}
+
+	if resp.StatusCode == http.StatusOK {
+		var kv btwcutil.KV
+		if err := json.Unmarshal(body, &kv); err != nil {
+			return nil, fmt.Errorf("failed to unmarshal secret response: %w", err)
+		}
+		return &kv, nil
+	}
+
+	return nil, parseError(body, resp.StatusCode, fmt.Sprintf("%s/%s", path, name))
+}
+
+// GetSecrets fetches a list of secrets at the specified folder path.
+func (c *Client) GetSecrets(ctx context.Context, folderPath *string, recursive bool) ([]btwcutil.KVListItem, error) {
+	path := formatPath(folderPath)
+
+	endpoint := fmt.Sprintf("%s/static", c.baseURL.String())
+
+	// The list endpoint uses "path" (GET /static?path=...) per the API spec,
+	// while the single-secret endpoint uses "folder". These are intentionally different parameter names.
+	params := url.Values{}
+	if folderPath != nil && *folderPath != "" {
+		params.Set("path", *folderPath)
+	}
+	if recursive {
+		params.Set("recursive", "true")
+	}
+
+	// Add query string if there are parameters
+	if len(params) > 0 {
+		endpoint += "?" + params.Encode()
+	}
+
+	req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint, http.NoBody)
+	if err != nil {
+		return nil, fmt.Errorf("failed to create request: %w", err)
+	}
+
+	c.setHeaders(req)
+
+	resp, err := c.httpClient.Do(req)
+	if err != nil {
+		return nil, fmt.Errorf("failed to list secrets: %w", err)
+	}
+	defer func() { _ = resp.Body.Close() }()
+
+	body, err := io.ReadAll(io.LimitReader(resp.Body, maxResponseBytes))
+	if err != nil {
+		return nil, fmt.Errorf("failed to read response body: %w", err)
+	}
+
+	if resp.StatusCode == http.StatusOK {
+		var listResp struct {
+			Data  []btwcutil.KVListItem `json:"data"`
+			Error string                `json:"error,omitempty"`
+		}
+		if err := json.Unmarshal(body, &listResp); err != nil {
+			return nil, fmt.Errorf("failed to unmarshal list response: %w", err)
+		}
+
+		// Check for API error in response body even with 200 status
+		if listResp.Error != "" {
+			return nil, fmt.Errorf("beyondtrust API error: %s", listResp.Error)
+		}
+
+		// Empty folder is valid - return empty list
+		if len(listResp.Data) == 0 {
+			return []btwcutil.KVListItem{}, nil
+		}
+
+		return listResp.Data, nil
+	}
+
+	return nil, parseError(body, resp.StatusCode, path)
+}
+
+// GenerateDynamicSecret calls the dynamic secret generation endpoint.
+func (c *Client) GenerateDynamicSecret(ctx context.Context, secretName string, folderPath *string) (*btwcutil.GeneratedSecret, error) {
+	path := formatPath(folderPath)
+
+	endpoint := fmt.Sprintf("%s/dynamic/%s/generate", c.baseURL.String(), url.PathEscape(secretName))
+
+	// Add folder query parameter if specified
+	if folderPath != nil && *folderPath != "" {
+		endpoint += fmt.Sprintf("?folder=%s", url.QueryEscape(*folderPath))
+	}
+
+	req, err := http.NewRequestWithContext(ctx, http.MethodPost, endpoint, http.NoBody)
+	if err != nil {
+		return nil, fmt.Errorf("failed to create request: %w", err)
+	}
+
+	c.setHeaders(req)
+
+	resp, err := c.httpClient.Do(req)
+	if err != nil {
+		return nil, fmt.Errorf("failed to generate dynamic secret: %w", err)
+	}
+	defer func() { _ = resp.Body.Close() }()
+
+	body, err := io.ReadAll(io.LimitReader(resp.Body, maxResponseBytes))
+	if err != nil {
+		return nil, fmt.Errorf("failed to read response body: %w", err)
+	}
+
+	if resp.StatusCode == http.StatusCreated || resp.StatusCode == http.StatusOK {
+		var wrapped struct {
+			Secret btwcutil.GeneratedSecret `json:"secret"`
+		}
+		if err := json.Unmarshal(body, &wrapped); err != nil {
+			return nil, fmt.Errorf("failed to unmarshal generated secret response: %w", err)
+		}
+		return &wrapped.Secret, nil
+	}
+
+	return nil, parseError(body, resp.StatusCode, path)
+}
+
+// setHeaders adds required headers to the HTTP request.
+func (c *Client) setHeaders(req *http.Request) {
+	req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", c.token))
+	req.Header.Set(apiVersionHeader, apiVersion)
+	req.Header.Set("Content-Type", "application/json")
+	req.Header.Set("Accept", "application/json")
+}
+
+// validateServerURL checks if the provided server URL is valid.
+func validateServerURL(server string) error {
+	server = strings.TrimSpace(server)
+	if server == "" {
+		return fmt.Errorf("server URL is required")
+	}
+
+	parsedURL, err := url.ParseRequestURI(server)
+	if err != nil {
+		return fmt.Errorf("invalid server URL %q: %w", server, err)
+	}
+
+	// Validate that the URL has both scheme and host
+	if parsedURL.Scheme == "" {
+		return fmt.Errorf("server URL %q is missing scheme (e.g., https://)", server)
+	}
+	if parsedURL.Host == "" {
+		return fmt.Errorf("server URL %q is missing host", server)
+	}
+
+	return nil
+}
+
+// formatPath returns the string value of the given path pointer.
+func formatPath(pathPtr *string) string {
+	if pathPtr == nil || *pathPtr == "" {
+		return "/"
+	}
+	return *pathPtr
+}
+
+// parseError attempts to parse an error response from the API.
+func parseError(body []byte, statusCode int, path string) error {
+	var errResp errorResponse
+
+	// Try to parse structured error response
+	if err := json.Unmarshal(body, &errResp); err == nil {
+		var msg strings.Builder
+
+		if errResp.Error != "" {
+			msg.WriteString(errResp.Error)
+		}
+
+		if errResp.Message != "" {
+			if msg.Len() > 0 {
+				msg.WriteString(": ")
+			}
+			msg.WriteString(errResp.Message)
+		}
+
+		// Include details if present
+		if len(errResp.Details) > 0 {
+			detailsJSON, _ := json.Marshal(errResp.Details)
+			if msg.Len() > 0 {
+				msg.WriteString(" ")
+			}
+			msg.WriteString(fmt.Sprintf("(details: %s)", string(detailsJSON)))
+		}
+
+		if msg.Len() > 0 {
+			return &APIError{
+				StatusCode: statusCode,
+				Message:    fmt.Sprintf("API error (HTTP %d): %s at path %q (see %s)", statusCode, msg.String(), path, apiDocsURL),
+				Path:       path,
+			}
+		}
+	}
+
+	// Fallback error
+	return &APIError{
+		StatusCode: statusCode,
+		Message:    fmt.Sprintf("API error (HTTP %d): unexpected response at path %q (see %s)", statusCode, path, apiDocsURL),
+		Path:       path,
+	}
+}
+
+// Ensure Client implements btwcutil.Client interface.
+var _ btwcutil.Client = (*Client)(nil)
+
+// NewBeyondtrustWorkloadCredentialsClient is a wrapper for backward compatibility.
+func NewBeyondtrustWorkloadCredentialsClient(server, token string) (btwcutil.Client, error) {
+	return NewClient(server, token)
+}
+
+// NewBeyondtrustWorkloadCredentialsClientWithCustomCA is a wrapper for backward compatibility.
+func NewBeyondtrustWorkloadCredentialsClientWithCustomCA(server, token string, caBundlePEM []byte) (btwcutil.Client, error) {
+	return NewClientWithCustomCA(server, token, caBundlePEM)
+}

+ 43 - 0
providers/v1/beyondtrustworkloadcredentials/httpclient/types.go

@@ -0,0 +1,43 @@
+/*
+Copyright © The ESO Authors
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+	https://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package httpclient
+
+import "fmt"
+
+// APIError represents an error response from the BeyondTrust Workload Credentials API.
+type APIError struct {
+	StatusCode int
+	Message    string
+	Path       string
+}
+
+func (e *APIError) Error() string {
+	if e == nil {
+		return "<nil>"
+	}
+	if e.Message != "" {
+		return e.Message
+	}
+	return fmt.Sprintf("beyondtrust api error status=%d path=%q", e.StatusCode, e.Path)
+}
+
+// errorResponse represents the structure of an error response from the API.
+type errorResponse struct {
+	Error   string         `json:"error"`
+	Message string         `json:"message"`
+	Details map[string]any `json:"details"`
+}

+ 335 - 0
providers/v1/beyondtrustworkloadcredentials/provider.go

@@ -0,0 +1,335 @@
+/*
+Copyright © The ESO Authors
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+	https://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package beyondtrustworkloadcredentials
+
+import (
+	"context"
+	"errors"
+	"fmt"
+	"net/url"
+	"regexp"
+	"strings"
+
+	kclient "sigs.k8s.io/controller-runtime/pkg/client"
+	"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
+
+	esv1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1"
+	"github.com/external-secrets/external-secrets/providers/v1/beyondtrustworkloadcredentials/httpclient"
+	btwcutil "github.com/external-secrets/external-secrets/providers/v1/beyondtrustworkloadcredentials/util"
+	"github.com/external-secrets/external-secrets/runtime/esutils"
+	"github.com/external-secrets/external-secrets/runtime/esutils/resolvers"
+)
+
+var (
+	// ErrNoStore is returned when the BeyondtrustWorkloadCredentials SecretStore is missing or invalid.
+	ErrNoStore = errors.New("missing or invalid BeyondtrustWorkloadCredentials SecretStore")
+	// ErrNoAPIKey is returned when the API Token is missing or invalid.
+	ErrNoAPIKey = errors.New("missing or invalid BeyondtrustWorkloadCredentials API Token in BeyondtrustWorkloadCredentials SecretStore")
+	// ErrNoTokenName is returned when the API Token name is missing or invalid.
+	ErrNoTokenName = errors.New("missing or invalid BeyondtrustWorkloadCredentials API Token name in BeyondtrustWorkloadCredentials SecretStore")
+	// ErrNoTokenKey is returned when the API Token key is missing or invalid.
+	ErrNoTokenKey = errors.New("missing or invalid BeyondtrustWorkloadCredentials API Token key in BeyondtrustWorkloadCredentials SecretStore")
+	// ErrNoServer is returned when the BeyondtrustWorkloadCredentials Server is missing or invalid.
+	ErrNoServer = errors.New("missing or invalid BeyondtrustWorkloadCredentials Server in BeyondtrustWorkloadCredentials SecretStore")
+	// ErrNoAPIURL is returned when the Server API URL is missing or invalid.
+	ErrNoAPIURL = errors.New("missing or invalid BeyondtrustWorkloadCredentials Server API URL in BeyondtrustWorkloadCredentials SecretStore")
+	// ErrNoSiteID is returned when the Server site ID is missing or invalid.
+	ErrNoSiteID = errors.New("missing or invalid BeyondtrustWorkloadCredentials Server site ID in BeyondtrustWorkloadCredentials SecretStore")
+)
+
+// Provider is a BeyondtrustWorkloadCredentials provider implementing NewClient and ValidateStore for the esv1.Provider interface.
+type Provider struct {
+	// NewBeyondtrustWorkloadCredentialsClient is a function that returns a new BeyondTrust Secrets client.
+	// This is used for testing to inject a fake client.
+	NewBeyondtrustWorkloadCredentialsClient func(server, token string) (btwcutil.Client, error)
+}
+
+// https://github.com/external-secrets/external-secrets/issues/644
+var _ esv1.SecretsClient = &Client{}
+var _ esv1.Provider = &Provider{}
+
+// NewClient constructs a BeyondtrustWorkloadCredentials SecretsManager Provider.
+func (p *Provider) NewClient(ctx context.Context, store esv1.GenericStore, kube kclient.Client, namespace string) (esv1.SecretsClient, error) {
+	storeSpec := store.GetSpec()
+	if storeSpec == nil || storeSpec.Provider == nil || storeSpec.Provider.BeyondtrustWorkloadCredentials == nil {
+		return nil, ErrNoStore
+	}
+
+	BeyondtrustWorkloadCredentialsStoreSpec := storeSpec.Provider.BeyondtrustWorkloadCredentials
+	storeKind := store.GetKind()
+
+	// fetch server values from spec
+	serverURL, apiKey, err := fetchServerValuesFromSpec(ctx, BeyondtrustWorkloadCredentialsStoreSpec, kube, namespace, storeKind)
+	if err != nil {
+		return nil, err
+	}
+
+	// create BeyondtrustWorkloadCredentials client
+	BeyondtrustWorkloadCredentialsClient, err := p.newClient(ctx, serverURL, apiKey, BeyondtrustWorkloadCredentialsStoreSpec, kube, namespace, storeKind)
+	if err != nil {
+		return nil, fmt.Errorf("failed to create BeyondtrustWorkloadCredentials client: %w", err)
+	}
+
+	client := &Client{
+		beyondtrustWorkloadCredentialsClient: BeyondtrustWorkloadCredentialsClient,
+		store:                                BeyondtrustWorkloadCredentialsStoreSpec,
+	}
+
+	return client, nil
+}
+
+// newClient is a shared helper creates the appropriate BeyondtrustWorkloadCredentials client based on the provided spec.
+func (p *Provider) newClient(
+	ctx context.Context,
+	serverURL, apiKey string,
+	btSpec *esv1.BeyondtrustWorkloadCredentialsProvider,
+	kube kclient.Client,
+	namespace, storeKind string,
+) (btwcutil.Client, error) {
+	// Fetch CA from CABundle/CAProvider using ESO helper
+	var caCert []byte
+	var err error
+	if btSpec != nil {
+		caCert, err = esutils.FetchCACertFromSource(ctx, esutils.CreateCertOpts{
+			StoreKind:  storeKind,
+			Client:     kube,
+			Namespace:  namespace,
+			CABundle:   btSpec.CABundle,
+			CAProvider: btSpec.CAProvider,
+		})
+		if err != nil {
+			return nil, fmt.Errorf("failed to fetch CA certificate: %w", err)
+		}
+	}
+
+	if len(caCert) > 0 {
+		return httpclient.NewBeyondtrustWorkloadCredentialsClientWithCustomCA(serverURL, apiKey, caCert)
+	}
+	if p.NewBeyondtrustWorkloadCredentialsClient != nil {
+		return p.NewBeyondtrustWorkloadCredentialsClient(serverURL, apiKey)
+	}
+	return httpclient.NewBeyondtrustWorkloadCredentialsClient(serverURL, apiKey)
+}
+
+// NewGeneratorClient creates a new BeyondtrustWorkloadCredentials client for the generator controller.
+func (p *Provider) NewGeneratorClient(ctx context.Context, kube kclient.Client, btSpec *esv1.BeyondtrustWorkloadCredentialsProvider, namespace string) (btwcutil.Client, error) {
+	if btSpec == nil {
+		return nil, ErrNoStore
+	}
+
+	serverURL, apiKey, err := fetchServerValuesFromSpec(ctx, btSpec, kube, namespace, "")
+	if err != nil {
+		return nil, err
+	}
+
+	client, err := p.newClient(ctx, serverURL, apiKey, btSpec, kube, namespace, "")
+	if err != nil {
+		return nil, fmt.Errorf("failed to create BeyondtrustWorkloadCredentials client: %w", err)
+	}
+
+	return client, nil
+}
+
+// ValidateStore checks if the BeyondtrustWorkloadCredentials store is valid.
+// The provider may return a warning and an error.
+// The intended use of the warning to indicate a deprecation of behavior
+// or other type of message that is NOT a validation failure but should be noticed by the user.
+func (p *Provider) ValidateStore(store esv1.GenericStore) (admission.Warnings, error) {
+	storeSpec := store.GetSpec()
+	if storeSpec == nil || storeSpec.Provider == nil || storeSpec.Provider.BeyondtrustWorkloadCredentials == nil {
+		return nil, ErrNoStore
+	}
+
+	BeyondtrustWorkloadCredentialsStoreSpec := storeSpec.Provider.BeyondtrustWorkloadCredentials
+
+	// validate token selector
+	if BeyondtrustWorkloadCredentialsStoreSpec.Auth == nil {
+		return nil, ErrNoAPIKey
+	}
+	tokenRef := BeyondtrustWorkloadCredentialsStoreSpec.Auth.APIKey.Token
+	if err := esutils.ValidateSecretSelector(store, tokenRef); err != nil {
+		return nil, err
+	}
+	if tokenRef.Name == "" {
+		return nil, ErrNoTokenName
+	}
+
+	// validate server config is present and contains required fields
+	if BeyondtrustWorkloadCredentialsStoreSpec.Server == nil {
+		return nil, ErrNoServer
+	}
+	if BeyondtrustWorkloadCredentialsStoreSpec.Server.APIURL == "" {
+		return nil, ErrNoAPIURL
+	}
+
+	// Validate APIURL format
+	if err := validateAPIURL(BeyondtrustWorkloadCredentialsStoreSpec.Server.APIURL); err != nil {
+		return nil, fmt.Errorf("invalid apiUrl: %w", err)
+	}
+
+	if BeyondtrustWorkloadCredentialsStoreSpec.Server.SiteID == "" {
+		return nil, ErrNoSiteID
+	}
+
+	// Validate SiteID format (should be UUID)
+	if err := validateSiteID(BeyondtrustWorkloadCredentialsStoreSpec.Server.SiteID); err != nil {
+		return nil, fmt.Errorf("invalid siteId: %w", err)
+	}
+
+	return nil, nil
+}
+
+// Capabilities returns the BeyondtrustWorkloadCredentials provider Capabilities (Read, Write, ReadWrite).
+func (p *Provider) Capabilities() esv1.SecretStoreCapabilities {
+	return esv1.SecretStoreReadOnly
+}
+
+func loadAPIKeyFromSpec(ctx context.Context, spec *esv1.BeyondtrustWorkloadCredentialsProvider, kube kclient.Client, namespace, storeKind string) (string, error) {
+	if spec == nil {
+		return "", ErrNoStore
+	}
+	if spec.Auth == nil {
+		return "", ErrNoAPIKey
+	}
+
+	tokenRef := spec.Auth.APIKey.Token
+	if tokenRef.Name == "" {
+		return "", ErrNoTokenName
+	}
+	if tokenRef.Key == "" {
+		return "", ErrNoTokenKey
+	}
+
+	return resolvers.SecretKeyRef(ctx, kube, storeKind, namespace, &tokenRef)
+}
+
+func loadURLFromSpec(spec *esv1.BeyondtrustWorkloadCredentialsProvider) (string, string, error) {
+	if spec == nil {
+		return "", "", ErrNoStore
+	}
+	if spec.Server == nil {
+		return "", "", ErrNoServer
+	}
+
+	if spec.Server.APIURL == "" {
+		return "", "", ErrNoAPIURL
+	}
+
+	// Validate APIURL format
+	if err := validateAPIURL(spec.Server.APIURL); err != nil {
+		return "", "", fmt.Errorf("invalid apiUrl: %w", err)
+	}
+
+	if spec.Server.SiteID == "" {
+		return "", "", ErrNoSiteID
+	}
+
+	// Validate SiteID format
+	if err := validateSiteID(spec.Server.SiteID); err != nil {
+		return "", "", fmt.Errorf("invalid siteId: %w", err)
+	}
+
+	return spec.Server.APIURL, spec.Server.SiteID, nil
+}
+
+func fetchServerValuesFromSpec(ctx context.Context, spec *esv1.BeyondtrustWorkloadCredentialsProvider, kube kclient.Client, namespace, storeKind string) (string, string, error) {
+	if spec == nil {
+		return "", "", ErrNoStore
+	}
+
+	apiKey, err := loadAPIKeyFromSpec(ctx, spec, kube, namespace, storeKind)
+	if err != nil {
+		return "", "", fmt.Errorf("failed to load credentials: %w", err)
+	}
+
+	baseURL, siteID, err := loadURLFromSpec(spec)
+	if err != nil {
+		return "", "", fmt.Errorf("failed to load server URL configuration: %w", err)
+	}
+
+	// Normalize baseURL by removing trailing slash to prevent double slashes
+	baseURL = strings.TrimRight(baseURL, "/")
+	serverURL := fmt.Sprintf("%s/%s/secrets", baseURL, siteID)
+
+	return serverURL, apiKey, nil
+}
+
+// NewProvider creates a new Provider instance.
+func NewProvider() esv1.Provider {
+	return &Provider{
+		NewBeyondtrustWorkloadCredentialsClient: httpclient.NewBeyondtrustWorkloadCredentialsClient,
+	}
+}
+
+// ProviderSpec returns the provider specification for registration.
+func ProviderSpec() *esv1.SecretStoreProvider {
+	return &esv1.SecretStoreProvider{
+		BeyondtrustWorkloadCredentials: &esv1.BeyondtrustWorkloadCredentialsProvider{},
+	}
+}
+
+// MaintenanceStatus returns the maintenance status of the provider.
+func MaintenanceStatus() esv1.MaintenanceStatus {
+	return esv1.MaintenanceStatusMaintained
+}
+
+// validateAPIURL validates the BeyondTrust API URL format.
+func validateAPIURL(apiURL string) error {
+	if apiURL == "" {
+		return fmt.Errorf("apiUrl cannot be empty")
+	}
+
+	parsedURL, err := url.Parse(apiURL)
+	if err != nil {
+		return fmt.Errorf("failed to parse apiUrl: %w", err)
+	}
+
+	if parsedURL.Scheme == "" {
+		return fmt.Errorf("apiUrl must include a scheme (https)")
+	}
+
+	if parsedURL.Scheme != "https" {
+		return fmt.Errorf("apiUrl must use https scheme, got %q", parsedURL.Scheme)
+	}
+
+	if parsedURL.Host == "" {
+		return fmt.Errorf("apiUrl must include a host")
+	}
+
+	return nil
+}
+
+// validateSiteID validates the BeyondTrust site ID format (must be a valid UUID).
+func validateSiteID(siteID string) error {
+	if siteID == "" {
+		return fmt.Errorf("siteId cannot be empty")
+	}
+
+	// Validate UUID v4 format: xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx
+	// Where y is one of [8, 9, a, b] (RFC 4122 variant bits)
+	uuidPattern := `^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$`
+	matched, err := regexp.MatchString(uuidPattern, siteID)
+	if err != nil {
+		return fmt.Errorf("failed to validate siteId format: %w", err)
+	}
+
+	if !matched {
+		return fmt.Errorf("siteId must be a valid UUID format, got %q", siteID)
+	}
+
+	return nil
+}

+ 662 - 0
providers/v1/beyondtrustworkloadcredentials/provider_test.go

@@ -0,0 +1,662 @@
+/*
+Copyright © The ESO Authors
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+	https://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package beyondtrustworkloadcredentials
+
+import (
+	"context"
+	"encoding/json"
+	"fmt"
+	"path"
+	"strings"
+	"testing"
+
+	"github.com/google/go-cmp/cmp"
+
+	esv1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1"
+	"github.com/external-secrets/external-secrets/providers/v1/beyondtrustworkloadcredentials/fake"
+	btwcutil "github.com/external-secrets/external-secrets/providers/v1/beyondtrustworkloadcredentials/util"
+)
+
+const (
+	validSecretName   = "apikey"
+	validFolderPath   = "valid/folder/path"
+	invalidSecretName = "INVALID_NAME"
+	invalidFolderPath = "INVALID_PATH"
+)
+
+///////////////////////
+// test case structs //
+///////////////////////
+
+type beyondtrustworkloadcredentialsGetSecretTestCase struct {
+	label                 string
+	fakeBtsecretsClient   *fake.BeyondtrustWorkloadCredentialsClient
+	ctx                   context.Context
+	name                  *string
+	folderPath            *string
+	remoteRef             esv1.ExternalSecretDataRemoteRef
+	fakeBtsecretsResponse *btwcutil.KV
+	fakeBtsecretsError    *string
+	expectedError         *string
+	expectedResponse      []byte
+}
+
+type beyondtrustworkloadcredentialsGetAllSecretsTestCase struct {
+	label                     string
+	fakeBtsecretsClient       *fake.BeyondtrustWorkloadCredentialsClient
+	ctx                       context.Context
+	name                      *string
+	names                     []string
+	folderPath                *string
+	remoteRef                 esv1.ExternalSecretFind
+	fakeBtsecretsGetResponse  *btwcutil.KV
+	fakeBtsecretsGetResponses []btwcutil.KV
+	fakeBtsecretsListResponse []btwcutil.KVListItem
+	fakeBtsecretsGetError     *string
+	fakeBtsecretsListError    *string
+	expectedError             *string
+	expectedResponse          map[string][]byte
+}
+
+////////////
+// makers //
+////////////
+
+// makeValidGetSecretTestCase creates a valid test case for GetSecret tests.
+func makeValidGetSecretTestCase() *beyondtrustworkloadcredentialsGetSecretTestCase {
+	return &beyondtrustworkloadcredentialsGetSecretTestCase{
+		fakeBtsecretsClient: &fake.BeyondtrustWorkloadCredentialsClient{},
+		ctx:                 context.Background(),
+		name:                new(validSecretName),
+		folderPath:          new(validFolderPath),
+		remoteRef:           esv1.ExternalSecretDataRemoteRef{Key: validSecretName, Property: ""},
+	}
+}
+
+// makeValidGetAllSecretsTestCase creates a valid test case for GetSecrets tests.
+func makeValidGetAllSecretsTestCase() *beyondtrustworkloadcredentialsGetAllSecretsTestCase {
+	return &beyondtrustworkloadcredentialsGetAllSecretsTestCase{
+		fakeBtsecretsClient: &fake.BeyondtrustWorkloadCredentialsClient{},
+		ctx:                 context.Background(),
+		name:                new(validSecretName),
+		folderPath:          new(validFolderPath),
+		remoteRef:           esv1.ExternalSecretFind{},
+	}
+}
+
+// makeValidGetSecretTestCaseWithValues injects values into the faked BeyondtrustWorkloadCredentialsClient for GetSecret tests.
+func makeValidGetSecretTestCaseWithValues(tweaks ...func(tc *beyondtrustworkloadcredentialsGetSecretTestCase)) *beyondtrustworkloadcredentialsGetSecretTestCase {
+	vtc := makeValidGetSecretTestCase()
+	for _, fn := range tweaks {
+		fn(vtc)
+	}
+
+	vtc.fakeBtsecretsClient.WithValues(vtc.ctx, vtc.name, vtc.folderPath, vtc.fakeBtsecretsResponse, nil, vtc.fakeBtsecretsError, nil)
+
+	return vtc
+}
+
+// makeValidGetAllSecretsTestCaseWithValues injects values into the faked BeyondtrustWorkloadCredentialsClient for GetSecrets tests.
+func makeValidGetAllSecretsTestCaseWithValues(tweaks ...func(tc *beyondtrustworkloadcredentialsGetAllSecretsTestCase)) *beyondtrustworkloadcredentialsGetAllSecretsTestCase {
+	vtc := makeValidGetAllSecretsTestCase()
+	for _, fn := range tweaks {
+		fn(vtc)
+	}
+
+	vtc.fakeBtsecretsClient.WithValues(vtc.ctx, vtc.name, vtc.folderPath, vtc.fakeBtsecretsGetResponse, vtc.fakeBtsecretsListResponse, vtc.fakeBtsecretsGetError, vtc.fakeBtsecretsListError)
+
+	return vtc
+}
+
+// makeValidGetAllSecretsTestCaseWithMultiValues injects values with multiple GET responses into the faked BeyondtrustWorkloadCredentialsClient for GetSecrets tests.
+func makeValidGetAllSecretsTestCaseWithMultiValues(tweaks ...func(tc *beyondtrustworkloadcredentialsGetAllSecretsTestCase)) *beyondtrustworkloadcredentialsGetAllSecretsTestCase {
+	vtc := makeValidGetAllSecretsTestCase()
+	for _, fn := range tweaks {
+		fn(vtc)
+	}
+
+	vtc.fakeBtsecretsClient.WithMultiValues(vtc.ctx, vtc.names, vtc.folderPath, vtc.fakeBtsecretsGetResponses, vtc.fakeBtsecretsListResponse, vtc.fakeBtsecretsGetError, vtc.fakeBtsecretsListError)
+
+	return vtc
+}
+
+///////////
+// tests //
+///////////
+
+func TestGetSecret(t *testing.T) {
+	// happy paths
+
+	validSecret := func(tc *beyondtrustworkloadcredentialsGetSecretTestCase) {
+		fakeKV := &btwcutil.KV{
+			Path:   fmt.Sprintf("%s/%s", validFolderPath, validSecretName),
+			Secret: map[string]any{"valid": "test"},
+		}
+
+		fakeKVBytes, err := json.Marshal(fakeKV.Secret)
+		if err != nil {
+			t.Errorf("failed to marshal fake KV: %#v, error: %q", fakeKV, err.Error())
+		}
+
+		tc.label = "GetSecret - Valid"
+		tc.fakeBtsecretsResponse = fakeKV
+		tc.expectedResponse = fakeKVBytes
+	}
+
+	validSecretProperty := func(tc *beyondtrustworkloadcredentialsGetSecretTestCase) {
+		propertyValue := "test"
+
+		fakeKV := &btwcutil.KV{
+			Path:   fmt.Sprintf("%s/%s", validFolderPath, validSecretName),
+			Secret: map[string]any{"valid": propertyValue, "doNotInclude": "this"},
+		}
+
+		tc.label = "GetSecret - Valid Property"
+		tc.remoteRef = esv1.ExternalSecretDataRemoteRef{Key: validSecretName, Property: "valid"}
+		tc.fakeBtsecretsResponse = fakeKV
+		tc.expectedResponse = []byte(propertyValue)
+	}
+
+	// sad paths
+
+	clientError := func(tc *beyondtrustworkloadcredentialsGetSecretTestCase) {
+		tc.label = "GetSecret - Client Error"
+		tc.name = new(invalidSecretName)
+		tc.folderPath = new(invalidFolderPath)
+		tc.remoteRef = esv1.ExternalSecretDataRemoteRef{Key: invalidSecretName}
+		tc.fakeBtsecretsError = new("beyondtrustworkloadcredentials error")
+		tc.expectedError = new("failed to get secret")
+	}
+
+	nilSecret := func(tc *beyondtrustworkloadcredentialsGetSecretTestCase) {
+		fakeKV := &btwcutil.KV{
+			Path: fmt.Sprintf("%s/%s", invalidFolderPath, invalidSecretName),
+		}
+
+		tc.label = "GetSecret - Nil Secret"
+		tc.name = new(invalidSecretName)
+		tc.folderPath = new(invalidFolderPath)
+		tc.remoteRef = esv1.ExternalSecretDataRemoteRef{Key: invalidSecretName}
+		tc.fakeBtsecretsResponse = fakeKV
+		tc.expectedError = new("secret value is nil")
+	}
+
+	invalidSecretProperty := func(tc *beyondtrustworkloadcredentialsGetSecretTestCase) {
+		fakeKV := &btwcutil.KV{
+			Path:   fmt.Sprintf("%s/%s", invalidFolderPath, invalidSecretName),
+			Secret: map[string]any{"invalid": "test"},
+		}
+
+		ref := esv1.ExternalSecretDataRemoteRef{Key: invalidSecretName, Property: "nonexistant"}
+
+		tc.label = "GetSecret - Invalid Property"
+		tc.name = new(invalidSecretName)
+		tc.folderPath = new(invalidFolderPath)
+		tc.remoteRef = ref
+		tc.fakeBtsecretsResponse = fakeKV
+		tc.expectedError = new(fmt.Sprintf("property %s not found in secret", ref.Property))
+	}
+
+	invalidSecret := func(tc *beyondtrustworkloadcredentialsGetSecretTestCase) {
+		fakeKV := &btwcutil.KV{
+			Path:   fmt.Sprintf("%s/%s", invalidFolderPath, invalidSecretName),
+			Secret: map[string]any{"invalid": func() {}},
+		}
+
+		tc.label = "GetSecret - Invalid"
+		tc.name = new(invalidSecretName)
+		tc.folderPath = new(invalidFolderPath)
+		tc.remoteRef = esv1.ExternalSecretDataRemoteRef{Key: invalidSecretName}
+		tc.fakeBtsecretsResponse = fakeKV
+		tc.expectedError = new("failed to marshal secret")
+	}
+
+	testCases := []*beyondtrustworkloadcredentialsGetSecretTestCase{
+		// happy paths
+		makeValidGetSecretTestCaseWithValues(validSecret),
+		makeValidGetSecretTestCaseWithValues(validSecretProperty),
+		// sad paths
+		makeValidGetSecretTestCaseWithValues(clientError),
+		makeValidGetSecretTestCaseWithValues(nilSecret),
+		makeValidGetSecretTestCaseWithValues(invalidSecretProperty),
+		makeValidGetSecretTestCaseWithValues(invalidSecret),
+	}
+
+	c := Client{store: &esv1.BeyondtrustWorkloadCredentialsProvider{}}
+
+	for i, tc := range testCases {
+		t.Run(tc.label, func(t *testing.T) {
+			c.beyondtrustWorkloadCredentialsClient = tc.fakeBtsecretsClient
+			c.store.FolderPath = *tc.folderPath
+
+			out, err := c.GetSecret(context.Background(), tc.remoteRef)
+
+			// assert error
+
+			if tc.expectedError == nil && err != nil {
+				t.Errorf("[%d] unexpected error: expected nil, got %q", i, err.Error())
+			}
+
+			if tc.expectedError != nil && !ErrorContains(err, *tc.expectedError) {
+				t.Errorf("[%d] unexpected error: expected %q, got %q", i, *tc.expectedError, err)
+			}
+
+			// assert response
+
+			if tc.expectedResponse == nil && out != nil {
+				t.Errorf("[%d] unexpected response: expected nil, got %#v", i, out)
+			}
+
+			if tc.expectedResponse != nil && !cmp.Equal(out, tc.expectedResponse) {
+				t.Errorf("[%d] unexpected response: expected %#v, got %#v", i, tc.expectedResponse, out)
+			}
+		})
+	}
+}
+
+func ErrorContains(out error, want string) bool {
+	if out == nil {
+		return want == ""
+	}
+	if want == "" {
+		return false
+	}
+	return strings.Contains(out.Error(), want)
+}
+
+func TestGetAllSecrets(t *testing.T) {
+	// happy paths
+
+	validSecretKVs := func(tc *beyondtrustworkloadcredentialsGetAllSecretsTestCase) {
+		fakeKV := &btwcutil.KV{
+			Path:   fmt.Sprintf("%s/%s", validFolderPath, validSecretName),
+			Secret: map[string]any{"key1": "val1", "key2": "val2", "key3": "val3"},
+		}
+
+		fakeListItem := btwcutil.KVListItem{
+			Path: fakeKV.Path,
+		}
+
+		fakeKVBytes := map[string][]byte{
+			fakeKV.Path + "/key1": []byte("val1"),
+			fakeKV.Path + "/key2": []byte("val2"),
+			fakeKV.Path + "/key3": []byte("val3"),
+		}
+
+		tc.label = "GetAllSecrets - Secret KVs - Valid"
+		tc.remoteRef = esv1.ExternalSecretFind{Path: new(validFolderPath)}
+		tc.fakeBtsecretsListResponse = []btwcutil.KVListItem{fakeListItem}
+		tc.fakeBtsecretsGetResponse = fakeKV
+		tc.expectedResponse = fakeKVBytes
+	}
+
+	validNamedSecrets := func(tc *beyondtrustworkloadcredentialsGetAllSecretsTestCase) {
+		findName := fmt.Sprintf("%s-include", validSecretName)
+
+		fakeKV1 := btwcutil.KV{
+			Path:   fmt.Sprintf("%s/%s-fakeKV1", validFolderPath, findName),
+			Secret: map[string]any{"key1": "val1", "key2": "val2", "key3": "val3"},
+		}
+
+		fakeKV2 := btwcutil.KV{
+			Path: fmt.Sprintf("%s/%s", validFolderPath, validSecretName),
+		}
+
+		fakeKV3 := btwcutil.KV{
+			Path:   fmt.Sprintf("%s/%s-fakeKV3", validFolderPath, findName),
+			Secret: map[string]any{"key6": "val6"},
+		}
+
+		fakeListItem1 := btwcutil.KVListItem{
+			Path: fakeKV1.Path,
+		}
+
+		fakeListItem2 := btwcutil.KVListItem{
+			Path: fakeKV2.Path,
+		}
+
+		fakeListItem3 := btwcutil.KVListItem{
+			Path: fakeKV3.Path,
+		}
+
+		fakeResponseBytes := map[string][]byte{
+			fakeKV1.Path + "/key1": []byte("val1"),
+			fakeKV1.Path + "/key2": []byte("val2"),
+			fakeKV1.Path + "/key3": []byte("val3"),
+			fakeKV3.Path + "/key6": []byte("val6"),
+		}
+
+		_, name1 := path.Split(fakeListItem1.Path)
+		_, name3 := path.Split(fakeListItem3.Path)
+
+		tc.label = "GetAllSecrets - Secret KVs - Valid Find RegExp"
+		tc.names = []string{name1, name3}
+		tc.remoteRef = esv1.ExternalSecretFind{Name: &esv1.FindName{RegExp: fmt.Sprintf("^%s.*", findName)}}
+		tc.fakeBtsecretsGetResponses = []btwcutil.KV{fakeKV1, fakeKV3}
+		tc.fakeBtsecretsListResponse = []btwcutil.KVListItem{fakeListItem1, fakeListItem2, fakeListItem3}
+		tc.expectedResponse = fakeResponseBytes
+	}
+
+	validAllSecrets := func(tc *beyondtrustworkloadcredentialsGetAllSecretsTestCase) {
+		findPath := fmt.Sprintf("%s/%s-include", validFolderPath, validSecretName)
+
+		fakeKV1 := btwcutil.KV{
+			Path:   fmt.Sprintf("%s-fakeKV1", findPath),
+			Secret: map[string]any{"key1": "val1", "key2": "val2", "key3": "val3"},
+		}
+
+		fakeKV2 := btwcutil.KV{
+			Path:   fmt.Sprintf("%s/%s", validFolderPath, validSecretName),
+			Secret: map[string]any{"key4": "val4", "key5": "val5"},
+		}
+
+		fakeKV3 := btwcutil.KV{
+			Path:   fmt.Sprintf("%s-fakeKV3", findPath),
+			Secret: map[string]any{"key6": "val6"},
+		}
+
+		fakeListItem1 := btwcutil.KVListItem{
+			Path: fmt.Sprintf("%s-fakeKV1", findPath),
+		}
+
+		fakeListItem2 := btwcutil.KVListItem{
+			Path: fmt.Sprintf("%s/%s", validFolderPath, validSecretName),
+		}
+
+		fakeListItem3 := btwcutil.KVListItem{
+			Path: fmt.Sprintf("%s-fakeKV3", findPath),
+		}
+
+		fakeResponseBytes := map[string][]byte{
+			fakeKV1.Path + "/key1": []byte("val1"),
+			fakeKV1.Path + "/key2": []byte("val2"),
+			fakeKV1.Path + "/key3": []byte("val3"),
+			fakeKV2.Path + "/key4": []byte("val4"),
+			fakeKV2.Path + "/key5": []byte("val5"),
+			fakeKV3.Path + "/key6": []byte("val6"),
+		}
+
+		_, name1 := path.Split(fakeListItem1.Path)
+		_, name2 := path.Split(fakeListItem2.Path)
+		_, name3 := path.Split(fakeListItem3.Path)
+
+		tc.label = "GetAllSecrets - List Secrets - Valid"
+		tc.names = []string{name1, name2, name3}
+		tc.fakeBtsecretsGetResponses = []btwcutil.KV{fakeKV1, fakeKV2, fakeKV3}
+		tc.fakeBtsecretsListResponse = []btwcutil.KVListItem{fakeListItem1, fakeListItem2, fakeListItem3}
+		tc.expectedResponse = fakeResponseBytes
+	}
+
+	// sad paths
+
+	clientGetError := func(tc *beyondtrustworkloadcredentialsGetAllSecretsTestCase) {
+		tc.label = "GetAllSecrets - Secret KVs - Client Error"
+		tc.name = new(invalidSecretName)
+		tc.folderPath = new(invalidFolderPath)
+		tc.remoteRef = esv1.ExternalSecretFind{Path: new(invalidFolderPath)}
+		tc.fakeBtsecretsListError = new("beyondtrustworkloadcredentials list error")
+		tc.expectedError = new("failed to list secrets:")
+	}
+
+	nilSecret := func(tc *beyondtrustworkloadcredentialsGetAllSecretsTestCase) {
+		fakeKV := &btwcutil.KV{
+			Path: fmt.Sprintf("%s/%s", invalidFolderPath, invalidSecretName),
+		}
+
+		fakeListItem := btwcutil.KVListItem{
+			Path: fakeKV.Path,
+		}
+
+		tc.label = "GetAllSecrets - Secret KVs - Nil Secret"
+		tc.name = new(invalidSecretName)
+		tc.folderPath = new(invalidFolderPath)
+		tc.remoteRef = esv1.ExternalSecretFind{Path: new(invalidFolderPath)}
+		tc.fakeBtsecretsListResponse = []btwcutil.KVListItem{fakeListItem}
+		tc.fakeBtsecretsGetResponse = fakeKV
+		// Provider now returns NoSecretError when no results are found
+		tc.expectedError = new("Secret does not exist")
+	}
+
+	invalidSecret := func(tc *beyondtrustworkloadcredentialsGetAllSecretsTestCase) {
+		fakeKV := &btwcutil.KV{
+			Path:   fmt.Sprintf("%s/%s", invalidFolderPath, invalidSecretName),
+			Secret: map[string]any{"invalid": func() {}},
+		}
+
+		fakeListItem := btwcutil.KVListItem{
+			Path: fakeKV.Path,
+		}
+
+		tc.label = "GetAllSecrets - Secret KVs - Invalid Secret"
+		tc.name = new(invalidSecretName)
+		tc.folderPath = new(invalidFolderPath)
+		tc.remoteRef = esv1.ExternalSecretFind{Path: new(invalidFolderPath)}
+		tc.fakeBtsecretsListResponse = []btwcutil.KVListItem{fakeListItem}
+		tc.fakeBtsecretsGetResponse = fakeKV
+		tc.expectedError = new("failed to marshal secret value for key")
+	}
+
+	clientListError := func(tc *beyondtrustworkloadcredentialsGetAllSecretsTestCase) {
+		tc.label = "GetAllSecrets - List Secrets - Client Error"
+		tc.name = new(invalidSecretName)
+		tc.folderPath = new(invalidFolderPath)
+		tc.fakeBtsecretsListError = new("beyondtrustworkloadcredentials list error")
+		tc.expectedError = new("failed to list secrets:")
+	}
+
+	invalidFindRegex := func(tc *beyondtrustworkloadcredentialsGetAllSecretsTestCase) {
+		tc.label = "GetAllSecrets - List Secrets - Invalid Find RegExp"
+		tc.name = new(invalidSecretName)
+		tc.folderPath = new(invalidFolderPath)
+		tc.remoteRef = esv1.ExternalSecretFind{Name: &esv1.FindName{RegExp: "[invalid-regex"}}
+		tc.expectedError = new("invalid name regexp")
+	}
+
+	clientGetErrorInList := func(tc *beyondtrustworkloadcredentialsGetAllSecretsTestCase) {
+		tc.label = "GetAllSecrets - List Secrets - Get KVs - Client Error"
+		tc.name = new(invalidSecretName)
+		tc.folderPath = new(invalidFolderPath)
+		tc.fakeBtsecretsListResponse = []btwcutil.KVListItem{{}}
+		tc.fakeBtsecretsGetError = new("beyondtrustworkloadcredentials get error in list")
+		tc.expectedError = new("failed to get secret at path")
+	}
+
+	nilSecretInList := func(tc *beyondtrustworkloadcredentialsGetAllSecretsTestCase) {
+		fakeKV := &btwcutil.KV{
+			Path: fmt.Sprintf("%s/%s", invalidFolderPath, invalidSecretName),
+		}
+
+		tc.label = "GetAllSecrets - List Secrets - Get KVs - Nil Secret"
+		tc.name = new(invalidSecretName)
+		tc.folderPath = new(invalidFolderPath)
+		tc.fakeBtsecretsGetResponse = fakeKV
+		tc.fakeBtsecretsListResponse = []btwcutil.KVListItem{{Path: fakeKV.Path}}
+		// In list mode, skip missing entries; when no results are found, return NoSecretError
+		tc.expectedError = new("Secret does not exist")
+	}
+
+	invalidSecretInList := func(tc *beyondtrustworkloadcredentialsGetAllSecretsTestCase) {
+		fakeKV := &btwcutil.KV{
+			Path:   fmt.Sprintf("%s/%s", invalidFolderPath, invalidSecretName),
+			Secret: map[string]any{"invalid": func() {}},
+		}
+
+		tc.label = "GetAllSecrets - List Secrets - Get KVs - Invalid"
+		tc.name = new(invalidSecretName)
+		tc.folderPath = new(invalidFolderPath)
+		tc.fakeBtsecretsGetResponse = fakeKV
+		tc.fakeBtsecretsListResponse = []btwcutil.KVListItem{{Path: fakeKV.Path}}
+		tc.expectedError = new("failed to marshal secret value for key")
+	}
+
+	testCases := []*beyondtrustworkloadcredentialsGetAllSecretsTestCase{
+		// happy paths
+		makeValidGetAllSecretsTestCaseWithValues(validSecretKVs),
+		makeValidGetAllSecretsTestCaseWithMultiValues(validNamedSecrets),
+		makeValidGetAllSecretsTestCaseWithMultiValues(validAllSecrets),
+		// sad paths
+		makeValidGetAllSecretsTestCaseWithValues(clientGetError),
+		makeValidGetAllSecretsTestCaseWithValues(nilSecret),
+		makeValidGetAllSecretsTestCaseWithValues(invalidSecret),
+		makeValidGetAllSecretsTestCaseWithValues(clientListError),
+		makeValidGetAllSecretsTestCaseWithValues(invalidFindRegex),
+		makeValidGetAllSecretsTestCaseWithValues(clientGetErrorInList),
+		makeValidGetAllSecretsTestCaseWithValues(nilSecretInList),
+		makeValidGetAllSecretsTestCaseWithValues(invalidSecretInList),
+	}
+
+	c := Client{store: &esv1.BeyondtrustWorkloadCredentialsProvider{}}
+
+	for i, tc := range testCases {
+		t.Run(tc.label, func(t *testing.T) {
+			c.beyondtrustWorkloadCredentialsClient = tc.fakeBtsecretsClient
+			c.store.FolderPath = *tc.folderPath
+
+			out, err := c.GetAllSecrets(context.Background(), tc.remoteRef)
+
+			// assert error
+
+			if tc.expectedError == nil && err != nil {
+				t.Errorf("[%d] unexpected error: expected nil, got %q", i, err.Error())
+			}
+
+			if tc.expectedError != nil && !ErrorContains(err, *tc.expectedError) {
+				t.Errorf("[%d] unexpected error: expected %q, got %q", i, *tc.expectedError, err)
+			}
+
+			// assert response
+
+			if tc.expectedResponse == nil && out != nil {
+				t.Errorf("[%d] unexpected response: expected nil, got %#v", i, out)
+			}
+
+			if tc.expectedResponse != nil && !cmp.Equal(out, tc.expectedResponse) {
+				t.Errorf("[%d] unexpected response: expected %#v, got %#v", i, tc.expectedResponse, out)
+			}
+		})
+	}
+}
+
+func TestGetSecretMap(t *testing.T) {
+	// happy paths
+
+	validSecretMap := func(tc *beyondtrustworkloadcredentialsGetSecretTestCase) {
+		fakeKV := &btwcutil.KV{
+			Path:   fmt.Sprintf("%s/%s", validFolderPath, validSecretName),
+			Secret: map[string]any{"username": "admin", "password": "secret123", "port": 5432},
+		}
+
+		tc.label = "GetSecretMap - Valid"
+		tc.fakeBtsecretsResponse = fakeKV
+		// For GetSecretMap tests, we use a map comparison, not a single byte response
+		// This test will use a custom comparison below
+	}
+
+	validSecretMapWithTypes := func(tc *beyondtrustworkloadcredentialsGetSecretTestCase) {
+		fakeKV := &btwcutil.KV{
+			Path: fmt.Sprintf("%s/%s", validFolderPath, validSecretName),
+			Secret: map[string]any{
+				"username": "admin",
+				"config":   map[string]string{"env": "prod", "region": "us-east-1"},
+			},
+		}
+
+		tc.label = "GetSecretMap - Valid With Complex Types"
+		tc.fakeBtsecretsResponse = fakeKV
+	}
+
+	// sad paths
+
+	clientError := func(tc *beyondtrustworkloadcredentialsGetSecretTestCase) {
+		tc.label = "GetSecretMap - Client Error"
+		tc.name = new(invalidSecretName)
+		tc.folderPath = new(invalidFolderPath)
+		tc.remoteRef = esv1.ExternalSecretDataRemoteRef{Key: invalidSecretName}
+		tc.fakeBtsecretsError = new("beyondtrustworkloadcredentials error")
+		tc.expectedError = new("failed to get secret")
+	}
+
+	nilSecret := func(tc *beyondtrustworkloadcredentialsGetSecretTestCase) {
+		fakeKV := &btwcutil.KV{
+			Path: fmt.Sprintf("%s/%s", invalidFolderPath, invalidSecretName),
+		}
+
+		tc.label = "GetSecretMap - Nil Secret"
+		tc.name = new(invalidSecretName)
+		tc.folderPath = new(invalidFolderPath)
+		tc.remoteRef = esv1.ExternalSecretDataRemoteRef{Key: invalidSecretName}
+		tc.fakeBtsecretsResponse = fakeKV
+		tc.expectedError = new("secret value is nil")
+	}
+
+	invalidSecret := func(tc *beyondtrustworkloadcredentialsGetSecretTestCase) {
+		fakeKV := &btwcutil.KV{
+			Path: fmt.Sprintf("%s/%s", invalidFolderPath, invalidSecretName),
+			Secret: map[string]any{
+				"valid":   "test",
+				"invalid": func() {}, // Unmarshalable type
+			},
+		}
+
+		tc.label = "GetSecretMap - Invalid"
+		tc.name = new(invalidSecretName)
+		tc.folderPath = new(invalidFolderPath)
+		tc.remoteRef = esv1.ExternalSecretDataRemoteRef{Key: invalidSecretName}
+		tc.fakeBtsecretsResponse = fakeKV
+		tc.expectedError = new("failed to marshal secret value for key")
+	}
+
+	testCases := []*beyondtrustworkloadcredentialsGetSecretTestCase{
+		// happy paths
+		makeValidGetSecretTestCaseWithValues(validSecretMap),
+		makeValidGetSecretTestCaseWithValues(validSecretMapWithTypes),
+		// sad paths
+		makeValidGetSecretTestCaseWithValues(clientError),
+		makeValidGetSecretTestCaseWithValues(nilSecret),
+		makeValidGetSecretTestCaseWithValues(invalidSecret),
+	}
+
+	c := Client{store: &esv1.BeyondtrustWorkloadCredentialsProvider{}}
+
+	for i, tc := range testCases {
+		t.Run(tc.label, func(t *testing.T) {
+			c.beyondtrustWorkloadCredentialsClient = tc.fakeBtsecretsClient
+			c.store.FolderPath = *tc.folderPath
+
+			out, err := c.GetSecretMap(context.Background(), tc.remoteRef)
+
+			// assert error
+			if tc.expectedError == nil && err != nil {
+				t.Errorf("[%d] unexpected error: expected nil, got %q", i, err.Error())
+			}
+
+			if tc.expectedError != nil && !ErrorContains(err, *tc.expectedError) {
+				t.Errorf("[%d] unexpected error: expected %q, got %q", i, *tc.expectedError, err)
+			}
+
+			// For happy path tests with complex types, just verify no error and non-nil response
+			if i < 2 && tc.expectedError == nil {
+				if out == nil {
+					t.Errorf("[%d] unexpected response: expected non-nil map, got nil", i)
+				}
+				if len(out) == 0 {
+					t.Errorf("[%d] unexpected response: expected non-empty map, got empty", i)
+				}
+			}
+		})
+	}
+}

+ 76 - 0
providers/v1/beyondtrustworkloadcredentials/util/beyondtrustworkloadcredentials.go

@@ -0,0 +1,76 @@
+/*
+Copyright © The ESO Authors
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    https://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Package btwcutil provides utility types and functions for interacting with BeyondtrustWorkloadCredentials.
+package btwcutil
+
+import (
+	"context"
+	"net/url"
+)
+
+// VersionInfo represents version information for a secret or folder.
+type VersionInfo struct {
+	Version   int    `json:"version"`
+	CreatedAt string `json:"createdAt"`
+	DeletedAt string `json:"deletedAt,omitempty"`
+}
+
+// SecretMetadata represents metadata for a secret, including tags and version information.
+type SecretMetadata struct {
+	ID        string            `json:"id"`
+	Tags      map[string]string `json:"tags,omitempty"`
+	Version   int               `json:"version,omitempty"`
+	CreatedAt string            `json:"createdAt,omitempty"`
+	DeletedAt string            `json:"deletedAt,omitempty"`
+	CreatedBy string            `json:"createdBy,omitempty"`
+}
+
+// KV represents a key-value secret with its metadata.
+type KV struct {
+	Secret   map[string]any  `json:"secret"`
+	Type     string          `json:"type,omitempty"`
+	Path     string          `json:"path,omitempty"`
+	Metadata *SecretMetadata `json:"metadata,omitempty"`
+	Versions []VersionInfo   `json:"versions,omitempty"`
+}
+
+// KVListItem represents a minimal secret list item.
+type KVListItem struct {
+	Path     string          `json:"path"`
+	Type     string          `json:"type"`
+	Metadata *SecretMetadata `json:"metadata,omitempty"`
+	Versions []VersionInfo   `json:"versions,omitempty"`
+}
+
+// GeneratedSecret represents a dynamically generated secret response.
+type GeneratedSecret struct {
+	AccessKeyID     string `json:"accessKeyId"`
+	SecretAccessKey string `json:"secretAccessKey"`
+	SessionToken    string `json:"sessionToken,omitempty"`
+	LeaseID         string `json:"leaseId"`
+	Expiration      string `json:"expiration"`
+}
+
+// Client defines the interface for a BeyondtrustWorkloadCredentials client with methods for secret operations.
+type Client interface {
+	BaseURL() *url.URL
+	SetBaseURL(urlStr string) error
+	CheckSession(ctx context.Context) error
+	GetSecret(ctx context.Context, name string, folderPath *string) (*KV, error)
+	GetSecrets(ctx context.Context, folderPath *string, recursive bool) ([]KVListItem, error)
+	GenerateDynamicSecret(ctx context.Context, name string, folderPath *string) (*GeneratedSecret, error)
+}

+ 11 - 0
runtime/esutils/resolvers/generator.go

@@ -152,6 +152,17 @@ func clusterGeneratorToVirtual(gen *genv1alpha1.ClusterGenerator) (client.Object
 			},
 			Spec: *gen.Spec.Generator.ACRAccessTokenSpec,
 		}, nil
+	case genv1alpha1.GeneratorKindBeyondtrustWorkloadCredentialsDynamicSecret:
+		if gen.Spec.Generator.BeyondtrustWorkloadCredentialsDynamicSecretSpec == nil {
+			return nil, fmt.Errorf("when kind is %s, BeyondtrustWorkloadCredentialsDynamicSecretSpec must be set", gen.Spec.Kind)
+		}
+		return &genv1alpha1.BeyondtrustWorkloadCredentialsDynamicSecret{
+			TypeMeta: metav1.TypeMeta{
+				APIVersion: genv1alpha1.SchemeGroupVersion.String(),
+				Kind:       genv1alpha1.BeyondtrustWorkloadCredentialsDynamicSecretKind,
+			},
+			Spec: *gen.Spec.Generator.BeyondtrustWorkloadCredentialsDynamicSecretSpec,
+		}, nil
 	case genv1alpha1.GeneratorKindCloudsmithAccessToken:
 		if gen.Spec.Generator.CloudsmithAccessTokenSpec == nil {
 			return nil, fmt.Errorf("when kind is %s, CloudsmithAccessTokenSpec must be set", gen.Spec.Kind)

+ 2 - 2
tests/__snapshot__/clusterexternalsecret-v1.yaml

@@ -20,7 +20,7 @@ spec:
       sourceRef:
         generatorRef:
           apiVersion: external-secrets.io/v1
-          kind: "ACRAccessToken" # "ACRAccessToken", "ClusterGenerator", "CloudsmithAccessToken", "ECRAuthorizationToken", "Fake", "GCRAccessToken", "GithubAccessToken", "QuayAccessToken", "Password", "SSHKey", "STSSessionToken", "UUID", "VaultDynamicSecret", "Webhook", "Grafana", "MFA"
+          kind: "ACRAccessToken" # "ACRAccessToken", "BeyondtrustWorkloadCredentialsDynamicSecret", "ClusterGenerator", "CloudsmithAccessToken", "ECRAuthorizationToken", "Fake", "GCRAccessToken", "GithubAccessToken", "QuayAccessToken", "Password", "SSHKey", "STSSessionToken", "UUID", "VaultDynamicSecret", "Webhook", "Grafana", "MFA"
           name: string
         storeRef:
           kind: "SecretStore" # "SecretStore", "ClusterSecretStore"
@@ -57,7 +57,7 @@ spec:
       sourceRef:
         generatorRef:
           apiVersion: external-secrets.io/v1
-          kind: "ACRAccessToken" # "ACRAccessToken", "ClusterGenerator", "CloudsmithAccessToken", "ECRAuthorizationToken", "Fake", "GCRAccessToken", "GithubAccessToken", "QuayAccessToken", "Password", "SSHKey", "STSSessionToken", "UUID", "VaultDynamicSecret", "Webhook", "Grafana", "MFA"
+          kind: "ACRAccessToken" # "ACRAccessToken", "BeyondtrustWorkloadCredentialsDynamicSecret", "ClusterGenerator", "CloudsmithAccessToken", "ECRAuthorizationToken", "Fake", "GCRAccessToken", "GithubAccessToken", "QuayAccessToken", "Password", "SSHKey", "STSSessionToken", "UUID", "VaultDynamicSecret", "Webhook", "Grafana", "MFA"
           name: string
         storeRef:
           kind: "SecretStore" # "SecretStore", "ClusterSecretStore"

+ 23 - 1
tests/__snapshot__/clustergenerator-v1alpha1.yaml

@@ -26,6 +26,28 @@ spec:
       registry: string
       scope: string
       tenantId: string
+    beyondtrustWorkloadCredentialsDynamicSecretSpec:
+      controller: string
+      provider:
+        auth:
+          apikey:
+            token:
+              key: string
+              name: string
+              namespace: string
+        caBundle: c3RyaW5n
+        caProvider:
+          key: string
+          name: string
+          namespace: string
+          type: "Secret" # "Secret", "ConfigMap"
+        folderPath: string
+        server:
+          apiUrl: string
+          siteId: string
+      retrySettings:
+        maxRetries: 1
+        retryInterval: string
     cloudsmithAccessTokenSpec:
       apiUrl: string
       orgSlug: string
@@ -354,4 +376,4 @@ spec:
           name: string
       timeout: string
       url: string
-  kind: "ACRAccessToken" # "ACRAccessToken", "CloudsmithAccessToken", "ECRAuthorizationToken", "Fake", "GCRAccessToken", "GithubAccessToken", "QuayAccessToken", "Password", "SSHKey", "STSSessionToken", "UUID", "VaultDynamicSecret", "Webhook", "Grafana"
+  kind: "ACRAccessToken" # "ACRAccessToken", "BeyondtrustWorkloadCredentialsDynamicSecret", "CloudsmithAccessToken", "ECRAuthorizationToken", "Fake", "GCRAccessToken", "GithubAccessToken", "QuayAccessToken", "Password", "SSHKey", "STSSessionToken", "UUID", "VaultDynamicSecret", "Webhook", "Grafana", "MFA"

+ 17 - 0
tests/__snapshot__/clustersecretstore-v1.yaml

@@ -171,6 +171,23 @@ spec:
         retrievalType: string
         separator: string
         verifyCA: true
+    beyondtrustworkloadcredentials:
+      auth:
+        apikey:
+          token:
+            key: string
+            name: string
+            namespace: string
+      caBundle: c3RyaW5n
+      caProvider:
+        key: string
+        name: string
+        namespace: string
+        type: "Secret" # "Secret", "ConfigMap"
+      folderPath: string
+      server:
+        apiUrl: string
+        siteId: string
     bitwardensecretsmanager:
       apiURL: string
       auth:

+ 2 - 2
tests/__snapshot__/externalsecret-v1.yaml

@@ -15,7 +15,7 @@ spec:
     sourceRef:
       generatorRef:
         apiVersion: external-secrets.io/v1
-        kind: "ACRAccessToken" # "ACRAccessToken", "ClusterGenerator", "CloudsmithAccessToken", "ECRAuthorizationToken", "Fake", "GCRAccessToken", "GithubAccessToken", "QuayAccessToken", "Password", "SSHKey", "STSSessionToken", "UUID", "VaultDynamicSecret", "Webhook", "Grafana", "MFA"
+        kind: "ACRAccessToken" # "ACRAccessToken", "BeyondtrustWorkloadCredentialsDynamicSecret", "ClusterGenerator", "CloudsmithAccessToken", "ECRAuthorizationToken", "Fake", "GCRAccessToken", "GithubAccessToken", "QuayAccessToken", "Password", "SSHKey", "STSSessionToken", "UUID", "VaultDynamicSecret", "Webhook", "Grafana", "MFA"
         name: string
       storeRef:
         kind: "SecretStore" # "SecretStore", "ClusterSecretStore"
@@ -52,7 +52,7 @@ spec:
     sourceRef:
       generatorRef:
         apiVersion: external-secrets.io/v1
-        kind: "ACRAccessToken" # "ACRAccessToken", "ClusterGenerator", "CloudsmithAccessToken", "ECRAuthorizationToken", "Fake", "GCRAccessToken", "GithubAccessToken", "QuayAccessToken", "Password", "SSHKey", "STSSessionToken", "UUID", "VaultDynamicSecret", "Webhook", "Grafana", "MFA"
+        kind: "ACRAccessToken" # "ACRAccessToken", "BeyondtrustWorkloadCredentialsDynamicSecret", "ClusterGenerator", "CloudsmithAccessToken", "ECRAuthorizationToken", "Fake", "GCRAccessToken", "GithubAccessToken", "QuayAccessToken", "Password", "SSHKey", "STSSessionToken", "UUID", "VaultDynamicSecret", "Webhook", "Grafana", "MFA"
         name: string
       storeRef:
         kind: "SecretStore" # "SecretStore", "ClusterSecretStore"

+ 1 - 1
tests/__snapshot__/pushsecret-v1alpha1.yaml

@@ -45,7 +45,7 @@ spec:
   selector:
     generatorRef:
       apiVersion: external-secrets.io/v1alpha1
-      kind: "ACRAccessToken" # "ACRAccessToken", "ClusterGenerator", "CloudsmithAccessToken", "ECRAuthorizationToken", "Fake", "GCRAccessToken", "GithubAccessToken", "QuayAccessToken", "Password", "SSHKey", "STSSessionToken", "UUID", "VaultDynamicSecret", "Webhook", "Grafana", "MFA"
+      kind: "ACRAccessToken" # "ACRAccessToken", "BeyondtrustWorkloadCredentialsDynamicSecret", "ClusterGenerator", "CloudsmithAccessToken", "ECRAuthorizationToken", "Fake", "GCRAccessToken", "GithubAccessToken", "QuayAccessToken", "Password", "SSHKey", "STSSessionToken", "UUID", "VaultDynamicSecret", "Webhook", "Grafana", "MFA"
       name: string
     secret:
       name: string

+ 17 - 0
tests/__snapshot__/secretstore-v1.yaml

@@ -171,6 +171,23 @@ spec:
         retrievalType: string
         separator: string
         verifyCA: true
+    beyondtrustworkloadcredentials:
+      auth:
+        apikey:
+          token:
+            key: string
+            name: string
+            namespace: string
+      caBundle: c3RyaW5n
+      caProvider:
+        key: string
+        name: string
+        namespace: string
+        type: "Secret" # "Secret", "ConfigMap"
+      folderPath: string
+      server:
+        apiUrl: string
+        siteId: string
     bitwardensecretsmanager:
       apiURL: string
       auth: