---
# === IDENTITY ===
id: business/erp-integration/erp-authentication-comparison/2026
canonical_question: "How do authentication methods compare across major ERPs - OAuth flows, certificates, service accounts?"
aliases:
  - "ERP API authentication comparison OAuth vs certificates"
  - "Which OAuth flow should I use for Salesforce vs SAP vs Oracle vs Dynamics 365?"
  - "Service account authentication for ERP integrations"
  - "Certificate-based vs token-based ERP authentication"
entity_type: erp_integration
domain: business > erp-integration > erp-authentication-comparison
region: global
jurisdiction: global
temporal_scope: 2024-2026

# === SYSTEM PROFILE ===
systems:
  - name: "Salesforce"
    vendor: "Salesforce"
    version: "API v62.0"
    edition: "Enterprise, Unlimited, Developer"
    deployment: "cloud"
    api_surface: "REST, SOAP, Bulk, Streaming"
  - name: "SAP S/4HANA Cloud"
    vendor: "SAP"
    version: "2408"
    edition: "Public Cloud, Private Cloud"
    deployment: "cloud"
    api_surface: "OData v4, SOAP, REST"
  - name: "Oracle Fusion Cloud ERP"
    vendor: "Oracle"
    version: "24B"
    edition: "Enterprise"
    deployment: "cloud"
    api_surface: "REST, SOAP, FBDI"
  - name: "Microsoft Dynamics 365"
    vendor: "Microsoft"
    version: "Dataverse Web API v9.2"
    edition: "Enterprise, Business Central"
    deployment: "cloud"
    api_surface: "OData v4, REST"
  - name: "NetSuite"
    vendor: "Oracle"
    version: "2024.2+"
    edition: "SuiteCloud"
    deployment: "cloud"
    api_surface: "REST, SuiteTalk SOAP"
  - name: "Workday"
    vendor: "Workday"
    version: "v43.0+"
    edition: "Enterprise"
    deployment: "cloud"
    api_surface: "REST, SOAP (WWS)"

# === VERIFICATION ===
last_verified: 2026-03-02
confidence: 0.88
version: 1.0
first_published: 2026-03-02

# === TEMPORAL VALIDITY ===
temporal_validity:
  status: volatile
  last_breaking_change: "NetSuite deprecating TBA (OAuth 1.0) for new integrations as of 2027.1"
  next_review: 2026-08-29
  change_sensitivity: high

# === CONSTRAINTS ===
constraints:
  - "Salesforce JWT Bearer flow requires a connected app with uploaded X.509 digital certificate; self-signed works for dev, CA-signed required for prod"
  - "SAP S/4HANA Cloud OAuth requires a communication arrangement configured per API scenario; no wildcard OAuth scopes"
  - "Oracle Fusion Cloud ERP deprecated basic auth for new integrations; OAuth 2.0 authorization code flow is now required"
  - "Microsoft Dynamics 365 client secrets expire after max 24 months; certificate-based auth has no expiration if cert is renewed"
  - "NetSuite TBA (OAuth 1.0) cannot create new integrations after 2027.1; must migrate to OAuth 2.0"
  - "Workday OAuth requires an Integration System User (ISU) with domain security policies; personal accounts must never be used for integrations"

# === SKIP CONDITIONS ===
skip_this_unit_if:
  - condition: "User needs authentication details for a single ERP system only"
    use_instead: "business/erp-integration/{system}-api-capabilities/2026"
  - condition: "User needs rate limit details rather than authentication"
    use_instead: "business/erp-integration/erp-rate-limits-comparison/2026"
  - condition: "User needs SSO/SAML for end-user login, not API integration"
    use_instead: "business/erp-integration/erp-sso-saml-comparison/2026"

# === AGENT HINTS ===
inputs_needed:
  - key: integration_pattern
    question: "What integration pattern do you need?"
    type: choice
    options:
      - "server-to-server (no user context, automated/scheduled)"
      - "user-context (delegated, acting on behalf of a user)"
      - "machine-to-machine (certificate-based, no secrets)"
      - "legacy migration (basic auth or API keys to OAuth)"
  - key: erp_system
    question: "Which ERP system are you integrating with?"
    type: choice
    options:
      - "Salesforce"
      - "SAP S/4HANA Cloud"
      - "Oracle Fusion Cloud ERP"
      - "Microsoft Dynamics 365"
      - "NetSuite"
      - "Workday"
  - key: security_requirement
    question: "What's your security posture?"
    type: choice
    options:
      - "standard (OAuth client credentials or JWT)"
      - "high security (mutual TLS, certificate-based)"
      - "compliance-driven (SOC 2, HIPAA, FedRAMP)"

# === DISTRIBUTION ===
canonical_source: "https://knowledgelib.io/business/erp-integration/erp-authentication-comparison/2026"
suggested_citation: "Source: knowledgelib.io — AI Knowledge Library (verified 2026-03-02)"

