Skip to content

Connect with OAuth (IDCS device flow)

This guide walks an Oracle Identity Domain Administrator through registering EPM Workbench for per-user OAuth sign-in, so that Pull / Push / Deploy / Execute run as the individual developer — and Oracle’s own job console and audit log attribute the work to the human, instead of to a shared Service Account.

It was written from a live end-to-end setup against a real Pod, so it calls out where the Oracle console deviates from the official docs and where the setup produces opaque errors. If you are debugging a failing setup, read the Troubleshooting table first — every error we hit is mapped to its cause and fix there.

There are two separate things to configure, and both are required. Missing the second is the single most common failure — it produces an opaque 401 with no useful message.

What it isWhere it lives
Step 1 — the client appThe application EPM Workbench signs in as (a public “Mobile” app)Domains → your domain → Integrated applications
Step 2 — the resource serverTelling the Pod to accept those OAuth tokensDomains → your domain → Oracle cloud services → your Pod

Step 1 is “who is asking.” Step 2 is “the Pod agreeing to listen.” Do both.

  • You have the Identity Domain Administrator role in the identity domain backing the Pod (OCI Console → Identity & Security → Domains → your domain → the role is on your user).
  • The Pod is Gen 2 (OCI). OAuth 2.0 is Gen-2 only; Classic / Gen-1 Pods stay on Basic auth with a Service Account.
  • You know which identity domain governs EPM. A tenancy can have several domains (ERP / OIC / etc. may use different ones); pick the one over EPM. In a single-domain tenancy this is just Default.

Step 1 — Register the client application

Section titled “Step 1 — Register the client application”
  1. OCI Console → ☰ → Identity & Security → Domains → open the domain that governs EPM.
  2. Note the Domain URL from the Details tab — e.g. https://idcs-<hash>.identity.oraclecloud.com. You need this later (it’s the OAuth host). It is not derivable from your Pod URL, so copy it now.
  3. Go to Integrated applications → Add application.
  4. Choose Mobile Application.

    Why Mobile and not Confidential: a Mobile app is a public client (no client secret). EPM Workbench is a distributed desktop extension and cannot keep a secret. Oracle’s own description of Mobile Application — “hosted on the resource owner’s machine … cannot maintain the confidentiality of their client secret” — is exactly our case. The “reduced integration options” note on that card does not exclude Device Code.

  5. Name it (e.g. epm-workbench). Leave all URL fields (sign-in/out/error/callback) blank — device flow has no redirect. Leave the display toggles off. Click Submit.

    OAuth configuration is not on this creation form. A “Configure OAuth on the Application details page” note appears — that is the remaining substeps, below.

  6. On the app’s OAuth configuration tab, click Edit OAuth configuration:
    • Allowed grant types: enable Device code and Refresh token. Turn off Implicit.
    • Redirect / Post-logout / Logout URLs: leave blank.
    • Client IP address: choose Anywhere. (“Restrict by network perimeter” needs a configured perimeter policy and can silently block token requests.)
    • Bypass consent: off (optional — on skips a one-time consent screen).
    • Token issuance policy → Add resources: ON. Click Add scope, find your Pod in the list (see the naming note below), expand it, and check its urn:opc:serviceInstanceID=…urn:opc:resource:consumer::all scope. Click Add.

      Copy the scope from Oracle, don’t retype it. It embeds a long service-instance OCID; a single wrong character gives invalid_scope. The row is truncated on screen — copy the full value. One resource per token is an IDCS hard limit; add more Pods later as additional scopes.

    • Add app roles: ON. Click Add roles, choose Identity Domain Administrator, Add.

      This is mandatory per Oracle’s EPM REST OAuth doc. Without it, authorization is denied (“Access to the application is denied”). It is a required trust grant for the client app.

    • Submit.
  7. Back on the app page, Actions → Activate the application. Copy the Client ID (not a secret — it goes into EPM Workbench’s committed config).

Pods appear in the scope picker under two naming generations:

  • Older: Planning_<name> (type “Oracle Enterprise Performance Management”).
  • Newer: bare <name> like planning-test (type “Planning Service”).

Pick the one matching your target Pod. If unsure which is which, the Pod URL’s leading segment (e.g. planning-test-…) matches the newer bare name.

Step 2 — Configure the Pod as a resource server

