All insights

Platform

SOAP login() needs a new permission in Winter '27: find your exposure first

A Winter '27 release update gates the SOAP API login() call behind the Use Any API Auth permission, and the integration users that have signed in for years are the ones that lack it. Here is how to find every caller before December 1, and the stopgap that is not the fix.

The integration that has signed in the same way since 2015 is about to start failing. A username, a password, and a security token against the SOAP login() endpoint. It fails server to server, with nothing in the UI to notice, and the message is INSUFFICIENT_ACCESS: SOAP API login() requires the Use Any API Auth user permission.

Winter ‘27 ships a release update that gates the SOAP API login() call behind the Use Any API Auth user permission. Enforcement is dated December 1, 2026, across all orgs. The users most likely to lack the permission are exactly the ones you built to be minimal: integration users on stripped-down profiles, where nobody ever granted a permission whose name did nothing until now.

So the first job is not to fix anything. It is to find out whether you are exposed at all, and which users break if you do nothing. Grant the permission to buy time, then migrate those callers off login() before the deadline that comes after this one. I will take the audit first, then the stopgap, then the real fix and why it has a clock on it too.

What the gate is, and what it is not

Two permissions have confusingly similar names, and granting the wrong one fixes nothing.

Use Any API Auth (PermissionsUseAnyApiAuth) is the one that now gates SOAP login(). Before Winter ‘27 this permission existed but did nothing for the login() call. The release update turns it into a requirement: no permission, no SOAP login, for every user that authenticates that way.

Use Any API Client is a different permission entirely, tied to API Access Control and connected-app allow-listing. Teams that have turned on API Access Control know this one. It does not satisfy the new requirement, and if you grant it expecting the SOAP errors to stop, they will not.

What is not affected matters just as much. OAuth 2.0 connections are untouched: authorization code, client credentials, and JWT bearer flows do not call SOAP login() and do not need this permission. If an integration already authenticates through a connected app with OAuth, it sails through December 1 without a change. The blast radius is specifically the legacy username-password-token callers against the SOAP endpoint.

Find every caller before you touch a permission

You cannot grant a permission to the right users if you do not know who they are. Login History is where the evidence lives. It records the API type of every login, so a SOAP call shows up distinctly from a REST or UI session, and it retains up to six months (or 20,000 records, whichever comes first), which is enough to catch a monthly batch integration.

Run this in the Developer Console or your query tool to list the SOAP callers of the last 90 days:

SELECT UserId, Application, ApiType, COUNT(Id) logins
FROM LoginHistory
WHERE LoginTime = LAST_N_DAYS:90
  AND ApiType LIKE 'SOAP%'
GROUP BY UserId, Application, ApiType
ORDER BY COUNT(Id) DESC

Every row is an integration that authenticates with SOAP login(). The Application column usually names the tool (a middleware product, a backup vendor, a homegrown connector), which is how you work out who owns the thing you are about to change.

Now list the users who already hold Use Any API Auth, through a profile or a permission set:

SELECT Assignee.Name, PermissionSet.Label, PermissionSet.IsOwnedByProfile
FROM PermissionSetAssignment
WHERE PermissionSetId IN (
    SELECT Id FROM PermissionSet WHERE PermissionsUseAnyApiAuth = true
)

Profiles are backed by permission sets under the hood, so IsOwnedByProfile = true rows are profile grants and the rest are permission-set grants. The query catches both. Any user in the first list who is not in the second breaks at enforcement. That difference is your work list.

For orgs with heavier traffic than Login History’s retention can hold, the Login event in Event Monitoring carries the same API-type detail with longer retention, and the Event Log File Browser will show you the tools actively hitting the login() resource. Either way, do not rely on memory for this. The integration nobody remembers is the one that pages you at 2 a.m. on December 1.

The stopgap: grant the permission, and know it is a stopgap

Once you have the work list, the immediate fix is to assign Use Any API Auth to each affected user. Do it through a dedicated permission set rather than editing profiles, so the grant is visible, revocable, and not buried in a profile nobody audits.