# === RELATED UNITS ===
related_kos:
  depends_on: []
  related_to:
    - id: "business/erp-integration/erp-rate-limits-comparison/2026"
      label: "ERP Rate Limits Comparison"
  solves: []
  alternative_to: []
  often_confused_with: []

# === SOURCES ===
sources:
  - id: src1
    title: "OAuth 2.0 JWT Bearer Flow for Server-to-Server Integration"
    author: Salesforce
    url: https://help.salesforce.com/s/articleView?id=sf.remoteaccess_oauth_jwt_flow.htm
    type: official_docs
    published: 2025-10-15
    reliability: authoritative
  - id: src2
    title: "Complete Guide to SAP S/4HANA APIs: REST and SOAP Integration"
    author: Apideck
    url: https://www.apideck.com/blog/guide-to-sap-4-hana-rest-and-soap-api
    type: technical_blog
    published: 2025-06-10
    reliability: high
  - id: src3
    title: "Use OAuth authentication with Microsoft Dataverse"
    author: Microsoft
    url: https://learn.microsoft.com/en-us/power-apps/developer/data-platform/authenticate-oauth
    type: official_docs
    published: 2026-01-07
    reliability: authoritative
  - id: src4
    title: "OAuth 2.0 vs OAuth 1.0 for NetSuite in 2025"
    author: LST Consultancy
    url: https://lstconsultancy.com/oauth2-vs-oauth1-for-netsuite/
    type: technical_blog
    published: 2025-04-22
    reliability: moderate_high
  - id: src5
    title: "Complete Guide to Workday REST API Integration and Security"
    author: Reco
    url: https://www.reco.ai/hub/workday-rest-api-integration-security
    type: technical_blog
    published: 2025-08-15
    reliability: moderate_high
  - id: src6
    title: "What OAuth 2.0 Authorization Flows Are Available in Salesforce?"
    author: SFDC Developers
    url: https://sfdcdevelopers.com/2025/09/24/what-different-oauth2-0-authorization-flows-are-available-in-salesforce/
    type: community_resource
    published: 2025-09-24
    reliability: moderate_high
  - id: src7
    title: "OAuth 2.0 in SAP BTP: Secure API Access"
    author: SAP
    url: https://help.sap.com/docs/cloud-integration/sap-cloud-integration/oauth-2-0
    type: official_docs
    published: 2025-05-20
    reliability: authoritative
  - id: src8
    title: "Configuring OAuth for Oracle ERP Cloud in OIC"
    author: Apps Associates
    url: https://appsassociates.com/blog/configuring-oauth-for-oracle-erp-cloud-in-oic/
    type: technical_blog
    published: 2025-07-12
    reliability: moderate_high
---

# ERP Authentication Comparison: OAuth Flows, Certificates & Service Accounts Across Major ERPs

## 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 as the OAuth provider, Dynamics 365 uses Entra ID (Azure AD), NetSuite is deprecating TBA (OAuth 1.0), and Workday couples OAuth with Integration System Users (ISUs).
- **Key limit**: Token lifetimes range from 1 hour (Dynamics 365, Oracle) to session-timeout-dependent (Salesforce, default 2h); 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 card 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 (SAP ECC, Dynamics AX) are excluded -- they typically use different mechanisms (SAP Logon Tickets, Windows Integrated Auth).

| 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 with 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 only) | 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 (OAuth 1.0, deprecated) | Yes (M2M RSA) | No | No |
| Workday | Built-in OAuth | OAuth 2.0, ISU (username/password), SAML | Yes (JWT bearer) | No | No |

## Rate Limits & Quotas

### Authentication-Specific Limits

| System | Token Requests/Hour | Concurrent Auth Sessions | Failed Auth Lockout | Notes |
|---|---|---|---|---|
| Salesforce | No explicit limit on token requests | 5 active tokens per user per connected app | Lockout after 10 failed attempts | Session timeout configurable by admin (default 2h) |
| SAP S/4HANA Cloud | Throttled per communication arrangement | Per-tenant concurrency limits | Configurable lockout policy | Token endpoint rate-limited to 100 req/s |
| Oracle Fusion Cloud | IDCS: 50 token req/min per client | Limited by IDCS tier | 5 failed attempts = 30-min lockout | Refresh token valid for 7 days by default |
| Dynamics 365 | Entra ID: 10 req/s per tenant | No hard limit; throttled by Entra ID | Smart lockout (configurable) | Access token default lifetime 60-90 min |
| NetSuite | No explicit token limit | 10 concurrent sessions per integration | Lockout after 6 failed attempts | OAuth 2.0 tokens valid for 60 min |
| Workday | No explicit limit | Per-ISU concurrency | Configurable lockout | Non-expiring refresh tokens supported |

[src1, src2, src3, src4, src5]

## Authentication

### OAuth Flow Comparison by System

