1# Configuring workload identity federation for Oracle Cloud Infrastructure
2
3Use 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.
4
5This setup does not require an OpenAI API key, a custom Oracle OAuth resource application, or dynamic group grants to a custom application.
6
7## Set up the OCI workload
8
9Run 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.
10
11The signer obtains credentials from the [OCI instance metadata service](https://docs.oracle.com/en-us/iaas/Content/Compute/Tasks/gettingmetadata.htm). Verify the workload can reach the link-local metadata endpoint:
12
13```bash
14curl --fail --silent \
15 --header "Authorization: Bearer Oracle" \
16 http://169.254.169.254/opc/v2/instance/id
17```
18
19The 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.
20
21### Request an Oracle identity token
22
23Use `InstancePrincipalsSecurityTokenSigner` from the OCI Python SDK to sign an OAuth token exchange request to your identity domain:
24
25```text
26POST https://<identity-domain>/oauth2/v1/token
27Content-Type: application/x-www-form-urlencoded;charset=utf-8
28
29grant_type=urn:ietf:params:oauth:grant-type:token-exchange
30scope=urn:opc:idm:__myscopes__
31requested_token_type=urn:ietf:params:oauth:token-type:access_token
32```
33
34The `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.
35
36### Verify the token
37
38Set `TOKEN` to an access token generated by the actual OCI workload, then use the existing local JWT decoder to inspect its claims:
39
40```python
41import base64
42import json
43import os
44
45payload = os.environ["TOKEN"].split(".")[1]
46payload += "=" * (-len(payload) % 4)
47print(json.dumps(json.loads(base64.urlsafe_b64decode(payload)), indent=2))
48```
49
50
51The 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.
52
53A decoded Oracle access token can contain the following claims:
54
55```json
56{
57 "iss": "https://identity.oraclecloud.com/",
58 "aud": [
59 "https://idcs-example.us-phoenix-1.identity.oraclecloud.com",
60 "https://idcs-example.identity.oraclecloud.com"
61 ],
62 "sub_type": "instance",
63 "ipst_instance": "ocid1.instance.oc1.phx.<instance-id>",
64 "ipst_compartment": "ocid1.compartment.oc1..<compartment-id>",
65 "domain_id": "ocid1.domain.oc1..<domain-id>",
66 "ca_ocid": "ocid1.tenancy.oc1..<tenancy-id>",
67 "tenant": "idcs-example",
68 "exp": 1782369434,
69 "iat": 1782365834
70}
71```
72
73Use 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.
74
75## Set up workload identity federation
76
77Create 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.
78
79### Set up the Workload Identity Provider
80
811. **Create the Workload Identity Provider.** Set **Name** to a unique value, such as `oracle-cloud-prod`. Use **Description**, such as `Production OCI instance principal`, to identify the trusted workload.
82
832. **Set the issuer and audience.** Set **OIDC Issuer URL** to the token's `iss` claim, such as `https://identity.oraclecloud.com/`. Set **Audience** to one of the `aud` values in the same token.
84
853. **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 retrieves `https://idcs-example.identity.oraclecloud.com/.well-known/openid-configuration`, then uses the discovery document's `jwks_uri` to 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 from `https://<identity-domain>/admin/v1/SigningCert/jwk` instead.
86
874. **Add attribute transformations only when you need derived attributes.** You can use raw Oracle claims such as `ipst_instance`, `ipst_compartment`, `domain_id`, and `ca_ocid` directly in service account mapping assertions. For an explicitly derived instance attribute, enter `instance` with the expression `assertion.ipst_instance` to create `openai.instance`.
88
89Oracle's [OpenID Connect discovery reference](https://docs.oracle.com/en/cloud/paas/identity-cloud/idcsa/op-well-known-openid-configuration-get.html) 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**.
90
91If your identity domain publishes discovery metadata at the token issuer,
92 leave custom discovery disabled and use standard OIDC discovery. If OpenAI
93 cannot reach the tenant discovery document or signing-key endpoint, disable
94 custom discovery, enable **Use uploaded JWKS for token verification**, and
95 upload the tenant's public JWKS from
96 `https://<identity-domain>/admin/v1/SigningCert/jwk`. Custom discovery and
97 uploaded JWKS cannot be enabled at the same time. Update uploaded keys when
98 Oracle rotates its signing certificates.
99
100### Set up the service account mapping
101
1021. **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.
103
1042. **Match the narrowest stable OCI identity.** To grant access to one instance, set **Key** to `ipst_instance` and **Value** to the exact instance OCID from the verified token. To grant access to instances across one compartment, set **Key** to `ipst_compartment` and **Value** to the exact compartment OCID.
105
1063. **Add domain and tenancy boundaries when needed.** Add further mapping rows for `domain_id` or `ca_ocid` to limit the workload to a particular Oracle identity domain or tenancy. Add `sub_type` with the value `instance` when the token includes that claim and you want to require an instance principal. All mapping rows must match.
107
1084. **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.
109
1105. **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.
111
112An OKE workload that uses the standard instance principal signer inherits the
113 worker node's identity. An instance-level mapping authorizes that node, not
114 just one pod. Use a more specific, supported OCI workload identity when you
115 need isolation between pods sharing a worker node.
116
117## Use the token in code
118
119Install the OpenAI, OCI, and Requests Python packages:
120
121```bash
122pip install openai oci requests
123```
124
125Set `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.
126
127The 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:
128
129Authenticate with an OCI instance principal
130
131```python
132import os
133
134import oci
135import requests
136from openai import OpenAI
137from openai.auth import SubjectTokenProvider
138
139
140def oracle_instance_principal_token_provider(
141 identity_domain_url: str,
142) -> SubjectTokenProvider:
143 def get_token() -> str:
144 signer = oci.auth.signers.InstancePrincipalsSecurityTokenSigner()
145 response = requests.post(
146 f"{identity_domain_url.rstrip('/')}/oauth2/v1/token",
147 data={
148 "grant_type": "urn:ietf:params:oauth:grant-type:token-exchange",
149 "scope": "urn:opc:idm:__myscopes__",
150 "requested_token_type": "urn:ietf:params:oauth:token-type:access_token",
151 },
152 headers={
153 "Content-Type": "application/x-www-form-urlencoded;charset=utf-8",
154 },
155 auth=signer,
156 timeout=30,
157 )
158 response.raise_for_status()
159
160 token = response.json().get("access_token")
161 if not isinstance(token, str) or not token:
162 raise RuntimeError("Oracle IDCS did not return an access token.")
163
164 return token
165
166 return {"token_type": "jwt", "get_token": get_token}
167
168
169client = OpenAI(
170 workload_identity={
171 "identity_provider_id": os.environ["OPENAI_IDENTITY_PROVIDER_ID"],
172 "service_account_id": os.environ["OPENAI_SERVICE_ACCOUNT_ID"],
173 "provider": oracle_instance_principal_token_provider(
174 os.environ["OCI_IDENTITY_DOMAIN_URL"]
175 ),
176 },
177)
178
179response = client.responses.create(
180 model="gpt-5.6-terra",
181 input="Say hello from Oracle Cloud Infrastructure workload identity federation.",
182)
183
184print(response.output_text)
185```
186
187
188The 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.
189
190## OCI security recommendations
191
192- Map one instance with `ipst_instance` when only one workload should have access.
193- Use `ipst_compartment` only when every eligible instance in that compartment should share the mapping.
194- Add `domain_id` or `ca_ocid` to enforce identity domain and tenancy boundaries.
195- Use a separate OpenAI service account for each application and environment.
196- Verify whether an OKE token represents a worker node before relying on pod-level isolation.
197- Use the audience present in the issued Oracle token rather than assuming an OpenAI-specific audience.
198- Rotate uploaded public keys when Oracle rotates its signing keys if your identity domain cannot use OIDC discovery.