Przeglądaj źródła

feat(barbican): Support OpenStack Barbican authentication via Application Credentials (#6082)

* Add support for application credentials in barbican provider

Signed-off-by: JaviMore <morenogonzalezjavier@gmail.com>

* Update generated api documentation

Signed-off-by: JaviMore <morenogonzalezjavier@gmail.com>

* add required-pair validation, improve test, Add CRD default for authType

Signed-off-by: JaviMore <morenogonzalezjavier@gmail.com>

* Improve validations, refactor validatestore, remove unnecessary code

Signed-off-by: JaviMore <morenogonzalezjavier@gmail.com>

* Restore validation for auth modes, minor issues

Signed-off-by: JaviMore <morenogonzalezjavier@gmail.com>

* user and pass as pointers, extracted auth to prevent drift

Signed-off-by: JaviMore <morenogonzalezjavier@gmail.com>

* fix fail fast, fix helpers, nitpick comments

Signed-off-by: JaviMore <morenogonzalezjavier@gmail.com>

* Improve descriptions and test

Signed-off-by: JaviMore <morenogonzalezjavier@gmail.com>

* Improve bundle.yaml description and test

Signed-off-by: JaviMore <morenogonzalezjavier@gmail.com>

* Update comment

Signed-off-by: JaviMore <morenogonzalezjavier@gmail.com>

* Remove unstestable scenarios test

Signed-off-by: JaviMore <morenogonzalezjavier@gmail.com>

* Fix lint errors after make revieable

Signed-off-by: JaviMore <morenogonzalezjavier@gmail.com>

* Add minlength validation to string fields

Signed-off-by: JaviMore <morenogonzalezjavier@gmail.com>

---------

Signed-off-by: JaviMore <morenogonzalezjavier@gmail.com>
Signed-off-by: Javas <morenogonzalezjavier@gmail.com>
Co-authored-by: Gergely Bräutigam <gergely.brautigam@sap.com>
Co-authored-by: Jean-Philippe Evrard <jean-philippe.evrard+rochepub@external.roche.com>
Javas 1 tydzień temu
rodzic
commit
82fea1a519

+ 49 - 2
apis/externalsecrets/v1/secretstore_barbican_types.go

@@ -20,10 +20,22 @@ import (
 	esmeta "github.com/external-secrets/external-secrets/apis/meta/v1"
 )
 
+// BarbicanAuthType defines the authentication method used by the Barbican provider.
+// +kubebuilder:validation:Enum=password;applicationCredential
+type BarbicanAuthType string
+
+const (
+	// BarbicanAuthTypePassword uses username/password Keystone authentication.
+	BarbicanAuthTypePassword BarbicanAuthType = "password"
+	// BarbicanAuthTypeApplicationCredential uses OpenStack Application Credentials.
+	BarbicanAuthTypeApplicationCredential BarbicanAuthType = "applicationCredential"
+)
+
 // BarbicanProviderUsernameRef defines a reference to a secret containing username for the Barbican provider.
 // +kubebuilder:validation:MinProperties=1
 // +kubebuilder:validation:MaxProperties=1
 type BarbicanProviderUsernameRef struct {
+	// +kubebuilder:validation:MinLength:=1
 	Value     string                    `json:"value,omitempty"`
 	SecretRef *esmeta.SecretKeySelector `json:"secretRef,omitempty"`
 }
@@ -33,6 +45,20 @@ type BarbicanProviderPasswordRef struct {
 	SecretRef *esmeta.SecretKeySelector `json:"secretRef"`
 }
 
+// BarbicanProviderAppCredIDRef defines a reference to an Application Credential ID.
+// +kubebuilder:validation:MinProperties=1
+// +kubebuilder:validation:MaxProperties=1
+type BarbicanProviderAppCredIDRef struct {
+	// +kubebuilder:validation:MinLength:=1
+	Value     string                    `json:"value,omitempty"`
+	SecretRef *esmeta.SecretKeySelector `json:"secretRef,omitempty"`
+}
+
+// BarbicanProviderAppCredSecretRef defines a reference to an Application Credential Secret.
+type BarbicanProviderAppCredSecretRef struct {
+	SecretRef *esmeta.SecretKeySelector `json:"secretRef"`
+}
+
 // BarbicanProvider setup a store to sync secrets with barbican.
 type BarbicanProvider struct {
 	AuthURL    string       `json:"authURL,omitempty"`
@@ -43,7 +69,28 @@ type BarbicanProvider struct {
 }
 
 // BarbicanAuth contains the authentication information for Barbican.
+// +kubebuilder:validation:XValidation:rule="(has(self.authType) && self.authType == 'applicationCredential') || (has(self.username) && has(self.password))",message="password auth requires both username and password"
+// +kubebuilder:validation:XValidation:rule="self.authType != 'applicationCredential' || (has(self.applicationCredentialID) && has(self.applicationCredentialSecret))",message="applicationCredential auth requires both applicationCredentialID and applicationCredentialSecret"
+// +kubebuilder:validation:XValidation:rule="(has(self.authType) && self.authType == 'applicationCredential') || (!has(self.applicationCredentialID) && !has(self.applicationCredentialSecret))",message="password auth should not include applicationCredential fields"
+// +kubebuilder:validation:XValidation:rule="self.authType != 'applicationCredential' || (!has(self.username) && !has(self.password))",message="applicationCredential auth should not include password fields"
 type BarbicanAuth struct {
-	Username BarbicanProviderUsernameRef `json:"username"`
-	Password BarbicanProviderPasswordRef `json:"password"`
+	// AuthType selects how Barbican authenticates.
+	// - "password": use username and password.
+	// - "applicationCredential": use application credential ID and secret.
+	// Defaults to "password".
+	// +optional
+	// +kubebuilder:default="password"
+	AuthType *BarbicanAuthType `json:"authType,omitempty"`
+
+	// Username / Password authentication fields.
+	// +optional
+	Username *BarbicanProviderUsernameRef `json:"username,omitempty"`
+	// +optional
+	Password *BarbicanProviderPasswordRef `json:"password,omitempty"`
+
+	// ID of the application credential used for authentication.
+	// +optional
+	ApplicationCredentialID *BarbicanProviderAppCredIDRef `json:"applicationCredentialID,omitempty"`
+	// +optional
+	ApplicationCredentialSecret *BarbicanProviderAppCredSecretRef `json:"applicationCredentialSecret,omitempty"`
 }

+ 65 - 2
apis/externalsecrets/v1/zz_generated.deepcopy.go

@@ -448,8 +448,31 @@ func (in *AzureKVProvider) DeepCopy() *AzureKVProvider {
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *BarbicanAuth) DeepCopyInto(out *BarbicanAuth) {
 	*out = *in
-	in.Username.DeepCopyInto(&out.Username)
-	in.Password.DeepCopyInto(&out.Password)
+	if in.AuthType != nil {
+		in, out := &in.AuthType, &out.AuthType
+		*out = new(BarbicanAuthType)
+		**out = **in
+	}
+	if in.Username != nil {
+		in, out := &in.Username, &out.Username
+		*out = new(BarbicanProviderUsernameRef)
+		(*in).DeepCopyInto(*out)
+	}
+	if in.Password != nil {
+		in, out := &in.Password, &out.Password
+		*out = new(BarbicanProviderPasswordRef)
+		(*in).DeepCopyInto(*out)
+	}
+	if in.ApplicationCredentialID != nil {
+		in, out := &in.ApplicationCredentialID, &out.ApplicationCredentialID
+		*out = new(BarbicanProviderAppCredIDRef)
+		(*in).DeepCopyInto(*out)
+	}
+	if in.ApplicationCredentialSecret != nil {
+		in, out := &in.ApplicationCredentialSecret, &out.ApplicationCredentialSecret
+		*out = new(BarbicanProviderAppCredSecretRef)
+		(*in).DeepCopyInto(*out)
+	}
 }
 
 // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BarbicanAuth.
@@ -478,6 +501,46 @@ func (in *BarbicanProvider) DeepCopy() *BarbicanProvider {
 	return out
 }
 
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *BarbicanProviderAppCredIDRef) DeepCopyInto(out *BarbicanProviderAppCredIDRef) {
+	*out = *in
+	if in.SecretRef != nil {
+		in, out := &in.SecretRef, &out.SecretRef
+		*out = new(apismetav1.SecretKeySelector)
+		(*in).DeepCopyInto(*out)
+	}
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BarbicanProviderAppCredIDRef.
+func (in *BarbicanProviderAppCredIDRef) DeepCopy() *BarbicanProviderAppCredIDRef {
+	if in == nil {
+		return nil
+	}
+	out := new(BarbicanProviderAppCredIDRef)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *BarbicanProviderAppCredSecretRef) DeepCopyInto(out *BarbicanProviderAppCredSecretRef) {
+	*out = *in
+	if in.SecretRef != nil {
+		in, out := &in.SecretRef, &out.SecretRef
+		*out = new(apismetav1.SecretKeySelector)
+		(*in).DeepCopyInto(*out)
+	}
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BarbicanProviderAppCredSecretRef.
+func (in *BarbicanProviderAppCredSecretRef) DeepCopy() *BarbicanProviderAppCredSecretRef {
+	if in == nil {
+		return nil
+	}
+	out := new(BarbicanProviderAppCredSecretRef)
+	in.DeepCopyInto(out)
+	return out
+}
+
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *BarbicanProviderPasswordRef) DeepCopyInto(out *BarbicanProviderPasswordRef) {
 	*out = *in

+ 104 - 5
config/crds/bases/external-secrets.io_clustersecretstores.yaml

@@ -885,6 +885,92 @@ spec:
                         description: BarbicanAuth contains the authentication information
                           for Barbican.
                         properties:
+                          applicationCredentialID:
+                            description: ID of the application credential used for
+                              authentication.
+                            maxProperties: 1
+                            minProperties: 1
+                            properties:
+                              secretRef:
+                                description: |-
+                                  SecretKeySelector is a reference to a specific 'key' within a Secret resource.
+                                  In some instances, `key` is a required field.
+                                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
+                              value:
+                                minLength: 1
+                                type: string
+                            type: object
+                          applicationCredentialSecret:
+                            description: BarbicanProviderAppCredSecretRef defines
+                              a reference to an Application Credential Secret.
+                            properties:
+                              secretRef:
+                                description: |-
+                                  SecretKeySelector is a reference to a specific 'key' within a Secret resource.
+                                  In some instances, `key` is a required field.
+                                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:
+                            - secretRef
+                            type: object
+                          authType:
+                            default: password
+                            description: |-
+                              AuthType selects how Barbican authenticates.
+                              - "password": use username and password.
+                              - "applicationCredential": use application credential ID and secret.
+                              Defaults to "password".
+                            enum:
+                            - password
+                            - applicationCredential
+                            type: string
                           password:
                             description: BarbicanProviderPasswordRef defines a reference
                               to a secret containing password for the Barbican provider.
@@ -922,8 +1008,7 @@ spec:
                             - secretRef
                             type: object
                           username:
-                            description: BarbicanProviderUsernameRef defines a reference
-                              to a secret containing username for the Barbican provider.
+                            description: Username / Password authentication fields.
                             maxProperties: 1
                             minProperties: 1
                             properties:
@@ -957,12 +1042,26 @@ spec:
                                     type: string
                                 type: object
                               value:
+                                minLength: 1
                                 type: string
                             type: object
-                        required:
-                        - password
-                        - username
                         type: object
+                        x-kubernetes-validations:
+                        - message: password auth requires both username and password
+                          rule: (has(self.authType) && self.authType == 'applicationCredential')
+                            || (has(self.username) && has(self.password))
+                        - message: applicationCredential auth requires both applicationCredentialID
+                            and applicationCredentialSecret
+                          rule: self.authType != 'applicationCredential' || (has(self.applicationCredentialID)
+                            && has(self.applicationCredentialSecret))
+                        - message: password auth should not include applicationCredential
+                            fields
+                          rule: (has(self.authType) && self.authType == 'applicationCredential')
+                            || (!has(self.applicationCredentialID) && !has(self.applicationCredentialSecret))
+                        - message: applicationCredential auth should not include password
+                            fields
+                          rule: self.authType != 'applicationCredential' || (!has(self.username)
+                            && !has(self.password))
                       authURL:
                         type: string
                       domainName:

+ 104 - 5
config/crds/bases/external-secrets.io_secretstores.yaml

@@ -885,6 +885,92 @@ spec:
                         description: BarbicanAuth contains the authentication information
                           for Barbican.
                         properties:
+                          applicationCredentialID:
+                            description: ID of the application credential used for
+                              authentication.
+                            maxProperties: 1
+                            minProperties: 1
+                            properties:
+                              secretRef:
+                                description: |-
+                                  SecretKeySelector is a reference to a specific 'key' within a Secret resource.
+                                  In some instances, `key` is a required field.
+                                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
+                              value:
+                                minLength: 1
+                                type: string
+                            type: object
+                          applicationCredentialSecret:
+                            description: BarbicanProviderAppCredSecretRef defines
+                              a reference to an Application Credential Secret.
+                            properties:
+                              secretRef:
+                                description: |-
+                                  SecretKeySelector is a reference to a specific 'key' within a Secret resource.
+                                  In some instances, `key` is a required field.
+                                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:
+                            - secretRef
+                            type: object
+                          authType:
+                            default: password
+                            description: |-
+                              AuthType selects how Barbican authenticates.
+                              - "password": use username and password.
+                              - "applicationCredential": use application credential ID and secret.
+                              Defaults to "password".
+                            enum:
+                            - password
+                            - applicationCredential
+                            type: string
                           password:
                             description: BarbicanProviderPasswordRef defines a reference
                               to a secret containing password for the Barbican provider.
@@ -922,8 +1008,7 @@ spec:
                             - secretRef
                             type: object
                           username:
-                            description: BarbicanProviderUsernameRef defines a reference
-                              to a secret containing username for the Barbican provider.
+                            description: Username / Password authentication fields.
                             maxProperties: 1
                             minProperties: 1
                             properties:
@@ -957,12 +1042,26 @@ spec:
                                     type: string
                                 type: object
                               value:
+                                minLength: 1
                                 type: string
                             type: object
-                        required:
-                        - password
-                        - username
                         type: object
+                        x-kubernetes-validations:
+                        - message: password auth requires both username and password
+                          rule: (has(self.authType) && self.authType == 'applicationCredential')
+                            || (has(self.username) && has(self.password))
+                        - message: applicationCredential auth requires both applicationCredentialID
+                            and applicationCredentialSecret
+                          rule: self.authType != 'applicationCredential' || (has(self.applicationCredentialID)
+                            && has(self.applicationCredentialSecret))
+                        - message: password auth should not include applicationCredential
+                            fields
+                          rule: (has(self.authType) && self.authType == 'applicationCredential')
+                            || (!has(self.applicationCredentialID) && !has(self.applicationCredentialSecret))
+                        - message: applicationCredential auth should not include password
+                            fields
+                          rule: self.authType != 'applicationCredential' || (!has(self.username)
+                            && !has(self.password))
                       authURL:
                         type: string
                       domainName:

+ 186 - 8
deploy/crds/bundle.yaml

@@ -3157,6 +3157,88 @@ spec:
                         auth:
                           description: BarbicanAuth contains the authentication information for Barbican.
                           properties:
+                            applicationCredentialID:
+                              description: ID of the application credential used for authentication.
+                              maxProperties: 1
+                              minProperties: 1
+                              properties:
+                                secretRef:
+                                  description: |-
+                                    SecretKeySelector is a reference to a specific 'key' within a Secret resource.
+                                    In some instances, `key` is a required field.
+                                  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
+                                value:
+                                  minLength: 1
+                                  type: string
+                              type: object
+                            applicationCredentialSecret:
+                              description: BarbicanProviderAppCredSecretRef defines a reference to an Application Credential Secret.
+                              properties:
+                                secretRef:
+                                  description: |-
+                                    SecretKeySelector is a reference to a specific 'key' within a Secret resource.
+                                    In some instances, `key` is a required field.
+                                  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:
+                                - secretRef
+                              type: object
+                            authType:
+                              default: password
+                              description: |-
+                                AuthType selects how Barbican authenticates.
+                                - "password": use username and password.
+                                - "applicationCredential": use application credential ID and secret.
+                                Defaults to "password".
+                              enum:
+                                - password
+                                - applicationCredential
+                              type: string
                             password:
                               description: BarbicanProviderPasswordRef defines a reference to a secret containing password for the Barbican provider.
                               properties:
@@ -3192,7 +3274,7 @@ spec:
                                 - secretRef
                               type: object
                             username:
-                              description: BarbicanProviderUsernameRef defines a reference to a secret containing username for the Barbican provider.
+                              description: Username / Password authentication fields.
                               maxProperties: 1
                               minProperties: 1
                               properties:
@@ -3225,12 +3307,19 @@ spec:
                                       type: string
                                   type: object
                                 value:
+                                  minLength: 1
                                   type: string
                               type: object
-                          required:
-                            - password
-                            - username
                           type: object
+                          x-kubernetes-validations:
+                            - message: password auth requires both username and password
+                              rule: (has(self.authType) && self.authType == 'applicationCredential') || (has(self.username) && has(self.password))
+                            - message: applicationCredential auth requires both applicationCredentialID and applicationCredentialSecret
+                              rule: self.authType != 'applicationCredential' || (has(self.applicationCredentialID) && has(self.applicationCredentialSecret))
+                            - message: password auth should not include applicationCredential fields
+                              rule: (has(self.authType) && self.authType == 'applicationCredential') || (!has(self.applicationCredentialID) && !has(self.applicationCredentialSecret))
+                            - message: applicationCredential auth should not include password fields
+                              rule: self.authType != 'applicationCredential' || (!has(self.username) && !has(self.password))
                         authURL:
                           type: string
                         domainName:
@@ -16520,6 +16609,88 @@ spec:
                         auth:
                           description: BarbicanAuth contains the authentication information for Barbican.
                           properties:
+                            applicationCredentialID:
+                              description: ID of the application credential used for authentication.
+                              maxProperties: 1
+                              minProperties: 1
+                              properties:
+                                secretRef:
+                                  description: |-
+                                    SecretKeySelector is a reference to a specific 'key' within a Secret resource.
+                                    In some instances, `key` is a required field.
+                                  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
+                                value:
+                                  minLength: 1
+                                  type: string
+                              type: object
+                            applicationCredentialSecret:
+                              description: BarbicanProviderAppCredSecretRef defines a reference to an Application Credential Secret.
+                              properties:
+                                secretRef:
+                                  description: |-
+                                    SecretKeySelector is a reference to a specific 'key' within a Secret resource.
+                                    In some instances, `key` is a required field.
+                                  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:
+                                - secretRef
+                              type: object
+                            authType:
+                              default: password
+                              description: |-
+                                AuthType selects how Barbican authenticates.
+                                - "password": use username and password.
+                                - "applicationCredential": use application credential ID and secret.
+                                Defaults to "password".
+                              enum:
+                                - password
+                                - applicationCredential
+                              type: string
                             password:
                               description: BarbicanProviderPasswordRef defines a reference to a secret containing password for the Barbican provider.
                               properties:
@@ -16555,7 +16726,7 @@ spec:
                                 - secretRef
                               type: object
                             username:
-                              description: BarbicanProviderUsernameRef defines a reference to a secret containing username for the Barbican provider.
+                              description: Username / Password authentication fields.
                               maxProperties: 1
                               minProperties: 1
                               properties:
@@ -16588,12 +16759,19 @@ spec:
                                       type: string
                                   type: object
                                 value:
+                                  minLength: 1
                                   type: string
                               type: object
-                          required:
-                            - password
-                            - username
                           type: object
+                          x-kubernetes-validations:
+                            - message: password auth requires both username and password
+                              rule: (has(self.authType) && self.authType == 'applicationCredential') || (has(self.username) && has(self.password))
+                            - message: applicationCredential auth requires both applicationCredentialID and applicationCredentialSecret
+                              rule: self.authType != 'applicationCredential' || (has(self.applicationCredentialID) && has(self.applicationCredentialSecret))
+                            - message: password auth should not include applicationCredential fields
+                              rule: (has(self.authType) && self.authType == 'applicationCredential') || (!has(self.applicationCredentialID) && !has(self.applicationCredentialSecret))
+                            - message: applicationCredential auth should not include password fields
+                              rule: self.authType != 'applicationCredential' || (!has(self.username) && !has(self.password))
                         authURL:
                           type: string
                         domainName:

+ 143 - 0
docs/api/spec.md

@@ -1180,6 +1180,23 @@ configuration is not supported with the legacy go-autorest SDK.</p>
 <tbody>
 <tr>
 <td>
+<code>authType</code></br>
+<em>
+<a href="#external-secrets.io/v1.BarbicanAuthType">
+BarbicanAuthType
+</a>
+</em>
+</td>
+<td>
+<em>(Optional)</em>
+<p>AuthType selects how Barbican authenticates.
+- &ldquo;password&rdquo;: use username and password.
+- &ldquo;applicationCredential&rdquo;: use application credential ID and secret.
+Defaults to &ldquo;password&rdquo;.</p>
+</td>
+</tr>
+<tr>
+<td>
 <code>username</code></br>
 <em>
 <a href="#external-secrets.io/v1.BarbicanProviderUsernameRef">
@@ -1188,6 +1205,8 @@ BarbicanProviderUsernameRef
 </em>
 </td>
 <td>
+<em>(Optional)</em>
+<p>Username / Password authentication fields.</p>
 </td>
 </tr>
 <tr>
@@ -1200,10 +1219,62 @@ BarbicanProviderPasswordRef
 </em>
 </td>
 <td>
+<em>(Optional)</em>
+</td>
+</tr>
+<tr>
+<td>
+<code>applicationCredentialID</code></br>
+<em>
+<a href="#external-secrets.io/v1.BarbicanProviderAppCredIDRef">
+BarbicanProviderAppCredIDRef
+</a>
+</em>
+</td>
+<td>
+<em>(Optional)</em>
+<p>ID of the application credential used for authentication.</p>
+</td>
+</tr>
+<tr>
+<td>
+<code>applicationCredentialSecret</code></br>
+<em>
+<a href="#external-secrets.io/v1.BarbicanProviderAppCredSecretRef">
+BarbicanProviderAppCredSecretRef
+</a>
+</em>
+</td>
+<td>
+<em>(Optional)</em>
 </td>
 </tr>
 </tbody>
 </table>
+<h3 id="external-secrets.io/v1.BarbicanAuthType">BarbicanAuthType
+(<code>string</code> alias)</p></h3>
+<p>
+(<em>Appears on:</em>
+<a href="#external-secrets.io/v1.BarbicanAuth">BarbicanAuth</a>)
+</p>
+<p>
+<p>BarbicanAuthType defines the authentication method used by the Barbican provider.</p>
+</p>
+<table>
+<thead>
+<tr>
+<th>Value</th>
+<th>Description</th>
+</tr>
+</thead>
+<tbody><tr><td><p>&#34;applicationCredential&#34;</p></td>
+<td><p>BarbicanAuthTypeApplicationCredential uses OpenStack Application Credentials.</p>
+</td>
+</tr><tr><td><p>&#34;password&#34;</p></td>
+<td><p>BarbicanAuthTypePassword uses username/password Keystone authentication.</p>
+</td>
+</tr></tbody>
+</table>
 <h3 id="external-secrets.io/v1.BarbicanProvider">BarbicanProvider
 </h3>
 <p>
@@ -1275,6 +1346,78 @@ BarbicanAuth
 </tr>
 </tbody>
 </table>
+<h3 id="external-secrets.io/v1.BarbicanProviderAppCredIDRef">BarbicanProviderAppCredIDRef
+</h3>
+<p>
+(<em>Appears on:</em>
+<a href="#external-secrets.io/v1.BarbicanAuth">BarbicanAuth</a>)
+</p>
+<p>
+<p>BarbicanProviderAppCredIDRef defines a reference to an Application Credential ID.</p>
+</p>
+<table>
+<thead>
+<tr>
+<th>Field</th>
+<th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td>
+<code>value</code></br>
+<em>
+string
+</em>
+</td>
+<td>
+</td>
+</tr>
+<tr>
+<td>
+<code>secretRef</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>
+</td>
+</tr>
+</tbody>
+</table>
+<h3 id="external-secrets.io/v1.BarbicanProviderAppCredSecretRef">BarbicanProviderAppCredSecretRef
+</h3>
+<p>
+(<em>Appears on:</em>
+<a href="#external-secrets.io/v1.BarbicanAuth">BarbicanAuth</a>)
+</p>
+<p>
+<p>BarbicanProviderAppCredSecretRef defines a reference to an Application Credential Secret.</p>
+</p>
+<table>
+<thead>
+<tr>
+<th>Field</th>
+<th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td>
+<code>secretRef</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>
+</td>
+</tr>
+</tbody>
+</table>
 <h3 id="external-secrets.io/v1.BarbicanProviderPasswordRef">BarbicanProviderPasswordRef
 </h3>
 <p>

+ 79 - 11
docs/provider/barbican.md

@@ -6,16 +6,19 @@ Barbican is OpenStack's Key Manager service that provides secure storage, provis
 
 ## Authentication
 
-The Barbican provider uses OpenStack Keystone authentication. You need to provide:
+The Barbican provider supports two OpenStack Keystone authentication modes:
 
-- **AuthURL**: The OpenStack Keystone authentication endpoint
-- **TenantName**: The OpenStack tenant/project name
-- **DomainName**: The OpenStack domain name (optional)
-- **Region**: The OpenStack region (optional)
-- **Username**: OpenStack username (stored in a Kubernetes secret)
-- **Password**: OpenStack password (stored in a Kubernetes secret)
+- `password` (default): Username + password.
+- `applicationCredential`: OpenStack Application Credentials.
 
-## Example
+### Required provider fields
+
+- **authURL**: OpenStack Keystone authentication endpoint.
+- **region**: OpenStack region (optional).
+- **tenantName**: OpenStack project/tenant (optional, depending on your Keystone setup).
+- **domainName**: OpenStack domain (required for password auth in environments that require domain scoping).
+
+## Example User Name/Password Authentication
 
 First, create a secret containing your OpenStack credentials:
 
@@ -57,6 +60,47 @@ spec:
 
 **NOTE:** In case of a `ClusterSecretStore`, be sure to provide `namespace` for the `secretRef` with the namespace of the secret that contains the credentials.
 
+## Example Application Credential Authentication
+
+You can authenticate using OpenStack Application Credentials by setting `auth.authType: applicationCredential`.
+
+Create a secret with Application Credential ID and credential secret:
+
+```yaml
+apiVersion: v1
+kind: Secret
+metadata:
+  name: barbican-appcred
+type: Opaque
+data:
+  appCredID: YXBwLWNyZWQtaWQ= # base64 encoded app credential ID
+  appCredSecret: YXBwLWNyZWQtc2VjcmV0 # base64 encoded app credential secret
+```
+
+Use it in a `SecretStore`:
+
+```yaml
+apiVersion: external-secrets.io/v1
+kind: SecretStore
+metadata:
+  name: barbican-backend-appcred-id
+spec:
+  provider:
+    barbican:
+      authURL: "https://keystone.example.com:5000/v3"
+      region: "RegionOne"
+      auth:
+        authType: applicationCredential
+        applicationCredentialID:
+          secretRef:
+            name: "barbican-appcred"
+            key: "appCredID"
+        applicationCredentialSecret:
+          secretRef:
+            name: "barbican-appcred"
+            key: "appCredSecret"
+```
+
 ## Creating an ExternalSecret
 
 Now you can create an ExternalSecret that uses the Barbican provider to retrieve secrets:
@@ -183,13 +227,14 @@ spec:
             key: "password"
             namespace: "default"  # Required for ClusterSecretStore
 ```
+The same `namespace` rule applies to `applicationCredentialID.secretRef` and `applicationCredentialSecret.secretRef` when using `ClusterSecretStore`.
 
 ## Configuration Reference
 
 | Field | Type | Required | Description |
 |-------|------|----------|-------------|
 | `authURL` | string | Yes | OpenStack Keystone authentication endpoint URL |
-| `tenantName` | string | Yes | OpenStack tenant/project name |
+| `tenantName` | string | No | OpenStack tenant/project name |
 | `domainName` | string | No | OpenStack domain name |
 | `region` | string | No | OpenStack region |
 | `auth` | BarbicanAuth | Yes | Authentication credentials |
@@ -200,8 +245,18 @@ The `BarbicanAuth` type contains the authentication information:
 
 | Field | Type | Required | Description |
 |-------|------|----------|-------------|
-| `username` | BarbicanProviderUsernameRef | Yes | OpenStack username (from secret or literal value) |
-| `password` | BarbicanProviderPasswordRef | Yes | OpenStack password (from secret only) |
+| `authType` | BarbicanAuthType | No | Auth mode: `password` (default) or `applicationCredential` |
+| `username` | BarbicanProviderUsernameRef | Conditional | Required for `password` |
+| `password` | BarbicanProviderPasswordRef | Conditional | Required for `password` |
+| `applicationCredentialID` | BarbicanProviderAppCredIDRef | Conditional | Required for `applicationCredential` |
+| `applicationCredentialSecret` | BarbicanProviderAppCredSecretRef | Conditional | Required for `applicationCredential` |
+
+### BarbicanAuthType
+
+| Value | Description |
+|-------|-------------|
+| `password` | Keystone username/password authentication |
+| `applicationCredential` | Keystone Application Credential authentication |
 
 ### BarbicanProviderUsernameRef
 
@@ -220,6 +275,19 @@ The `BarbicanProviderPasswordRef` type requires a reference to a Kubernetes secr
 |-------|------|----------|-------------|
 | `secretRef` | SecretKeySelector | Yes | Reference to a Kubernetes secret |
 
+### BarbicanProviderAppCredIDRef
+
+| Field | Type | Required | Description |
+|-------|------|----------|-------------|
+| `value` | string | No | Literal Application Credential ID |
+| `secretRef` | SecretKeySelector | No | Reference to a Kubernetes secret containing the Application Credential ID |
+
+### BarbicanProviderAppCredSecretRef
+
+| Field | Type | Required | Description |
+|-------|------|----------|-------------|
+| `secretRef` | SecretKeySelector | Yes | Reference to a Kubernetes secret containing the Application Credential secret |
+
 ## Limitations
 
 - The Barbican provider is **read-only**. Creating, updating, or deleting secrets is not supported (`PushSecret` and `DeletionPolicy: Delete` will fail).

+ 1 - 19
providers/v1/barbican/go.mod

@@ -13,13 +13,9 @@ require (
 )
 
 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
@@ -39,19 +35,10 @@ require (
 	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/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/kr/text v0.2.0 // 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
@@ -60,14 +47,10 @@ require (
 	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.53.0 // indirect
 	golang.org/x/net v0.56.0 // indirect
 	golang.org/x/oauth2 v0.36.0 // indirect
 	golang.org/x/sync v0.22.0 // indirect
@@ -89,7 +72,6 @@ require (
 	sigs.k8s.io/randfill v1.0.0 // indirect
 	sigs.k8s.io/structured-merge-diff/v6 v6.3.3 // indirect
 	sigs.k8s.io/yaml v1.6.0 // indirect
-	software.sslmate.com/src/go-pkcs12 v0.7.0 // indirect
 )
 
 replace (

+ 1 - 41
providers/v1/barbican/go.sum

@@ -1,27 +1,20 @@
-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/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/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
 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=
@@ -66,8 +59,6 @@ github.com/go-openapi/testify/v2 v2.4.0 h1:8nsPrHVCWkQ4p8h1EsRVymA2XABB4OT40gcvA
 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/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=
@@ -81,8 +72,6 @@ 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/gophercloud/gophercloud/v2 v2.8.0 h1:of2+8tT6+FbEYHfYC8GBu8TXJNsXYSNm9KuvpX7Neqo=
 github.com/gophercloud/gophercloud/v2 v2.8.0/go.mod h1:Ki/ILhYZr/5EPebrPL9Ej+tUg4lqx71/YH2JWVeU+Qk=
-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.5 h1:/h1gH5Ce+VWNLSWqPzOVn6XBO+vJbCNGvjoaGBFW2IE=
@@ -93,22 +82,6 @@ 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=
@@ -136,20 +109,12 @@ github.com/prometheus/procfs v0.20.1 h1:XwbrGOIplXW/AU3YhIhLODXMJYyC1isLFfYCsTEy
 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/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=
@@ -164,8 +129,6 @@ 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.53.0 h1:QZ4Muo8THX6CizN2vPPd5fBGHyogrdK9fG4wLPFUsto=
-golang.org/x/crypto v0.53.0/go.mod h1:DNLU434OwVakk9PzuwV8w62mAJpRJL3vsgcfp4Qnsio=
 golang.org/x/mod v0.37.0 h1:vF1DjpVEshcIqoEaauuHebaLk1O1forxjxBaVn884JQ=
 golang.org/x/mod v0.37.0/go.mod h1:m8S8VeM9r4dzDwjrKO0a1sZP3YjeMamRRlD+fmR2Q/0=
 golang.org/x/net v0.56.0 h1:Rw8j/hFzGvJUZwNBXnAtf5sVDVt+65SK2C7IxCxZt5o=
@@ -195,7 +158,6 @@ gopkg.in/evanphx/json-patch.v4 v4.13.0 h1:czT3CmqEaQ1aanPc5SdlgQrrEIb8w/wwCvWWnf
 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.36.3 h1:NxB+05W2UGqXWFXcLO0RB5cnqnUPP5v5sVlaOH0Iz4w=
@@ -222,5 +184,3 @@ sigs.k8s.io/structured-merge-diff/v6 v6.3.3 h1:u08YRbVUi59ri4YD6cg0UqNM4Dimn0sIl
 sigs.k8s.io/structured-merge-diff/v6 v6.3.3/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=

+ 123 - 31
providers/v1/barbican/provider.go

@@ -27,19 +27,24 @@ import (
 	"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
 
 	esv1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1"
-	"github.com/external-secrets/external-secrets/runtime/esutils"
 	"github.com/external-secrets/external-secrets/runtime/esutils/resolvers"
 )
 
 const (
-	errGeneric      = "barbican provider error: %w"
-	errMissingField = "barbican provider missing required field: %w"
-	errAuthFailed   = "barbican provider authentication failed: %w"
-	errClientInit   = "barbican provider client initialization failed: %w"
+	errGeneric         = "barbican provider error: %w"
+	errMissingField    = "barbican provider missing required field: %w"
+	errAuthFailed      = "barbican provider authentication failed: %w"
+	errClientInit      = "barbican provider client initialization failed: %w"
+	errUnsupportedAuth = "barbican provider unsupported auth type: %s"
 )
 
 var _ esv1.Provider = &Provider{}
 
+var (
+	authenticatedClient = openstack.AuthenticatedClient
+	newKeyManagerV1     = openstack.NewKeyManagerV1
+)
+
 // Provider implements the Barbican provider.
 type Provider struct{}
 
@@ -53,28 +58,59 @@ func (p *Provider) ValidateStore(store esv1.GenericStore) (admission.Warnings, e
 	if store == nil {
 		return nil, fmt.Errorf(errGeneric, errors.New("store is nil"))
 	}
+
 	provider, err := getProvider(store)
 	if err != nil {
 		return nil, err
 	}
+
 	if provider.AuthURL == "" {
 		return nil, fmt.Errorf(errMissingField, errors.New("authURL is required"))
 	}
-	if provider.Auth.Username.Value == "" && provider.Auth.Username.SecretRef == nil {
-		return nil, fmt.Errorf(errMissingField, errors.New("auth.username requires either value or secretRef"))
+
+	authType := resolveAuthType(provider.Auth)
+
+	switch authType {
+	case esv1.BarbicanAuthTypePassword:
+		return nil, validatePasswordAuth(provider.Auth)
+	case esv1.BarbicanAuthTypeApplicationCredential:
+		return nil, validateAppCredAuth(provider.Auth)
+	default:
+		return nil, fmt.Errorf(errUnsupportedAuth, authType)
 	}
-	if provider.Auth.Username.SecretRef != nil {
-		if err := esutils.ValidateSecretSelector(store, *provider.Auth.Username.SecretRef); err != nil {
-			return nil, fmt.Errorf(errGeneric, err)
-		}
+}
+
+func resolveAuthType(auth esv1.BarbicanAuth) esv1.BarbicanAuthType {
+	if auth.AuthType != nil {
+		return *auth.AuthType
+	}
+	return esv1.BarbicanAuthTypePassword
+}
+
+func validatePasswordAuth(auth esv1.BarbicanAuth) error {
+	if auth.Username == nil {
+		return fmt.Errorf(errMissingField, errors.New("username is required for password auth"))
+	}
+	if auth.Username.Value == "" && auth.Username.SecretRef == nil {
+		return fmt.Errorf(errMissingField, errors.New("username must specify either value or secretRef"))
 	}
-	if provider.Auth.Password.SecretRef == nil {
-		return nil, fmt.Errorf(errMissingField, errors.New("auth.password.secretRef is required"))
+	if auth.Password == nil || auth.Password.SecretRef == nil {
+		return fmt.Errorf(errMissingField, errors.New("password secretRef is required"))
 	}
-	if err := esutils.ValidateSecretSelector(store, *provider.Auth.Password.SecretRef); err != nil {
-		return nil, fmt.Errorf(errGeneric, err)
+	return nil
+}
+
+func validateAppCredAuth(auth esv1.BarbicanAuth) error {
+	if auth.ApplicationCredentialID == nil {
+		return fmt.Errorf(errMissingField, errors.New("applicationCredentialID is required for applicationCredential auth"))
+	}
+	if auth.ApplicationCredentialID.Value == "" && auth.ApplicationCredentialID.SecretRef == nil {
+		return fmt.Errorf(errMissingField, errors.New("applicationCredentialID must specify either value or secretRef"))
+	}
+	if auth.ApplicationCredentialSecret == nil || auth.ApplicationCredentialSecret.SecretRef == nil {
+		return fmt.Errorf(errMissingField, errors.New("applicationCredentialSecret secretRef is required for applicationCredential auth"))
 	}
-	return nil, nil
+	return nil
 }
 
 // NewClient creates a new Barbican client.
@@ -100,45 +136,101 @@ func newClient(ctx context.Context, store esv1.GenericStore, kube client.Client,
 		return nil, fmt.Errorf(errMissingField, errors.New("authURL is required"))
 	}
 
+	authType := resolveAuthType(provider.Auth)
+
+	var authopts gophercloud.AuthOptions
+	switch authType {
+	case esv1.BarbicanAuthTypePassword:
+		authopts, err = buildPasswordAuthOpts(ctx, store, kube, namespace, provider)
+	case esv1.BarbicanAuthTypeApplicationCredential:
+		authopts, err = buildAppCredAuthOpts(ctx, store, kube, namespace, provider)
+	default:
+		return nil, fmt.Errorf(errUnsupportedAuth, authType)
+	}
+	if err != nil {
+		return nil, err
+	}
+
+	auth, err := authenticatedClient(ctx, authopts)
+	if err != nil {
+		return nil, fmt.Errorf(errAuthFailed, err)
+	}
+
+	barbicanClient, err := newKeyManagerV1(auth, gophercloud.EndpointOpts{
+		Region: provider.Region,
+	})
+	if err != nil {
+		return nil, fmt.Errorf(errClientInit, err)
+	}
+
+	return &Client{keyManager: barbicanClient}, nil
+}
+
+func buildPasswordAuthOpts(ctx context.Context, store esv1.GenericStore, kube client.Client, namespace string, provider *esv1.BarbicanProvider) (gophercloud.AuthOptions, error) {
+	if err := validatePasswordAuth(provider.Auth); err != nil {
+		return gophercloud.AuthOptions{}, err
+	}
+
 	username := provider.Auth.Username.Value
+	var err error
 
 	if username == "" {
 		username, err = resolvers.SecretKeyRef(ctx, kube, store.GetKind(), namespace, provider.Auth.Username.SecretRef)
 		if err != nil {
-			return nil, fmt.Errorf(errMissingField, err)
+			return gophercloud.AuthOptions{}, fmt.Errorf(errMissingField, err)
+		}
+		if username == "" {
+			return gophercloud.AuthOptions{}, fmt.Errorf(errMissingField, errors.New("username secret value is empty"))
 		}
 	}
-
 	password, err := resolvers.SecretKeyRef(ctx, kube, store.GetKind(), namespace, provider.Auth.Password.SecretRef)
 	if err != nil {
-		return nil, fmt.Errorf(errMissingField, err)
+		return gophercloud.AuthOptions{}, fmt.Errorf(errMissingField, err)
+	}
+	if password == "" {
+		return gophercloud.AuthOptions{}, fmt.Errorf(errMissingField, errors.New("password secret value is empty"))
 	}
 
-	authopts := gophercloud.AuthOptions{
+	return gophercloud.AuthOptions{
 		IdentityEndpoint: provider.AuthURL,
 		TenantName:       provider.TenantName,
 		DomainName:       provider.DomainName,
 		Username:         username,
 		Password:         password,
+	}, nil
+}
+
+func buildAppCredAuthOpts(ctx context.Context, store esv1.GenericStore, kube client.Client, namespace string, provider *esv1.BarbicanProvider) (gophercloud.AuthOptions, error) {
+	if err := validateAppCredAuth(provider.Auth); err != nil {
+		return gophercloud.AuthOptions{}, err
 	}
 
-	auth, err := openstack.AuthenticatedClient(ctx, authopts)
-	if err != nil {
-		return nil, fmt.Errorf(errAuthFailed, err)
+	appCredID := provider.Auth.ApplicationCredentialID.Value
+	var err error
+
+	if appCredID == "" {
+		appCredID, err = resolvers.SecretKeyRef(ctx, kube, store.GetKind(), namespace, provider.Auth.ApplicationCredentialID.SecretRef)
+		if err != nil {
+			return gophercloud.AuthOptions{}, fmt.Errorf(errMissingField, err)
+		}
+		if appCredID == "" {
+			return gophercloud.AuthOptions{}, fmt.Errorf(errMissingField, errors.New("applicationCredentialID secret value is empty"))
+		}
 	}
 
-	barbicanClient, err := openstack.NewKeyManagerV1(auth, gophercloud.EndpointOpts{
-		Region: provider.Region,
-	})
+	appCredSecret, err := resolvers.SecretKeyRef(ctx, kube, store.GetKind(), namespace, provider.Auth.ApplicationCredentialSecret.SecretRef)
 	if err != nil {
-		return nil, fmt.Errorf(errClientInit, err)
+		return gophercloud.AuthOptions{}, fmt.Errorf(errMissingField, err)
 	}
-
-	c := &Client{
-		keyManager: barbicanClient,
+	if appCredSecret == "" {
+		return gophercloud.AuthOptions{}, fmt.Errorf(errMissingField, errors.New("applicationCredentialSecret secret value is empty"))
 	}
 
-	return c, nil
+	return gophercloud.AuthOptions{
+		IdentityEndpoint:            provider.AuthURL,
+		ApplicationCredentialSecret: appCredSecret,
+		ApplicationCredentialID:     appCredID,
+	}, nil
 }
 
 // NewProvider constructs a new Barbican provider.

+ 590 - 33
providers/v1/barbican/provider_test.go

@@ -20,6 +20,7 @@ import (
 	"context"
 	"testing"
 
+	"github.com/gophercloud/gophercloud/v2"
 	"github.com/stretchr/testify/assert"
 	corev1 "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -30,14 +31,17 @@ import (
 )
 
 const (
-	testAuthURL    = "https://keystone.example.com/v3"
-	testTenantName = "test-tenant"
-	testDomainName = "default"
-	testRegion     = "RegionOne"
-	testUsername   = "test-user"
-	testPassword   = "test-password"
-	testSecretName = "barbican-creds"
-	testNamespace  = "default"
+	testAuthURL        = "https://keystone.example.com/v3"
+	testTenantName     = "test-tenant"
+	testDomainName     = "default"
+	testRegion         = "RegionOne"
+	testUsername       = "test-user"
+	testPassword       = "test-password"
+	testSecretName     = "barbican-creds"
+	testNamespace      = "default"
+	testAppCredID      = "app-cred-id-123"
+	testAppCredSecret  = "app-cred-secret-456"
+	testAppCredSecName = "barbican-app-creds"
 )
 
 type validateStoreTestCase struct {
@@ -65,17 +69,33 @@ func TestValidateStore(t *testing.T) {
 			errorMsg:    "store is nil",
 		},
 		{
-			name:        "valid store should pass validation",
+			name:        "valid password store should pass validation",
 			store:       makeValidSecretStore(),
 			expectError: false,
 		},
 		{
-			name:        "username as value should pass validation",
-			store:       makeSecretStoreWithValueUsername(),
+			name:        "valid password store with explicit authType should pass",
+			store:       makeSecretStoreWithExplicitPasswordAuthType(),
 			expectError: false,
 		},
 		{
-			name:        "nil barbican provider should return error",
+			name:        "valid appCredential store should pass validation",
+			store:       makeSecretStoreWithAppCredAuth(),
+			expectError: false,
+		},
+		{
+			name:        "valid appCredential store with inline ID should pass",
+			store:       makeSecretStoreWithAppCredValueID(),
+			expectError: false,
+		},
+		{
+			name:        "nil provider should return error",
+			store:       makeSecretStoreWithNilProvider(),
+			expectError: true,
+			errorMsg:    "provider barbican is nil",
+		},
+		{
+			name:        "nil barbican should return error",
 			store:       makeSecretStoreWithNilBarbican(),
 			expectError: true,
 			errorMsg:    "provider barbican is nil",
@@ -87,27 +107,52 @@ func TestValidateStore(t *testing.T) {
 			errorMsg:    "authURL is required",
 		},
 		{
-			name:        "username without value or secretRef should return error",
+			name:        "password auth missing username should return error",
+			store:       makeSecretStorePasswordNoUsername(),
+			expectError: true,
+			errorMsg:    "username is required for password auth",
+		},
+		{
+			name:        "password auth missing password should return error",
+			store:       makeSecretStorePasswordNoPassword(),
+			expectError: true,
+			errorMsg:    "password secretRef is required",
+		},
+		{
+			name:        "password auth username present but empty should return error",
 			store:       makeSecretStoreWithEmptyUsername(),
 			expectError: true,
-			errorMsg:    "auth.username",
+			errorMsg:    "username must specify either value or secretRef",
 		},
 		{
-			name:        "missing password secretRef should return error",
+			name:        "password auth password present with nil secretRef should return error",
 			store:       makeSecretStoreWithNoPasswordRef(),
 			expectError: true,
-			errorMsg:    "auth.password",
+			errorMsg:    "password secretRef is required",
 		},
 		{
-			name:        "cluster store without secretRef namespace should return error",
-			store:       makeClusterSecretStoreNoNamespace(),
+			name:        "appCredential auth missing ID should return error",
+			store:       makeSecretStoreAppCredNoID(),
 			expectError: true,
-			errorMsg:    "namespace",
+			errorMsg:    "applicationCredentialID is required",
 		},
 		{
-			name:        "cluster store with secretRef namespace should pass",
-			store:       makeClusterSecretStoreWithNamespace(),
-			expectError: false,
+			name:        "appCredential auth ID with no value or secretRef should return error",
+			store:       makeSecretStoreAppCredEmptyID(),
+			expectError: true,
+			errorMsg:    "applicationCredentialID must specify either value or secretRef",
+		},
+		{
+			name:        "appCredential auth missing secret should return error",
+			store:       makeSecretStoreAppCredNoSecret(),
+			expectError: true,
+			errorMsg:    "applicationCredentialSecret secretRef is required",
+		},
+		{
+			name:        "unsupported auth type should return error",
+			store:       makeSecretStoreWithUnsupportedAuthType(),
+			expectError: true,
+			errorMsg:    "unsupported auth type",
 		},
 	}
 
@@ -178,6 +223,7 @@ func TestNewClient(t *testing.T) {
 		name        string
 		store       esv1.GenericStore
 		kube        *clientfake.ClientBuilder
+		namespace   string
 		expectError bool
 		errorMsg    string
 	}{
@@ -221,23 +267,172 @@ func TestNewClient(t *testing.T) {
 			expectError: true,
 			errorMsg:    "provider barbican is nil",
 		},
+		// Backward compatibility: password auth with no authType set (defaults to password)
+		{
+			name:        "password auth without explicit authType should pass (backward compat)",
+			store:       makeValidSecretStore(),
+			kube:        clientfake.NewClientBuilder().WithObjects(makeValidSecret()),
+			expectError: false,
+		},
+		// Backward compatibility: password auth with explicit authType=password
+		{
+			name:        "password auth with explicit authType=password should pass",
+			store:       makeSecretStoreWithExplicitPasswordAuthType(),
+			kube:        clientfake.NewClientBuilder().WithObjects(makeValidSecret()),
+			expectError: false,
+		},
+		// Application credential auth type tests
+		{
+			name:        "appCredential auth with valid secret should pass",
+			store:       makeSecretStoreWithAppCredAuth(),
+			kube:        clientfake.NewClientBuilder().WithObjects(makeValidAppCredSecret()),
+			expectError: false,
+		},
+		{
+			name:        "appCredential auth with value appCredID should pass",
+			store:       makeSecretStoreWithAppCredValueID(),
+			kube:        clientfake.NewClientBuilder().WithObjects(makeAppCredSecretWithNoID()),
+			expectError: false,
+		},
+		{
+			name:        "appCredential auth missing appCredID secret should return error",
+			store:       makeSecretStoreWithAppCredAuth(),
+			kube:        clientfake.NewClientBuilder(),
+			expectError: true,
+			errorMsg:    "missing required field",
+		},
+		{
+			name:        "appCredential auth missing appCredSecret in secret should return error",
+			store:       makeSecretStoreWithAppCredAuth(),
+			kube:        clientfake.NewClientBuilder().WithObjects(makeAppCredSecretWithMissingSecret()),
+			expectError: true,
+			errorMsg:    "missing required field",
+		},
+		{
+			name:        "appCredential auth missing authURL should return error",
+			store:       makeSecretStoreWithAppCredMissingAuthURL(),
+			kube:        clientfake.NewClientBuilder().WithObjects(makeValidAppCredSecret()),
+			expectError: true,
+			errorMsg:    "missing required field",
+		},
+		{
+			name:        "unsupported auth type should return error",
+			store:       makeSecretStoreWithUnsupportedAuthType(),
+			kube:        clientfake.NewClientBuilder().WithObjects(makeValidSecret()),
+			expectError: true,
+			errorMsg:    "unsupported auth type",
+		},
+		{
+			name:        "cluster secret store without explicit secretRef namespace should use the ExternalSecret namespace",
+			store:       makeClusterSecretStoreNoNamespace(),
+			kube:        clientfake.NewClientBuilder().WithObjects(makeValidSecret()),
+			namespace:   testNamespace,
+			expectError: false,
+		},
+		{
+			name:        "cluster secret store with explicit secretRef namespace should use that namespace instead of the ExternalSecret namespace",
+			store:       makeClusterSecretStoreWithNamespace(),
+			kube:        clientfake.NewClientBuilder().WithObjects(makeValidSecret()),
+			namespace:   "some-other-namespace",
+			expectError: false,
+		},
 	}
 
 	for _, tc := range testCases {
 		t.Run(tc.name, func(t *testing.T) {
+			// Stub the OpenStack auth/client boundary so happy-path cases succeed
+			// deterministically without a real OpenStack endpoint.
+			origAuthClient := authenticatedClient
+			origKeyManager := newKeyManagerV1
+			authenticatedClient = func(_ context.Context, _ gophercloud.AuthOptions) (*gophercloud.ProviderClient, error) {
+				return &gophercloud.ProviderClient{}, nil
+			}
+			newKeyManagerV1 = func(_ *gophercloud.ProviderClient, _ gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
+				return &gophercloud.ServiceClient{}, nil
+			}
+			t.Cleanup(func() {
+				authenticatedClient = origAuthClient
+				newKeyManagerV1 = origKeyManager
+			})
+
+			namespace := tc.namespace
+			if namespace == "" {
+				namespace = testNamespace
+			}
+
 			provider := &Provider{}
 			fakeClient := tc.kube.Build()
-
-			// Note: This test will fail when trying to actually connect to OpenStack
-			// In a real test environment, we would need to mock the OpenStack client
-			_, err := provider.NewClient(context.Background(), tc.store, fakeClient, testNamespace)
+			_, err := provider.NewClient(context.Background(), tc.store, fakeClient, namespace)
 
 			if tc.expectError {
 				assert.Error(t, err)
 				assert.Contains(t, err.Error(), tc.errorMsg)
 			} else {
-				// This would only pass with proper OpenStack mocking
-				assert.Error(t, err) // We expect an error due to missing OpenStack mock
+				assert.NoError(t, err)
+			}
+		})
+	}
+}
+
+func TestNewClientAuthTypeDefaultsToPassword(t *testing.T) {
+	// Verify that when AuthType is nil, the provider defaults to password auth
+	// and resolves username/password credentials correctly.
+	store := makeValidSecretStore()
+	assert.Nil(t, store.Spec.Provider.Barbican.Auth.AuthType, "AuthType should be nil for backward compatibility test")
+
+	// Stub the OpenStack boundary so the call succeeds deterministically.
+	origAuthClient := authenticatedClient
+	origKeyManager := newKeyManagerV1
+	authenticatedClient = func(_ context.Context, _ gophercloud.AuthOptions) (*gophercloud.ProviderClient, error) {
+		return &gophercloud.ProviderClient{}, nil
+	}
+	newKeyManagerV1 = func(_ *gophercloud.ProviderClient, _ gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
+		return &gophercloud.ServiceClient{}, nil
+	}
+	t.Cleanup(func() {
+		authenticatedClient = origAuthClient
+		newKeyManagerV1 = origKeyManager
+	})
+
+	fakeClient := clientfake.NewClientBuilder().WithObjects(makeValidSecret()).Build()
+	provider := &Provider{}
+	_, err := provider.NewClient(context.Background(), store, fakeClient, testNamespace)
+	assert.NoError(t, err)
+}
+
+func TestGetProviderWithAuthType(t *testing.T) {
+	testCases := []struct {
+		name             string
+		store            esv1.GenericStore
+		expectedAuthType *esv1.BarbicanAuthType
+	}{
+		{
+			name:             "password store with no authType set (backward compat)",
+			store:            makeValidSecretStore(),
+			expectedAuthType: nil,
+		},
+		{
+			name:             "password store with explicit authType",
+			store:            makeSecretStoreWithExplicitPasswordAuthType(),
+			expectedAuthType: barbicanAuthTypePtr(esv1.BarbicanAuthTypePassword),
+		},
+		{
+			name:             "appCredential store with authType",
+			store:            makeSecretStoreWithAppCredAuth(),
+			expectedAuthType: barbicanAuthTypePtr(esv1.BarbicanAuthTypeApplicationCredential),
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			provider, err := getProvider(tc.store)
+
+			assert.NoError(t, err)
+			assert.NotNil(t, provider)
+			if tc.expectedAuthType == nil {
+				assert.Nil(t, provider.Auth.AuthType)
+			} else {
+				assert.Equal(t, *tc.expectedAuthType, *provider.Auth.AuthType)
 			}
 		})
 	}
@@ -259,13 +454,13 @@ func makeValidSecretStore() *esv1.SecretStore {
 					DomainName: testDomainName,
 					Region:     testRegion,
 					Auth: esv1.BarbicanAuth{
-						Username: esv1.BarbicanProviderUsernameRef{
+						Username: &esv1.BarbicanProviderUsernameRef{
 							SecretRef: &esmeta.SecretKeySelector{
 								Name: testSecretName,
 								Key:  "username",
 							},
 						},
-						Password: esv1.BarbicanProviderPasswordRef{
+						Password: &esv1.BarbicanProviderPasswordRef{
 							SecretRef: &esmeta.SecretKeySelector{
 								Name: testSecretName,
 								Key:  "password",
@@ -280,7 +475,7 @@ func makeValidSecretStore() *esv1.SecretStore {
 
 func makeSecretStoreWithValueUsername() *esv1.SecretStore {
 	store := makeValidSecretStore()
-	store.Spec.Provider.Barbican.Auth.Username = esv1.BarbicanProviderUsernameRef{
+	store.Spec.Provider.Barbican.Auth.Username = &esv1.BarbicanProviderUsernameRef{
 		Value: testUsername,
 	}
 	return store
@@ -306,13 +501,13 @@ func makeSecretStoreWithMissingAuthURL() *esv1.SecretStore {
 
 func makeSecretStoreWithEmptyUsername() *esv1.SecretStore {
 	store := makeValidSecretStore()
-	store.Spec.Provider.Barbican.Auth.Username = esv1.BarbicanProviderUsernameRef{}
+	store.Spec.Provider.Barbican.Auth.Username = &esv1.BarbicanProviderUsernameRef{}
 	return store
 }
 
 func makeSecretStoreWithNoPasswordRef() *esv1.SecretStore {
 	store := makeValidSecretStore()
-	store.Spec.Provider.Barbican.Auth.Password = esv1.BarbicanProviderPasswordRef{}
+	store.Spec.Provider.Barbican.Auth.Password = &esv1.BarbicanProviderPasswordRef{}
 	return store
 }
 
@@ -369,3 +564,365 @@ func makeSecretWithMissingPassword() *corev1.Secret {
 		},
 	}
 }
+
+// Helper: returns a pointer to a BarbicanAuthType.
+func barbicanAuthTypePtr(t esv1.BarbicanAuthType) *esv1.BarbicanAuthType {
+	return new(t)
+}
+
+// Helper: password auth store with explicit authType=password.
+func makeSecretStoreWithExplicitPasswordAuthType() *esv1.SecretStore {
+	store := makeValidSecretStore()
+	store.Spec.Provider.Barbican.Auth.AuthType = barbicanAuthTypePtr(esv1.BarbicanAuthTypePassword)
+	return store
+}
+
+// Helper: application credential auth store.
+func makeSecretStoreWithAppCredAuth() *esv1.SecretStore {
+	return &esv1.SecretStore{
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      "test-store-appcred",
+			Namespace: testNamespace,
+		},
+		Spec: esv1.SecretStoreSpec{
+			Provider: &esv1.SecretStoreProvider{
+				Barbican: &esv1.BarbicanProvider{
+					AuthURL:    testAuthURL,
+					TenantName: testTenantName,
+					DomainName: testDomainName,
+					Region:     testRegion,
+					Auth: esv1.BarbicanAuth{
+						AuthType: barbicanAuthTypePtr(esv1.BarbicanAuthTypeApplicationCredential),
+						ApplicationCredentialID: &esv1.BarbicanProviderAppCredIDRef{
+							SecretRef: &esmeta.SecretKeySelector{
+								Name: testAppCredSecName,
+								Key:  "app-cred-id",
+							},
+						},
+						ApplicationCredentialSecret: &esv1.BarbicanProviderAppCredSecretRef{
+							SecretRef: &esmeta.SecretKeySelector{
+								Name: testAppCredSecName,
+								Key:  "app-cred-secret",
+							},
+						},
+					},
+				},
+			},
+		},
+	}
+}
+
+// Helper: application credential auth store with inline value for appCredID.
+func makeSecretStoreWithAppCredValueID() *esv1.SecretStore {
+	store := makeSecretStoreWithAppCredAuth()
+	store.Spec.Provider.Barbican.Auth.ApplicationCredentialID = &esv1.BarbicanProviderAppCredIDRef{
+		Value: testAppCredID,
+	}
+	return store
+}
+
+// Helper: application credential auth store missing authURL.
+func makeSecretStoreWithAppCredMissingAuthURL() *esv1.SecretStore {
+	store := makeSecretStoreWithAppCredAuth()
+	store.Spec.Provider.Barbican.AuthURL = ""
+	return store
+}
+
+// Helper: unsupported auth type store.
+func makeSecretStoreWithUnsupportedAuthType() *esv1.SecretStore {
+	store := makeValidSecretStore()
+	unsupported := esv1.BarbicanAuthType("kerberos")
+	store.Spec.Provider.Barbican.Auth.AuthType = &unsupported
+	return store
+}
+
+// Helper: valid k8s secret for application credentials.
+func makeValidAppCredSecret() *corev1.Secret {
+	return &corev1.Secret{
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      testAppCredSecName,
+			Namespace: testNamespace,
+		},
+		Data: map[string][]byte{
+			"app-cred-id":     []byte(testAppCredID),
+			"app-cred-secret": []byte(testAppCredSecret),
+		},
+	}
+}
+
+// Helper: k8s secret with only the app credential secret (no ID).
+func makeAppCredSecretWithNoID() *corev1.Secret {
+	return &corev1.Secret{
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      testAppCredSecName,
+			Namespace: testNamespace,
+		},
+		Data: map[string][]byte{
+			"app-cred-secret": []byte(testAppCredSecret),
+		},
+	}
+}
+
+// Helper: k8s secret with empty username value.
+func makeSecretWithEmptyUsername() *corev1.Secret {
+	return &corev1.Secret{
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      testSecretName,
+			Namespace: testNamespace,
+		},
+		Data: map[string][]byte{
+			"username": []byte(""),
+			"password": []byte(testPassword),
+		},
+	}
+}
+
+// Helper: k8s secret with empty password value.
+func makeSecretWithEmptyPassword() *corev1.Secret {
+	return &corev1.Secret{
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      testSecretName,
+			Namespace: testNamespace,
+		},
+		Data: map[string][]byte{
+			"username": []byte(testUsername),
+			"password": []byte(""),
+		},
+	}
+}
+
+// Helper: k8s secret with empty app-cred-id value.
+func makeAppCredSecretWithEmptyID() *corev1.Secret {
+	return &corev1.Secret{
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      testAppCredSecName,
+			Namespace: testNamespace,
+		},
+		Data: map[string][]byte{
+			"app-cred-id":     []byte(""),
+			"app-cred-secret": []byte(testAppCredSecret),
+		},
+	}
+}
+
+// Helper: k8s secret with empty app-cred-secret value.
+func makeAppCredSecretWithEmptySecret() *corev1.Secret {
+	return &corev1.Secret{
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      testAppCredSecName,
+			Namespace: testNamespace,
+		},
+		Data: map[string][]byte{
+			"app-cred-id":     []byte(testAppCredID),
+			"app-cred-secret": []byte(""),
+		},
+	}
+}
+
+// Helper: k8s secret with app credential ID but missing app credential secret.
+func makeAppCredSecretWithMissingSecret() *corev1.Secret {
+	return &corev1.Secret{
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      testAppCredSecName,
+			Namespace: testNamespace,
+		},
+		Data: map[string][]byte{
+			"app-cred-id": []byte(testAppCredID),
+			// missing app-cred-secret key
+		},
+	}
+}
+
+func TestBuildPasswordAuthOpts(t *testing.T) {
+	ctx := context.Background()
+
+	testCases := []struct {
+		name        string
+		store       *esv1.SecretStore
+		kube        *clientfake.ClientBuilder
+		expectError bool
+		errorMsg    string
+		wantUser    string
+		wantPass    string
+	}{
+		{
+			name:        "resolve username and password from secret",
+			store:       makeValidSecretStore(),
+			kube:        clientfake.NewClientBuilder().WithObjects(makeValidSecret()),
+			expectError: false,
+			wantUser:    testUsername,
+			wantPass:    testPassword,
+		},
+		{
+			name:        "missing username secret returns error",
+			store:       makeValidSecretStore(),
+			kube:        clientfake.NewClientBuilder(),
+			expectError: true,
+			errorMsg:    "missing required field",
+		},
+		{
+			name:        "missing password key in secret returns error",
+			store:       makeValidSecretStore(),
+			kube:        clientfake.NewClientBuilder().WithObjects(makeSecretWithMissingPassword()),
+			expectError: true,
+			errorMsg:    "missing required field",
+		},
+		{
+			name:        "empty username in secret returns error",
+			store:       makeValidSecretStore(),
+			kube:        clientfake.NewClientBuilder().WithObjects(makeSecretWithEmptyUsername()),
+			expectError: true,
+			errorMsg:    "username secret value is empty",
+		},
+		{
+			name:        "empty password in secret returns error",
+			store:       makeValidSecretStore(),
+			kube:        clientfake.NewClientBuilder().WithObjects(makeSecretWithEmptyPassword()),
+			expectError: true,
+			errorMsg:    "password secret value is empty",
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			prov := tc.store.Spec.Provider.Barbican
+			opts, err := buildPasswordAuthOpts(ctx, tc.store, tc.kube.Build(), testNamespace, prov)
+
+			if tc.expectError {
+				assert.Error(t, err)
+				assert.Contains(t, err.Error(), tc.errorMsg)
+			} else {
+				assert.NoError(t, err)
+				assert.Equal(t, tc.wantUser, opts.Username)
+				assert.Equal(t, tc.wantPass, opts.Password)
+				assert.Equal(t, testAuthURL, opts.IdentityEndpoint)
+				assert.Equal(t, testTenantName, opts.TenantName)
+				assert.Equal(t, testDomainName, opts.DomainName)
+			}
+		})
+	}
+}
+
+func TestBuildAppCredAuthOpts(t *testing.T) {
+	ctx := context.Background()
+
+	testCases := []struct {
+		name        string
+		store       *esv1.SecretStore
+		kube        *clientfake.ClientBuilder
+		expectError bool
+		errorMsg    string
+		wantCredID  string
+		wantCredSec string
+	}{
+		{
+			name:        "resolve appCredID and appCredSecret from secret",
+			store:       makeSecretStoreWithAppCredAuth(),
+			kube:        clientfake.NewClientBuilder().WithObjects(makeValidAppCredSecret()),
+			expectError: false,
+			wantCredID:  testAppCredID,
+			wantCredSec: testAppCredSecret,
+		},
+		{
+			name:        "nil applicationCredentialID returns error",
+			store:       makeSecretStoreAppCredNoID(),
+			kube:        clientfake.NewClientBuilder(),
+			expectError: true,
+			errorMsg:    "applicationCredentialID is required for applicationCredential auth",
+		},
+		{
+			name:        "nil applicationCredentialSecret returns error",
+			store:       makeSecretStoreAppCredNoSecret(),
+			kube:        clientfake.NewClientBuilder(),
+			expectError: true,
+			errorMsg:    "applicationCredentialSecret secretRef is required for applicationCredential auth",
+		},
+		{
+			name:        "appCredID with no value and no secretRef returns error",
+			store:       makeSecretStoreAppCredEmptyID(),
+			kube:        clientfake.NewClientBuilder(),
+			expectError: true,
+			errorMsg:    "applicationCredentialID must specify either value or secretRef",
+		},
+		{
+			name:        "missing appCredID secret object returns error",
+			store:       makeSecretStoreWithAppCredAuth(),
+			kube:        clientfake.NewClientBuilder(),
+			expectError: true,
+			errorMsg:    "missing required field",
+		},
+		{
+			name:        "missing appCredSecret key in secret returns error",
+			store:       makeSecretStoreWithAppCredAuth(),
+			kube:        clientfake.NewClientBuilder().WithObjects(makeAppCredSecretWithMissingSecret()),
+			expectError: true,
+			errorMsg:    "missing required field",
+		},
+		{
+			name:        "empty appCredID in secret returns error",
+			store:       makeSecretStoreWithAppCredAuth(),
+			kube:        clientfake.NewClientBuilder().WithObjects(makeAppCredSecretWithEmptyID()),
+			expectError: true,
+			errorMsg:    "applicationCredentialID secret value is empty",
+		},
+		{
+			name:        "empty appCredSecret in secret returns error",
+			store:       makeSecretStoreWithAppCredAuth(),
+			kube:        clientfake.NewClientBuilder().WithObjects(makeAppCredSecretWithEmptySecret()),
+			expectError: true,
+			errorMsg:    "applicationCredentialSecret secret value is empty",
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			prov := tc.store.Spec.Provider.Barbican
+			opts, err := buildAppCredAuthOpts(ctx, tc.store, tc.kube.Build(), testNamespace, prov)
+
+			if tc.expectError {
+				assert.Error(t, err)
+				assert.Contains(t, err.Error(), tc.errorMsg)
+			} else {
+				assert.NoError(t, err)
+				assert.Equal(t, tc.wantCredID, opts.ApplicationCredentialID)
+				assert.Equal(t, tc.wantCredSec, opts.ApplicationCredentialSecret)
+				assert.Equal(t, testAuthURL, opts.IdentityEndpoint)
+			}
+		})
+	}
+}
+
+// Helper: password auth store with no username (empty value, nil secretRef).
+func makeSecretStorePasswordNoUsername() *esv1.SecretStore {
+	store := makeValidSecretStore()
+	store.Spec.Provider.Barbican.Auth.Username = nil
+	return store
+}
+
+// Helper: password auth store with no password secretRef.
+func makeSecretStorePasswordNoPassword() *esv1.SecretStore {
+	store := makeValidSecretStore()
+	store.Spec.Provider.Barbican.Auth.Password = nil
+	return store
+}
+
+// Helper: appCredential auth with nil ApplicationCredentialID.
+func makeSecretStoreAppCredNoID() *esv1.SecretStore {
+	store := makeSecretStoreWithAppCredAuth()
+	store.Spec.Provider.Barbican.Auth.ApplicationCredentialID = nil
+	return store
+}
+
+// Helper: appCredential auth with ApplicationCredentialID present but empty (no value, no secretRef).
+func makeSecretStoreAppCredEmptyID() *esv1.SecretStore {
+	store := makeSecretStoreWithAppCredAuth()
+	store.Spec.Provider.Barbican.Auth.ApplicationCredentialID = &esv1.BarbicanProviderAppCredIDRef{}
+	return store
+}
+
+// Helper: appCredential auth with nil ApplicationCredentialSecret.
+func makeSecretStoreAppCredNoSecret() *esv1.SecretStore {
+	store := makeSecretStoreWithAppCredAuth()
+	store.Spec.Provider.Barbican.Auth.ApplicationCredentialSecret = nil
+	return store
+}

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

@@ -121,6 +121,18 @@ spec:
       vaultUrl: string
     barbican:
       auth:
+        applicationCredentialID:
+          secretRef:
+            key: string
+            name: string
+            namespace: string
+          value: string
+        applicationCredentialSecret:
+          secretRef:
+            key: string
+            name: string
+            namespace: string
+        authType: "password"
         password:
           secretRef:
             key: string

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

@@ -121,6 +121,18 @@ spec:
       vaultUrl: string
     barbican:
       auth:
+        applicationCredentialID:
+          secretRef:
+            key: string
+            name: string
+            namespace: string
+          value: string
+        applicationCredentialSecret:
+          secretRef:
+            key: string
+            name: string
+            namespace: string
+        authType: "password"
         password:
           secretRef:
             key: string