Section titled “Step 2 — Configure the Pod as a resource server”

Do not skip this. Without it, tokens are issued but every EPM REST call returns 401.

  1. In the same domain, go to Oracle cloud services (a tab alongside Integrated applications).
  2. Click your Pod in the list (e.g. planning-test). This is the Pod as a resource server, separate from the client app you just made.
  3. On its OAuth configuration tab, click Edit OAuth configuration:
    • Enable Allow token refresh.

      Without this, requesting offline_access fails with unauthorized_client: "The resource does not support offline access." and no refresh token is ever issued.

    • Enable Add secondary audience, click Add, and enter the EPM Pod URL exactly, e.g. https://<pod>.epm.<region>.ocs.oraclecloud.com.

      This is the fix for the invalid_session 401. The token’s primary audience is the serviceInstanceID, but EPM’s Access Manager validates the token against the host you are calling (the Pod URL). Adding the Pod URL as a secondary audience puts it in the token’s aud, so EPM accepts the session.

    • Submit.

A per-user token issued for this Pod will now be accepted by its REST APIs.

Three non-secret values (they go into committed config — a domain-level domain.json, or pod.json for a single Pod):

ValueExampleNotes
Identity-domain hosthttps://idcs-<hash>.identity.oraclecloud.comFrom the domain Details tab. Not derivable from the Pod URL.
Client IDc95fa57c0792414982680ae1e99c40f0From the client app. Public, not a secret.
Scopeurn:opc:serviceInstanceID=<env-OCID>urn:opc:resource:consumer::allPer-Pod. Copy verbatim from the scope picker.

The developer never supplies these by hand. They are committed once by whoever sets up the Pod or Domain. Each developer then just runs Connect with your EPM account and signs in through their normal SSO / MFA. Their refresh token stays in VS Code SecretStorage on their machine and is never shared.

Reference: the flow EPM Workbench performs

Section titled “Reference: the flow EPM Workbench performs”

Provided so you can reproduce it with curl when debugging.

  1. Device requestPOST {host}/oauth2/v1/device, body response_type=device_code&client_id=<id>&scope=<scope> offline_access. Returns user_code, verification_uri ({host}/ui/v1/device), device_code, expires_in.
    • response_type=device_code is required (IDCS-specific; not in the RFC). client_id goes in the body, never HTTP Basic.
  2. The developer opens the verification URL, signs in (SSO / MFA), enters the code, and approves.
  3. PollPOST {host}/oauth2/v1/token, body grant_type=urn:ietf:params:oauth:grant-type:device_code&device_code=<code>&client_id=<id>. Returns access_token (~3600s) and refresh_token (~7d).
  4. Call EPMAuthorization: Bearer <access_token> against {pod}/HyperionPlanning/rest/... or {pod}/interop/rest/....
  5. On expiry, refreshPOST {host}/oauth2/v1/token, grant_type=refresh_token&refresh_token=… → new access + rotated refresh token.
SymptomCauseFix
400 invalid_request on the device requestMissing response_type=device_code (IDCS requires it)Add response_type=device_code to the device request body
invalid_client on token/refreshSent client credentials as HTTP Basic on a public clientSend client_id in the request body, no Basic auth
invalid_scope on the device requestScope string mistyped (the embedded env OCID is off by a character)Copy the scope verbatim from the OCI scope picker; don’t retype
unauthorized_client: "The resource does not support offline access."Resource server has Allow token refresh offStep 2 → enable Allow token refresh on the Pod’s OAuth config
Browser: “Access to the application is denied”Client app missing the Identity Domain Administrator app roleStep 1 → Add app roles → Identity Domain Administrator
401 WWW-Authenticate: Bearer error="invalid_session" on every EPM REST call, even though the token looks validPod URL not in the token’s aud (EPM’s OAM validates audience against the host called)Step 2 → Add secondary audience = the EPM Pod URL
Refresh token → EPM scope exchange fails 400Trying to elevate a domain-only (openid) refresh token to the EPM scopeRequest the EPM scope directly in the device flow (with offline_access); don’t exchange a domain refresh token

Quick sanity check that the Pod itself is fine (isolates OAuth from everything else): the same REST call with Basic auth (a Service Account username and password) should return 200. If Basic works and OAuth returns 401 invalid_session, the problem is Step 2 (secondary audience), not your Pod or your user’s roles.