For the complete documentation index, see llms.txt. Markdown versions of documentation pages are available by appending .md to the page URL.
Primary navigation

Configuring workload identity federation for Idira

Use Idira Secure Workload Access (SWA) to call the OpenAI API without storing an OpenAI API key. SWA assigns your workload a SPIFFE ID and issues a JWT-SVID, a signed token that represents that identity. The OpenAI SDK exchanges the token for a short-lived OpenAI access token with the permissions of a mapped service account.

This guide covers the OpenAI API setup. For Codex, use the token delivery steps here, then follow Use workload identity with Codex.

Before you begin

You need:

  • An SWA deployment with a trust domain and an attestation policy that assigns the expected SPIFFE ID to your workload.
  • Access to the SWA Agent’s Workload API socket from the workload.
  • Permission to manage Workload Identity Providers, service account mappings, projects, and service accounts in your OpenAI organization.
  • An OpenAI SDK that supports workload identity credentials.

Complete the SWA infrastructure setup using the prerequisites in Idira’s OpenAI integration guide.

Get the issuer and signing keys

Retrieve your trust domain’s jwt.discovery_endpoints.oidc_discovery_url and jwt.discovery_endpoints.jwks_uri. Follow Idira’s OIDC configuration instructions to find these values.

The discovery document and JWKS must be reachable by OpenAI over HTTPS without credentials. The issuer is the HTTPS URL in the token’s iss claim. The SPIFFE ID in sub identifies your workload.

Choose a dedicated audience for OpenAI access, such as https://api.openai.com/v1. Use the same value when requesting the JWT-SVID and configuring the OpenAI provider. The audience identifies the token’s intended recipient; it doesn’t set the token exchange endpoint.

SWA defaults to RS512 signing with a 4096-bit RSA key. OpenAI’s JWT verifier accepts this combination. For other SWA signing options, see Idira’s JWT requirements.

Deliver a fresh JWT-SVID to the workload

Install spiffe-helper in the workload environment to request and refresh a JWT-SVID from the SWA Agent. Save the following configuration as helper.conf. The helper writes /var/run/spiffe/openai.jwt, the path used by the shared SPIFFE SDK examples:

agent_address = "/tmp/swa-agent/public/api.sock"
cert_dir      = "/var/run/spiffe"
daemon_mode   = true

jwt_svids = [{
  jwt_audience       = "https://api.openai.com/v1"
  jwt_svid_file_name = "openai.jwt"
}]

In Kubernetes, run the helper as a sidecar in the workload’s pod. Share a memory-backed emptyDir volume at /var/run/spiffe, and mount the SWA socket’s parent directory, /tmp/swa-agent/public, into the helper. On VMs, use /run/swa-agent/api.sock for agent_address and keep the token directory on memory-backed storage. Follow Idira’s deployment instructions for socket access and attestation.

Create the token directory before starting the helper, and give the helper write access. Start the helper with this configuration in the sidecar or VM service:

spiffe-helper -config helper.conf

Restrict access to the token file and agent socket to the processes that need them. Keep the helper running so it replaces the token before it expires.

If your language has a SPIFFE Workload API client, you can instead fetch a fresh JWT-SVID inside the subject token provider. See the direct Workload API option in Idira’s integration guide.

Inspect the token

From inside the workload, read the token into the local decoder’s environment:

export TOKEN="$(cat /var/run/spiffe/openai.jwt)"
const parts = process.env.TOKEN?.split(".") ?? [];
if (parts.length !== 3) {
  throw new Error("Expected a compact JWT with three segments");
}

const decode = (segment) => {
  if (!/^[A-Za-z0-9_-]+$/.test(segment) || segment.length % 4 === 1) {
    throw new Error("JWT segment is not valid Base64URL");
  }
  const bytes = Buffer.from(segment, "base64url");
  if (bytes.toString("base64url") !== segment) {
    throw new Error("JWT segment is not valid Base64URL");
  }
  const decoded = new TextDecoder("utf-8", { fatal: true }).decode(bytes);
  const value = JSON.parse(decoded);
  if (value === null || Array.isArray(value) || typeof value !== "object") {
    throw new Error("JWT segment is not a JSON object");
  }
  return decoded;
};

console.log("Header:");
console.log(decode(parts[0]));
console.log("\nPayload:");
console.log(decode(parts[1]));

Check these fields before creating the OpenAI provider:

Field Expected value
Header alg The configured signing algorithm, such as RS512.
Header kid The ID of a signing key in the trust domain’s public JWKS.
iss The trust domain’s HTTPS issuer URL.
sub The SPIFFE ID assigned to this workload.
aud Contains the audience you chose for OpenAI access.
iat and exp The token’s issue and expiry times. Use a token that hasn’t expired.

