This is an automated email from the ASF dual-hosted git repository.

davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git


The following commit(s) were added to refs/heads/main by this push:
     new b4164ad87cc9 CAMEL-24495: camel-salesforce-maven-plugin - support JWT 
and Client Credentials authentication
b4164ad87cc9 is described below

commit b4164ad87cc9e8e837dad30fe4b2425365940012
Author: Torsten Mielke <[email protected]>
AuthorDate: Mon Aug 31 20:00:15 2026 +0200

    CAMEL-24495: camel-salesforce-maven-plugin - support JWT and Client 
Credentials authentication
    
    The Maven plugin previously only supported Username-Password authentication,
    with the grant type hardcoded in the codegen layer. Salesforce is retiring 
the
    USERNAME_PASSWORD grant type in Winter '27 (February 2027), so this adds
    support for the JWT and Client Credentials flows.
    
    Adds an explicit authenticationType parameter to select the auth type, while
    still auto-detecting it from whichever credentials are provided. Refactors 
the
    codegen login config to a no-arg constructor plus setters to support that
    auto-detection, and makes userName optional since Client Credentials doesn't
    need it. Adds Mojo-level validation that rejects ambiguous credential
    combinations early with an actionable error message — without it, omitting
    the password while providing clientSecret and userName would silently
    auto-detect Client Credentials instead of failing for what was meant to be
    Username-Password.
    
    Adds SalesforceMojoValidationTest covering all credential-validation paths,
    and manual integration tests for all three authentication types. Refactors
    AbstractSalesforceMojoTest into a static setup-helper utility class, moving
    login tests into CamelSalesforceLoginManualIT to separate test 
infrastructure
    from test logic. Updates the plugin README with per-auth-type requirements
    and adds an upgrade-guide entry.
    
    Co-authored-by: Claude Opus 4.6 <[email protected]>
    
    Closes #25876
---
 .../codegen/AbstractSalesforceExecution.java       |  28 +++-
 .../camel-salesforce-maven-plugin/README.md        | 182 +++++++++++++--------
 .../apache/camel/maven/AbstractSalesforceMojo.java |  32 ++--
 .../camel/maven/AbstractSalesforceMojoTest.java    | 103 ++++--------
 ...Test.java => CamelSalesforceLoginManualIT.java} | 118 +++++++------
 .../camel/maven/CamelSalesforceMojoManualIT.java   |   4 +-
 .../camel/maven/GeneratePubSubMojoManualIT.java    |   4 +-
 .../camel/maven/SalesforceMojoValidationTest.java  | 153 +++++++++++++++++
 .../org/apache/camel/maven/SchemaMojoManualIT.java |   4 +-
 .../ROOT/pages/camel-4x-upgrade-guide-4_23.adoc    |  10 ++
 10 files changed, 420 insertions(+), 218 deletions(-)

diff --git 
a/components/camel-salesforce/camel-salesforce-codegen/src/main/java/org/apache/camel/component/salesforce/codegen/AbstractSalesforceExecution.java
 
b/components/camel-salesforce/camel-salesforce-codegen/src/main/java/org/apache/camel/component/salesforce/codegen/AbstractSalesforceExecution.java
index 043e09e5a35f..4be104abe72e 100644
--- 
a/components/camel-salesforce/camel-salesforce-codegen/src/main/java/org/apache/camel/component/salesforce/codegen/AbstractSalesforceExecution.java
+++ 
b/components/camel-salesforce/camel-salesforce-codegen/src/main/java/org/apache/camel/component/salesforce/codegen/AbstractSalesforceExecution.java
@@ -25,6 +25,7 @@ import java.util.Set;
 import java.util.concurrent.ExecutorService;
 
 import org.apache.camel.CamelContext;
+import org.apache.camel.component.salesforce.AuthenticationType;
 import org.apache.camel.component.salesforce.SalesforceHttpClient;
 import org.apache.camel.component.salesforce.SalesforceLoginConfig;
 import org.apache.camel.component.salesforce.api.SalesforceException;