| System | S2S Flow | User-Context Flow | Token Lifetime | Refresh Token? | Certificate Required? |
|---|---|---|---|---|---|
| Salesforce | JWT Bearer | Authorization Code (Web Server) | Session timeout (default 2h) | No (JWT) / Yes (Auth Code) | Yes (JWT requires X.509 cert) |
| SAP S/4HANA Cloud | Client Credentials | SAML Bearer Assertion | 3600s (1h default) | Yes (SAML flow) | Optional (mTLS available) |
| Oracle Fusion Cloud | Auth Code (via IDCS) | Auth Code + PKCE | 3600s (1h) | Yes (7-day lifetime) | Optional (JWT assertion) |
| Dynamics 365 | Client Credentials + Secret/Cert | Auth Code + PKCE | 60-90 min | Yes (until revoked) | Recommended over secrets |
| NetSuite | OAuth 2.0 Machine-to-Machine | Auth Code + PKCE | 3600s (1h) | Yes (7-day default) | Yes (RSA cert for M2M) |
| Workday | Refresh Token (non-expiring) | Auth Code | Until revoked | Yes (non-expiring option) | Optional (JWT bearer) |

[src1, src2, src3, src4, src5, src6]

### Authentication Gotchas

- **Salesforce JWT Bearer flow** requires a connected app with a pre-uploaded X.509 digital certificate. The admin must also pre-authorize the connected app for the integration user -- there is no interactive consent prompt. Self-signed certs work for dev; CA-signed required for production. [src1, src6]
- **SAP S/4HANA Cloud** requires a Communication Arrangement per API scenario. You cannot get a single OAuth token that grants access to all APIs -- each arrangement defines its own scope and auth configuration. [src2, src7]
- **Oracle Fusion Cloud** access tokens are tied to the IDCS/IAM client. If you plan to use OAuth with Oracle Integration Cloud (OIC), the setup flow differs from direct Fusion REST API access -- OIC uses its own IDCS app registration. [src8]
- **Dynamics 365** client secrets expire after a maximum of 24 months and cannot be extended. Certificate-based auth avoids this problem entirely -- the certificate can be renewed without changing the app registration. Microsoft recommends certificates over secrets. [src3]
- **NetSuite** OAuth 2.0 machine-to-machine authentication requires an RSA certificate uploaded to the integration record. The SuiteCloud SDK version 24.2+ only supports OAuth 2.0; TBA is being deprecated. [src4]
- **Workday** refresh tokens can be made non-expiring, but the ISU account must remain active. If the ISU is terminated (even accidentally during org restructuring), all integrations using that ISU break immediately. [src5]

## Constraints
<!-- Agents: read this section before recommending any integration approach from this unit.
     These are hard constraints that must not be violated. -->

- **Salesforce**: JWT Bearer flow does not return a refresh token; you must generate a new JWT for each session. The connected app must be pre-authorized by an admin for the specific integration user profile.
- **SAP S/4HANA Cloud**: Communication Arrangements are mandatory -- you cannot skip this step. OAuth scopes are tied to specific communication scenarios, not to the OAuth client itself.
- **Oracle Fusion Cloud**: Basic authentication is deprecated for new integrations as of 2025. If IDCS is the identity provider, you must create a Confidential Application in IDCS before OAuth works. Recent (July 2025) update: Fusion SaaS Identity Domain no longer requires certificate upload for trust.
- **Dynamics 365**: Application users (service principals) require a custom security role. The built-in "System Administrator" role cannot be assigned to application users. Client secrets have a hard 24-month maximum expiration.
- **NetSuite**: As of 2027.1, no new TBA integrations can be created for SOAP, REST, or RESTlets. OAuth 2.0 with PKCE is the only path forward. Machine-to-machine flow requires RSA certificate configuration.
- **Workday**: Every integration requires configuring domain security policies for the ISU's security group. Personal user accounts must never be used for integrations -- ISU termination or security group changes will break integrations instantly.

## Integration Pattern Decision Tree

