offline_access scope and default to 7 days. [src1, src5]Oracle ERP Cloud (Fusion Cloud Applications) centralizes all authentication through OCI IAM identity domains (the successor to Oracle Identity Cloud Service / IDCS). Every Fusion Cloud environment comes with a pre-provisioned identity domain where Fusion Applications are auto-registered as resource applications. Third-party systems must be registered as confidential client applications. This card covers the detailed implementation patterns for each authentication flow. For a high-level overview of which method to choose, see the prerequisite card (oracle-erp-cloud-authentication).
| Property | Value |
|---|---|
| Vendor | Oracle |
| System | Oracle ERP Cloud (Fusion Cloud Applications) Release 25B |
| API Surface | REST, SOAP |
| Current API Version | Release 25B (quarterly: 25A, 25B, 25C, 25D) |
| Editions Covered | All Oracle Fusion Cloud editions |
| Deployment | Cloud |
| API Docs | Oracle Fusion Cloud OAuth Configuration |
| Status | GA |
Oracle Fusion Cloud exposes multiple API surfaces. Authentication applies uniformly -- the same OAuth token works for REST, SOAP, and UCM endpoints. The token's scope and the integration user's Fusion roles determine which operations are permitted.
| API Surface | Protocol | Best For | Auth Methods | Token Type | Real-time? | Bulk? |
|---|---|---|---|---|---|---|
| REST API (v2) | HTTPS/JSON | Individual record CRUD, queries | OAuth 2.0, JWT, Basic Auth | Bearer | Yes | No |
| SOAP API | HTTPS/XML | Web service operations, ERP Integration Service | OAuth 2.0, JWT, Basic Auth, SAML | SAML/Bearer | Yes | Via ERP Integration Service |
| UCM (WebCenter Content) | HTTPS | FBDI file upload, report retrieval | OAuth 2.0, Basic Auth | Bearer | No | Yes |
| BI Publisher | HTTPS/SOAP | Scheduled extracts, custom reports | OAuth 2.0, Basic Auth | Bearer | No | Yes |
| Business Events | HTTPS/JSON | Outbound event notifications | OAuth 2.0 (subscriber-side) | Bearer | Yes | N/A |
| Limit Type | Value | Applies To | Notes |
|---|---|---|---|
| Max request body size | ~10 MB (varies) | REST API | Larger payloads via UCM/FBDI |
| Max concurrent requests | Not published (fair-use) | All API surfaces | Adaptive throttling per tenant/pod |
| Session timeout (default) | 8 hours (28,800s) | All sessions | Configurable in identity domain settings |
| Token request rate | Not published | OAuth token endpoint | Excessive requests trigger 429 |
| FBDI file size limit | 250 MB | File-Based Data Import | Split larger files |
| Limit Type | Value | Window | Edition Differences |
|---|---|---|---|
| API call rate limit | Not published (fair-use) | Rolling | Adaptive throttling; no fixed cap |
| Burst throttle | HTTP 429 on excessive calls | Per-minute | Back off exponentially |
| Token endpoint rate | Fair-use throttling | Per-minute | Cache tokens to avoid limits |
| FBDI/ESS jobs | Subject to ESS queue | Per-tenant | Shared with scheduled processes |
Detailed implementation patterns for each Oracle ERP Cloud authentication flow.
| Flow | Grant Type | Use When | Token Lifetime | Refresh? | Security Level |
|---|---|---|---|---|---|
| Client Credentials (2-legged) | client_credentials | Server-to-server, no user context | Access: 1h; Refresh: 7d | Yes (with offline_access) | High |
| JWT Assertion | urn:ietf:params:oauth:grant-type:jwt-bearer | Server-to-server, certificate trust | Access: 1h (configurable) | New JWT per request | Highest |
| Authorization Code (3-legged) | authorization_code | User-context operations | Access: 1h; Refresh: 7d | Yes | High |
| Resource Owner Password | password | Legacy, testing only | Access: 1h | No | Low |
| SAML 2.0 Bearer Assertion | urn:ietf:params:oauth:grant-type:saml2-bearer | Cross-domain identity propagation | Access: 1h | No | High |
| Basic Authentication | N/A (HTTP header) | Quick testing only | Session timeout (8h) | N/A | Lowest |
Most common pattern for server-to-server integrations. The confidential application authenticates using its client ID and client secret. All API calls execute under the integration user's identity. [src1]
Most secure pattern for machine-to-machine. Instead of a shared client secret, the application signs a JWT with a private key. The public certificate is uploaded to the confidential application. [src2]
Used when the application needs to act on behalf of a specific user. Requires interactive browser-based authentication. Not suitable for unattended batch processing. [src1]
Enables browser-based Single Sign-On with external Identity Providers (Azure AD, Okta, ADFS, PingFederate). Applies only to interactive browser-based authentication -- API-level OAuth flows bypass federation entirely. [src6, src7]
| IdP | Protocol | User Provisioning | Notes |
|---|---|---|---|
| Microsoft Entra ID (Azure AD) | SAML 2.0 | Manual or SCIM | Download Entra SAML metadata; configure as IdP |
| Okta | SAML 2.0 | SCIM (automatic) | Okta as IdP, identity domain as SP |
| PingFederate | SAML 2.0 | Manual | Export PingFederate metadata |
| ADFS | SAML 2.0 | Manual or DirSync | Export ADFS federation metadata XML |
| Any SAML 2.0 IdP | SAML 2.0 | Manual | Upload IdP metadata document |
Flows end-user identity through OIC or other middleware to Fusion Cloud for audit and security purposes. Uses OAuth JWT User Assertion security policy. [src3]
Exchange Azure AD tokens for Oracle access tokens without requiring users to re-authenticate against the Oracle identity domain. [src7]
exp clock skew tolerance is ~5 minutes. Server clock drift beyond that causes invalid_grant errors. Use NTP. [src2]urn:opc:resource:consumer::all), not standard openid profile. Incorrect scope returns 400 with minimal diagnostic info. [src1]START -- Choosing an Oracle ERP Cloud authentication pattern
|-- What type of client?
| |-- Server/backend (unattended)
| | |-- Can you manage certificates and PKI lifecycle?
| | | |-- YES --> Pattern 2: JWT Assertion (most secure)
| | | |-- NO --> Pattern 1: Client Credentials
| | |-- Need user-level audit trail?
| | |-- YES --> Pattern 5: Identity Propagation
| | |-- NO --> Pattern 1: Client Credentials
| |-- Browser-based (user-interactive)
| | |-- Users have existing corporate IdP credentials?
| | | |-- YES, Azure AD --> Pattern 6: Token Exchange
| | | |-- YES, Okta/ADFS --> Pattern 4: SAML + Pattern 3
| | | |-- NO --> Pattern 3: Authorization Code
| |-- Middleware (OIC, MuleSoft, Boomi)
| | |-- OIC within same identity domain?
| | | |-- YES --> OAuth auto-configured
| | | |-- NO --> Manual registration + Pattern 1 or 2
| | |-- Need identity propagation?
| | |-- YES --> Pattern 5
| | |-- NO --> Pattern 1
| |-- Quick test / debugging
| |-- Basic Auth (dev/test only, never production)
|-- LBAC enabled?
| |-- YES --> Allowlist IPs FIRST
| |-- NO --> Proceed
|-- Multi-environment?
|-- YES --> Separate app registration per environment
|-- NO --> Single registration
| Grant Type | Client Secret Required | User Interaction | Refresh Token | MFA Compatible | Production Ready |
|---|---|---|---|---|---|
| Client Credentials | Yes | No | Yes (with offline_access) | N/A | Yes |
| JWT Assertion | No (certificate) | No | No (new JWT per request) | N/A | Yes (recommended) |
| Authorization Code | Yes | Yes (browser) | Yes | Yes | Yes |
| Authorization Code + PKCE | No (code verifier) | Yes (browser) | Yes | Yes | Yes |
| Resource Owner Password | Yes | No | No | No | No (legacy) |
| SAML Bearer Assertion | Yes | No | No | Depends on IdP | Yes (specialized) |
| Endpoint | URL Pattern | Purpose |
|---|---|---|
| Token (IDCS-migrated) | https://<tenant>.identity.oraclecloud.com/oauth2/v1/token | Apps in OracleIdentityCloudService domain |
| Token (Default domain) | https://<region>.identity.oraclecloud.com/oauth2/v1/token | Apps in Default identity domain |
| Authorize | https://<domain>.identity.oraclecloud.com/oauth2/v1/authorize | Authorization Code flow |
| JWKS | https://<domain>.identity.oraclecloud.com/admin/v1/SigningCert/jwk | Token validation / public keys |
| Userinfo | https://<domain>.identity.oraclecloud.com/oauth2/v1/userinfo | Authenticated user claims |
| OpenID Discovery | https://<domain>.identity.oraclecloud.com/.well-known/openid-configuration | Auto-discovery of all endpoints |
| SAML Metadata | https://<domain>.identity.oraclecloud.com/fed/v1/metadata | SP metadata for SAML federation |
Create the OAuth client that all patterns depend on. Navigate to OCI Console > Identity & Security > Domains > Applications > Add Application > Confidential Application. Configure grant types, scopes, and activate. [src1]
Verify: Applications list shows Status: Active.
Exchange client credentials for an access token (Pattern 1). [src1, src4]
curl -X POST \
"https://<tenant>.identity.oraclecloud.com/oauth2/v1/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-u "<CLIENT_ID>:<CLIENT_SECRET>" \
-d "grant_type=client_credentials&scope=urn:opc:resource:consumer::all"
Verify: Response contains "token_type": "Bearer" and "expires_in": 3600.
Create a signed JWT and exchange for access token (Pattern 2). [src2]
# Generate key pair (one-time)
openssl genrsa -out private_key.pem 4096
openssl req -new -x509 -key private_key.pem -out public_cert.pem -days 730
# Upload public_cert.pem to confidential app > Certificates tab
# Exchange signed JWT for access token
curl -X POST \
"https://<tenant>.identity.oraclecloud.com/oauth2/v1/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer&assertion=<SIGNED_JWT>&scope=urn:opc:resource:consumer::all"
Verify: Response contains "access_token" without sending any client secret.
Set up SSO with an external Identity Provider (Pattern 4). Navigate to Identity Providers in your identity domain, upload IdP metadata, configure NameID mapping. [src6, src7]
Verify: User can access Fusion Cloud via IdP login without Oracle credentials.
Prevent excessive token requests for long-running integrations. [src5]
Verify: Multiple calls within token lifetime return cached token without endpoint hit.
Configure middleware to flow end-user identity to Fusion Cloud for audit trail compliance. [src3]
Verify: Fusion Cloud audit trail shows end user's username, not integration service account.
# Input: Private key PEM file, Client ID, Token endpoint
# Output: Access token via JWT assertion (no shared secret)
import time
import jwt # PyJWT>=2.8.0
import requests # requests>=2.31.0
def get_oracle_token_jwt(token_url, client_id, private_key_path):
with open(private_key_path, "r") as f:
private_key = f.read()
now = int(time.time())
claims = {
"iss": client_id, "sub": client_id,
"aud": token_url, "iat": now, "exp": now + 300,
}
signed_jwt = jwt.encode(claims, private_key, algorithm="RS256")
resp = requests.post(token_url, data={
"grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer",
"assertion": signed_jwt,
"scope": "urn:opc:resource:consumer::all",
}, headers={"Content-Type": "application/x-www-form-urlencoded"}, timeout=30)
resp.raise_for_status()
return resp.json()["access_token"]
// Input: Client ID, Redirect URI, endpoints
// Output: Access token via authorization code + PKCE
import crypto from "crypto";
function generatePKCE() {
const verifier = crypto.randomBytes(32).toString("base64url");
const challenge = crypto.createHash("sha256")
.update(verifier).digest("base64url");
return { verifier, challenge };
}
async function exchangeCodeForToken(tokenEndpoint, clientId,
clientSecret, code, redirectUri, codeVerifier) {
const credentials = Buffer.from(
`${clientId}:${clientSecret}`).toString("base64");
const resp = await fetch(tokenEndpoint, {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
Authorization: `Basic ${credentials}`,
},
body: new URLSearchParams({
grant_type: "authorization_code", code,
redirect_uri: redirectUri, code_verifier: codeVerifier,
}),
});
if (!resp.ok) throw new Error(`Failed: ${resp.status}`);
return resp.json();
}
# Pattern 1: Client Credentials
curl -X POST \
"https://<tenant>.identity.oraclecloud.com/oauth2/v1/token" \
-u "<CLIENT_ID>:<CLIENT_SECRET>" \
-d "grant_type=client_credentials&scope=urn:opc:resource:consumer::all"
# Pattern 2: JWT Assertion
curl -X POST \
"https://<tenant>.identity.oraclecloud.com/oauth2/v1/token" \
-d "grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer&assertion=<JWT>&scope=urn:opc:resource:consumer::all"
# Verify token against Fusion Cloud
curl "https://<erp-host>.fa.ocs.oraclecloud.com/fscmRestApi/resources/latest" \
-H "Authorization: Bearer <ACCESS_TOKEN>" -w "\nHTTP: %{http_code}\n"
| Configuration Element | OCI IAM Location | Format | Required For | Gotcha |
|---|---|---|---|---|
| Client ID | Confidential App > General | UUID string | All OAuth flows | Must be from the correct identity domain |
| Client Secret | Confidential App > General | String | Client Creds, Auth Code | Rotate every 90 days; store in vault |
| Token endpoint URL | Domain > Overview | URL | All OAuth flows | Varies between IDCS-migrated and Default domains |
| Redirect URI | Confidential App > Client Config | URL | Authorization Code only | Must match exactly (including trailing slash) |
| Certificate (PEM) | Confidential App > Certificates | PEM file | JWT Assertion only | Upload full chain for production |
| Scope | Confidential App > Resources | URN string | All OAuth flows | urn:opc:resource:consumer::all or specific |
| SAML Metadata | Security > Identity Providers | XML file | SAML Federation only | Download fresh from IdP before setup |
<tenant>.identity.oraclecloud.com; Default domain = <region>.identity.oraclecloud.com. Use OpenID Discovery as authoritative source. [src1]urn:opc:resource:consumer::all). Standard OAuth scopes like openid profile return 400. [src1]exp requires seconds-precision Unix timestamps. Millisecond timestamps cause invalid_grant. [src2]openssl pkcs12 -in cert.p12 -out cert.pem -nodes. [src2]| Code | Meaning | Cause | Resolution |
|---|---|---|---|
| 400 | Invalid token request | Wrong grant_type, missing scope, inactive app, malformed JWT | Verify grant type enabled; check scope URN; ensure app Active |
| 401 | Unauthorized / LBAC block | Expired token, wrong creds, LBAC blocking | Check token expiry; verify LBAC allowlist |
| 403 | Insufficient permissions | Integration user lacks Fusion roles | Assign required application roles |
| 429 | Rate throttled | Excessive API or token requests | Exponential backoff; cache tokens |
invalid_grant | JWT assertion rejected | Expired JWT, clock skew >5min, wrong aud | Sync clocks; verify aud = token endpoint |
invalid_client | Client auth failed | Wrong ID/secret, inactive app, cert mismatch | Verify credentials; check certificate chain |
invalid_scope | Scope not allowed | Scope not configured on app | Add scope in app resource config |
unauthorized_client | Grant type not enabled | App missing requested grant type | Enable grant type in app settings |
Automate certificate expiry monitoring; rotate 30 days before expiry. [src2]Use CIDR blocks in LBAC allowlist; monitor for 401 spikes. [src4]Dedicated sign-on policy rule for service accounts above MFA rule. [src6]Use OpenID Discovery to dynamically resolve token URL. [src1]Copy URI directly from app into OCI IAM; verify exact match. [src1]Request new token each run for infrequent jobs. [src5]# BAD -- credentials in every request, exposed in logs
response = requests.get(url, auth=("user", "P@ssw0rd"))
# GOOD -- token-based, credentials from vault, token cached
token = token_mgr.get_token()
response = requests.get(url, headers={"Authorization": f"Bearer {token}"})
# BAD -- 1000 invoices = 1000 token requests (200-500ms each)
for inv_id in ids:
token = get_fresh_token(...)
requests.get(f"{base}/invoices/{inv_id}", headers={"Authorization": f"Bearer {token}"})
# GOOD -- one token per hour, reused across all calls
token_mgr = OracleTokenManager(...)
for inv_id in ids:
token = token_mgr.get_token() # cached or refreshes if <60s left
requests.get(f"{base}/invoices/{inv_id}", headers={"Authorization": f"Bearer {token}"})
# BAD -- breaks on IDCS migration or domain changes
TOKEN_URL = "https://idcs-abc123.identity.oraclecloud.com/oauth2/v1/token"
# GOOD -- resilient to endpoint changes
disco = requests.get(f"{domain}/.well-known/openid-configuration").json()
TOKEN_URL = disco["token_endpoint"]
# BAD -- compromised dev cert exposes production
# GOOD -- each environment has own key pair
# prod: CA-signed, 2-year | test/dev: self-signed, 1-year
openssl genrsa -out prod_key.pem 4096
openssl req -new -x509 -key prod_key.pem -out prod_cert.pem -days 730
Migrate to Client Credentials or JWT Assertion immediately. [src4]Automate monitoring; alert 30/14/7 days before expiry. [src2]Register in OracleIdentityCloudService domain. [src1]Check LBAC first; test from allowlisted IP. [src4]Always use PKCE (S256) even with confidential clients. [src5]Use OAuth grant types for all API auth. [src6, src7]# Discover all identity domain endpoints
curl -s "https://<domain>.identity.oraclecloud.com/.well-known/openid-configuration" | python3 -m json.tool
# Test Client Credentials token request
curl -v -X POST "https://<domain>.identity.oraclecloud.com/oauth2/v1/token" \
-u "<CLIENT_ID>:<CLIENT_SECRET>" \
-d "grant_type=client_credentials&scope=urn:opc:resource:consumer::all"
# Decode JWT payload (inspect without verification)
echo "<TOKEN>" | cut -d'.' -f2 | base64 -d 2>/dev/null | python3 -m json.tool
# Verify token against Fusion Cloud
curl -w "\nHTTP: %{http_code}\n" \
"https://<erp-host>.fa.ocs.oraclecloud.com/fscmRestApi/resources/latest" \
-H "Authorization: Bearer <TOKEN>"
# Check certificate expiry
openssl x509 -in cert.pem -noout -enddate
# Test LBAC (compare from two IPs)
curl -s -o /dev/null -w "%{http_code}" "https://<erp-host>.fa.ocs.oraclecloud.com/fscmRestApi/resources/latest" \
-H "Authorization: Bearer <TOKEN>"
| Release | Date | Status | Breaking Changes | Migration Notes |
|---|---|---|---|---|
| OCI IAM Identity Domains GA | 2022-02 | Current | IDCS console moved to OCI Console | All IDCS APIs, client IDs, tokens continue to work |
| IDCS-to-IAM Region Migration | 2023-03 to 2024-06 | Complete | Automatic migration | No code changes required |
| Identity Domain Replication | 2024-08 | Current | None | Cross-region DR available |
| Fusion Cloud Release 25A | 2025-01 | Current | None auth-related | Quarterly release; endpoints unchanged |
| Fusion Cloud Release 25B | 2025-04 | Current | None auth-related | OAuth config UI improvements |
| OCI IAM OAuth 2.1 Alignment | Planned 2025-2026 | Upcoming | Password grant likely deprecated | Migrate off password grant proactively |
| Use When | Don't Use When | Use Instead |
|---|---|---|
| Implementing specific OAuth grant type flows | Need high-level auth method overview | Oracle ERP Cloud Authentication |
| Setting up SAML federation with external IdPs | Need Oracle NetSuite auth (TBA/OAuth) | NetSuite TBA vs OAuth 2.0 |
| Configuring identity propagation through middleware | Need general OAuth 2.0 patterns | software/patterns/oauth2-client-credentials/2026 |
| Managing certificate lifecycle for JWT flows | Need Salesforce OAuth auth | Salesforce OAuth Authentication |
| Multi-environment auth strategy (prod/test/dev) | Need Oracle ERP REST API capabilities | Oracle ERP Cloud REST API |