A connected app controls how an external system signs in to Salesforce and what OAuth scopes it can request. In 2026, existing OAuth integrations can still run, but Salesforce restricts new connected app creation starting with Spring ’26 and recommends the external client app model for new integrations.
This article explains how the two models work, when to keep a legacy integration, how to configure OAuth access, and how to secure production API connections without storing secrets in Apex.
What is a connected app in Salesforce?
A connected app is a Salesforce authorization framework for integrating an external application through OAuth 2.0, SAML, or OpenID Connect. Admins have used it for middleware, mobile apps, Data Loader, reporting platforms, partner portals, and service accounts that call the Salesforce API.
For an existing implementation, the connected app usually holds the OAuth settings: callback URL, selected scopes, consumer key, secret or certificate, and policy controls. The token still runs in a Salesforce user context, so object permissions, field-level security, sharing, and API permissions remain part of the authorization decision.
Salesforce documents the model in Connected Apps and the Metadata API type in ConnectedApp Metadata API. Use those docs before changing a production integration.
How does a connected app handle OAuth access?
A connected app does not grant record access by itself. It defines which OAuth flow and scopes a client can request. Salesforce then issues a token for the approved user or configured integration user. A token with the api scope can call APIs, but it cannot bypass that user’s CRUD, FLS, sharing, or license restrictions.
| OAuth flow | Best fit | Production note |
|---|---|---|
| Web server flow | Web app signs in a user and receives an authorization code. | Callback URL must match the registered redirect URI. |
| JWT bearer flow | Trusted server uses a certificate and avoids interactive login. | Track certificate expiry and rotate before failure. |
| Client credentials flow | Backend job always runs as one configured integration user. | Use one integration user and one app per external system. |
| Refresh token flow | User-approved app needs continued access after login. | Review refresh token policy and revocation process. |
| Username-password flow | Legacy special scenario only. | Avoid it; Salesforce recommends alternate flows, and ECAs do not support it. |
Salesforce lists OAuth flow behavior in OAuth Authorization Flows. REST API authorization can use either the newer ECA framework or a connected app, as described in the REST API Developer Guide.
What is an external client app?
An external client app is the newer Salesforce framework for authorizing third-party or custom applications. It separates developer-managed app settings from admin-managed access policies, which is useful when an ISV, platform team, or integration team builds the app and a subscriber org admin controls who can use it.
External client app setup for new integrations
Use an external client app for new API authorization work unless you have a verified connected-app-only requirement. Salesforce states that external client apps are packageable frameworks, and ExternalClientApplication metadata is available in API version 59.0 and later. Some configurable policy metadata uses later API versions, so align your deployment tool with the target org release.
Connected apps Salesforce teams should review
Connected apps Salesforce teams already depend on should be inventoried, not blindly removed. Capture the owner, consumer key, callback URLs, scopes, token policy, permitted users, certificate or secret owner, and business process before planning a migration.
Salesforce connected apps in existing orgs
Salesforce connected apps created before Spring ’26 can continue to operate, but new creation is restricted. That means new integration design should start with External Client App Manager unless Salesforce documentation or Support confirms a different path for your org.
Salesforce connected app vs external client app decision point
The salesforce connected app vs external client app decision depends on ownership, packaging, OAuth flow support, and admin policy control. Use the older model only for existing dependencies or features that the newer model does not yet replace.
How do connected apps Salesforce features compare with ECA?
| Area | OAuth app | External client app |
|---|---|---|
| 2026 creation | Restricted as of Spring ’26; existing apps continue. | Recommended for new authorization work. |
| Packaging | Often tied to legacy packaging patterns. | Designed for packageable app distribution. |
| Metadata | ConnectedApp. |
ExternalClientApplication plus OAuth settings and policy metadata. |
| Admin policy | Policies and settings are closer together. | Settings and subscriber policies are separated. |
| Username-password flow | Supported only for special legacy scenarios. | Not supported. |
| Migration | Some local apps can be migrated. | Target framework for supported migration. |
Before choosing, review Salesforce’s official feature comparison and migration guidance.
How to configure an external client app for Salesforce API access
- Open Setup and search for App Manager or External Client App Manager.
- Create the app and enter a clear name, API name, contact email, and description.
- Choose Local for one-org use or the packaged option for a distributable app.
- Enable OAuth settings and enter the exact HTTPS callback URL for authorization-code flows.
- Select only required scopes. Many API integrations need
api; addrefresh_tokenoroffline_accessonly when long-lived access is justified. - Configure permitted users, IP restrictions, refresh token policy, and session settings.
- Assign access through permission sets instead of broad profiles.
- Store the consumer secret outside source control and rotate it after exposure, vendor change, or scheduled review.
Salesforce documents these steps in Configure the External Client App OAuth Settings. Trailhead also provides a project at Create and Configure an External Client App.
Client credentials token request example
curl --request POST 'https://YOUR_MY_DOMAIN.my.salesforce.com/services/oauth2/token' --header 'Content-Type: application/x-www-form-urlencoded' --data-urlencode 'grant_type=client_credentials' --data-urlencode 'client_id=YOUR_CONSUMER_KEY' --data-urlencode 'client_secret=YOUR_CONSUMER_SECRET'
Run this only from a trusted server that can protect the secret. Do not run client credentials flow from browser JavaScript, a mobile app, or a desktop client that exposes secrets.
Best practices for connected app and ECA security
- Use a dedicated integration user. One user per external system improves auditability.
- Use permission sets. Grant only the API, object, field, Apex, and app permissions required.
- Prefer admin-approved access. Avoid uncontrolled self-authorization for systems that touch customer data.
- Review scopes. Remove
fullwhen narrower scopes and Salesforce permissions meet the need. - Set token policies. Review refresh token validity, idle timeout, and revocation procedures.
- Restrict IP ranges when possible. Use known egress addresses for server integrations.
- Monitor usage. Review OAuth usage, login history, setup audit trail, and stale authorizations during access reviews.
For related SalesforceTutorial references, see Salesforce API integration basics, Salesforce REST API examples, Data Loader authentication and setup, and SOQL query examples for API integrations.
How to call Salesforce from Apex after OAuth is configured
Do not store tokens, consumer keys, or secrets in Apex. Configure the authorization layer first, then use a Named Credential or External Credential in the org that makes the callout. The example below uses a Named Credential called Order_Status_Salesforce; replace API v64.0 with the version your integration has tested.
public with sharing class SalesforceOrderStatusClient {
public class IntegrationException extends Exception {}
public static String findRecentAccounts() {
String soql = 'SELECT Id, Name FROM Account ORDER BY LastModifiedDate DESC LIMIT 5';
HttpRequest req = new HttpRequest();
req.setMethod('GET');
req.setHeader('Accept', 'application/json');
req.setEndpoint('callout:Order_Status_Salesforce/services/data/v64.0/query?q=' + EncodingUtil.urlEncode(soql, 'UTF-8'));
HttpResponse res = new Http().send(req);
if (res.getStatusCode() >= 200 && res.getStatusCode() < 300) {
return res.getBody();
}
throw new IntegrationException('Salesforce API call failed. HTTP ' + res.getStatusCode() + ' ' + res.getStatus());
}
}
@IsTest
private class SalesforceOrderStatusClientTest {
private class SuccessMock implements HttpCalloutMock {
public HttpResponse respond(HttpRequest req) {
System.assertEquals('GET', req.getMethod());
System.assert(req.getEndpoint().startsWith('callout:Order_Status_Salesforce/'));
HttpResponse res = new HttpResponse();
res.setStatusCode(200);
res.setHeader('Content-Type', 'application/json');
res.setBody('{"totalSize":0,"done":true,"records":[]}');
return res;
}
}
@IsTest
static void returnsApiResponseBody() {
Test.setMock(HttpCalloutMock.class, new SuccessMock());
Test.startTest();
String body = SalesforceOrderStatusClient.findRecentAccounts();
Test.stopTest();
System.assert(body.contains('"records"'));
}
}
The code is bulk-safe because it performs one callout in one method, but Apex still has a limit of 100 callouts per transaction. For larger sync jobs, use Queueable Apex, Batch Apex, MuleSoft, or another integration layer instead of making callouts inside record loops.
How to migrate a connected app to an external client app
- Inventory scopes, callback URLs, consumer key owner, token settings, permitted users, and dependent clients.
- Identify the OAuth flow. If the app uses username-password flow, redesign before migration because ECAs do not support it.
- Check for connected-app-only features and packaging requirements.
- Create or migrate in a sandbox first.
- Map access to permission sets and test with a low-privilege integration user.
- Validate success, expired token, revoked access, inactive user, and insufficient permission scenarios.
- Document rollback before changing production credentials.
Common errors with connected app setup
| Error | Likely cause | Fix |
|---|---|---|
| New Connected App is missing | Spring ’26 creation restriction. | Use an external client app for new work unless Salesforce Support confirms an exception. |
redirect_uri_mismatch |
Callback URL mismatch. | Match the redirect URI exactly, including scheme, host, and path. |
invalid_client |
Wrong key, secret, domain, or authentication method. | Verify credentials and rotate the secret if exposure is possible. |
| Valid login but API failure | User lacks API, object, field, or Apex access. | Fix the permission set assigned to the integration user. |
Frequently Asked Questions
Is a connected app deprecated in Salesforce?
Existing connected app integrations can continue to run, but creating new legacy apps is restricted as of Spring ’26. For new Salesforce API authorization work, start with an external client app unless you have a documented legacy requirement.
Should I use a connected app or external client app in 2026?
Use an external client app for new integrations, especially when packaging, subscriber policy control, or long-term governance matters. Keep an existing OAuth app only when it is working, documented, secured, and not ready for a tested migration.
Why is New Connected App missing in Salesforce Setup?
The action can be unavailable because Salesforce restricts creation starting with Spring ’26. Check Salesforce Help for the current release behavior and use External Client App Manager for new work where possible.
Do external client apps support username-password OAuth flow?
No. Salesforce migration guidance says ECAs do not support username-password flow. Redesign the integration with client credentials, JWT bearer, web server flow, or another supported OAuth pattern before migration.
Can I migrate a connected app to an external client app?
Salesforce provides migration guidance for supported local apps. Before migration, check OAuth flow support, callback URLs, policies, packaging needs, and any connected-app-only feature your client depends on.