```
START -- Which ERP auth flow should I use?
|
+-- What's the integration pattern?
|   |
|   +-- Server-to-Server (no user interaction, automated/scheduled)
|   |   +-- Salesforce --> JWT Bearer flow (certificate-based, no refresh token)
|   |   +-- SAP S/4HANA Cloud --> Client Credentials (via BTP or Communication Arrangement)
|   |   +-- Oracle Fusion Cloud --> Auth Code via IDCS (service account) or JWT Assertion
|   |   +-- Dynamics 365 --> Client Credentials with Certificate (recommended) or Secret
|   |   +-- NetSuite --> OAuth 2.0 Machine-to-Machine (RSA certificate)
|   |   +-- Workday --> OAuth 2.0 Refresh Token with ISU (non-expiring refresh)
|   |
|   +-- User-Context (delegated, acting on behalf of a user)
|   |   +-- Salesforce --> Authorization Code (Web Server) flow
|   |   +-- SAP S/4HANA Cloud --> SAML Bearer Assertion (principal propagation)
|   |   +-- Oracle Fusion Cloud --> Authorization Code + PKCE
|   |   +-- Dynamics 365 --> Authorization Code + PKCE via Entra ID
|   |   +-- NetSuite --> Authorization Code + PKCE
|   |   +-- Workday --> Authorization Code flow
|   |
|   +-- Machine-to-Machine (highest security, no secrets in transit)
|   |   +-- Salesforce --> JWT Bearer (certificate signs every request)
|   |   +-- SAP S/4HANA Cloud --> mTLS (Client Certificate auth)
|   |   +-- Oracle Fusion Cloud --> JWT Client Assertion
|   |   +-- Dynamics 365 --> Certificate thumbprint auth
|   |   +-- NetSuite --> OAuth 2.0 M2M with RSA certificate
|   |   +-- Workday --> OAuth 2.0 JWT Bearer with registered cert
|   |
|   +-- Legacy Migration (basic auth / API keys --> OAuth)
|       +-- Salesforce --> Username-Password flow (TEMPORARY, migrate to JWT)
|       +-- SAP --> Basic Auth (dev only, migrate to OAuth Client Credentials)
|       +-- Oracle --> Basic Auth (deprecated, migrate to OAuth)
|       +-- Dynamics 365 --> Not applicable (always used Entra ID)
|       +-- NetSuite --> TBA (deprecated 2027.1, migrate to OAuth 2.0)
|       +-- Workday --> ISU password (migrate to OAuth + ISU)
|
+-- Security requirement?
|   +-- Standard --> OAuth Client Credentials or JWT
|   +-- High --> Certificate-based auth or mTLS
|   +-- Compliance (SOC 2, HIPAA) --> Certificate + token rotation + audit logging
|
+-- Token management?
    +-- Short-lived tokens (1-2h) --> Standard for all systems
    +-- Long-lived access --> Use refresh tokens (all except Salesforce JWT)
    +-- Non-expiring --> Workday only (non-expiring refresh tokens)
```

## Quick Reference

### Authentication Method Comparison Matrix

| Capability | Salesforce | SAP S/4HANA Cloud | Oracle Fusion Cloud | Dynamics 365 | NetSuite | Workday |
|---|---|---|---|---|---|---|
| **OAuth 2.0** | Yes (5 flows) | Yes | Yes (via IDCS/IAM) | Yes (via Entra ID) | Yes | Yes |
| **Preferred S2S** | JWT Bearer | Client Credentials | Auth Code (IDCS) | Client Cred + Cert | OAuth 2.0 M2M | Refresh Token + ISU |
| **Certificate Auth** | X.509 for JWT | mTLS, Client Cert | JWT Assertion | Certificate thumbprint | RSA for M2M | JWT Bearer cert |
| **Token Lifetime** | Session (2h default) | 1h | 1h | 60-90 min | 1h | Until revoked |
| **Refresh Tokens** | Auth Code only | SAML flow | Yes (7-day) | Yes (until revoked) | Yes (7-day default) | Non-expiring option |
| **Basic Auth** | Username-Password (legacy) | Dev/test only | Deprecated | Not supported | Not supported | ISU password |
| **SAML** | SAML Assertion flow | SAML Bearer Assertion | SAML (IDCS) | SAML via Entra ID | Not supported | SAML 2.0 |
| **API Keys** | No | No | No | No | No | No |
| **Service Accounts** | Integration User + Connected App | Communication User | IDCS Confidential App | Application User (Entra ID) | Integration Record | ISU |
| **MFA Impact** | Breaks Username-Password flow | Transparent (SSO) | Transparent (IDCS) | Transparent (Entra ID) | N/A for OAuth 2.0 | N/A for OAuth 2.0 |
| **Identity Provider** | Built-in | SAP IAS / BTP | IDCS / OCI IAM | Microsoft Entra ID | Built-in | Built-in |
| **Deprecation Risk** | Username-Password flow | Basic Auth | Basic Auth | Client Secret (24-mo expiry) | TBA (2027.1) | ISU password |

[src1, src2, src3, src4, src5, src6, src7, src8]

## Step-by-Step Integration Guide

### 1. Salesforce: Configure JWT Bearer Authentication

Create a connected app in Salesforce Setup, upload an X.509 certificate, and configure the integration user. [src1, src6]

```bash
# Generate a 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"

# Create JWT and exchange 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=$(python3 -c "
import jwt, time
payload = {
    'iss': 'YOUR_CONNECTED_APP_CLIENT_ID',
    'sub': 'integration@yourorg.com',
    'aud': 'https://login.salesforce.com',
    'exp': int(time.time()) + 300
}
print(jwt.encode(payload, open('salesforce_private.pem').read(), algorithm='RS256'))
")"
```

**Verify**: Response contains `access_token` and `instance_url` fields. Token works for API calls until session timeout.

### 2. SAP S/4HANA Cloud: Configure OAuth Client Credentials

Set up a Communication Arrangement with OAuth 2.0 Client Credentials grant type in SAP BTP. [src2, src7]

