We are experiencing an OAuth2 authentication issue with the standard Salesforce connector on n8n Cloud.
Environment
n8n Cloud
Salesforce Sandbox
Standard Salesforce OAuth2 credential
n8n Connected App installed
Users are allowed to self-authorize
IP restrictions are relaxed
Behavior
Authentication with an SSO-enabled Salesforce user works successfully.
Authentication with Salesforce users using username/password fails after successful login and authorization.
Salesforce Login History shows the authentication as successful and the authorization code is returned to n8n. The subsequent token exchange fails with:
invalid_grant
authentication failure
Expected behavior
Both SSO and username/password Salesforce users should be able to complete the OAuth2 flow.
Could you please check why the token exchange fails specifically for non-SSO users in a Salesforce Sandbox?
If you are authenticating via OAuth2 and experience connection or authorization failures, Salesforce user profiles often lack a required permission
In Salesforce, navigate to Setup > search for Profiles.
Select the user profile attempting to connect.
Under Administrative Permissions, enable Approve Connected Apps for Non-Admins (or Approve apps connected not installed).
Save your changes.
Also, OAuth2 requires creating an External Client App (or legacy Connected App) in Salesforce with the Full access (full) and Perform requests at any time (refresh_token, offline_access) scopes
For Connected App, ensure the OAuth policies in Salesforce permit the user’s profile under Manage Profiles or Manage Permission Sets
The affected user has Salesforce System Administrator permissions. The n8n Connected App is installed, All users may self-authorize is enabled and IP restrictions are relaxed.
The same n8n Salesforce OAuth integration works with an SSO-enabled user. With a username/password user, Salesforce authentication and authorization complete successfully and Salesforce Login History shows Remote Access Client: Success. An authorization code is returned to n8n.
The failure occurs only during the subsequent token exchange: invalid_grant / authentication failure
Therefore this does not appear to be a Connected App approval or profile permission issue. Could you please check the token exchange performed by n8n and whether there is any difference in handling Sandbox users authenticated via username/password versus SSO?
The failure is likely caused by an environment endpoint mismatch during the OAuth handshake. While the authorization phase succeeds because the credentials are valid, the subsequent token exchange fails with invalid_grant because n8n is likely attempting to exchange the authorization code at the wrong endpoint (e.g., using the Production URL login.salesforce.com for a code generated via the Sandbox URL test.salesforce.com).
This discrepancy manifests specifically for username/password users because they rely on the base URL provided by the client. In contrast, SSO users are routed through an external Identity Provider (IdP) that explicitly defines the authentication path, effectively masking any mismatch between the authorization and exchange endpoints.
To resolve this, verify that the “Environment” setting in the n8n Salesforce credentials exactly matches the target org (Production vs. Sandbox). If the issue persists despite correct settings, check for restrictive MyDomain policies in Salesforce that may prevent non-SSO users from completing the token exchange via the generic Salesforce login endpoints.
The credential is already configured for Sandbox, and we are using the Custom Domain workaround. We have verified that the authorization request is routed through the correct Salesforce Sandbox/MyDomain endpoint.
The flow is:
Sandbox/MyDomain authorization → successful
Username/password authentication → successful
Salesforce Login History → Remote Access Client: Success
Could you please verify on the n8n side which token endpoint is actually used for the code exchange?
Specifically, we would like to rule out that the authorization code generated via the Sandbox/MyDomain endpoint is subsequently exchanged against login.salesforce.com or another mismatching endpoint.
Since the token exchange is performed server-side by n8n Cloud, we cannot verify this from our side.
Hi @mevaco_chris Welcome!
To answer directly on the endpoint: the Salesforce OAuth2 credential derives the token URL from Environment Type and nothing else, so with Sandbox selected n8n posts the code to https://test.salesforce.com/services/oauth2/token every time. It is a hidden field with no override, which is why a code issued through the Use Custom Domain login on your MyDomain host cannot be redeemed and comes back as invalid_grant.
Switch the Salesforce node to OAuth2 JWT and set the My Domain URL field on the JWT credential to your sandbox domain: https://company--sandbox.sandbox.my.salesforce.com
That field overrides both the assertion audience and the token endpoint, so the exchange lands on the same host that issued the grant. It also removes the interactive login entirely, so SSO and username/password users end up on the same path. You’ll need an External Client App with JWT Bearer Flow enabled and a certificate uploaded.
Thanks for the clarification. This explains the invalid_grant error.
However, switching to JWT seems more like a workaround than a solution to the underlying issue.
If the Salesforce OAuth2 credential always uses test.salesforce.com as the token endpoint for Sandbox environments, while authentication via a Sandbox MyDomain is required/supported, the standard OAuth2 credential currently cannot complete this flow correctly.
Moving to JWT would require us to introduce and maintain a separate authentication setup including an External Client App, certificate and private key lifecycle solely to work around this limitation.
Is this considered a known limitation/bug of the Salesforce OAuth2 credential? If so, is there an existing issue or planned fix to allow the token endpoint to use the configured MyDomain as well?
Our preference would be to use the standard Salesforce OAuth2 credential as intended rather than changing the authentication architecture as a workaround.
invalid_grant sounds like a potential scope issue which is odd as we send full and refresh_token. Out of interest do you have PKCE enabled in your Salesforce app?