mirror of
https://github.com/forcedotcom/afv-library.git
synced 2026-07-30 03:09:50 +08:00
8.5 KiB
8.5 KiB
Migration Guide: Connected App → External Client App
Step-by-step guide for migrating from Connected Apps to External Client Apps (ECAs).
Why Migrate?
| Feature | Connected App | External Client App |
|---|---|---|
| Metadata Compliance | Partial | Full |
| Secret in Sandboxes | Visible | Hidden |
| Key Rotation | Manual | Automated via API |
| Multi-Org Distribution | Manual recreation | Native 2GP packaging |
| Audit Logging | Limited | MFA + full audit |
| Security Model | Open by default | Closed by default |
Migrate when:
- Distributing to multiple orgs (ISV, enterprise)
- Compliance requires audit trails
- DevOps needs automated credential rotation
- Moving to 2GP packaging
Migration Process
Phase 1: Assessment
1.1 Inventory Current Apps
# List all Connected Apps
sf org list metadata --metadata-type ConnectedApp --target-org <org>
# Retrieve for analysis
sf project retrieve start --metadata ConnectedApp --output-dir ./migration-review
1.2 Document Configuration
For each Connected App, record:
| Setting | Value |
|---|---|
| App Name | |
| Consumer Key | |
| OAuth Scopes | |
| Callback URLs | |
| IP Restrictions | |
| Token Policy | |
| Certificate (if JWT) |
1.3 Identify Integrations
List all systems using each Connected App:
- External applications
- CI/CD pipelines
- Third-party tools
- Mobile applications
Phase 2: Planning
2.1 Choose Distribution Model
| Scenario | Distribution State |
|---|---|
| Single org only | Local |
| Multiple orgs, same company | Local per org or Packageable |
| ISV/AppExchange | Packageable |
2.2 Plan Credential Rollover
Timeline Example:
Week 1: Create ECA, deploy to DevHub
Week 2: Update integration systems with new credentials
Week 3: Test in sandbox environments
Week 4: Production cutover
Week 5-8: Monitor, keep old app active as fallback
Week 9: Deactivate Connected App
Week 12: Delete Connected App
Phase 3: Create External Client App
3.1 Prepare Scratch Org (if needed)
// config/project-scratch-def.json
{
"orgName": "ECA Migration Dev",
"edition": "Developer",
"features": [
"ExternalClientApps",
"ExtlClntAppSecretExposeCtl"
]
}
3.2 Create ECA Metadata Files
File 1: Header (MyApp.eca-meta.xml)
<?xml version="1.0" encoding="UTF-8"?>
<ExternalClientApplication xmlns="http://soap.sforce.com/2006/04/metadata">
<contactEmail>team@company.com</contactEmail>
<description>Migrated from Connected App: MyConnectedApp</description>
<distributionState>Local</distributionState>
<isProtected>false</isProtected>
<label>MyApp</label>
</ExternalClientApplication>
File 2: Global OAuth (MyApp.ecaGlblOauth-meta.xml)
<?xml version="1.0" encoding="UTF-8"?>
<ExtlClntAppGlobalOauthSettings xmlns="http://soap.sforce.com/2006/04/metadata">
<callbackUrl>https://app.example.com/oauth/callback</callbackUrl>
<externalClientApplication>MyApp</externalClientApplication>
<isConsumerSecretOptional>false</isConsumerSecretOptional>
<isPkceRequired>true</isPkceRequired>
<label>MyApp Global OAuth</label>
<shouldRotateConsumerKey>true</shouldRotateConsumerKey>
<shouldRotateConsumerSecret>true</shouldRotateConsumerSecret>
</ExtlClntAppGlobalOauthSettings>
File 3: OAuth Settings (MyApp.ecaOauth-meta.xml)
<?xml version="1.0" encoding="UTF-8"?>
<ExtlClntAppOauthSettings xmlns="http://soap.sforce.com/2006/04/metadata">
<commaSeparatedOauthScopes>Api, RefreshToken</commaSeparatedOauthScopes>
<externalClientApplication>MyApp</externalClientApplication>
<label>MyApp OAuth Settings</label>
</ExtlClntAppOauthSettings>
Optional companion metadata (retrieve-first): MyApp.ecaOauthSecurity-meta.xml
- Use this when you need to source-control ECA OAuth security settings now supported by the CLI/SDR registry.
- Recommended workflow: retrieve from an org first, then commit the retrieved file under
extlClntAppOauthSecuritySettings/.
3.3 Deploy to DevHub/Org
sf project deploy start \
--metadata ExternalClientApplication:MyApp \
--metadata ExtlClntAppGlobalOauthSettings:MyApp \
--metadata ExtlClntAppOauthSettings:MyApp \
--target-org <target-org>
3.4 Retrieve New Consumer Key
After deployment:
- Go to Setup → External Client App Manager
- Select your ECA
- View Consumer Key (MFA required)
- Securely store credentials
Phase 4: Update Integrations
4.1 Update External Systems
For each integrated system:
- Update OAuth endpoint (if different environment)
- Replace Consumer Key
- Replace Consumer Secret
- Test authentication flow
- Verify API access
4.2 Configuration Mapping
| Connected App Setting | ECA Equivalent |
|---|---|
| Consumer Key | New Consumer Key (different) |
| Consumer Secret | New Consumer Secret (different) |
| Callback URL | Same (in ecaGlblOauth) |
| Scopes | Same (in ecaOauth) |
| IP Relaxation | In ecaPlcy (admin-managed) |
| Refresh Token Policy | In ecaPlcy (admin-managed) |
| OAuth security controls | In ecaOauthSecurity when source-controlled (retrieve-first) |
Phase 5: Testing
5.1 Test Checklist
- Authorization Code flow works
- Token refresh works
- API calls succeed
- Scopes are correct
- Error handling works
- Logout/revocation works
5.2 Test Script
# Test Authorization Code Flow
curl "https://login.salesforce.com/services/oauth2/authorize?\
response_type=code&\
client_id=<NEW_CONSUMER_KEY>&\
redirect_uri=<CALLBACK_URL>&\
scope=api%20refresh_token"
# Test Token Exchange
curl -X POST https://login.salesforce.com/services/oauth2/token \
-d "grant_type=authorization_code" \
-d "code=<AUTH_CODE>" \
-d "client_id=<NEW_CONSUMER_KEY>" \
-d "client_secret=<NEW_CONSUMER_SECRET>" \
-d "redirect_uri=<CALLBACK_URL>"
Phase 6: Cutover
6.1 Production Deployment
# Deploy ECA to production
sf project deploy start \
--metadata ExternalClientApplication:MyApp \
--metadata ExtlClntAppGlobalOauthSettings:MyApp \
--metadata ExtlClntAppOauthSettings:MyApp \
--target-org production
6.2 Cutover Steps
- Deploy ECA to production
- Configure policies in Setup
- Update production integrations
- Monitor for errors
- Keep Connected App active as fallback
Phase 7: Decommission
7.1 Deactivate Connected App
- Go to Setup → Connected Apps → Manage Connected Apps
- Select the old Connected App
- Click "Edit Policies"
- Set "Permitted Users" to "Admin approved users are pre-authorized"
- Remove all user/profile assignments
7.2 Monitor Period
- Monitor for 30 days minimum
- Check for authentication failures
- Investigate any traffic to old app
7.3 Delete Connected App
After monitoring period:
# Remove from source control
rm force-app/main/default/connectedApps/OldApp.connectedApp-meta.xml
# Or delete via Setup UI
Rollback Plan
If migration fails:
- Immediate: Revert external systems to old Consumer Key
- Short-term: Keep Connected App active during transition
- Long-term: Document issues and retry migration
Common Issues
| Issue | Cause | Solution |
|---|---|---|
| "Invalid consumer key" | Using old key | Update to new ECA key |
| "Callback URL mismatch" | URL not in ECA config | Add URL to ecaGlblOauth |
| "Scope not allowed" | Scope not in ecaOauth |
Add required scope |
| "User not authorized" | No policy assignment | Assign user via Permission Set |
| "MFA required" | ECA security | Complete MFA challenge |
Automation Script
#!/bin/bash
# migrate-connected-app.sh
APP_NAME=$1
TARGET_ORG=$2
echo "📦 Migrating Connected App: $APP_NAME"
# 1. Retrieve existing Connected App
sf project retrieve start \
--metadata "ConnectedApp:$APP_NAME" \
--target-org $TARGET_ORG \
--output-dir ./migration
# 2. Create ECA source directories
mkdir -p force-app/main/default/externalClientApps
mkdir -p force-app/main/default/extlClntAppGlobalOauthSets
mkdir -p force-app/main/default/extlClntAppOauthSettings
# Optional when you source-control security settings:
mkdir -p force-app/main/default/extlClntAppOauthSecuritySettings
# 3. Deploy ECA (assumes files are created)
sf project deploy start \
--source-dir force-app/main/default \
--target-org $TARGET_ORG
echo "✅ ECA deployed. Retrieve new Consumer Key from Setup."