```bash
# Request access token via Client Credentials
curl -X POST "https://{your-sap-tenant}.authentication.{region}.hana.ondemand.com/oauth/token" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=client_credentials" \
  -d "client_id=YOUR_CLIENT_ID" \
  -d "client_secret=YOUR_CLIENT_SECRET"

# Use token to call S/4HANA OData API
curl -X GET "https://{your-s4hana-host}/sap/opu/odata/sap/API_BUSINESS_PARTNER/A_BusinessPartner?\$top=10" \
  -H "Authorization: Bearer ACCESS_TOKEN" \
  -H "Accept: application/json"
```

**Verify**: `200 OK` with JSON response containing business partner data.

### 3. Dynamics 365: Configure Certificate-Based S2S Authentication

Register an app in Microsoft Entra ID, upload a certificate, and create an application user in Dynamics 365. [src3]

```bash
# Generate certificate
openssl req -x509 -nodes -days 730 -newkey rsa:2048 \
  -keyout d365_private.pem \
  -out d365_cert.pem \
  -subj "/CN=D365Integration"

# Request token using certificate (Python example)
python3 -c "
from msal import ConfidentialClientApplication
import json

app = ConfidentialClientApplication(
    client_id='YOUR_APP_CLIENT_ID',
    authority='https://login.microsoftonline.com/YOUR_TENANT_ID',
    client_credential={
        'private_key': open('d365_private.pem').read(),
        'thumbprint': 'YOUR_CERT_THUMBPRINT'
    }
)
result = app.acquire_token_for_client(scopes=['https://yourorg.crm.dynamics.com/.default'])
print(json.dumps(result, indent=2))
"
```

**Verify**: Response contains `access_token`. Use in `Authorization: Bearer {token}` header for Dataverse API calls.

### 4. NetSuite: Configure OAuth 2.0 Machine-to-Machine

Create an integration record, generate RSA certificate, and configure OAuth 2.0 M2M flow. [src4]

```bash
# Generate RSA key pair for NetSuite M2M
openssl genrsa -out netsuite_private.pem 4096
openssl rsa -in netsuite_private.pem -pubout -out netsuite_public.pem

# OAuth 2.0 token request (Client Credentials with certificate assertion)
curl -X POST "https://{account_id}.suitetalk.api.netsuite.com/services/rest/auth/oauth2/v1/token" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=client_credentials" \
  -d "client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer" \
  -d "client_assertion=YOUR_SIGNED_JWT"
```

**Verify**: `200 OK` with `access_token` and `token_type: Bearer`. Token valid for 60 minutes.

## Code Examples

### Python: Multi-ERP Authentication Factory

```python
# Input:  ERP system name, credentials dict
# Output: Authenticated session with valid access token

import requests
import jwt
import time
from msal import ConfidentialClientApplication

class ERPAuthFactory:
    """Factory for creating authenticated sessions across ERP systems."""

    @staticmethod
    def salesforce_jwt(client_id: str, username: str, private_key_path: str,
                       login_url: str = "https://login.salesforce.com") -> dict:
        """Salesforce JWT Bearer flow -- no refresh token returned."""
        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  # 5-minute JWT expiry
        }
        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()  # {'access_token': '...', 'instance_url': '...'}

    @staticmethod
    def sap_client_credentials(token_url: str, client_id: str,
                                client_secret: str) -> dict:
        """SAP S/4HANA Cloud Client Credentials flow."""
        resp = requests.post(token_url, data={
            'grant_type': 'client_credentials',
            'client_id': client_id,
            'client_secret': client_secret
        })
        resp.raise_for_status()
        return resp.json()  # {'access_token': '...', 'expires_in': 3600}

    @staticmethod
    def dynamics365_certificate(client_id: str, tenant_id: str,
                                 private_key_path: str,
                                 thumbprint: str, resource_url: str) -> dict:
        """Dynamics 365 certificate-based S2S auth via MSAL."""
        with open(private_key_path, 'r') as f:
            private_key = f.read()

        app = ConfidentialClientApplication(
            client_id=client_id,
            authority=f"https://login.microsoftonline.com/{tenant_id}",
            client_credential={'private_key': private_key, 'thumbprint': thumbprint}
        )
        result = app.acquire_token_for_client(
            scopes=[f"{resource_url}/.default"]
        )
        return result  # {'access_token': '...', 'expires_in': 3599}
```

### cURL: Quick Token Test for Each ERP

```bash
# Input:  Valid credentials for each ERP system
# Output: Access token or error message

# --- Salesforce (Username-Password flow -- testing only) ---
curl -X POST https://login.salesforce.com/services/oauth2/token \
  -d "grant_type=password" \
  -d "client_id=YOUR_CLIENT_ID" \
  -d "client_secret=YOUR_CLIENT_SECRET" \
  -d "username=YOUR_USERNAME" \
  -d "password=YOUR_PASSWORD_PLUS_SECURITY_TOKEN"

# --- SAP S/4HANA Cloud (Client Credentials) ---
curl -X POST "https://YOUR_TENANT.authentication.eu10.hana.ondemand.com/oauth/token" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -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_ID/oauth2/v2.0/token" \
  -d "grant_type=client_credentials" \
  -d "client_id=APP_ID" \
  -d "client_secret=SECRET" \
  -d "scope=https://yourorg.crm.dynamics.com/.default"

# --- Workday (Refresh Token) ---
curl -X POST "https://YOUR_DOMAIN.workday.com/ccx/oauth2/YOUR_TENANT/token" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=refresh_token" \
  -d "client_id=CID" \
  -d "client_secret=CSEC" \
  -d "refresh_token=YOUR_REFRESH_TOKEN"
```

