ERP Authentication Comparison: OAuth Flows, Certificates & Service Accounts Across Major ERPs
Type: ERP Integration
System: Salesforce, SAP S/4HANA, Oracle Fusion, Dynamics 365, NetSuite, Workday
Confidence: 0.88
Sources: 8
Verified: 2026-03-02
Freshness: evolving
TL;DR
- Bottom line: Every major ERP now supports OAuth 2.0, but implementation details vary wildly -- Salesforce offers 5 OAuth flows, SAP requires communication arrangements per scenario, Oracle uses IDCS/IAM, Dynamics 365 uses Entra ID, NetSuite is deprecating TBA, and Workday couples OAuth with ISUs.
- Key limit: Token lifetimes range from 1 hour (Dynamics 365, Oracle) to 2 hours (Salesforce default); certificate and refresh token expiration policies are the #1 cause of unattended integration failures.
- Watch out for: NetSuite TBA (OAuth 1.0) deprecation -- as of 2027.1, no new TBA integrations can be created; existing ones must migrate to OAuth 2.0.
- Best for: Architects evaluating which OAuth flow to use for server-to-server ERP integrations, or teams migrating from basic auth / API keys to OAuth 2.0.
- Authentication: Use JWT Bearer (Salesforce), Client Credentials (SAP, Dynamics 365), Authorization Code with PKCE (Oracle, NetSuite), or OAuth + ISU (Workday) for server-to-server.
System Profile
This comparison covers the six most widely integrated cloud ERPs as of early 2026. Each system uses OAuth 2.0 as the primary API authentication mechanism, but the specific flows, token management, and certificate requirements differ substantially. On-premise deployments are excluded.
| System | Role | Primary Auth | Preferred S2S Flow |
| Salesforce | CRM + Platform | OAuth 2.0 (5 flows) | JWT Bearer |
| SAP S/4HANA Cloud | ERP Core | OAuth 2.0 + SAML | Client Credentials |
| Oracle Fusion Cloud ERP | ERP Core | OAuth 2.0 via IDCS/IAM | Authorization Code |
| Microsoft Dynamics 365 | ERP/CRM | OAuth 2.0 via Entra ID | Client Credentials + Certificate |
| NetSuite | ERP/Financials | OAuth 2.0 (migrating from TBA) | Authorization Code + PKCE |
| Workday | HCM/Finance | OAuth 2.0 + ISU | Refresh Token (non-expiring) |
API Surfaces & Capabilities
| System | OAuth Provider | Supported Auth Methods | Certificate Auth? | Mutual TLS? | API Key Auth? |
| Salesforce | Built-in OAuth | OAuth 2.0 (5 flows), SAML | Yes (JWT flow) | No | No |
| SAP S/4HANA Cloud | SAP BTP / Cloud Identity | OAuth 2.0, SAML 2.0, Client Cert, Basic (dev) | Yes (mTLS) | Yes | No |
| Oracle Fusion Cloud | IDCS / OCI IAM | OAuth 2.0, Basic (deprecated), SAML | Yes (JWT assertion) | No | No |
| Dynamics 365 | Microsoft Entra ID | OAuth 2.0, Certificate, Client Secret | Yes | No | No |
| NetSuite | Built-in OAuth | OAuth 2.0, TBA (deprecated) | Yes (M2M RSA) | No | No |
| Workday | Built-in OAuth | OAuth 2.0, ISU, SAML | Yes (JWT bearer) | No | No |
Rate Limits & Quotas
Authentication-Specific Limits
| System | Token Requests/Hour | Concurrent Sessions | Failed Auth Lockout | Notes |
| Salesforce | No explicit limit | 5 active tokens/user/app | 10 failed attempts | Session timeout configurable (default 2h) |
| SAP S/4HANA Cloud | Throttled per arrangement | Per-tenant limits | Configurable | Token endpoint: 100 req/s |
| Oracle Fusion Cloud | IDCS: 50 req/min/client | Limited by IDCS tier | 5 attempts = 30-min lock | Refresh token: 7-day default |
| Dynamics 365 | Entra ID: 10 req/s/tenant | No hard limit | Smart lockout | Access token: 60-90 min |
| NetSuite | No explicit limit | 10 concurrent/integration | 6 failed attempts | OAuth 2.0 tokens: 60 min |
| Workday | No explicit limit | Per-ISU | Configurable | Non-expiring refresh tokens |
Authentication
OAuth Flow Comparison by System
| System | S2S Flow | User-Context Flow | Token Lifetime | Refresh Token? | Certificate Required? |
| Salesforce | JWT Bearer | Auth Code (Web Server) | Session timeout (2h default) | No (JWT) / Yes (Auth Code) | Yes (X.509) |
| SAP S/4HANA Cloud | Client Credentials | SAML Bearer Assertion | 3600s (1h) | Yes (SAML flow) | Optional (mTLS) |
| Oracle Fusion Cloud | Auth Code (via IDCS) | Auth Code + PKCE | 3600s (1h) | Yes (7-day) | Optional (JWT assertion) |
| Dynamics 365 | Client Cred + Secret/Cert | Auth Code + PKCE | 60-90 min | Yes (until revoked) | Recommended over secrets |
| NetSuite | OAuth 2.0 M2M | Auth Code + PKCE | 3600s (1h) | Yes (7-day default) | Yes (RSA for M2M) |
| Workday | Refresh Token (non-expiring) | Auth Code | Until revoked | Yes (non-expiring) | Optional (JWT bearer) |
Authentication Gotchas
- Salesforce JWT Bearer flow requires a connected app with pre-uploaded X.509 certificate. Admin must pre-authorize the app. Self-signed certs for dev; CA-signed for production. [src1, src6]
- SAP S/4HANA Cloud requires a Communication Arrangement per API scenario. No wildcard OAuth scopes. [src2, src7]
- Oracle Fusion Cloud token endpoint URLs change during IDCS-to-OCI-IAM migration. Use discovery document instead of hardcoded URLs. [src8]
- Dynamics 365 client secrets expire after max 24 months. Certificate-based auth avoids this. Microsoft recommends certificates over secrets. [src3]
- NetSuite TBA (OAuth 1.0) cannot create new integrations after 2027.1. OAuth 2.0 M2M requires RSA certificate. [src4]
- Workday refresh tokens can be non-expiring, but ISU termination breaks all integrations using that ISU immediately. [src5]
Constraints
- Salesforce: JWT Bearer flow does not return a refresh token. Connected app must be pre-authorized by admin for the integration user profile.
- SAP S/4HANA Cloud: Communication Arrangements are mandatory. OAuth scopes are tied to specific communication scenarios, not the OAuth client.
- Oracle Fusion Cloud: Basic auth deprecated for new integrations as of 2025. Must create Confidential Application in IDCS before OAuth works.
- Dynamics 365: Application users require custom security role. Client secrets have 24-month maximum expiration.
- NetSuite: No new TBA integrations after 2027.1 for SOAP, REST, or RESTlets. M2M requires RSA certificate.
- Workday: Every integration requires domain security policy configuration for the ISU's security group. ISU termination breaks integrations instantly.
Integration Pattern Decision Tree
START -- Which ERP auth flow should I use?
|
+-- Server-to-Server (no user interaction)
| +-- Salesforce --> JWT Bearer (certificate-based)
| +-- SAP S/4HANA Cloud --> Client Credentials (via BTP)
| +-- Oracle Fusion Cloud --> Auth Code (IDCS service account)
| +-- Dynamics 365 --> Client Credentials + Certificate
| +-- NetSuite --> OAuth 2.0 Machine-to-Machine (RSA cert)
| +-- Workday --> Refresh Token with ISU (non-expiring)
|
+-- User-Context (delegated)
| +-- Salesforce --> Authorization Code (Web Server)
| +-- SAP S/4HANA Cloud --> SAML Bearer Assertion
| +-- Oracle Fusion Cloud --> Authorization Code + PKCE
| +-- Dynamics 365 --> Authorization Code + PKCE (Entra ID)
| +-- NetSuite --> Authorization Code + PKCE
| +-- Workday --> Authorization Code
|
+-- Machine-to-Machine (highest security)
| +-- Salesforce --> JWT Bearer (cert signs every request)
| +-- SAP S/4HANA Cloud --> mTLS (Client Certificate)
| +-- Oracle Fusion Cloud --> JWT Client Assertion
| +-- Dynamics 365 --> Certificate thumbprint auth
| +-- NetSuite --> OAuth 2.0 M2M with RSA cert
| +-- Workday --> JWT Bearer with registered cert
|
+-- Security requirement?
+-- Standard --> OAuth Client Credentials or JWT
+-- High --> Certificate-based or mTLS
+-- Compliance --> Certificate + token rotation + audit
Quick Reference
| Capability | Salesforce | SAP S/4HANA | Oracle Fusion | Dynamics 365 | NetSuite | Workday |
| OAuth 2.0 | Full (5 flows) | Full | Full (IDCS) | Full (Entra ID) | Full | Full |
| Best S2S | JWT Bearer | Client Cred | Auth Code (IDCS) | Client Cred + Cert | M2M (RSA) | Refresh Token |
| Certificate Auth | X.509 | mTLS + Client Cert | JWT Assertion | Cert thumbprint | RSA | JWT Bearer cert |
| Token Lifetime | 2h (configurable) | 1h | 1h | 60-90 min | 1h | Configurable |
| Refresh Token | Auth Code only | SAML flow | 7-day | Until revoked | 7-day | Non-expiring |
| Secret Expiry | No limit | No limit | Configurable | 24-month max | No limit | No limit |
| Identity Provider | Built-in | SAP IAS/BTP | IDCS/OCI IAM | Entra ID | Built-in | Built-in |
| Scope Model | Connected App | Comm. Arrangement | IDCS App scopes | Azure permissions | Integration record | Domain policies |
| mTLS Support | No | Yes | No | No | No | No |
| Legacy Auth | Username-Password | Basic (dev) | Basic (dep.) | N/A | TBA (dep. 2027.1) | ISU password |
| Setup Complexity | Moderate | High | High | Low-Moderate | Moderate | High |
Step-by-Step Integration Guide
1. Salesforce: JWT Bearer Authentication
Create a connected app, upload X.509 certificate, configure integration user. [src1, src6]
# Generate self-signed certificate (dev/testing only)
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
-keyout salesforce_private.pem -out salesforce_cert.pem \
-subj "/CN=SalesforceIntegration/O=YourOrg"
# Exchange JWT for access token
curl -X POST https://login.salesforce.com/services/oauth2/token \
-d "grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer" \
-d "assertion=YOUR_SIGNED_JWT"
Verify: Response contains access_token and instance_url.
2. SAP S/4HANA Cloud: Client Credentials
Set up Communication Arrangement with OAuth 2.0 in SAP BTP. [src2, src7]
curl -X POST "https://{tenant}.authentication.{region}.hana.ondemand.com/oauth/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials&client_id=CID&client_secret=CSEC"
Verify: 200 OK with JSON containing access_token and expires_in.
3. Dynamics 365: Certificate-Based S2S
Register app in Entra ID, upload certificate, create application user. [src3]
# Test with WhoAmI after obtaining token
curl -H "Authorization: Bearer ACCESS_TOKEN" \
-H "OData-Version: 4.0" \
"https://YOUR_ORG.crm.dynamics.com/api/data/v9.2/WhoAmI"
Verify: JSON response with UserId and OrganizationId.
4. NetSuite: OAuth 2.0 Machine-to-Machine
Create integration record, generate RSA certificate, configure M2M flow. [src4]
# Generate RSA key pair
openssl genrsa -out netsuite_private.pem 4096
openssl rsa -in netsuite_private.pem -pubout -out netsuite_public.pem
Verify: 200 OK with access_token and token_type: Bearer. Token valid 60 minutes.
Code Examples
Python: Multi-ERP Authentication Factory
# Input: ERP system name, credentials dict
# Output: Authenticated session with valid access token
import requests, jwt, time
from msal import ConfidentialClientApplication
class ERPAuthFactory:
@staticmethod
def salesforce_jwt(client_id, username, private_key_path,
login_url="https://login.salesforce.com"):
with open(private_key_path, 'r') as f:
private_key = f.read()
payload = {'iss': client_id, 'sub': username,
'aud': login_url, 'exp': int(time.time()) + 300}
assertion = jwt.encode(payload, private_key, algorithm='RS256')
resp = requests.post(f"{login_url}/services/oauth2/token",
data={'grant_type': 'urn:ietf:params:oauth:grant-type:jwt-bearer',
'assertion': assertion})
resp.raise_for_status()
return resp.json()
@staticmethod
def dynamics365_certificate(client_id, tenant_id, private_key_path,
thumbprint, resource_url):
with open(private_key_path, 'r') as f:
private_key = f.read()
app = ConfidentialClientApplication(client_id,
authority=f"https://login.microsoftonline.com/{tenant_id}",
client_credential={'private_key': private_key,
'thumbprint': thumbprint})
return app.acquire_token_for_client(
scopes=[f"{resource_url}/.default"])
cURL: Quick Token Test for Each ERP
# --- Salesforce (Username-Password -- testing only) ---
curl -X POST https://login.salesforce.com/services/oauth2/token \
-d "grant_type=password&client_id=CID&client_secret=CSEC&username=USER&password=PASS"
# --- SAP (Client Credentials) ---
curl -X POST "https://TENANT.authentication.eu10.hana.ondemand.com/oauth/token" \
-d "grant_type=client_credentials&client_id=CID&client_secret=CSEC"
# --- Dynamics 365 (Client Secret -- testing only) ---
curl -X POST "https://login.microsoftonline.com/TENANT/oauth2/v2.0/token" \
-d "grant_type=client_credentials&client_id=CID&client_secret=SEC&scope=https://org.crm.dynamics.com/.default"
# --- Workday (Refresh Token) ---
curl -X POST "https://DOMAIN.workday.com/ccx/oauth2/TENANT/token" \
-d "grant_type=refresh_token&client_id=CID&client_secret=CSEC&refresh_token=RT"
Data Mapping
Authentication Credential Mapping
| Concept | Salesforce | SAP S/4HANA | Oracle Fusion | Dynamics 365 | NetSuite | Workday |
| Client ID | Connected App Consumer Key | OAuth Client ID (BTP) | IDCS Client ID | Entra ID App ID | Integration Consumer Key | API Client ID |
| Client Secret | Connected App Consumer Secret | OAuth Client Secret (BTP) | IDCS Client Secret | Entra ID Client Secret | Integration Consumer Secret | API Client Secret |
| Service Account | Integration User + Connected App | Communication User | IDCS Confidential App | Application User (Entra ID) | Integration Record | ISU |
| Token Endpoint | /services/oauth2/token | /oauth/token (BTP UAA) | /oauth2/v1/token (IDCS) | /oauth2/v2.0/token | /services/rest/auth/oauth2/v1/token | /ccx/oauth2/{tenant}/token |
Data Type Gotchas
- Salesforce access tokens are org-scoped for JWT flow -- all API calls execute under the integration user's permissions. [src1]
- SAP BTP OAuth tokens are bound to a specific subaccount and communication arrangement. [src2]
- Dynamics 365 tokens include audience claim (
aud) that must match the environment URL exactly. [src3]
- NetSuite OAuth 2.0 tokens are bound to the integration record and account ID. [src4]
Error Handling & Failure Points
Common Error Codes
| Error | System(s) | Meaning | Resolution |
invalid_grant | All | Token request rejected | Regenerate JWT assertion or refresh token |
invalid_client | All | Client credentials rejected | Verify credentials; check secret expiration |
INVALID_SESSION_ID | Salesforce | Access token expired | Generate new JWT and request new token |
AADSTS700027 | Dynamics 365 | Certificate validation failed | Upload correct cert; check thumbprint |
AADSTS7000215 | Dynamics 365 | Invalid client secret | Generate new secret or use certificate |
USER_EXCEPTION | NetSuite | Auth failed | Check integration record; migrate to OAuth 2.0 |
401 - invalid_token | Workday | Token expired or ISU deactivated | Request new token; verify ISU is active |
Failure Points in Production
- Salesforce session timeout change: Admins can reduce from 2h to 15min. Fix:
Generate fresh JWT per batch; implement expiry checking. [src1]
- SAP Communication Arrangement deactivation: Maintenance/system copy invalidates all OAuth tokens. Fix:
Monitor arrangement status; set up alerts. [src2]
- Oracle IDCS-to-OCI-IAM migration: Token endpoint URLs change. Fix:
Use .well-known/openid-configuration discovery. [src8]
- Dynamics 365 silent secret expiry: No warning notification. Fix:
Use certificate auth; monitor via Graph API. [src3]
- NetSuite TBA deprecation: Cannot re-create if accidentally deleted post-2027.1. Fix:
Migrate to OAuth 2.0 now. [src4]
- Workday ISU termination: All integrations fail immediately. Fix:
Separate ISUs per integration; implement health monitoring. [src5]
Anti-Patterns
Wrong: Hardcoding client secrets in source code
# BAD -- secrets in source code get committed to Git
client_secret = "dJk8s!kLm9@pQrSt"
token = get_token(client_id, client_secret)
Correct: Use a secrets manager or environment variables
# GOOD -- secrets retrieved from vault at runtime
import os
client_secret = os.environ["D365_CLIENT_SECRET"] # Or use Key Vault
Wrong: Single service account across all integrations
# BAD -- one ISU for all Workday integrations; termination breaks everything
config = {"isu": "global_integration_user", "password": "shared"}
Correct: Dedicated service accounts per integration
# GOOD -- separate ISU per integration; failures are isolated
integrations = {
"payroll": {"isu": "ISU_PAYROLL", "client_id": "payroll_client"},
"hcm": {"isu": "ISU_HCM", "client_id": "hcm_client"}
}
Wrong: Caching tokens without expiry checks
# BAD -- cached token used without checking validity
cached_token = redis.get("sf_token")
response = requests.get(url, headers={"Authorization": f"Bearer {cached_token}"})
Correct: Token refresh with expiry awareness
# GOOD -- check expiry before using cached token
def get_valid_token(cache, auth_func, buffer_seconds=300):
cached = cache.get("token_data")
if cached and cached["expires_at"] > time.time() + buffer_seconds:
return cached["access_token"]
token_data = auth_func()
token_data["expires_at"] = time.time() + token_data.get("expires_in", 3600)
cache.set("token_data", token_data)
return token_data["access_token"]
Common Pitfalls
- Using Username-Password flow in production (Salesforce): Breaks when MFA is enforced. Fix:
Migrate to JWT Bearer for all S2S integrations. [src1]
- Not pinning OAuth scope per arrangement (SAP): Results in
invalid_scope errors. Fix: Document exact scope string per arrangement. [src2]
- Ignoring token endpoint URL changes (Oracle): IDCS-to-OCI-IAM migration changes URLs. Fix:
Use .well-known/openid-configuration discovery. [src8]
- Relying on client secrets for long-running integrations (D365): Expire after 24 months max. Fix:
Use certificate-based auth for production. [src3]
- Delaying TBA-to-OAuth migration (NetSuite): No new TBA after 2027.1. Fix:
Start OAuth 2.0 migration now. [src4]
- Sharing ISU credentials across environments (Workday): Security risk. Fix:
Create separate ISUs per environment. [src5]
Diagnostic Commands
# Salesforce: Verify token and connected app
curl -H "Authorization: Bearer TOKEN" \
https://INSTANCE.my.salesforce.com/services/oauth2/userinfo
# SAP S/4HANA Cloud: Test OAuth token
curl -H "Authorization: Bearer TOKEN" \
"https://HOST/sap/opu/odata/sap/API_BUSINESS_PARTNER/A_BusinessPartner?\$top=1"
# Oracle Fusion Cloud: Introspect token
curl -X POST "https://IDCS_HOST/oauth2/v1/introspect" \
-u "CLIENT_ID:CLIENT_SECRET" -d "token=TOKEN"
# Dynamics 365: Test with WhoAmI
curl -H "Authorization: Bearer TOKEN" -H "OData-Version: 4.0" \
"https://ORG.crm.dynamics.com/api/data/v9.2/WhoAmI"
# NetSuite: Verify OAuth 2.0 token
curl -H "Authorization: Bearer TOKEN" \
"https://ACCOUNT.suitetalk.api.netsuite.com/services/rest/record/v1/customer?limit=1"
# Workday: Test ISU + OAuth
curl -H "Authorization: Bearer TOKEN" \
"https://DOMAIN.workday.com/ccx/api/v1/TENANT/workers?limit=1"
Version History & Compatibility
| System | Auth Change | Date | Impact | Migration Notes |
| Salesforce | MFA enforcement | 2024-02 | Username-Password breaks | Migrate to JWT Bearer |
| SAP S/4HANA Cloud | Comm. Arrangement OAuth GA | 2023 | Standard auth method | No migration for new integrations |
| Oracle Fusion | Basic Auth deprecated | 2025 | New integrations must use OAuth | Existing basic auth still works |
| Oracle Fusion | IDCS to OCI IAM migration | 2025-2026 | Token endpoints change | Use discovery document |
| Dynamics 365 | ADAL sunset | 2022-06 | Must use MSAL | Update client libraries |
| NetSuite | TBA deprecation | 2027.1 | No new TBA integrations | Migrate to OAuth 2.0 M2M |
| Workday | OAuth 2.0 M2M support | 2024 | JWT Bearer flow available | Recommended for new |
When to Use / When Not to Use
| Use When | Don't Use When | Use Instead |
| Evaluating OAuth flows across multiple ERPs | Need auth details for one ERP only | System-specific API capability card |
| Planning multi-ERP integration platform | Need rate limit or data model details | ERP rate limits comparison card |
| Migrating from basic auth to OAuth 2.0 | Need SSO/SAML for end-user login | SSO/SAML comparison card |
| Designing certificate management strategy | Need specific code for one system | System-specific integration guide |
Cross-System Comparison
| Capability | Salesforce | SAP S/4HANA | Oracle Fusion | Dynamics 365 | NetSuite | Workday |
| OAuth 2.0 | Full (5 flows) | Full | Full (IDCS) | Full (Entra ID) | Full | Full |
| Best S2S | JWT Bearer | Client Cred | Auth Code (IDCS) | Client Cred + Cert | M2M (RSA) | Refresh Token |
| Certificate Auth | X.509 | mTLS | JWT Assertion | Cert thumbprint | RSA | JWT Bearer cert |
| Token Lifetime | 2h (configurable) | 1h | 1h | 60-90 min | 1h | Configurable |
| Refresh Token | Auth Code only | SAML flow | 7-day | Until revoked | 7-day | Non-expiring |
| mTLS | No | Yes | No | No | No | No |
| Legacy Auth | Username-Password | Basic (dev) | Basic (dep.) | N/A | TBA (dep. 2027.1) | ISU password |
| Setup Complexity | Moderate | High | High | Low-Moderate | Moderate | High |
| Documentation | Excellent | Good (complex) | Good (scattered) | Excellent | Good | Moderate |
Important Caveats
- Token lifetimes are configurable by admins -- the defaults listed here can be changed. Always check with the target org's admin.
- Sandbox vs production auth endpoints differ across all systems. Always parameterize endpoints.
- Certificate renewal is the #1 cause of unattended integration failures across all ERPs. Implement automated cert renewal monitoring.
- Regional/sovereign cloud differences may affect identity provider endpoints (SAP, Oracle, Microsoft sovereign clouds).
- This comparison covers cloud deployments only. On-premise (SAP ECC, Oracle EBS, Dynamics AX/NAV) use different auth mechanisms.
- Auth method availability varies by edition/tier. Verify with vendor licensing documentation.
Related Units