SPB Git forge

spb/doc-api

Public
2commits 1branches 0releases
15.7 MBsize
maindefault branch
13 days agolast push
Python 88.3% TypeScript 7.6% Shell 4.1%
9.8 KB

# OpenAI authentication, API keys & identity — atlas

Status: DOCUMENTED; bearer auth, org/project header semantics and the invalid-key / missing-auth / scope-error shapes are LIVE_VERIFIED (2026-09-18). Admin keys, service accounts, workload identity federation and mTLS are DOCUMENTED / ACCOUNT_RESTRICTED (not provisioned on our account). Sources: API overview → Authentication · Production best practices → API keys · Admin APIs guide · Workload identity federation guide + token exchange reference · Mutual TLS · IP allowlist · Supported countries · Error codes · OpenAPI schemas ProjectApiKey, AdminApiKey, ProjectServiceAccount, Invite, Role. Last verified: 2026-09-18.

# 1. Credential types

Credential Prefix / form Scope Created by Notes
Project API key (user-owned) sk-proj-… one project; inherits the owning user's project access (`owner_project_access: active inactive` — key stops working if the user leaves) user in dashboard
Project API key (service-account-owned) sk-proj-… one project, not tied to a human POST /v1/organization/projects/{p}/service_accounts (returns api_key.value once) or POST …/service_accounts/{id}/api_keys (expires_at optional) best for production workloads; service account role `owner
Legacy user API key sk-… the user across all their orgs/projects → needs OpenAI-Organization / OpenAI-Project headers to pick the target dashboard (legacy) usage untracked if created before 2023-12-20
Admin API key sk-admin-… (object: organization.admin_api_key, owner type: user) Administration endpoints only (/v1/organization/*, /v1/projects/*); refused elsewhere org owner at Settings → Organization → Admin keys or POST /v1/organization/admin_api_keys (name, optional expires_at) redacted_value: sk-admin...def; list/retrieve/delete via API
Workload identity access token opaque/JWT bearer, ≤ 1 h project + service account of the mapping; optional narrowing scope (e.g. api.model.read api.model.request) POST https://auth.openai.com/oauth/token (RFC 8693 token exchange) no refresh token; cannot call Admin endpoints, DELETE /v1/models/{id}, POST /v1/images/request_audit
X.509 workload identity client certificate → bearer same as above POST https://mtls.auth.openai.com/oauth/token over mTLS, then API calls to mtls.api.openai.com with the certificate bearer not cert-bound (no DPoP/cnf)
Realtime ephemeral client secret ek_… one Realtime session, short TTL POST /v1/realtime/client_secrets for browsers (WebRTC / WS subprotocol openai-insecure-api-key.<ek>)
Webhook signing secret whsec_… one webhook endpoint endpoint creation / rotate_secret not an API credential; see docs/openai/webhooks.md

# 2. Sending credentials

http
Authorization: Bearer <key or access token>
OpenAI-Organization: org-…      # optional; only for multi-org users / legacy user keys
OpenAI-Project: proj_…          # optional; only for legacy user keys

Observed with a project key (2026-09-18): a mismatching OpenAI-Organization → 401 mismatched_organization ("OpenAI-Organization header should match organization for API key"); an unknown OpenAI-Project → 401 invalid_project ("No such project"). Bogus key → 401 invalid_api_key (type invalid_request_error); empty header → 401 "Missing bearer authentication in header" (code null). Revocation propagates within seconds; other auth changes ≤ 15 min.

Base URLs: https://api.openai.com/v1 (global), regional https://{us,eu,au,ca,jp,in,sg,kr,gb,ae}.api.openai.com/v1 (see data-residency-and-regions.md), https://mtls.api.openai.com/v1 (mTLS), wss://api.openai.com/v1/realtime (Realtime WS), https://auth.openai.com/oauth/token and https://mtls.auth.openai.com/oauth/token (token exchange).

SDK env vars: OPENAI_API_KEY, OPENAI_ADMIN_KEY, OPENAI_ORG_ID, OPENAI_PROJECT_ID, OPENAI_BASE_URL, OPENAI_WEBHOOK_SECRET, OPENAI_LOG.

# 3. Organization / project model & roles

Level Roles Powers
Organization owner — all reader rights + billing + members; reader — make API requests, view org info, create/update/delete resources Invite.role, OrganizationUser.role
Project owner, member ProjectUser.role, ProjectServiceAccount.role
Custom roles `Role{permissions[], resource_type: api.organization api.project, predefined_role}; bound to users or groups (/organization/{users,groups}/{id}/roles, /projects/{p}/{users,groups}/{id}/roles`)
Groups organization.group with members; can hold org or project roles; SCIM can provision (scim.enabled audit event) /v1/organization/groups

Scopes (identifiers seen in docs/spec/error messages): admin-side api.management.read (+ write implied), api.usage.read, api.audit_logs.read, api.roles.read, api.groups.read/write, api.mtls.read/write, api.external_storage.read; API-side api.model.request, api.model.read, api.responses.write, api.files.read/write, api.batch.read/write, api.vector_store.read, api.videos.read/write, api.voices.read/write, api.agents.read/write, api.vaults.read/write, api.webhooks.read, api.traces.read, api.safety.read, api.safety.alerts.read, api.apps.read/write. A missing scope yields 403 ({"error": "<string>"} or code: insufficient_permissions) — audit logs yield 401.

# 4. Governance controls (documented)

Control Where Effect
Key expiry per key expires_at; org/project max key lifetime (project ≤ org) new keys must expire within the limit
API Key Governance org/project settings allow only service-account keys / only user keys / block new key creation; org restrictions win
IP allowlist Settings → Organization → Security → IP allowlist outside IP → 401 ip_not_authorized; audit events ip_allowlist.*
Model permissions POST /v1/organization/projects/{p}/model_permissions (allow_list / deny_list) denied model → 404 model_not_found
Hosted tool permissions …/hosted_tool_permissions restrict web search / code interpreter / MCP etc.
Spend limits & alerts org/project spend_limit, spend_alerts 429 organization_spend_limit_exceeded / project_spend_limit_exceeded
Mutual TLS certificates API + mtls.api.openai.com host; CEL filters on subject fields enforce client certs per org/project
Supported countries policy page (≈ 190 countries/territories; e.g. no China, Russia, Iran, North Korea, Hong Kong, Belarus, Venezuela) 403 unsupported_country_region_territory; Ukraine "with certain exceptions"
Data residency / retention project region + retention policy see data-residency-and-regions.md

# 5. Workload identity federation (WIF) — quick reference

  1. Org owner creates a Workload Identity Provider (OIDC issuer + audience + JWKS, or X.509 using active mTLS roots) — max 50 providers/org, 50 mappings/provider. Supported issuers documented: AWS, GCP, Azure/Entra, GitHub Actions, Kubernetes, Oracle Cloud, SPIFFE (JWT-SVID only), X.509; arbitrary OIDC issuers not yet supported.
  2. Add a service account mapping (attribute conditions on raw claims like sub, repository, or derived openai.* attributes via CEL; optional Permissions = scopes). Exactly one enabled mapping must match.
  3. Workload exchanges its token:
bash
curl https://auth.openai.com/oauth/token -H "Content-Type: application/json" -d '{
  "grant_type": "urn:ietf:params:oauth:grant-type:token-exchange",
  "subject_token_type": "urn:ietf:params:oauth:token-type:jwt",     # or …:id_token ; X.509: urn:openai:params:oauth:token-type:x509 (no subject_token)
  "subject_token": "'"$EXTERNAL_JWT"'",
  "identity_provider_id": "'"$IDP_ID"'", "service_account_id": "'"$SA_ID"'" }'
# → {"access_token":"…","issued_token_type":"urn:ietf:params:oauth:token-type:access_token","token_type":"Bearer","expires_in":3600,"expires_at":1789045200,"scope":"api.model.read api.model.request"}
  1. Use Authorization: Bearer <access_token>; renew before expires_at (token never outlives the subject token / certificate; lifetime ≤ 1 h; no refresh token). Errors: invalid_subject_token (JWT/cert verification), invalid_grant (attribute conditions, mapping, provider), missing/unsupported parameters; scope in the request grants nothing. Audit events: workload_identity_provider.*, workload_identity_provider_mapping.*, tenant.workload_identity.*. SDKs: Python OpenAI(workload_identity=…), Node new OpenAI({ workloadIdentity }) (openai/auth/subject-token-providers), both mutually exclusive with apiKey.

# 6. Key hygiene (from the docs + this atlas' rules)

Load keys from env/secret managers, never client-side; rotate on a schedule (create → switch → verify → revoke); log x-request-id; prefer service-account keys with expiry for servers; use restricted keys (scopes) for narrow tasks; monitor with GET /v1/organization/usage/* + audit logs (api_key.created/updated/deleted, login.failed).