## Data Mapping

### Authentication Credential Mapping Across ERPs

| Concept | Salesforce | SAP S/4HANA Cloud | Oracle Fusion Cloud | Dynamics 365 | NetSuite | Workday |
|---|---|---|---|---|---|---|
| **Client ID** | Connected App Consumer Key | OAuth Client ID (BTP) | IDCS Client ID | Entra ID Application (client) 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 | Communication User | IDCS Confidential App | Application User | Integration Record User | ISU |
| **Certificate** | X.509 on Connected App | Client Cert on Comm. Arrangement | IDCS signing cert | Entra ID Certificate | RSA public key on Integration | Workday-registered cert |
| **Token Endpoint** | /services/oauth2/token | /oauth/token (BTP UAA) | /oauth2/v1/token (IDCS) | /oauth2/v2.0/token (Entra ID) | /services/rest/auth/oauth2/v1/token | /ccx/oauth2/{tenant}/token |
| **Scope Format** | N/A (connected app defines) | Per Communication Scenario | Defined in IDCS app | {resource}/.default | restlets, rest_webservices, suite_analytics | N/A |

### Data Type Gotchas

- Salesforce access tokens are org-scoped for JWT flow -- all API calls execute under the integration user's permissions, not a specific end-user. [src1]
- SAP BTP OAuth tokens are bound to a specific subaccount and communication arrangement -- tokens from one arrangement cannot access APIs on another. [src2, src7]
- Dynamics 365 tokens from Entra ID include audience claim (`aud`) that must match the environment URL exactly -- a token for `org1.crm.dynamics.com` will not work on `org2.crm.dynamics.com`. [src3]
- NetSuite OAuth 2.0 tokens are bound to the integration record and account ID -- you cannot reuse tokens across NetSuite accounts. [src4]

## Error Handling & Failure Points

### Common Authentication Error Codes

| Error | System(s) | Meaning | Cause | Resolution |
|---|---|---|---|---|
| `invalid_grant` | All | Token request rejected | Expired JWT, invalid cert, revoked refresh token | Regenerate JWT assertion or refresh token |
| `invalid_client` | All | Client credentials rejected | Wrong client ID/secret, expired secret | Verify credentials; check secret expiration |
| `INVALID_SESSION_ID` | Salesforce | Access token expired or invalid | Session timeout exceeded | Generate new JWT and request new token |
| `401 Unauthorized` | SAP, Oracle | Authentication failed | Invalid or expired token, wrong comm. arrangement | Re-authenticate; verify communication arrangement |
| `AADSTS700027` | Dynamics 365 | Certificate validation failed | Certificate not uploaded to Entra ID app, or expired | Upload correct certificate; check thumbprint |
| `AADSTS7000215` | Dynamics 365 | Invalid client secret | Secret expired (max 24 months) | Generate new secret or switch to certificate |
| `USER_EXCEPTION` | NetSuite | Auth failed | TBA tokens invalid, integration record disabled | Check integration record status; migrate to OAuth 2.0 |
| `401 - invalid_token` | Workday | Token expired or revoked | Access token expired, ISU deactivated | Request new token; verify ISU is active |

[src1, src2, src3, src4, src5]

### Failure Points in Production

- **Salesforce -- Session timeout change breaks integrations**: Admins can change session timeout from the default 2 hours to as low as 15 minutes. If your integration caches tokens, it may use expired tokens. Fix: `Always generate a new JWT and request a fresh token for each batch of operations, or implement token refresh logic that checks remaining validity`. [src1, src6]
- **SAP -- Communication Arrangement deactivation**: When a communication arrangement is deactivated during maintenance or system copy, all OAuth tokens tied to it become invalid. Fix: `Monitor arrangement status via /sap/opu/odata/sap/MANAGE_COMM_ARRANGEMENT; set up alerting on status changes`. [src2]
- **Oracle -- IDCS token endpoint URL changes during migration to OCI IAM**: Oracle is migrating identity services from IDCS to OCI IAM. Token endpoint URLs change during migration. Fix: `Use discovery document at /.well-known/openid-configuration instead of hardcoded URLs`. [src8]
- **Dynamics 365 -- Client secret silent expiry**: Client secrets expire with no warning notification from Entra ID. Integration fails at 2am on a Saturday. Fix: `Use certificate-based auth; or implement monitoring via Microsoft Graph API to check secret expiry dates`. [src3]
- **NetSuite -- TBA deprecation breaks existing integrations**: When NetSuite auto-upgrades to 2027.1+, TBA integrations cannot be re-created if accidentally deleted. Fix: `Migrate to OAuth 2.0 now; do not wait for the deprecation deadline`. [src4]
- **Workday -- ISU termination cascading failure**: If the Integration System User is terminated (intentionally or during reorg), all integrations using that ISU fail immediately with no grace period. Fix: `Create ISU accounts with no business process assignment; use separate ISUs per integration; implement ISU health monitoring`. [src5]

