OAuth 2 with Salesforce failed

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?

Hey @mevaco_chris, while you wait for a response, here are some things that might help:

Suggested resources

Automatically matched to your question.

Docs:

Forum:

@barn4k, @anon61017519 - you’ve helped with similar issues before, can you take a look?

Automatically suggested by n8n’s community bot. It’s a pilot - please share feedback here.

Hi @mevaco_chris

If you are authenticating via OAuth2 and experience connection or authorization failures, Salesforce user profiles often lack a required permission

  1. In Salesforce, navigate to Setup > search for Profiles.
  2. Select the user profile attempting to connect.
  3. Under Administrative Permissions, enable Approve Connected Apps for Non-Admins (or Approve apps connected not installed).
  4. 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

Thanks. We already verified these points.

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.

Thanks, this matches our observations.

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
  • Authorization code returned to n8n → successful
  • Token exchange → invalid_grant / authentication failure

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.

Please reach out to help@n8n.io

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.

Perhaps you can raise this as a feature request?

I’ll do so, thanks!

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?

Hi Jon, yes PKCE is enabled in our org.