# 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 ```bash # List all Connected Apps sf org list metadata --metadata-type ConnectedApp --target-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 ```text 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) ```json // 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 team@company.com Migrated from Connected App: MyConnectedApp Local false ``` **File 2: Global OAuth** (`MyApp.ecaGlblOauth-meta.xml`) ```xml https://app.example.com/oauth/callback MyApp false true true true ``` **File 3: OAuth Settings** (`MyApp.ecaOauth-meta.xml`) ```xml Api, RefreshToken MyApp ``` **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 ```bash sf project deploy start \ --metadata ExternalClientApplication:MyApp \ --metadata ExtlClntAppGlobalOauthSettings:MyApp \ --metadata ExtlClntAppOauthSettings:MyApp \ --target-org ``` #### 3.4 Retrieve New Consumer Key After deployment: 1. Go to Setup → External Client App Manager 2. Select your ECA 3. View Consumer Key (MFA required) 4. Securely store credentials --- ### Phase 4: Update Integrations #### 4.1 Update External Systems For each integrated system: 1. Update OAuth endpoint (if different environment) 2. Replace Consumer Key 3. Replace Consumer Secret 4. Test authentication flow 5. 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 ```bash # Test Authorization Code Flow curl "https://login.salesforce.com/services/oauth2/authorize?\ response_type=code&\ client_id=&\ redirect_uri=&\ scope=api%20refresh_token" # Test Token Exchange curl -X POST https://login.salesforce.com/services/oauth2/token \ -d "grant_type=authorization_code" \ -d "code=" \ -d "client_id=" \ -d "client_secret=" \ -d "redirect_uri=" ``` --- ### Phase 6: Cutover #### 6.1 Production Deployment ```bash # Deploy ECA to production sf project deploy start \ --metadata ExternalClientApplication:MyApp \ --metadata ExtlClntAppGlobalOauthSettings:MyApp \ --metadata ExtlClntAppOauthSettings:MyApp \ --target-org production ``` #### 6.2 Cutover Steps 1. Deploy ECA to production 2. Configure policies in Setup 3. Update production integrations 4. Monitor for errors 5. Keep Connected App active as fallback --- ### Phase 7: Decommission #### 7.1 Deactivate Connected App 1. Go to Setup → Connected Apps → Manage Connected Apps 2. Select the old Connected App 3. Click "Edit Policies" 4. Set "Permitted Users" to "Admin approved users are pre-authorized" 5. 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: ```bash # Remove from source control rm force-app/main/default/connectedApps/OldApp.connectedApp-meta.xml # Or delete via Setup UI ``` --- ## Rollback Plan If migration fails: 1. **Immediate**: Revert external systems to old Consumer Key 2. **Short-term**: Keep Connected App active during transition 3. **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 ```bash #!/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." ```