9.9 KiB
Testing & Validation Guide
This guide documents tested External Client App (ECA) and Connected App patterns based on systematic validation testing (December 2025).
Test Matrix
| Component Type | Template / Source | Test Status |
|---|---|---|
| Connected App (Basic) | connected-app-basic.xml |
✅ Verified |
| Connected App (Full OAuth) | connected-app-oauth.xml |
✅ Verified |
| Connected App (JWT Bearer) | connected-app-jwt.xml |
✅ Verified |
| Connected App (Canvas) | connected-app-canvas.xml |
⚠️ Location-dependent |
| External Client App | external-client-app.xml |
✅ Verified |
| ECA Global OAuth | eca-global-oauth.xml |
✅ Verified |
| ECA OAuth Settings | eca-oauth-settings.xml |
✅ Verified |
| ECA OAuth Security Settings | retrieve-first (ExtlClntAppOauthSecuritySettings) |
✅ CLI/registry-supported |
| ECA Configurable Policies | eca-policies.xml |
✅ Naming updated |
Critical File Naming Conventions
External Client App Files
| Metadata Type | Source Directory | File Suffix | Example |
|---|---|---|---|
| ExternalClientApplication | externalClientApps/ |
.eca-meta.xml |
externalClientApps/MyApp.eca-meta.xml |
| ExtlClntAppOauthSettings | extlClntAppOauthSettings/ |
.ecaOauth-meta.xml |
extlClntAppOauthSettings/MyApp.ecaOauth-meta.xml |
| ExtlClntAppGlobalOauthSettings | extlClntAppGlobalOauthSets/ |
.ecaGlblOauth-meta.xml |
extlClntAppGlobalOauthSets/MyApp.ecaGlblOauth-meta.xml |
| ExtlClntAppOauthSecuritySettings | extlClntAppOauthSecuritySettings/ |
.ecaOauthSecurity-meta.xml |
extlClntAppOauthSecuritySettings/MyApp.ecaOauthSecurity-meta.xml |
| ExtlClntAppOauthConfigurablePolicies | extlClntAppOauthPolicies/ |
.ecaOauthPlcy-meta.xml |
extlClntAppOauthPolicies/MyApp.ecaOauthPlcy-meta.xml |
| ExtlClntAppConfigurablePolicies | extlClntAppPolicies/ |
.ecaPlcy-meta.xml |
extlClntAppPolicies/MyApp.ecaPlcy-meta.xml |
⚠️ CRITICAL:
- Use
.ecaGlblOauth(abbreviated), NOT.ecaGlobalOauth - Use
.ecaPlcy, NOT.ecaPolicy ExtlClntAppOauthSecuritySettingsis now source-supported; prefer retrieve-first until you have an org-validated sample file
Connected App Files
| Metadata Type | File Suffix | Example |
|---|---|---|
| ConnectedApp | .connectedApp-meta.xml |
MyApp.connectedApp-meta.xml |
Common Deployment Errors & Solutions
Error: "Invalid or missing field" in ECA OAuth Settings
Cause: Using wrong schema with non-existent fields
Wrong Schema (FAILS):
<ExtlClntAppOauthSettings>
<isAdminApproved>true</isAdminApproved> <!-- DOES NOT EXIST -->
<isCodeCredentialsEnabled>true</isCodeCredentialsEnabled> <!-- DOES NOT EXIST -->
<scopes>Api</scopes> <!-- WRONG FORMAT -->
</ExtlClntAppOauthSettings>
Correct Schema:
<ExtlClntAppOauthSettings xmlns="http://soap.sforce.com/2006/04/metadata">
<commaSeparatedOauthScopes>Api, RefreshToken</commaSeparatedOauthScopes>
<externalClientApplication>MyApp</externalClientApplication>
<label>MyApp OAuth Settings</label>
</ExtlClntAppOauthSettings>
Error: "Missing required field" in ECA Global OAuth
Cause: Missing externalClientApplication or label
Wrong (FAILS):
<ExtlClntAppGlobalOauthSettings>
<callbackUrl>https://example.com/callback</callbackUrl>
<isPkceRequired>true</isPkceRequired>
</ExtlClntAppGlobalOauthSettings>
Correct:
<ExtlClntAppGlobalOauthSettings xmlns="http://soap.sforce.com/2006/04/metadata">
<callbackUrl>https://example.com/callback</callbackUrl>
<externalClientApplication>MyApp</externalClientApplication>
<isConsumerSecretOptional>true</isConsumerSecretOptional>
<isIntrospectAllTokens>false</isIntrospectAllTokens>
<isPkceRequired>true</isPkceRequired>
<isSecretRequiredForRefreshToken>false</isSecretRequiredForRefreshToken>
<label>MyApp Global OAuth</label>
<shouldRotateConsumerKey>false</shouldRotateConsumerKey>
<shouldRotateConsumerSecret>false</shouldRotateConsumerSecret>
</ExtlClntAppGlobalOauthSettings>
Error: "Organization is not configured to support location"
Cause: Canvas app using location that requires org feature enablement
Problematic Locations:
AppLauncher- Requires feature enablement- Some other locations may have similar requirements
Solution: Use universally available locations:
<canvasApp>
<locations>Visualforce</locations>
</canvasApp>
Error: Certificate not found (JWT Bearer)
Cause: Referenced certificate doesn't exist in org
Wrong:
<certificate>NonExistent_Cert</certificate>
Solution: Create certificate first, then reference it:
# 1. Create certificate in org (Setup > Certificate and Key Management)
# 2. Reference in Connected App
<certificate>My_JWT_Cert</certificate>
Field Reference
ExtlClntAppOauthSettings (ECA OAuth)
| Field | Type | Required | Description |
|---|---|---|---|
commaSeparatedOauthScopes |
String | Yes | Comma-separated scopes (e.g., "Api, RefreshToken") |
externalClientApplication |
String | Yes | Reference to parent ECA API name |
label |
String | Yes | Display label |
Available Scopes:
Api- REST/SOAP API accessRefreshToken- Offline accessOpenID- OpenID ConnectProfile- User profile infoEmail- User emailWeb- Web browser accessChatterApi- Chatter REST API
ExtlClntAppGlobalOauthSettings (ECA Global OAuth)
| Field | Type | Required | Description |
|---|---|---|---|
callbackUrl |
URL | Yes | OAuth redirect URI |
externalClientApplication |
String | Yes | Reference to parent ECA |
isConsumerSecretOptional |
Boolean | No | True for public clients with PKCE |
isIntrospectAllTokens |
Boolean | No | Enable token introspection |
isPkceRequired |
Boolean | No | Require PKCE (recommended for public clients) |
isSecretRequiredForRefreshToken |
Boolean | No | Require secret for refresh |
label |
String | Yes | Display label |
shouldRotateConsumerKey |
Boolean | No | Enable key rotation |
shouldRotateConsumerSecret |
Boolean | No | Enable secret rotation |
Deployment Order
For External Client Apps, deploy in this order:
# 1. Deploy base ECA first
sf project deploy start --metadata ExternalClientApplication:MyApp --target-org [alias]
# 2. Deploy OAuth settings
sf project deploy start --metadata ExtlClntAppOauthSettings:MyApp --target-org [alias]
# 3. Deploy Global OAuth (if needed)
sf project deploy start --metadata ExtlClntAppGlobalOauthSettings:MyApp --target-org [alias]
# 4. Deploy companion security/policy metadata when you use it
sf project deploy start --metadata ExtlClntAppOauthSecuritySettings:MyApp --target-org [alias]
sf project deploy start --metadata ExtlClntAppOauthConfigurablePolicies:MyApp --target-org [alias]
sf project deploy start --metadata ExtlClntAppConfigurablePolicies:MyApp --target-org [alias]
# Or deploy the whole package directory when all ECA source directories live under it
sf project deploy start --source-dir force-app/main/default --target-org [alias]
Post-Deployment Steps
Client Credentials Flow (ECA)
Client Credentials flow still requires post-deployment admin validation. Even with broader metadata support, verify these items in Setup after deployment:
- Create Permission Set with "Salesforce API Integration" permission
- Assign Permission Set to the External Client App
- Confirm the run-as / execution-user behavior and any org-specific OAuth security settings
JWT Bearer Flow (Connected App)
- Upload Certificate to org before deployment
- Pre-authorize Users after deployment (Setup > Manage Connected Apps)
Deployment Checklist
External Client App
.eca-meta.xmlfile exists underexternalClientApps/.ecaOauth-meta.xmlfile exists underextlClntAppOauthSettings/.ecaGlblOauth-meta.xmluses abbreviated suffix underextlClntAppGlobalOauthSets/- Optional companion files use the right suffixes:
.ecaOauthSecurity,.ecaOauthPlcy,.ecaPlcy - All companion files reference the same
externalClientApplicationname - Required files include
labelwhere applicable commaSeparatedOauthScopesuses string format, not individual<scopes>tags- Callback URL is HTTPS (or custom scheme for mobile)
- PKCE enabled for public clients
Connected App
.connectedApp-meta.xmlfile existscontactEmailis valid- Callback URL matches OAuth flow requirements
- Certificate exists in org (for JWT Bearer)
- Canvas locations are supported by org (if using Canvas)
Tested Configurations
Minimal ECA (API Integration)
Files:
├── externalClientApps/MyApp.eca-meta.xml
├── extlClntAppOauthSettings/MyApp.ecaOauth-meta.xml
└── extlClntAppGlobalOauthSets/MyApp.ecaGlblOauth-meta.xml
Scopes: Api, RefreshToken
PKCE: false (confidential client)
Mobile App ECA (PKCE)
Files:
├── externalClientApps/MobileApp.eca-meta.xml
├── extlClntAppOauthSettings/MobileApp.ecaOauth-meta.xml
└── extlClntAppGlobalOauthSets/MobileApp.ecaGlblOauth-meta.xml
Scopes: Api, RefreshToken, OpenID
PKCE: true (public client)
Callback: com.example.app://oauth/callback
Server-to-Server ECA
Files:
├── externalClientApps/ServiceApp.eca-meta.xml
├── extlClntAppOauthSettings/ServiceApp.ecaOauth-meta.xml
└── extlClntAppOauthSecuritySettings/ServiceApp.ecaOauthSecurity-meta.xml # retrieve-first when source controlling OAuth security settings
Scopes: Api
Note: Client Credentials still requires post-deployment Permission Set assignment and admin verification
Last Updated: April 2026 Based on testing with SF CLI and API v66.0