<?xml version="1.0" encoding="UTF-8"?>
<PermissionSet xmlns="http://soap.sforce.com/2006/04/metadata">
    <label>Integration SOAP Login</label>
    <hasActivationRequired>false</hasActivationRequired>
    <userPermissions>
        <enabled>true</enabled>
        <name>UseAnyApiAuth</name>
    </userPermissions>
</PermissionSet>

Assign it to the integration users your audit surfaced, and the SOAP logins keep working past December 1. Test the change before you trust it: the release update in Setup has a test-run option that enforces the new behaviour in a sandbox now, so you can confirm that granting the permission clears the error and that you have not missed a caller.

I want to be blunt about what this buys you, though. Granting the permission keeps a username-password-token integration alive. It does not make that integration modern, and it does not clear the next deadline.

The real fix, and the clock behind it

Use Any API Auth is a gate in front of a door that Salesforce is also closing. SOAP login() itself is scheduled for retirement for API versions 31.0 through 64.0 in Summer ‘27, after which those calls fail with the endpoint deactivated regardless of any permission. The permission grant gets you through Winter ‘27. It does nothing for Summer ‘27.

So treat the audit you just did as the input to a migration, not the end of a chore. The destination is OAuth through a connected app or an external client app, and the flow depends on the caller:

  • Server-to-server integrations with no human move to the client credentials flow, where the integration authenticates as a dedicated run-as user with a client ID and secret and never sends a password.
  • Callers that need to act as a specific user, or that run in environments where a stored secret is a risk, use the JWT bearer flow with a certificate instead of a shared secret.

Both are covered in depth in the username-password retirement migration, and both benefit from the same discipline you would apply to any connected-app and token governance: one app per integration, least-privilege scopes, and rotation you perform. The migration is more work than granting a permission. It is also the last time you have to think about this particular class of breakage, because an OAuth integration is not sitting in the path of either deadline.

What I would do this week

Run the two queries. If the first one returns nothing, you have no SOAP login() exposure and December 1 is a non-event for you, which is worth confirming rather than assuming. If it returns rows, cross them against the second query, stand up the permission set, and assign it to the users who need it so nothing breaks on the enforcement date.

Then, in the same breath, open a migration ticket for each of those integrations with a Summer ‘27 due date, because the permission you just granted is holding a door that closes anyway. The teams that treat December 1 as the whole task will be back here in six months. The ones that treat it as the prompt to retire SOAP login() for good will not.

Understanding the basics

What is the Use Any API Auth permission and why does SOAP login need it now?

Use Any API Auth (PermissionsUseAnyApiAuth) is a user permission that, as of a Winter ‘27 release update, is required to authenticate through the SOAP API login() call. The enforcement date is December 1, 2026, across all orgs. Users without it get INSUFFICIENT_ACCESS: SOAP API login() requires the Use Any API Auth user permission at login. The permission existed before Winter ‘27 but had no effect on login(), so most integration users were never granted it.

Is Use Any API Auth the same as Use Any API Client?

No, and granting one does not satisfy the other. Use Any API Client is tied to API Access Control and connected-app allow-listing. Use Any API Auth is the permission that specifically gates SOAP login(). If your SOAP integrations are failing after the release update, Use Any API Auth is the one they need.

Will OAuth integrations break on December 1, 2026?

No. The requirement applies only to callers that authenticate through the SOAP API login() operation. OAuth 2.0 flows, including authorization code, client credentials, and JWT bearer, do not call login() and are unaffected. The exposure is limited to legacy username-password-and-token integrations that sign in against the SOAP endpoint.

How do I find which integrations use SOAP login()?

Query the LoginHistory object filtered to ApiType LIKE 'SOAP%' over the last 90 days, grouped by user and application. Each row is an integration authenticating with SOAP login(), and the Application field usually names the tool. For traffic beyond Login History’s six-month, 20,000-record retention, use the Login event in Event Monitoring, which keeps the same detail for longer.


Auditing the integration users in an org that has accumulated a decade of connectors, or planning the move from SOAP login() to OAuth before Summer ‘27 without breaking a nightly job, is the kind of work our integration and Data Cloud team does under a change freeze. If Winter ‘27 lands this month and you want the exposure mapped before December 1, talk to us.

Keep reading

All insights