## Anti-Patterns

### Wrong: Hardcoding client secrets in source code

```python
# BAD -- secrets in source code get committed to Git
client_secret = "dJk8s!kLm9@pQrSt"  # Dynamics 365 secret
token = get_token(client_id, client_secret)
```

### Correct: Use a secrets manager or environment variables

```python
# GOOD -- secrets retrieved from vault at runtime
import os
from azure.keyvault.secrets import SecretClient

# Option 1: Environment variable
client_secret = os.environ["D365_CLIENT_SECRET"]

# Option 2: Azure Key Vault (preferred for Dynamics 365)
client = SecretClient(vault_url="https://myvault.vault.azure.net/", credential=credential)
client_secret = client.get_secret("d365-client-secret").value
```

### Wrong: Using a single service account across all integrations

```python
# BAD -- one ISU for all Workday integrations
# If this ISU is terminated, ALL integrations break
config = {
    "isu_username": "global_integration_user",
    "isu_password": "shared_password"  # Also bad: password auth
}
```

### Correct: Dedicated service accounts per integration with OAuth

```python
# GOOD -- separate ISU per integration, OAuth tokens
integrations = {
    "payroll_sync": {"isu": "ISU_PAYROLL", "client_id": "payroll_client"},
    "hcm_export":  {"isu": "ISU_HCM",     "client_id": "hcm_client"},
    "finance_sync": {"isu": "ISU_FINANCE", "client_id": "finance_client"}
}
# Each integration fails independently; blast radius is limited
```

### Wrong: Caching tokens without expiry checks

```python
# BAD -- cached token used without checking if it's still valid
cached_token = redis.get("salesforce_token")
response = requests.get(f"{instance_url}/services/data/v62.0/query",
                        headers={"Authorization": f"Bearer {cached_token}"})
```

### Correct: Token refresh with expiry awareness

```python
# GOOD -- check expiry before using cached token
import time

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 expired or will expire within buffer -- refresh
    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)**: This flow breaks when MFA is enforced (which Salesforce mandates). Fix: `Migrate to JWT Bearer flow for all server-to-server integrations immediately`. [src1, src6]
- **Not pinning OAuth scope per communication arrangement (SAP)**: Requesting broader scopes than the arrangement supports results in `invalid_scope` errors that are hard to debug. Fix: `Document the exact scope string per communication arrangement; include it in integration config`. [src2, src7]
- **Ignoring token endpoint URL changes (Oracle)**: IDCS-to-OCI-IAM migration changes the token endpoint URL. Fix: `Use .well-known/openid-configuration discovery instead of hardcoded URLs`. [src8]
- **Relying on client secrets for long-running integrations (Dynamics 365)**: Secrets expire after max 24 months. Fix: `Use certificate-based auth for all production integrations; set calendar reminders for cert renewal`. [src3]
- **Delaying TBA-to-OAuth migration (NetSuite)**: TBA will be read-only for existing integrations post-2027.1; no new TBA integrations allowed. Fix: `Start OAuth 2.0 migration now; use OAuth 2.0 M2M with RSA certificates`. [src4]
- **Sharing ISU credentials across environments (Workday)**: Using the same ISU in sandbox and production creates security risk and makes debugging impossible. Fix: `Create separate ISUs per environment; use OAuth with environment-specific client registrations`. [src5]

## Diagnostic Commands

```bash
# --- Salesforce: Check connected app and token validity ---
curl -H "Authorization: Bearer ACCESS_TOKEN" \
  https://YOUR_INSTANCE.my.salesforce.com/services/oauth2/userinfo
# Expected: JSON with user_id, organization_id, email

# --- SAP S/4HANA Cloud: Verify OAuth token ---
curl -H "Authorization: Bearer ACCESS_TOKEN" \
  "https://YOUR_HOST/sap/opu/odata/sap/API_BUSINESS_PARTNER/A_BusinessPartner?\$top=1"
# Expected: 200 OK with OData response

# --- Oracle Fusion Cloud: Introspect token ---
curl -X POST "https://YOUR_IDCS_HOST/oauth2/v1/introspect" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -u "CLIENT_ID:CLIENT_SECRET" \
  -d "token=ACCESS_TOKEN"
# Expected: JSON with active: true, exp, scope

# --- Dynamics 365: Test auth with WhoAmI ---
curl -H "Authorization: Bearer ACCESS_TOKEN" \
  -H "OData-Version: 4.0" \
  "https://YOUR_ORG.crm.dynamics.com/api/data/v9.2/WhoAmI"