@@ -157,6 +158,11 @@ public abstract class AbstractSalesforceExecution {
      */
     String userName;
 
+    /**
+     * Salesforce authentication type.
+     */
+    AuthenticationType authenticationType;
+
     /**
      * Salesforce API version.
      */
@@ -321,14 +327,16 @@ public abstract class AbstractSalesforceExecution {
     }
 
     private SalesforceLoginConfig getSalesforceLoginSession() {
-        if (keyStoreParameters != null) {
-            SalesforceLoginConfig salesforceLoginConfig
-                    = new SalesforceLoginConfig(loginUrl, clientId, userName, 
keyStoreParameters, false);
-            salesforceLoginConfig.setJwtAudience(jwtAudience);
-
-            return salesforceLoginConfig;
-        }
-        return new SalesforceLoginConfig(loginUrl, clientId, clientSecret, 
userName, password, false);
+        SalesforceLoginConfig config = new SalesforceLoginConfig();
+        config.setLoginUrl(loginUrl);
+        config.setClientId(clientId);
+        config.setClientSecret(clientSecret);
+        config.setUserName(userName);
+        config.setPassword(password);
+        config.setKeystore(keyStoreParameters);
+        config.setJwtAudience(jwtAudience);
+        config.setType(authenticationType);
+        return config;
     }
 
     private void disconnectFromSalesforce(final RestClient restClient) {
@@ -416,6 +424,10 @@ public abstract class AbstractSalesforceExecution {
         this.keyStoreParameters = keyStoreParameters;
     }
 
+    public void setAuthenticationType(AuthenticationType authenticationType) {
+        this.authenticationType = authenticationType;
+    }
+
     public void setUserName(String userName) {
         this.userName = userName;
     }
diff --git 
a/components/camel-salesforce/camel-salesforce-maven-plugin/README.md 
b/components/camel-salesforce/camel-salesforce-maven-plugin/README.md
index d06a2c1ea8a5..131cd7f431f2 100644
--- a/components/camel-salesforce/camel-salesforce-maven-plugin/README.md
+++ b/components/camel-salesforce/camel-salesforce-maven-plugin/README.md
@@ -4,7 +4,7 @@ This plugin generates DTOs for use with the [Camel Salesforce 
Component](https:/
 
 ## Usage ##
               
-This plugin provides three maven goals:
+This plugin provides three Maven goals:
                          
 * The `generate` goal generates DTOs for use with the REST API.
 * The `generatePubSub` goal generates Apache Avro `SpecificRecord` subclasses 
for use with the PubSub API.
@@ -12,29 +12,30 @@ This plugin provides three maven goals:
 
 The plugin configuration has the following properties.
 
-* clientId - Salesforce client Id for Remote API access
-* clientSecret - Salesforce client secret for Remote API access
-* userName - Salesforce account username
-* password - Salesforce account password (including secret token)
-* jwtAudience - Salesforce JWT audience (defaults to 
"https://login.salesforce.com";)
-* keystoreResource - Path to keystore file for JWT authentication
-* keystorePassword - Password for keystore file
-* keystoreType - Type of keystore file (defaults to "JKS")
-* loginUrl - Salesforce loginUrl (defaults to "https://login.salesforce.com";)
-* version - Salesforce Rest API version, defaults to 25.0
-* outputDirectory - Directory where to place generated DTOs, defaults to 
${project.build.directory}/generated-sources/camel-salesforce
-* includes - List of SObject types to include
-* topics - List of topics to include, .e.g., `/event/BatchApexErrorEvent`. 
This property only applies to the `generatePubSub` goal.
-* excludes - List of SObject types to exclude
-* includePattern - Java RegEx for SObject types to include
-* excludePattern - Java RegEx for SObject types to exclude
-* packageName - Java package name for generated DTOs, defaults to 
org.apache.camel.salesforce.dto.
-* customTypes - override default types in generated DTOs
-* useStringsForPicklists - Use strings instead of enumerations for picklists. 
Default is false.
-* childRelationshipNameSuffix - Suffix for child relationship property name. 
Necessary if an SObject
+* `clientId` - Salesforce client Id for Remote API access
+* `clientSecret` - Salesforce client secret for Remote API access
+* `userName` - Salesforce account username (required for Username-Password and 
JWT flows)
+* `password` - Salesforce account password (including secret token)
+* `authenticationType` - Salesforce authentication type: USERNAME_PASSWORD, 
JWT, or CLIENT_CREDENTIALS. If not specified, auto-detected from provided 
credentials.
+* `jwtAudience` - Salesforce JWT audience (defaults to 
"https://login.salesforce.com";)
+* `keystoreResource` - Path to keystore file for JWT authentication
+* `keystorePassword` - Password for keystore file
+* `keystoreType` - Type of keystore file (defaults to "JKS")
+* `loginUrl` - Salesforce loginUrl (defaults to "https://login.salesforce.com";)
+* `version` - Salesforce Rest API version, defaults to 25.0
+* `outputDirectory` - Directory where to place generated DTOs, defaults to 
${project.build.directory}/generated-sources/camel-salesforce
+* `includes` - List of SObject types to include
+* `topics` - List of topics to include, e.g., `/event/BatchApexErrorEvent`. 
This property only applies to the `generatePubSub` goal.
+* `excludes` - List of SObject types to exclude
+* `includePattern` - Java RegEx for SObject types to include
+* `excludePattern` - Java RegEx for SObject types to exclude
+* `packageName` - Java package name for generated DTOs, defaults to 
org.apache.camel.salesforce.dto.
+* `customTypes` - override default types in generated DTOs
+* `useStringsForPicklists` - Use strings instead of enumerations for 
picklists. Default is false.
+* `childRelationshipNameSuffix` - Suffix for child relationship property name. 
Necessary if an SObject
 has a lookup field with the same name as its Child Relationship Name. If 
setting to something other 
 than default, "List" is a sensible value.
-* enumerationOverrideProperties - Override picklist enum value generation via 
a java.util.Properties instance. 
+* `enumerationOverrideProperties` - Override picklist enum value generation 
via a java.util.Properties instance. 
 Property name format: `SObject.FieldName.PicklistValue`. Property value is the 
desired enum value. E.g.:
     ```
     <enumerationOverrideProperties>
@@ -47,57 +48,63 @@ Property name format: `SObject.FieldName.PicklistValue`. 
Property value is the d
 
 Additional properties to provide proxy information, if behind a firewall.
 
-* httpProxyHost
-* httpProxyPort
-* httpProxyUsername
-* httpProxyPassword
-* httpProxyRealm
-* httpProxyAuthUri
-* httpProxyUseDigestAuth
-* httpProxyIncludedAddresses
-* httpProxyExcludedAddresses
+* `httpProxyHost`
+* `httpProxyPort`
+* `httpProxyUsername`
+* `httpProxyPassword`
+* `httpProxyRealm`
+* `httpProxyAuthUri`
+* `httpProxyUseDigestAuth`
+* `httpProxyIncludedAddresses`
+* `httpProxyExcludedAddresses`
 
-There are two authentication methods supported by the plugin: 
Username-Password and JWT.
-The plugin will use the Username-Password method if the `clientSecret` is 
specified and will use the JWT method if the `keystoreResource` is specified.
+Three authentication methods are supported by the plugin: Username-Password, 
JWT, and Client Credentials.
+The plugin auto-detects the authentication method from the provided 
credentials, or you can set `authenticationType` explicitly.
 
-Sample pom.xml using Username-Password authentication:
+* **Username-Password** requires: `clientId`, `clientSecret`, `userName`, and 
`password`.<br>
+  Auto-detected when `password` is specified.
+* **JWT** requires: `clientId`, `userName`, `loginUrl` (My Domain URL), 
`keystoreResource`, and `keystorePassword`.<br>
+  `keystoreType` defaults to JKS, `jwtAudience` defaults to 
`https://login.salesforce.com`.<br>
+  Auto-detected when `keystoreResource` is specified.
+* **Client Credentials** requires: `clientId`, `clientSecret`, and `loginUrl` 
(My Domain URL).<br>
+  Auto-detected when only `clientId` and `clientSecret` are specified (no 
`password`, no `userName`, no `keystoreResource`).
+
+___
+<br>
+
+### Username-Password Authentication Type ###
+
+Sample pom.xml using **Username-Password** authentication:
 ```
 <?xml version="1.0" encoding="UTF-8"?>
 <project xmlns="http://maven.apache.org/POM/4.0.0"; 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/maven-v4_0_0.xsd";>
 
        <properties>
-           
-               
<camelSalesforce.clientId>5MVG9uudbyLbNPZOFutIHJpIb2nchnCiNE_NqeYcewMCPPT8_6VV_LQF_CJ813456GxzhxZdxlGwbYI_yzHmz</camelSalesforce.clientId>
+               
<camelSalesforce.clientId>5MVG9uudbyLbNPZOFut...GwbYI_yzHmz</camelSalesforce.clientId>
                
<camelSalesforce.clientSecret>5630289243049151316</camelSalesforce.clientSecret>
                
<camelSalesforce.userName>[email protected]</camelSalesforce.userName>
                
<camelSalesforce.password>foopasswordCbe5V27JxD0JXYFGJIdIEWB7p</camelSalesforce.password>
-               
-               
<camelSalesforce.loginUrl>https://myDomain.my.salesforce.com</camelSalesforce.loginUrl>
 
-               
+               
<camelSalesforce.loginUrl>https://myDomain.my.salesforce.com</camelSalesforce.loginUrl>
                
<camelSalesforce.httpProxyHost>foo.bar.com</camelSalesforce.httpProxyHost>
                
<camelSalesforce.httpProxyPort>8090</camelSalesforce.httpProxyPort>
-               
        </properties>
-
        ...
-       
        <build>
                ...
                <plugins>
                        ...
-                       
-                       <!-- camel maven saleforce for creating salesforce 
objects -->
+                       <!-- camel maven salesforce for creating salesforce 
objects -->
                        <plugin>
                                <groupId>org.apache.camel.maven</groupId>
                                
<artifactId>camel-salesforce-maven-plugin</artifactId>
-                               <version>2.17.1</version>
+                               <version>${camel.version}</version>
                                <configuration>
                                        
<clientId>${camelSalesforce.clientId}</clientId>
                                        
<clientSecret>${camelSalesforce.clientSecret}</clientSecret>
                                        
<userName>${camelSalesforce.userName}</userName>
                                        
<password>${camelSalesforce.password}</password>
-                                       <loginUrl 
default-value="https://login.salesforce.com";>${camelSalesforce.loginUrl}</loginUrl>
 
+                                       
<loginUrl>${camelSalesforce.loginUrl}</loginUrl>
                                        <includes>
                                                <include>Account</include>
                                                <include>Contacts</include>
@@ -106,7 +113,6 @@ Sample pom.xml using Username-Password authentication:
                                        
<httpProxyPort>${camelSalesforce.httpProxyPort}</httpProxyPort>
                                </configuration>
                        </plugin>
-
                </plugins>
        </build>
 
@@ -117,49 +123,46 @@ The plugin should be configured for the rest of the 
properties, and can be execu
 
        mvn camel-salesforce:generate -DcamelSalesforce.clientId=<clientid> 
-DcamelSalesforce.clientSecret=<clientsecret> 
-DcamelSalesforce.userName=<username> -DcamelSalesforce.password=<password>
 
-Sample pom.xml using JWT authentication:
+___
+<br>
+
+###  JWT Authentication Type ###
+
+Sample pom.xml using **JWT** authentication:
 ```
 <?xml version="1.0" encoding="UTF-8"?>
 <project xmlns="http://maven.apache.org/POM/4.0.0"; 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/maven-v4_0_0.xsd";>
 
        <properties>
-           
-               
<camelSalesforce.clientId>5MVG9uudbyLbNPZOFutIHJpIb2nchnCiNE_NqeYcewMCPPT8_6VV_LQF_CJ813456GxzhxZdxlGwbYI_yzHmz</camelSalesforce.clientId>
+               
<camelSalesforce.clientId>5MVG9uudbyLbNPZOFut...GwbYI_yzHmz</camelSalesforce.clientId>
                
<camelSalesforce.userName>[email protected]</camelSalesforce.userName>
                
<camelSalesforce.keystore.resource>src/main/resources/salesforce.jks</camelSalesforce.keystore.resource>
                
<camelSalesforce.keystore.password>foopasswordCbe5V27JxD0JXYFGJIdIEWB7p</camelSalesforce.keystore.password>
                
<camelSalesforce.keystore.type>JKS</camelSalesforce.keystore.type>
-               
                
<camelSalesforce.jwtAudience>https://login.salesforce.com</camelSalesforce.jwtAudience>
-               
-               
<camelSalesforce.loginUrl>https://myDomain.my.salesforce.com</camelSalesforce.loginUrl>
 
-               
+               
<camelSalesforce.loginUrl>https://myDomain.my.salesforce.com</camelSalesforce.loginUrl>
                
<camelSalesforce.httpProxyHost>foo.bar.com</camelSalesforce.httpProxyHost>
                
<camelSalesforce.httpProxyPort>8090</camelSalesforce.httpProxyPort>
-               
        </properties>
-
        ...
-       
        <build>
                ...
                <plugins>
                        ...
-                       
-                       <!-- camel maven saleforce for creating salesforce 
objects -->
+                       <!-- camel maven salesforce for creating salesforce 
objects -->
                        <plugin>
                                <groupId>org.apache.camel.maven</groupId>
                                
<artifactId>camel-salesforce-maven-plugin</artifactId>
-                               <version>2.17.1</version>
+                               <version>${camel.version}</version>
                                <configuration>
                                        
<clientId>${camelSalesforce.clientId}</clientId>
                                        
<userName>${camelSalesforce.userName}</userName>
                                        
<keystoreResource>${camelSalesforce.keystore.resource}</keystoreResource>
                                        
<keystorePassword>${camelSalesforce.keystore.password}</keystorePassword>
-                                       <keystoreType 
default-value="JKS">${camelSalesforce.keystore.type}</keystoreType>
-                                       <jwtAudience 
default-value="https://login.salesforce.com";>${camelSalesforce.jwtAudience}</jwtAudience>
-                                       <loginUrl 
default-value="https://login.salesforce.com";>${camelSalesforce.loginUrl}</loginUrl>
 
+                                       
<keystoreType>${camelSalesforce.keystore.type}</keystoreType>
+                                       
<jwtAudience>${camelSalesforce.jwtAudience}</jwtAudience>
+                                       
<loginUrl>${camelSalesforce.loginUrl}</loginUrl>
                                        <includes>
                                                <include>Account</include>
                                                <include>Contacts</include>
@@ -168,7 +171,6 @@ Sample pom.xml using JWT authentication:
                                        
<httpProxyPort>${camelSalesforce.httpProxyPort}</httpProxyPort>
                                </configuration>
                        </plugin>
-
                </plugins>
        </build>
 
@@ -177,9 +179,57 @@ Sample pom.xml using JWT authentication:
 For obvious security reasons it is recommended that the clientId, userName, 
keystoreResource, keystorePassword, keystoreType and jwtAudience fields be not 
set in the pom.xml. 
 The plugin should be configured for the rest of the properties, and can be 
executed using the following command:
 
-       mvn camel-salesforce:generate -DcamelSalesforce.clientId=<clientid> 
-DcamelSalesforce.userName=<username> 
-DcamelSalesforce.keystore.resource=<keystoreResource> 
-DcamelSalesforce.keystore.password=<keystorePassword> 
-DcamelSalesforce.keystore.type=<keystoreType> 
-DcamelSalesforce.jwtAudience=<jwtAudience>
+       mvn camel-salesforce:generate -DcamelSalesforce.clientId=<clientid> 
-DcamelSalesforce.userName=<username> 
-DcamelSalesforce.keystore.resource=<keystoreResource> 
-DcamelSalesforce.keystore.password=<keystorePassword> 
-DcamelSalesforce.keystore.type=<keystoreType> 
-DcamelSalesforce.jwtAudience=<jwtAudience> 
-DcamelSalesforce.loginUrl=<login-url>
+
+___
+<br>
+
+### Client Credentials Authentication Type ###
+
+Sample pom.xml using **Client Credentials** authentication
+```
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"; 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
+       xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/maven-v4_0_0.xsd";>
+
+       <properties>
+               
<camelSalesforce.clientId>5MVG9uudbyLbNPZOFut...GwbYI_yzHmz</camelSalesforce.clientId>
+               
<camelSalesforce.clientSecret>5630289243049151316</camelSalesforce.clientSecret>
+               
<camelSalesforce.loginUrl>https://myDomain.my.salesforce.com</camelSalesforce.loginUrl>
+       </properties>
+       ...
+       <build>
+               ...
+               <plugins>
+                       ...
+                       <!-- camel maven salesforce for creating salesforce 
objects -->
+                       <plugin>
+                               <groupId>org.apache.camel.maven</groupId>
+                               
<artifactId>camel-salesforce-maven-plugin</artifactId>
+                               <version>${camel.version}</version>
+                               <configuration>
+                                       
<clientId>${camelSalesforce.clientId}</clientId>
+                                       
<clientSecret>${camelSalesforce.clientSecret}</clientSecret>
+                                       
<loginUrl>${camelSalesforce.loginUrl}</loginUrl>
+                                       <includes>
+                                               <include>Account</include>
+                                               <include>Contacts</include>
+                                       </includes>
+                               </configuration>
+                       </plugin>
+               </plugins>
+       </build>
+
+</project>
+```
+For obvious security reasons it is recommended that the clientId and 
clientSecret fields be not set in the pom.xml.
+The plugin should be configured for the rest of the properties, and can be 
executed using the following command:
+
+       mvn camel-salesforce:generate -DcamelSalesforce.clientId=<clientid> 
-DcamelSalesforce.clientSecret=<clientsecret> 
-DcamelSalesforce.loginUrl=<login-url>
 
 
+___
+
 The generated DTOs use Jackson. All Salesforce field types are supported. Date 
and time fields are mapped to java.time.ZonedDateTime, and picklist fields are 
mapped to generated Java Enumerations.
 
 Relationship fields, e.g. `Contact.Account`, will be strongly typed if the 
referenced SObject type is listed in `includes`. Otherwise, the type of the 
reference object will be `AbstractDescribedSObjectBase`. Some useful but 
non-obvious SObjects to include are `RecordType`, `User`, `Group`, and `Name`.  
@@ -205,4 +255,4 @@ You can customize types, i.e. use java.time.LocalDateTime 
instead of the default
     </customTypes>
   </configuration>
 </plugin>
-````
+```
diff --git 
a/components/camel-salesforce/camel-salesforce-maven-plugin/src/main/java/org/apache/camel/maven/AbstractSalesforceMojo.java
 
b/components/camel-salesforce/camel-salesforce-maven-plugin/src/main/java/org/apache/camel/maven/AbstractSalesforceMojo.java
index 575ff03ca865..78a65bc147df 100644
--- 
a/components/camel-salesforce/camel-salesforce-maven-plugin/src/main/java/org/apache/camel/maven/AbstractSalesforceMojo.java
+++ 
b/components/camel-salesforce/camel-salesforce-maven-plugin/src/main/java/org/apache/camel/maven/AbstractSalesforceMojo.java
@@ -24,6 +24,7 @@ import java.security.KeyStore;
 import java.util.Map;
 import java.util.Set;
 
+import org.apache.camel.component.salesforce.AuthenticationType;
 import org.apache.camel.component.salesforce.SalesforceEndpointConfig;
 import org.apache.camel.component.salesforce.SalesforceLoginConfig;
 import 
org.apache.camel.component.salesforce.codegen.AbstractSalesforceExecution;
@@ -142,11 +143,18 @@ public abstract class AbstractSalesforceMojo extends 
AbstractMojo {
     final SSLContextParameters sslContextParameters = new 
SSLContextParameters();
 
     /**
-     * Salesforce username.
+     * Salesforce username. Required for USERNAME_PASSWORD and JWT 
authentication types.
      */
-    @Parameter(property = "camelSalesforce.userName", required = true)
+    @Parameter(property = "camelSalesforce.userName")
     String userName;
 
+    /**
+     * Salesforce authentication type. If not specified, auto-detected from 
provided credentials. Supported values:
+     * USERNAME_PASSWORD, JWT, CLIENT_CREDENTIALS.
+     */
+    @Parameter(property = "camelSalesforce.authenticationType")
+    AuthenticationType authenticationType;
+
     /**
      * Salesforce JWT Audience.
      */
@@ -213,6 +221,7 @@ public abstract class AbstractSalesforceMojo extends 
AbstractMojo {
         execution.setLoginUrl(loginUrl);
         execution.setUserName(userName);
         execution.setPassword(password);
+        execution.setAuthenticationType(authenticationType);
         execution.setVersion(version);
         execution.setSslContextParameters(sslContextParameters);
         execution.setJwtAudience(jwtAudience);
@@ -225,15 +234,7 @@ public abstract class AbstractSalesforceMojo extends 
AbstractMojo {
                     "Either property: clientSecret or property: 
keystoreResource must be provided.");
         } else if (clientSecret != null && keystoreResource != null) {
             throw new MojoExecutionException(
-                    "Property: clientSecret or property: keystoreResource must 
be provided.");
-        }
-
-        if (clientSecret != null) {
-            if (password == null) {
-                throw new MojoExecutionException(
-                        // NOTE: a text error message to clarify the problem
-                        "Property 'password' must be provided when property 
'clientSecret' was provided."); // NOSONAR
-            }
+                    "Only one of clientSecret or keystoreResource may be 
provided, not both.");
         }
 
         if (keystoreResource != null) {
@@ -243,6 +244,15 @@ public abstract class AbstractSalesforceMojo extends 
AbstractMojo {
                         "Property 'keystorePassword' must be provided when 
property 'keystoreResource' was provided."); // NOSONAR
             }
         }
+
+        if (authenticationType == null && clientSecret != null && userName != 
null && password == null
+                && keystoreResource == null) {
+            throw new MojoExecutionException(
+                    "Ambiguous authentication configuration: 'userName' and 
'clientSecret' are set but 'password' is missing. "
+                                             + "For Username-Password 
authentication, provide the 'password' property. "
+                                             + "For Client Credentials 
authentication, remove the 'userName' property "
+                                             + "or set 'authenticationType' to 
CLIENT_CREDENTIALS explicitly.");
+        }
     }
 
     private KeyStoreParameters generateKeyStoreParameters() {
diff --git 
a/components/camel-salesforce/camel-salesforce-maven-plugin/src/test/java/org/apache/camel/maven/AbstractSalesforceMojoTest.java
 
b/components/camel-salesforce/camel-salesforce-maven-plugin/src/test/java/org/apache/camel/maven/AbstractSalesforceMojoTest.java
index f0f177af315a..98f2bdffb99e 100644
--- 
a/components/camel-salesforce/camel-salesforce-maven-plugin/src/test/java/org/apache/camel/maven/AbstractSalesforceMojoTest.java
+++ 
b/components/camel-salesforce/camel-salesforce-maven-plugin/src/test/java/org/apache/camel/maven/AbstractSalesforceMojoTest.java
@@ -20,88 +20,18 @@ import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.InputStream;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
 import java.util.Properties;
 
+import org.apache.camel.component.salesforce.AuthenticationType;
 import org.apache.camel.component.salesforce.SalesforceEndpointConfig;
-import 
org.apache.camel.component.salesforce.codegen.AbstractSalesforceExecution;
-import org.apache.maven.plugin.MojoExecutionException;
-import org.apache.maven.plugin.MojoFailureException;
-import org.junit.jupiter.api.Test;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
-import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.jupiter.api.Assumptions.assumeTrue;
 
 public abstract class AbstractSalesforceMojoTest {
 
-    private static final Map<String, List<String>> NO_HEADERS = 
Collections.emptyMap();
+    static final String TEST_LOGIN_PROPERTIES = 
"../test-salesforce-login.properties";
 
-    private static final String TEST_LOGIN_PROPERTIES = 
"../test-salesforce-login.properties";
-
-    @Test
-    public void shouldLoginAndProvideRestClient() throws IOException, 
MojoExecutionException, MojoFailureException {
-        final AbstractSalesforceMojo mojo = new AbstractSalesforceMojo() {
-            final Logger logger = 
LoggerFactory.getLogger(AbstractSalesforceExecution.class.getName());
-
-            @Override
-            protected AbstractSalesforceExecution getSalesforceExecution() {
-                return new AbstractSalesforceExecution() {
-                    @Override
-                    protected void executeWithClient() {
-                        assertThat(getRestClient()).isNotNull();
-
-                        getRestClient().getGlobalObjects(NO_HEADERS, 
(response, headers, exception) -> {
-                            assertThat(exception).isNull();
-                        });
-                    }
-
-                    @Override
-                    protected Logger getLog() {
-                        return logger;
-                    }
-                };
-            }
-        };
-
-        setup(mojo);
-
-        mojo.execute();
-    }
-
-    @Test
-    public void shouldLoginWithJwtAndProvideRestClient() throws IOException, 
MojoExecutionException, MojoFailureException {
-        final AbstractSalesforceMojo mojo = new AbstractSalesforceMojo() {
-            final Logger logger = 
LoggerFactory.getLogger(AbstractSalesforceExecution.class.getName());
-
-            @Override
-            protected AbstractSalesforceExecution getSalesforceExecution() {
-                return new AbstractSalesforceExecution() {
-                    @Override
-                    protected void executeWithClient() {
-                        assertThat(getRestClient()).isNotNull();
-
-                        getRestClient().getGlobalObjects(NO_HEADERS, 
(response, headers, exception) -> {
-                            assertThat(exception).isNull();
-                        });
-                    }
-
-                    @Override
-                    protected Logger getLog() {
-                        return logger;
-                    }
-                };
-            }
-        };
-
-        setupJwt(mojo);
-
-        mojo.execute();
-    }
-
-    static void setup(final AbstractSalesforceMojo mojo) throws IOException {
+    static void setupUsernamePassword(final AbstractSalesforceMojo mojo) 
throws IOException {
         // load test-salesforce-login properties
         try (final InputStream stream = new 
FileInputStream(TEST_LOGIN_PROPERTIES)) {
             final Properties properties = new Properties();
@@ -110,6 +40,9 @@ public abstract class AbstractSalesforceMojoTest {
             mojo.clientSecret = 
properties.getProperty("salesforce.client.secret");
             mojo.userName = properties.getProperty("salesforce.username");
             mojo.password = properties.getProperty("salesforce.password");
+            assumeTrue(mojo.password != null && !mojo.password.isEmpty(),
+                    "Property 'salesforce.password' must be set in " + 
TEST_LOGIN_PROPERTIES
+                                                                          + " 
for USERNAME_PASSWORD authentication test");
             mojo.loginUrl = properties.getProperty("salesforce.login.url");
             mojo.version = SalesforceEndpointConfig.DEFAULT_VERSION;
         } catch (final FileNotFoundException e) {
@@ -147,4 +80,26 @@ public abstract class AbstractSalesforceMojoTest {
             throw exception;
         }
     }
+
+    static void setupClientCredentials(final AbstractSalesforceMojo mojo) 
throws IOException {
+        // load test-salesforce-login properties
+        try (final InputStream stream = new 
FileInputStream(TEST_LOGIN_PROPERTIES)) {
+            final Properties properties = new Properties();
+            properties.load(stream);
+            mojo.clientId = properties.getProperty("salesforce.client.id");
+            mojo.clientSecret = 
properties.getProperty("salesforce.client.secret");
+            mojo.authenticationType = AuthenticationType.CLIENT_CREDENTIALS;
+            mojo.loginUrl = properties.getProperty("salesforce.login.url");
+            mojo.version = SalesforceEndpointConfig.DEFAULT_VERSION;
+        } catch (final FileNotFoundException e) {
+            final FileNotFoundException exception
+                    = new FileNotFoundException(
+                            "Create a properties file named " + 
TEST_LOGIN_PROPERTIES
+                                                + " with clientId, 
clientSecret"
+                                                + " for a Salesforce connected 
app configured for Client Credentials flow.");
+            exception.initCause(e);
+
+            throw exception;
+        }
+    }
 }
diff --git 
a/components/camel-salesforce/camel-salesforce-maven-plugin/src/test/java/org/apache/camel/maven/AbstractSalesforceMojoTest.java
 
b/components/camel-salesforce/camel-salesforce-maven-plugin/src/test/java/org/apache/camel/maven/CamelSalesforceLoginManualIT.java
similarity index 51%
copy from 
components/camel-salesforce/camel-salesforce-maven-plugin/src/test/java/org/apache/camel/maven/AbstractSalesforceMojoTest.java
copy to 
components/camel-salesforce/camel-salesforce-maven-plugin/src/test/java/org/apache/camel/maven/CamelSalesforceLoginManualIT.java
index f0f177af315a..d324b5f63dd4 100644
--- 
a/components/camel-salesforce/camel-salesforce-maven-plugin/src/test/java/org/apache/camel/maven/AbstractSalesforceMojoTest.java
+++ 
b/components/camel-salesforce/camel-salesforce-maven-plugin/src/test/java/org/apache/camel/maven/CamelSalesforceLoginManualIT.java
@@ -16,16 +16,11 @@
  */
 package org.apache.camel.maven;
 
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
 import java.io.IOException;
-import java.io.InputStream;
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
-import java.util.Properties;
 
-import org.apache.camel.component.salesforce.SalesforceEndpointConfig;
 import 
org.apache.camel.component.salesforce.codegen.AbstractSalesforceExecution;
 import org.apache.maven.plugin.MojoExecutionException;
 import org.apache.maven.plugin.MojoFailureException;
@@ -33,16 +28,47 @@ import org.junit.jupiter.api.Test;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import static 
org.apache.camel.maven.AbstractSalesforceMojoTest.setupClientCredentials;
+import static org.apache.camel.maven.AbstractSalesforceMojoTest.setupJwt;
+import static 
org.apache.camel.maven.AbstractSalesforceMojoTest.setupUsernamePassword;
 import static org.assertj.core.api.Assertions.assertThat;
 
-public abstract class AbstractSalesforceMojoTest {
+/**
+ * Integration test that verifies Salesforce login with all supported 
authentication types. The {@code ManualIT} suffix
+ * prevents automatic execution by Maven Surefire and Failsafe — run 
explicitly with:
+ *
+ * <pre>
+ * mvn test -Dtest=CamelSalesforceLoginManualIT
+ * </pre>
+ *
+ * Requires a properties file at {@code ../test-salesforce-login.properties} 
with:
+ *
+ * <pre>
+ * # Required for USERNAME_PASSWORD test
+ * salesforce.client.id=...
+ * salesforce.client.secret=...
+ * salesforce.username=...
+ * salesforce.password=...
+ * salesforce.login.url=https://your-domain.my.salesforce.com
+ *
+ * # Required for CLIENT_CREDENTIALS test (uses client.id, client.secret, 
login.url from above)
+ *
+ * # Required for JWT test
+ * salesforce.keystore.resource=...
+ * salesforce.keystore.password=...
+ * salesforce.keystore.type=JKS
+ * </pre>
+ */
+public class CamelSalesforceLoginManualIT {
 
     private static final Map<String, List<String>> NO_HEADERS = 
Collections.emptyMap();
 
-    private static final String TEST_LOGIN_PROPERTIES = 
"../test-salesforce-login.properties";
+    private static final Logger logger = 
LoggerFactory.getLogger(CamelSalesforceLoginManualIT.class.getName());
 
     @Test
-    public void shouldLoginAndProvideRestClient() throws IOException, 
MojoExecutionException, MojoFailureException {
+    public void shouldLoginWithUsernamePasswordAndProvideRestClient()
+            throws IOException, MojoExecutionException, MojoFailureException {
+        logger.info("Testing 
shouldLoginWithUsernamePasswordAndProvideRestClient()");
         final AbstractSalesforceMojo mojo = new AbstractSalesforceMojo() {
             final Logger logger = 
LoggerFactory.getLogger(AbstractSalesforceExecution.class.getName());
 
@@ -66,13 +92,14 @@ public abstract class AbstractSalesforceMojoTest {
             }
         };
 
-        setup(mojo);
+        setupUsernamePassword(mojo);
 
         mojo.execute();
     }
 
     @Test
     public void shouldLoginWithJwtAndProvideRestClient() throws IOException, 
MojoExecutionException, MojoFailureException {
+        logger.info("Testing shouldLoginWithJwtAndProvideRestClient()");
         final AbstractSalesforceMojo mojo = new AbstractSalesforceMojo() {
             final Logger logger = 
LoggerFactory.getLogger(AbstractSalesforceExecution.class.getName());
 
@@ -101,50 +128,35 @@ public abstract class AbstractSalesforceMojoTest {
         mojo.execute();
     }
 
-    static void setup(final AbstractSalesforceMojo mojo) throws IOException {
-        // load test-salesforce-login properties
-        try (final InputStream stream = new 
FileInputStream(TEST_LOGIN_PROPERTIES)) {
-            final Properties properties = new Properties();
-            properties.load(stream);
-            mojo.clientId = properties.getProperty("salesforce.client.id");
-            mojo.clientSecret = 
properties.getProperty("salesforce.client.secret");
-            mojo.userName = properties.getProperty("salesforce.username");
-            mojo.password = properties.getProperty("salesforce.password");
-            mojo.loginUrl = properties.getProperty("salesforce.login.url");
-            mojo.version = SalesforceEndpointConfig.DEFAULT_VERSION;
-        } catch (final FileNotFoundException e) {
-            final FileNotFoundException exception
-                    = new FileNotFoundException(
-                            "Create a properties file named " + 
TEST_LOGIN_PROPERTIES
-                                                + " with clientId, 
clientSecret, userName, password"
-                                                + " for a Salesforce account 
with Merchandise and Invoice objects from Salesforce Guides.");
-            exception.initCause(e);
-
-            throw exception;
-        }
-    }
+    @Test
+    public void shouldLoginWithClientCredentialsAndProvideRestClient()
+            throws IOException, MojoExecutionException, MojoFailureException {
+        logger.info("Testing 
shouldLoginWithClientCredentialsAndProvideRestClient()");
+        final AbstractSalesforceMojo mojo = new AbstractSalesforceMojo() {
+            final Logger logger = 
LoggerFactory.getLogger(AbstractSalesforceExecution.class.getName());
+
+            @Override
+            protected AbstractSalesforceExecution getSalesforceExecution() {
+                return new AbstractSalesforceExecution() {
+                    @Override
+                    protected void executeWithClient() {
+                        assertThat(getRestClient()).isNotNull();
+
+                        getRestClient().getGlobalObjects(NO_HEADERS, 
(response, headers, exception) -> {
+                            assertThat(exception).isNull();
+                        });
+                    }
+
+                    @Override
+                    protected Logger getLog() {
+                        return logger;
+                    }
+                };
+            }
+        };
 
-    static void setupJwt(final AbstractSalesforceMojo mojo) throws IOException 
{
-        // load test-salesforce-login properties
-        try (final InputStream stream = new 
FileInputStream(TEST_LOGIN_PROPERTIES)) {
-            final Properties properties = new Properties();
-            properties.load(stream);
-            mojo.clientId = properties.getProperty("salesforce.client.id");
-            mojo.userName = properties.getProperty("salesforce.username");
-            mojo.loginUrl = properties.getProperty("salesforce.login.url");
-            mojo.keystoreResource = 
properties.getProperty("salesforce.keystore.resource");
-            mojo.keystorePassword = 
properties.getProperty("salesforce.keystore.password");
-            mojo.keystoreType = 
properties.getProperty("salesforce.keystore.type");
-            mojo.version = SalesforceEndpointConfig.DEFAULT_VERSION;
-        } catch (final FileNotFoundException e) {
-            final FileNotFoundException exception
-                    = new FileNotFoundException(
-                            "Create a properties file named " + 
TEST_LOGIN_PROPERTIES
-                                                + " with clientId, userName, 
keyStoreResource, keyStorePassword, keyStoreType"
-                                                + " for a Salesforce account 
with Merchandise and Invoice objects from Salesforce Guides.");
-            exception.initCause(e);
-
-            throw exception;
-        }
+        setupClientCredentials(mojo);
+
+        mojo.execute();
     }
 }
diff --git 
a/components/camel-salesforce/camel-salesforce-maven-plugin/src/test/java/org/apache/camel/maven/CamelSalesforceMojoManualIT.java
 
b/components/camel-salesforce/camel-salesforce-maven-plugin/src/test/java/org/apache/camel/maven/CamelSalesforceMojoManualIT.java
index 888a52f358af..86e697820a9b 100644
--- 
a/components/camel-salesforce/camel-salesforce-maven-plugin/src/test/java/org/apache/camel/maven/CamelSalesforceMojoManualIT.java
+++ 
b/components/camel-salesforce/camel-salesforce-maven-plugin/src/test/java/org/apache/camel/maven/CamelSalesforceMojoManualIT.java
@@ -34,7 +34,7 @@ import 
org.apache.camel.component.salesforce.SalesforceEndpointConfig;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.io.TempDir;
 
-import static org.apache.camel.maven.AbstractSalesforceMojoTest.setup;
+import static 
org.apache.camel.maven.AbstractSalesforceMojoTest.setupUsernamePassword;
 import static org.assertj.core.api.Assertions.assertThat;
 
 public class CamelSalesforceMojoManualIT {
@@ -71,7 +71,7 @@ public class CamelSalesforceMojoManualIT {
         final GenerateMojo mojo = new GenerateMojo();
 
         // set login properties
-        setup(mojo);
+        setupUsernamePassword(mojo);
 
         // set defaults
         mojo.version = SalesforceEndpointConfig.DEFAULT_VERSION;
diff --git 
a/components/camel-salesforce/camel-salesforce-maven-plugin/src/test/java/org/apache/camel/maven/GeneratePubSubMojoManualIT.java
 
b/components/camel-salesforce/camel-salesforce-maven-plugin/src/test/java/org/apache/camel/maven/GeneratePubSubMojoManualIT.java
index 19ae4db8c6b1..f82a36137111 100644
--- 
a/components/camel-salesforce/camel-salesforce-maven-plugin/src/test/java/org/apache/camel/maven/GeneratePubSubMojoManualIT.java
+++ 
b/components/camel-salesforce/camel-salesforce-maven-plugin/src/test/java/org/apache/camel/maven/GeneratePubSubMojoManualIT.java
@@ -35,7 +35,7 @@ import com.google.testing.compile.JavaFileObjects;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.io.TempDir;
 
-import static org.apache.camel.maven.AbstractSalesforceMojoTest.setup;
+import static 
org.apache.camel.maven.AbstractSalesforceMojoTest.setupUsernamePassword;
 import static org.assertj.core.api.Assertions.assertThat;
 
 public class GeneratePubSubMojoManualIT {
@@ -74,7 +74,7 @@ public class GeneratePubSubMojoManualIT {
         final GeneratePubSubMojo mojo = new GeneratePubSubMojo();
 
         // set login properties
-        setup(mojo);
+        setupUsernamePassword(mojo);
 
         // set additional properties specific to this Mojo
         try (final InputStream stream = new 
FileInputStream(TEST_LOGIN_PROPERTIES)) {
diff --git 
a/components/camel-salesforce/camel-salesforce-maven-plugin/src/test/java/org/apache/camel/maven/SalesforceMojoValidationTest.java
 
b/components/camel-salesforce/camel-salesforce-maven-plugin/src/test/java/org/apache/camel/maven/SalesforceMojoValidationTest.java
new file mode 100644
index 000000000000..c812365d5a6d
--- /dev/null
+++ 
b/components/camel-salesforce/camel-salesforce-maven-plugin/src/test/java/org/apache/camel/maven/SalesforceMojoValidationTest.java
@@ -0,0 +1,153 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.maven;
+
+import org.apache.camel.component.salesforce.AuthenticationType;
+import 
org.apache.camel.component.salesforce.codegen.AbstractSalesforceExecution;
+import org.apache.maven.plugin.MojoExecutionException;
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+/**
+ * Unit tests for authentication parameter validation in {@link 
AbstractSalesforceMojo}. These tests verify that
+ * {@code validateAuthenticationParameters()} rejects invalid credential 
combinations and accepts valid ones for all
+ * supported authentication types (USERNAME_PASSWORD, JWT, CLIENT_CREDENTIALS).
+ */
+public class SalesforceMojoValidationTest {
+
+    private static final String VALIDATION_PASSED = "validation passed";
+
+    private AbstractSalesforceMojo createMojo() {
+        return new AbstractSalesforceMojo() {
+            @Override
+            protected AbstractSalesforceExecution getSalesforceExecution() {
+                throw new RuntimeException(VALIDATION_PASSED);
+            }
+        };
+    }
+
+    // --- Validation rejection tests ---
+
+    // Validation must fail when no authentication credential (clientSecret or 
keystoreResource) is provided
+    @Test
+    void shouldRejectWhenNeitherClientSecretNorKeystoreProvided() {
+        AbstractSalesforceMojo mojo = createMojo();
+        mojo.clientId = "test-client-id";
+
+        assertThatThrownBy(mojo::execute)
+                .isInstanceOf(MojoExecutionException.class)
+                .hasMessageContaining("Either property: clientSecret or 
property: keystoreResource must be provided");
+    }
+
+    // clientSecret and keystoreResource are mutually exclusive — providing 
both must be rejected
+    @Test
+    void shouldRejectWhenBothClientSecretAndKeystoreProvided() {
+        AbstractSalesforceMojo mojo = createMojo();
+        mojo.clientId = "test-client-id";
+        mojo.clientSecret = "test-secret";
+        mojo.keystoreResource = "/some/keystore.jks";
+
+        assertThatThrownBy(mojo::execute)
+                .isInstanceOf(MojoExecutionException.class)
+                .hasMessageContaining("Only one of clientSecret or 
keystoreResource may be provided, not both");
+    }
+
+    // JWT authentication requires a keystore password to unlock the keystore
+    @Test
+    void shouldRejectKeystoreWithoutPassword() {
+        AbstractSalesforceMojo mojo = createMojo();
+        mojo.clientId = "test-client-id";
+        mojo.keystoreResource = "/some/keystore.jks";
+
+        assertThatThrownBy(mojo::execute)
+                .isInstanceOf(MojoExecutionException.class)
+                .hasMessageContaining("keystorePassword' must be provided");
+    }
+
+    // When clientSecret and userName are set but password is missing, the 
configuration is ambiguous:
+    // it could be USERNAME_PASSWORD (missing password) or CLIENT_CREDENTIALS 
(stray userName).
+    // Validation must reject this unless authenticationType is set explicitly.
+    @Test
+    void shouldRejectAmbiguousCredentialsWithoutAuthenticationType() {
+        AbstractSalesforceMojo mojo = createMojo();
+        mojo.clientId = "test-client-id";
+        mojo.clientSecret = "test-secret";
+        mojo.userName = "[email protected]";
+
+        assertThatThrownBy(mojo::execute)
+                .isInstanceOf(MojoExecutionException.class)
+                .hasMessageContaining("Ambiguous authentication 
configuration");
+    }
+
+    // --- Validation acceptance tests (one per auth method) ---
+
+    // USERNAME_PASSWORD: clientSecret + userName + password is a valid, 
unambiguous combination
+    @Test
+    void shouldAcceptUsernamePasswordCredentials() {
+        AbstractSalesforceMojo mojo = createMojo();
+        mojo.clientId = "test-client-id";
+        mojo.clientSecret = "test-secret";
+        mojo.userName = "[email protected]";
+        mojo.password = "test-password";
+
+        assertThatThrownBy(mojo::execute)
+                .isInstanceOf(MojoExecutionException.class)
+                .hasMessageContaining(VALIDATION_PASSED);
+    }
+
+    // JWT: keystoreResource + keystorePassword + userName is valid — no 
ambiguity since clientSecret is absent
+    @Test
+    void shouldAcceptJwtCredentials() {
+        AbstractSalesforceMojo mojo = createMojo();
+        mojo.clientId = "test-client-id";
+        mojo.keystoreResource = "/some/keystore.jks";
+        mojo.keystorePassword = "test-password";
+        mojo.userName = "[email protected]";
+
+        assertThatThrownBy(mojo::execute)
+                .isInstanceOf(MojoExecutionException.class)
+                .hasMessageContaining(VALIDATION_PASSED);
+    }
+
+    // CLIENT_CREDENTIALS (auto-detected): clientSecret without userName is 
unambiguously Client Credentials
+    @Test
+    void shouldAcceptClientCredentialsWithoutUserName() {
+        AbstractSalesforceMojo mojo = createMojo();
+        mojo.clientId = "test-client-id";
+        mojo.clientSecret = "test-secret";
+
+        assertThatThrownBy(mojo::execute)
+                .isInstanceOf(MojoExecutionException.class)
+                .hasMessageContaining(VALIDATION_PASSED);
+    }
+
+    // CLIENT_CREDENTIALS (explicit): clientSecret + userName would normally 
be ambiguous, but setting
+    // authenticationType explicitly resolves it — validation must accept this
+    @Test
+    void shouldAcceptExplicitClientCredentialsWithUserName() {
+        AbstractSalesforceMojo mojo = createMojo();
+        mojo.clientId = "test-client-id";
+        mojo.clientSecret = "test-secret";
+        mojo.userName = "[email protected]";
+        mojo.authenticationType = AuthenticationType.CLIENT_CREDENTIALS;
+
+        assertThatThrownBy(mojo::execute)
+                .isInstanceOf(MojoExecutionException.class)
+                .hasMessageContaining(VALIDATION_PASSED);
+    }
+}
diff --git 
a/components/camel-salesforce/camel-salesforce-maven-plugin/src/test/java/org/apache/camel/maven/SchemaMojoManualIT.java
 
b/components/camel-salesforce/camel-salesforce-maven-plugin/src/test/java/org/apache/camel/maven/SchemaMojoManualIT.java
index dc8b3fecba9b..6bbcd7e40625 100644
--- 
a/components/camel-salesforce/camel-salesforce-maven-plugin/src/test/java/org/apache/camel/maven/SchemaMojoManualIT.java
+++ 
b/components/camel-salesforce/camel-salesforce-maven-plugin/src/test/java/org/apache/camel/maven/SchemaMojoManualIT.java
@@ -26,7 +26,7 @@ import 
org.apache.camel.component.salesforce.api.utils.JsonUtils;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.io.TempDir;
 
-import static org.apache.camel.maven.AbstractSalesforceMojoTest.setup;
+import static 
org.apache.camel.maven.AbstractSalesforceMojoTest.setupUsernamePassword;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
 public class SchemaMojoManualIT {
@@ -37,7 +37,7 @@ public class SchemaMojoManualIT {
     @Test
     public void testExecuteJsonSchema() throws Exception {
         final SchemaMojo mojo = new SchemaMojo();
-        setup(mojo);
+        setupUsernamePassword(mojo);
 
         mojo.includes = new String[] { "Account" };
         mojo.outputDirectory = temp.toFile();
diff --git 
a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_23.adoc 
b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_23.adoc
index 0f8cea4457bf..fe97f08ebbed 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_23.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_23.adoc
@@ -512,6 +512,16 @@ Ordinary listings are unaffected, as a listed name is 
normally a single path seg
 resolves back inside the polled directory remains accepted. Two configurations 
can newly see files skipped: a
 server that reports names navigating above the polled directory, and a 
`fileName` expression (used when
 `useList=false`) that navigates above it. Set `jailStartingDirectory=false` if 
such a path is intended.
+=== camel-salesforce
+
+The `camel-salesforce-maven-plugin` now supports JWT and Client Credentials 
authentication in addition to
+the existing Username-Password flow.
+
+A new `authenticationType` configuration property allows explicitly selecting 
the authentication type.
+When not set, the plugin auto-detects the type from the provided credentials, 
matching the behavior of the
+Salesforce component. The `userName` property is no longer required, as it is 
not needed for the Client Credentials flow.
+
+See the plugin's `README.md` for the required properties per authentication 
type.
 
 === camel-as2
 

Reply via email to