The decoder reads the claims without verifying the signature. Don’t log the token or paste it into a web-based decoder. Unset TOKEN after inspection.

Configure OpenAI workload identity federation

Create the Workload Identity Provider

Go to Organization Settings > Security > Workload Identity Provider and create a provider with these settings:

Setting Value
Name A unique name, such as idira-prod.
Description The application and environment that use this provider.
OIDC Issuer URL The JWT-SVID’s iss value.
Audience The audience requested by the helper, such as https://api.openai.com/v1.
Use uploaded JWKS for token verification Keep this option off to use OIDC discovery.
Attribute transformations Leave empty when mapping directly from sub.

For OpenAI API token exchange, issuer comparison ignores trailing slashes. For example, https://issuer.example.com and https://issuer.example.com/ match. All other characters must match, including the scheme and path. Audience values must match exactly. Copy the issuer as issued to keep your configuration consistent.

If OpenAI can’t reach the discovery document or JWKS, follow the shared provider configuration guidance for uploaded public JWKS. Update uploaded keys when SWA rotates its signing keys.

Map the SPIFFE ID to a service account

  1. Create a service account mapping in the provider and give it a name, such as idira-production-workload.
  2. Set Key to sub and Value to the exact SPIFFE ID from the workload’s token, such as spiffe://prod.example.com/k8s-nodegroup/ns/my-namespace/sa/my-service-account.
  3. Select the Project and Service account that the workload can use.
  4. Select the Permissions the workload needs, such as api.model.request for model requests. These permissions can narrow the service account’s access; they cannot grant more access. Leaving permissions blank adds no WIF-specific restriction.

Use a separate service account for each application and environment. Prefer an exact SPIFFE ID. Use a trailing wildcard only when every identity under that prefix should receive the same access. Avoid overlapping mappings. See SPIFFE service account mappings for details.

Use the token in code

Set the IDs from your OpenAI provider and selected service account in the workload’s environment:

export OPENAI_IDENTITY_PROVIDER_ID="<workload identity provider ID>"
export OPENAI_SERVICE_ACCOUNT_ID="<service account ID>"

The following shared SPIFFE examples read /var/run/spiffe/openai.jwt. The subject token provider returns the current token whenever the SDK needs to exchange or refresh an OpenAI access token. SWA and the helper refresh the external JWT-SVID; the OpenAI SDK manages the OpenAI access token.

Authenticate with an Idira-issued SPIFFE JWT-SVID
import { readFile } from "node:fs/promises";
import OpenAI from "openai";

const tokenPath = "/var/run/spiffe/openai.jwt";
const identityProviderId = process.env.OPENAI_IDENTITY_PROVIDER_ID;
const serviceAccountId = process.env.OPENAI_SERVICE_ACCOUNT_ID;

if (!identityProviderId || !serviceAccountId) {
  throw new Error(
    "Set OPENAI_IDENTITY_PROVIDER_ID and OPENAI_SERVICE_ACCOUNT_ID"
  );
}

function spiffeJwtSvidProvider(path) {
  return {
    tokenType: "jwt",
    getToken: async () => {
      const token = (await readFile(path, "utf8")).trim();
      if (!token) {
        throw new Error("The SPIFFE JWT-SVID file is empty.");
      }
      return token;
    },
  };
}

const client = new OpenAI({
  workloadIdentity: {
    identityProviderId,
    serviceAccountId,
    provider: spiffeJwtSvidProvider(tokenPath),
  },
});

const response = await client.responses.create({
  model: "gpt-5.6-terra",
  input: "Say hello from SPIFFE workload identity federation.",
});

console.log(response.output_text);

Verify and troubleshoot

Run the example inside the attested workload with OPENAI_API_KEY unset. A successful request prints the model’s reply. Confirm that requests continue to succeed after the helper replaces the JWT-SVID and the SDK refreshes its OpenAI access token.

Symptom What to check
The helper receives no identity Check that the workload matches the SWA attestation policy and can access the agent socket. Run checks in the workload’s context.
The token file is missing or empty, or its token has expired Check that the helper is running, the shared volume and file path match, and the workload can read the file. Keep host clocks synchronized.
Token verification fails Check iss, aud, kid, token expiry, and discovery/JWKS reachability. Apply the issuer comparison rule described earlier.
No service account mapping matches Check the provider ID, service account ID, enabled mapping, and exact sub value.
More than one mapping matches Narrow or disable overlapping mappings so exactly one enabled mapping matches.
Exchange succeeds but the API rejects the request Check the mapped service account’s project, permissions, and applicable API access policies.

For SWA deployment diagnostics, see Idira’s integration troubleshooting.