Use Oracle Cloud Infrastructure (OCI) as a Workload Identity Provider by exchanging an Oracle Identity Cloud Service (IDCS) access token for a short-lived OpenAI access token. An OCI instance principal signs a token exchange request to an identity domain in the same tenancy. OpenAI validates the resulting token and authorizes the OCI workload to act as a mapped OpenAI service account.
This setup does not require an OpenAI API key, a custom Oracle OAuth resource application, or dynamic group grants to a custom application.
Set up the OCI workload
Run your workload on an OCI Compute instance with an instance principal. For Oracle Kubernetes Engine (OKE), confirm which identity signs the request: the standard instance principal signer typically identifies the worker node, not an individual Kubernetes pod.
The signer obtains credentials from the OCI instance metadata service. Verify the workload can reach the link-local metadata endpoint:
1
2
3
curl --fail --silent \
--header "Authorization: Bearer Oracle" \
http://169.254.169.254/opc/v2/instance/idThe workload must also be able to make outbound HTTPS requests to the identity domain in its tenancy. The metadata endpoint itself does not require a NAT gateway or an internet connection.
Request an Oracle identity token
Use InstancePrincipalsSecurityTokenSigner from the OCI Python SDK to sign an OAuth token exchange request to your identity domain:
POST https://<identity-domain>/oauth2/v1/token
Content-Type: application/x-www-form-urlencoded;charset=utf-8
grant_type=urn:ietf:params:oauth:grant-type:token-exchange
scope=urn:opc:idm:__myscopes__
requested_token_type=urn:ietf:params:oauth:token-type:access_tokenThe urn:opc:idm:__myscopes__ scope uses the instance principal’s existing authorization. Use the returned IDCS access token as the subject token for OpenAI workload identity federation. Do not replace the Oracle token audience with https://api.openai.com/v1; configure the OpenAI provider with an audience that appears in the actual Oracle token.
Verify the token
Set TOKEN to an access token generated by the actual OCI workload, then use the existing local JWT decoder to inspect its claims:
1
2
3
4
5
6
7
import base64
import json
import os
payload = os.environ["TOKEN"].split(".")[1]
payload += "=" * (-len(payload) % 4)
print(json.dumps(json.loads(base64.urlsafe_b64decode(payload)), indent=2))The decoder inspects the token without verifying its signature. Treat raw tokens as sensitive, do not log them, and do not paste production tokens into third-party JWT decoders.
A decoded Oracle access token can contain the following claims:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
"iss": "https://identity.oraclecloud.com/",
"aud": [
"https://idcs-example.us-phoenix-1.identity.oraclecloud.com",
"https://idcs-example.identity.oraclecloud.com"
],
"sub_type": "instance",
"ipst_instance": "ocid1.instance.oc1.phx.<instance-id>",
"ipst_compartment": "ocid1.compartment.oc1..<compartment-id>",
"domain_id": "ocid1.domain.oc1..<domain-id>",
"ca_ocid": "ocid1.tenancy.oc1..<tenancy-id>",
"tenant": "idcs-example",
"exp": 1782369434,
"iat": 1782365834
}Use the token issued by your own identity domain as the source of truth. Configure the exact iss value and one of the token’s aud values. Prefer the immutable ipst_instance, ipst_compartment, domain_id, and ca_ocid claims when authorizing a workload.
Set up workload identity federation
Create a Workload Identity Provider for your Oracle identity domain, then add a mapping for the OCI instance or compartment that can use the target OpenAI service account.
Set up the Workload Identity Provider
-
Create the Workload Identity Provider. Set Name to a unique value, such as
oracle-cloud-prod. Use Description, such asProduction OCI instance principal, to identify the trusted workload. -
Set the issuer and audience. Set OIDC Issuer URL to the token’s
issclaim, such ashttps://identity.oraclecloud.com/. Set Audience to one of theaudvalues in the same token. -
Configure tenant-specific OIDC discovery when available. If Use custom URL for OIDC discovery appears under Advanced, enable it. Set Custom OIDC discovery URL to your tenant-specific identity domain, such as
https://idcs-example.identity.oraclecloud.com. OpenAI retrieveshttps://idcs-example.identity.oraclecloud.com/.well-known/openid-configuration, then uses the discovery document’sjwks_urito retrieve the tenant’s public signing keys. If the custom discovery option does not appear, enable Use uploaded JWKS for token verification and upload the public JWKS fromhttps://<identity-domain>/admin/v1/SigningCert/jwkinstead. -
Add attribute transformations only when you need derived attributes. You can use raw Oracle claims such as
ipst_instance,ipst_compartment,domain_id, andca_ociddirectly in service account mapping assertions. For an explicitly derived instance attribute, enterinstancewith the expressionassertion.ipst_instanceto createopenai.instance.
Oracle’s OpenID Connect discovery reference shows why custom discovery is important: the discovery document can declare the global issuer https://identity.oraclecloud.com/ while publishing the token endpoint and jwks_uri on the tenant-specific identity domain. Keep the global issuer in OIDC Issuer URL and use the tenant domain for Custom OIDC discovery URL.
If your identity domain publishes discovery metadata at the token issuer,
leave custom discovery disabled and use standard OIDC discovery. If OpenAI
cannot reach the tenant discovery document or signing-key endpoint, disable
custom discovery, enable Use uploaded JWKS for token verification, and
upload the tenant’s public JWKS from
https://<identity-domain>/admin/v1/SigningCert/jwk. Custom discovery and
uploaded JWKS cannot be enabled at the same time. Update uploaded keys when
Oracle rotates its signing certificates.
Set up the service account mapping
-
Create a service account mapping. Set Name to a unique value, such as
oracle-instance-prod, and add a description that identifies the trusted OCI workload. -
Match the narrowest stable OCI identity. To grant access to one instance, set Key to
ipst_instanceand Value to the exact instance OCID from the verified token. To grant access to instances across one compartment, set Key toipst_compartmentand Value to the exact compartment OCID. -
Add domain and tenancy boundaries when needed. Add further mapping rows for
domain_idorca_ocidto limit the workload to a particular Oracle identity domain or tenancy. Addsub_typewith the valueinstancewhen the token includes that claim and you want to require an instance principal. All mapping rows must match. -
Choose the OpenAI target. Set Project to the project that owns the service account, then select the Service account that the trusted OCI workload can use.
-
Narrow API permissions if needed. Select only the Permissions needed by the workload. Mapping permissions can restrict the selected service account but cannot grant permissions the service account does not already have.
An OKE workload that uses the standard instance principal signer inherits the worker node’s identity. An instance-level mapping authorizes that node, not just one pod. Use a more specific, supported OCI workload identity when you need isolation between pods sharing a worker node.
Use the token in code
Install the OpenAI, OCI, and Requests Python packages:
pip install openai oci requestsSet OCI_IDENTITY_DOMAIN_URL to the base URL of the identity domain in the same tenancy as the workload. Set OPENAI_IDENTITY_PROVIDER_ID and OPENAI_SERVICE_ACCOUNT_ID to the IDs from your OpenAI provider and service account mapping.
The following example signs an Oracle token exchange request with the OCI instance principal, returns the IDCS access token to the OpenAI SDK, and lets the SDK exchange it for a short-lived OpenAI access token when needed:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
import os
import oci
import requests
from openai import OpenAI
from openai.auth import SubjectTokenProvider
def oracle_instance_principal_token_provider(
identity_domain_url: str,
) -> SubjectTokenProvider:
def get_token() -> str:
signer = oci.auth.signers.InstancePrincipalsSecurityTokenSigner()
response = requests.post(
f"{identity_domain_url.rstrip('/')}/oauth2/v1/token",
data={
"grant_type": "urn:ietf:params:oauth:grant-type:token-exchange",
"scope": "urn:opc:idm:__myscopes__",
"requested_token_type": "urn:ietf:params:oauth:token-type:access_token",
},
headers={
"Content-Type": "application/x-www-form-urlencoded;charset=utf-8",
},
auth=signer,
timeout=30,
)
response.raise_for_status()
token = response.json().get("access_token")
if not isinstance(token, str) or not token:
raise RuntimeError("Oracle IDCS did not return an access token.")
return token
return {"token_type": "jwt", "get_token": get_token}
client = OpenAI(
workload_identity={
"identity_provider_id": os.environ["OPENAI_IDENTITY_PROVIDER_ID"],
"service_account_id": os.environ["OPENAI_SERVICE_ACCOUNT_ID"],
"provider": oracle_instance_principal_token_provider(
os.environ["OCI_IDENTITY_DOMAIN_URL"]
),
},
)
response = client.responses.create(
model="gpt-5.6-terra",
input="Say hello from Oracle Cloud Infrastructure workload identity federation.",
)
print(response.output_text)The subject token provider requests a fresh Oracle token when the OpenAI SDK needs to renew the workload identity credential. Never print or persist the Oracle subject token or the resulting OpenAI access token.
OCI security recommendations
- Map one instance with
ipst_instancewhen only one workload should have access. - Use
ipst_compartmentonly when every eligible instance in that compartment should share the mapping. - Add
domain_idorca_ocidto enforce identity domain and tenancy boundaries. - Use a separate OpenAI service account for each application and environment.
- Verify whether an OKE token represents a worker node before relying on pod-level isolation.
- Use the audience present in the issued Oracle token rather than assuming an OpenAI-specific audience.
- Rotate uploaded public keys when Oracle rotates its signing keys if your identity domain cannot use OIDC discovery.