# Expected: JSON with UserId, OrganizationId

# --- NetSuite: Verify OAuth 2.0 token ---
curl -H "Authorization: Bearer ACCESS_TOKEN" \
  "https://ACCOUNT_ID.suitetalk.api.netsuite.com/services/rest/record/v1/customer?limit=1"
# Expected: 200 OK with customer record

# --- Workday: Test ISU + OAuth ---
curl -H "Authorization: Bearer ACCESS_TOKEN" \
  "https://YOUR_DOMAIN.workday.com/ccx/api/v1/YOUR_TENANT/workers?limit=1"
# Expected: 200 OK with worker data
```

## Version History & Compatibility

| System | Auth Change | Date | Impact | Migration Notes |
|---|---|---|---|---|
| Salesforce | MFA enforcement for all API logins | 2024-02 | Username-Password flow breaks | Migrate to JWT Bearer or Auth Code |
| SAP S/4HANA Cloud | Communication Arrangement OAuth 2.0 GA | 2023 | Standard auth method for cloud | No migration needed for new integrations |
| Oracle Fusion Cloud | Basic Auth deprecated for new integrations | 2025 | New integrations must use OAuth | Existing basic auth integrations still work |
| Oracle Fusion Cloud | IDCS migration to OCI IAM | 2025-2026 | Token endpoint URLs change | Use discovery document |
| Dynamics 365 | ADAL (Azure AD Auth Library) sunset | 2022-06 | Must use MSAL | Update client libraries |
| Dynamics 365 | Entra ID rebrand from Azure AD | 2023 | No functional change | Update documentation |
| NetSuite | TBA deprecation announced | 2024.2 | No new TBA after 2027.1 | Migrate to OAuth 2.0 M2M |
| Workday | OAuth 2.0 Machine-to-Machine support added | 2024 | JWT Bearer flow now available | Recommended for new integrations |

[src1, src2, src3, src4, src5, src6, src7, src8]

### Deprecation Policy

Salesforce maintains API version support for 3+ years and provides at least one year notice for authentication changes. SAP follows its standard API deprecation policy via SAP API Business Hub. Oracle provides 12-month notice for authentication deprecation. Microsoft publishes deprecation timelines via Entra ID documentation. NetSuite provides 2+ release cycles notice. Workday maintains backward compatibility within major API versions. [src1, src2, src3, src4, src5]

## When to Use / When Not to Use

| Use When | Don't Use When | Use Instead |
|---|---|---|
| Evaluating which OAuth flow to use across multiple ERPs | You only need auth details for one specific ERP | System-specific API capability card |
| Planning a multi-ERP integration platform that needs unified auth | You need rate limit or data model details | ERP rate limits comparison card |
| Migrating from basic auth or API keys to OAuth 2.0 | You need SSO/SAML for end-user login | SSO/SAML comparison card |
| Designing certificate management strategy across ERPs | You need specific code examples for one system | System-specific integration guide |
| Auditing service account security posture across ERP estate | You need iPaaS/middleware comparison | iPaaS comparison card |

## Cross-System Comparison

| Capability | Salesforce | SAP S/4HANA Cloud | Oracle Fusion Cloud | Dynamics 365 | NetSuite | Workday |
|---|---|---|---|---|---|---|
| **OAuth 2.0 Support** | Full (5 flows) | Full | Full (via IDCS) | Full (via Entra ID) | Full | Full |
| **Best S2S Flow** | JWT Bearer | Client Credentials | 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 Expiration** | 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 Auth (dev) | Basic Auth (dep.) | Not applicable | 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 default token lifetimes listed in this card can be changed by system administrators. Salesforce session timeout, Dynamics 365 token lifetime policy, and Oracle IDCS token settings are all admin-configurable. Always check with the target org's admin.
- **Sandbox vs production auth endpoints differ**: Salesforce uses `test.salesforce.com` for sandbox vs `login.salesforce.com` for production. SAP has different BTP subaccount URLs per landscape. Oracle has separate IDCS instances. Always parameterize endpoints.
- **Certificate renewal is the #1 cause of unattended integration failures**: Across all six ERPs, certificate expiration is the single most common cause of production outages for server-to-server integrations. Implement automated certificate renewal monitoring for every production integration.
- **Regional/sovereign cloud differences**: SAP, Oracle, and Microsoft have sovereign cloud instances (EU, US Gov, China) with different identity provider endpoints. Authentication URLs and certificate requirements may differ.
- **This comparison reflects cloud deployments only**: On-premise versions of SAP (ECC/R3), Oracle (E-Business Suite), and Dynamics (AX/NAV) use fundamentally different authentication mechanisms not covered here.
- **Auth method availability varies by edition/tier**: Some authentication methods (like Salesforce Streaming API or SAP mTLS) require specific edition tiers or add-on licenses. Always verify with vendor licensing documentation.

## Related Units

- [ERP Rate Limits Comparison](/business/erp-integration/erp-rate-limits-comparison/2026)
