TL;DR
- Winter '27 enforces Assign Use Any API Auth Permission for SOAP login(). In Salesforce's words, users without the permission "can no longer authenticate with SOAP API
login()and will encounter an error." - The permission is
PermissionsUseAnyApiAuth, labelled Use Any API Auth, on bothPermissionSetandProfile. - It is not the same as "Use Any API Client", which governs API Access Control and connected app self-authorization. There is no
PermissionsUseAnyApiClientfield at all. Several published write-ups conflate the two, and granting the wrong one leaves you exposed while feeling finished. - Integration users are where this bites, because they are usually built from a cut down profile precisely to limit reach, and this is not a permission anyone had a reason to grant deliberately.
- The failure is at authentication, server to server, with nothing in the UI. Same shape as the other integration breakers in this release, and this one is not deferred to 2027.
What You'll Learn
- How to size your exposure from Login History, and the query gotcha that stops most attempts
- The three queries that tell you who already holds the permission
- Why granting through a permission set beats editing a profile here
- How this relates to the separate Spring '27 retirement of older SOAP API versions
The Problem
SOAP login() is the oldest way to authenticate to Salesforce and it is still everywhere. Middleware built a decade ago uses it. Backup tools use it. Scripts that someone wrote once and nobody has opened since use it. It takes a username, a password and a security token, and hands back a session id.
Winter '27 does not remove the call. It changes who is allowed to make it. From enforcement, the calling user needs the Use Any API Auth permission, and without it the call errors.
That inverts the usual risk profile. Normally the accounts most exposed to a permission change are the over-provisioned ones. Here it is the opposite. A well run org gives its integration users the narrowest profile that works, which is exactly the population least likely to have a permission nobody previously needed. Doing the right thing for years is what creates the exposure.
The failure gives you nothing to notice. The exchange is server to server, so there is no error in the UI, no notification, and no degraded behaviour to spot. A nightly load simply stops arriving.
Quick Answer
Winter '27 requires users authenticating through SOAP API login() to hold the Use Any API Auth permission, whose API name is PermissionsUseAnyApiAuth on both PermissionSet and Profile. Find who logs in that way with a grouped query against LoginHistory, remembering that Application, Status and ApiType are groupable but not filterable, so the SOAP filter has to happen after the query rather than in the WHERE clause. Then check who already holds the permission by querying PermissionSet, Profile and PermissionSetAssignment on that field. Grant it through a permission set assigned to the specific integration users that surfaced, rather than by editing a profile, so the grant is visible, revocable and attached to a stated reason. Do not confuse this with the Use Any API Client permission, which is a different control and does not satisfy this requirement.
Step 1: Size the exposure from Login History
Ninety days rather than thirty, because a quarterly job that last ran in May will not appear in a short window and will still break:
SELECT UserId, Application, Status, COUNT(Id) logins
FROM LoginHistory
WHERE LoginTime = LAST_N_DAYS:90
GROUP BY UserId, Application, Status
ORDER BY COUNT(Id) DESC
Run it with the CLI:
sf data query --query "SELECT UserId, Application, Status, COUNT(Id) logins FROM LoginHistory WHERE LoginTime = LAST_N_DAYS:90 GROUP BY UserId, Application, Status ORDER BY COUNT(Id) DESC" --target-org yourorg
The gotcha that stops most people. You cannot write AND Application = 'SOAP Api' in the WHERE clause. On LoginHistory, Application, Status and ApiType are groupable but not filterable, and adding them to a filter returns a parse error rather than a filtered list. The fields you can filter on are LoginTime, UserId, LoginType, SourceIp and LoginUrl. Group first, then pick out the SOAP rows from the result.
Keep the Status column. Failed logins from an integration user are worth seeing before you change anything, because they often mean a second, older copy of an integration is still pointed at the org and nobody remembers it.
Step 2: Find who already holds the permission
Three queries, because the permission can arrive by three routes:
SELECT Id, Label FROM PermissionSet WHERE PermissionsUseAnyApiAuth = true
SELECT Id, Name FROM Profile WHERE PermissionsUseAnyApiAuth = true
SELECT AssigneeId, Assignee.Username FROM PermissionSetAssignment
WHERE PermissionSet.PermissionsUseAnyApiAuth = true
The third one is the answer to "who is actually covered", because a permission set that grants it and is assigned to nobody protects nobody. Compare that list against the user ids from Step 1. The difference is your work.
Step 3: Grant it deliberately
In Setup, create a permission set, open System Permissions, and enable Use Any API Auth. Assign it to the integration users identified in Step 1 and nothing else.
Prefer this over editing the profile, for three reasons that apply well beyond this change. A permission set assignment is visible in one query, as Step 2 shows. It can be removed without touching everyone who shares the profile. And it can carry a name that states why it exists, so the next person to audit your org finds a reason rather than a mystery.
Name it for the change rather than the mechanism. A permission set called Integration_SOAP_Login explains itself in a year. One called API_Perms_New does not.
Frequently Asked Questions
Q: Is this the same as the Use Any API Client permission?
A: No, and this is the most common error in published coverage of this change. They are separate permissions. Describing PermissionSet in an org returns PermissionsUseAnyApiAuth, labelled Use Any API Auth, and there is no PermissionsUseAnyApiClient field at all. Use Any API Client is a different control, tied to API Access Control and to connected app self-authorization, and Salesforce changed its behaviour separately in December 2025. Granting it does not satisfy this requirement. If an article tells you otherwise, check the field name in your own org, which takes about a minute.
Q: Does this mean SOAP login() is being retired?
A: Not in Winter '27. This change controls who may call it. A separate release update, SOAP API login() Call in SOAP API Versions 31.0 Through 64.0 Is Being Retired, is scheduled for Spring '27 and begins removing the older API versions the call can be made against. They are easy to conflate because both touch SOAP login(), but they enforce at different times and need different work. This one is a permission grant. That one is a version upgrade in your client.
Q: We use OAuth, not SOAP. Are we affected?
A: Only where something still uses SOAP login() that you have forgotten about, which is the usual case in an org more than a few years old. Step 1 answers this in one query, and it is worth running even when you are confident, because backup tools and monitoring agents are the two categories that most often turn out to be authenticating the old way.
Q: Should we just grant the permission to every integration user?
A: Grant it to the ones that need it, which Step 1 tells you precisely. Blanket grants are how orgs end up unable to explain why an account can do something. The whole reason this change is awkward is that integration users were deliberately kept narrow, and the right response is to keep them narrow and make one documented exception.
Q: What happens on the enforcement date if we do nothing?
A: Affected integrations stop authenticating and error. Because it fails at login rather than mid-transaction, there is no partial data to unpick, which is the one merciful thing about it. There is also nothing in the UI to tell an admin, so whoever monitors the downstream system will notice before anyone in Salesforce does.
Key Takeaways
- The permission is
PermissionsUseAnyApiAuth, labelled Use Any API Auth, on bothPermissionSetandProfile. Verify it in your own org rather than trusting any article, this one included. - It is not Use Any API Client. No
PermissionsUseAnyApiClientfield exists. Granting the wrong permission leaves you exposed while feeling done. Application,StatusandApiTypeare groupable but not filterable onLoginHistory. Group, then filter the results.- Your best run integrations are the most exposed, because narrow profiles are exactly what this change punishes.
- Grant through a permission set, assigned to named users, so the exception is visible in one query and revocable without collateral.
What's Next?
Run the Step 1 query today, because it costs a minute and tells you whether any of this applies. If it returns SOAP rows, work through Steps 2 and 3 before your org's Winter '27 date.
Then look at the rest of the release. The other enforced security change is Enable Profile Filtering, and three changes widely reported as Winter '27 have moved out of it, covered in Salesforce Winter '27 Security Readiness. If SOAP login() is in use, the OAuth username-password flow usually is too, and that retirement is now 20 February 2027.
Resources & References
- Salesforce Release Updates for Winter '27, under Enforced with This Release, in Salesforce Help. Check Setup, then Release Updates, in your own org for the status that applies to you.
LoginHistoryobject reference, for the filterable and groupable flags on each field.PermissionSetandProfileobject references, forPermissionsUseAnyApiAuth.- Salesforce Winter '27 Security Readiness for the full release picture and the sandbox preview window.
- Salesforce Security Enforcement in 2026 for the wider enforcement calendar.
Responses
Checking your session.
Loading responses.