Skip to content

Commit d122be8

Browse files
micheleRPclaude
andauthored
DOC-2034: Document OIDC authentication for AI Gateway agent connections (#516)
* DOC-2034: Document OIDC authentication for AI Gateway agent connections Add OIDC (OAuth 2.0 client_credentials grant) as a production authentication option alongside the existing API key quick-start. Includes service account setup, OIDC client configuration with tabbed examples (cURL, Python, Node.js), token lifecycle guidance, rp-aigw-id header in cURL examples, and troubleshooting updates. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fixes * DOC-2034: Make OIDC the only auth method for AI Gateway Remove the Quickstart (API key) section and make OIDC the sole authentication path, since API keys are not supported. Move SDK examples into the OIDC flow and update all references accordingly. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * DOC-2034: Fix remaining API token references to say OIDC token Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * DOC-2034: Address PR review comments for OIDC auth documentation - Reword OIDC intro to clarify service account credential flow - Add discovery URL explanation after OIDC config table - Update Python authlib example to use metadata discovery - Correct authlib token caching note to describe actual behavior - Clarify that AI Gateway does not auto-renew tokens - Update token lifecycle authlib bullet to match new guidance Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * incorporate doc review feedback --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent e5d47a4 commit d122be8

File tree

1 file changed

+146
-27
lines changed

1 file changed

+146
-27
lines changed

modules/ai-agents/pages/ai-gateway/builders/connect-your-agent.adoc

Lines changed: 146 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -22,30 +22,140 @@ After completing this guide, you will be able to:
2222
+
2323
If not, see xref:ai-gateway/builders/discover-gateways.adoc[].
2424

25-
* You have a Redpanda Cloud API token with access to the gateway.
25+
* You have a service account with OIDC client credentials. See xref:security:cloud-authentication.adoc[].
2626
* You have a development environment with your chosen programming language.
2727

2828
== Integration overview
2929

3030
Connecting to AI Gateway requires two configuration changes:
3131

3232
. *Change the base URL*: Point to the gateway endpoint instead of the provider's API. The gateway ID is embedded in the endpoint URL.
33-
. *Add authentication*: Use your Redpanda Cloud token instead of provider API keys
33+
. *Add authentication*: Use an OIDC access token from your service account instead of provider API keys.
3434

35-
== Quickstart
35+
[[authenticate-with-oidc]]
36+
== Authenticate with OIDC
3637

37-
=== Environment variables
38+
AI Gateway uses OIDC through service accounts that can be used as a `client_credentials` grant to authenticate and exchange for access and ID tokens.
39+
40+
=== Create a service account
41+
42+
. In the Redpanda Cloud UI, go to https://cloud.redpanda.com/organization-iam?tab=service-accounts[*Organization IAM* > *Service account*^].
43+
. Create a new service account and note the *Client ID* and *Client Secret*.
44+
45+
For details, see xref:security:cloud-authentication.adoc#authenticate-to-the-cloud-api[Authenticate to the Cloud API].
46+
47+
=== Configure your OIDC client
48+
49+
Use the following OIDC configuration:
50+
51+
[cols="1,2", options="header"]
52+
|===
53+
|Parameter |Value
54+
55+
|Discovery URL
56+
|`\https://auth.prd.cloud.redpanda.com/.well-known/openid-configuration`
57+
58+
|Token endpoint
59+
|`\https://auth.prd.cloud.redpanda.com/oauth/token`
60+
61+
|Audience
62+
|`cloudv2-production.redpanda.cloud`
63+
64+
|Grant type
65+
|`client_credentials`
66+
|===
67+
68+
The discovery URL returns OIDC metadata, including the token endpoint and other configuration details. Use an OIDC client library that supports metadata discovery (such as `openid-client` for Node.js) so that endpoints are resolved automatically. If your library does not support discovery, you can fetch the discovery URL directly and extract the required endpoints from the JSON response.
69+
70+
[tabs]
71+
====
72+
cURL::
73+
+
74+
--
75+
[source,bash]
76+
----
77+
AUTH_TOKEN=$(curl -s --request POST \
78+
--url 'https://auth.prd.cloud.redpanda.com/oauth/token' \
79+
--header 'content-type: application/x-www-form-urlencoded' \
80+
--data grant_type=client_credentials \
81+
--data client_id=<client-id> \
82+
--data client_secret=<client-secret> \
83+
--data audience=cloudv2-production.redpanda.cloud | jq -r .access_token)
84+
----
85+
86+
Replace `<client-id>` and `<client-secret>` with your service account credentials.
87+
--
88+
89+
Python (authlib)::
90+
+
91+
--
92+
[source,python]
93+
----
94+
from authlib.integrations.requests_client import OAuth2Session
95+
96+
client = OAuth2Session(
97+
client_id="<client-id>",
98+
client_secret="<client-secret>",
99+
)
100+
101+
# Discover token endpoint from OIDC metadata
102+
import requests
103+
metadata = requests.get(
104+
"https://auth.prd.cloud.redpanda.com/.well-known/openid-configuration"
105+
).json()
106+
token_endpoint = metadata["token_endpoint"]
107+
108+
token = client.fetch_token(
109+
token_endpoint,
110+
grant_type="client_credentials",
111+
audience="cloudv2-production.redpanda.cloud",
112+
)
113+
114+
access_token = token["access_token"]
115+
----
116+
117+
This example performs a one-time token fetch. For automatic token renewal on subsequent requests, pass `token_endpoint` to the `OAuth2Session` constructor. Note that for `client_credentials` grants, `authlib` obtains a new token rather than using a refresh token.
118+
--
119+
120+
Node.js (openid-client)::
121+
+
122+
[source,javascript]
123+
----
124+
import { Issuer } from 'openid-client';
125+
126+
const issuer = await Issuer.discover(
127+
'https://auth.prd.cloud.redpanda.com'
128+
);
129+
130+
const client = new issuer.Client({
131+
client_id: '<client-id>',
132+
client_secret: '<client-secret>',
133+
});
134+
135+
const tokenSet = await client.grant({
136+
grant_type: 'client_credentials',
137+
audience: 'cloudv2-production.redpanda.cloud',
138+
});
139+
140+
const accessToken = tokenSet.access_token;
141+
----
142+
====
143+
144+
=== Make authenticated requests
145+
146+
Requests require two headers:
147+
148+
* `Authorization: Bearer <token>` - your OIDC access token
149+
* `rp-aigw-id: <gateway-id>` - your AI Gateway ID
38150

39151
Set these environment variables for consistent configuration:
40152

41153
[source,bash]
42154
----
43155
export REDPANDA_GATEWAY_URL="<your-gateway-endpoint>"
44-
export REDPANDA_API_KEY="your-redpanda-cloud-token"
156+
export REDPANDA_GATEWAY_ID="<your-gateway-id>"
45157
----
46158

47-
Replace with your actual gateway endpoint and API token.
48-
49159
[tabs]
50160
====
51161
Python (OpenAI SDK)::
@@ -55,13 +165,13 @@ Python (OpenAI SDK)::
55165
import os
56166
from openai import OpenAI
57167
58-
# Configure client to use AI Gateway
168+
# Configure client to use AI Gateway with OIDC token
59169
client = OpenAI(
60170
base_url=os.getenv("REDPANDA_GATEWAY_URL"),
61-
api_key=os.getenv("REDPANDA_API_KEY"),
171+
api_key=access_token, # OIDC access token from Step 2
62172
)
63173
64-
# Make a request (same as before)
174+
# Make a request
65175
response = client.chat.completions.create(
66176
model="openai/gpt-5.2-mini", # Note: vendor/model_id format
67177
messages=[{"role": "user", "content": "Hello, AI Gateway!"}],
@@ -82,7 +192,7 @@ from anthropic import Anthropic
82192
83193
client = Anthropic(
84194
base_url=os.getenv("REDPANDA_GATEWAY_URL"),
85-
api_key=os.getenv("REDPANDA_API_KEY"),
195+
api_key=access_token, # OIDC access token from Step 2
86196
)
87197
88198
# Make a request
@@ -103,7 +213,7 @@ import OpenAI from 'openai';
103213
104214
const openai = new OpenAI({
105215
baseURL: process.env.REDPANDA_GATEWAY_URL,
106-
apiKey: process.env.REDPANDA_API_KEY,
216+
apiKey: accessToken, // OIDC access token from Step 2
107217
});
108218
109219
// Make a request
@@ -118,13 +228,12 @@ console.log(response.choices[0].message.content);
118228
119229
cURL::
120230
+
121-
For testing or shell scripts:
122-
+
123231
[source,bash]
124232
----
125233
curl ${REDPANDA_GATEWAY_URL}/chat/completions \
126-
-H "Authorization: Bearer ${REDPANDA_API_KEY}" \
234+
-H "Authorization: Bearer ${AUTH_TOKEN}" \
127235
-H "Content-Type: application/json" \
236+
-H "rp-aigw-id: ${REDPANDA_GATEWAY_ID}" \
128237
-d '{
129238
"model": "openai/gpt-5.2-mini",
130239
"messages": [{"role": "user", "content": "Hello, AI Gateway!"}],
@@ -133,6 +242,14 @@ curl ${REDPANDA_GATEWAY_URL}/chat/completions \
133242
----
134243
====
135244

245+
=== Token lifecycle management
246+
247+
IMPORTANT: Your agent is responsible for refreshing tokens before they expire. OIDC access tokens have a limited time-to-live (TTL), determined by the identity provider, and are not automatically renewed by the AI Gateway. Check the `expires_in` field in the token response for the exact duration.
248+
249+
* Proactively refresh tokens at approximately 80% of the token's TTL to avoid failed requests.
250+
* `authlib` (Python) can handle token renewal automatically when you pass `token_endpoint` to the `OAuth2Session` constructor. For `client_credentials` grants, it obtains a new token rather than using a refresh token.
251+
* For other languages, cache the token and its expiry time, then request a new token before the current one expires.
252+
136253
== Model naming convention
137254

138255
When making requests through AI Gateway, use the `vendor/model_id` format for the model parameter:
@@ -196,7 +313,7 @@ from openai import OpenAI, OpenAIError
196313
197314
client = OpenAI(
198315
base_url=os.getenv("REDPANDA_GATEWAY_URL"),
199-
api_key=os.getenv("REDPANDA_API_KEY"),
316+
api_key=access_token, # OIDC access token
200317
)
201318
202319
try:
@@ -210,7 +327,7 @@ except OpenAIError as e:
210327
if e.status_code == 400:
211328
print("Bad request - check model name and parameters")
212329
elif e.status_code == 401:
213-
print("Authentication failed - check API token")
330+
print("Authentication failed - check OIDC token")
214331
elif e.status_code == 404:
215332
print("Model not found - check available models")
216333
elif e.status_code == 429:
@@ -224,7 +341,7 @@ except OpenAIError as e:
224341
Common error codes:
225342

226343
* *400*: Bad request (invalid parameters, malformed JSON)
227-
* *401*: Authentication failed (invalid or missing API token)
344+
* *401*: Authentication failed (invalid or expired OIDC token)
228345
* *403*: Forbidden (no access to this gateway)
229346
* *404*: Model not found (model not enabled in gateway)
230347
* *429*: Rate limit exceeded (too many requests)
@@ -280,11 +397,11 @@ Compare responses, latency, and cost to determine the best model for your use ca
280397
import os
281398
from openai import OpenAI
282399
283-
def test_gateway_connection():
400+
def test_gateway_connection(access_token):
284401
"""Test basic connectivity to AI Gateway"""
285402
client = OpenAI(
286403
base_url=os.getenv("REDPANDA_GATEWAY_URL"),
287-
api_key=os.getenv("REDPANDA_API_KEY"),
404+
api_key=access_token, # OIDC access token
288405
)
289406
290407
try:
@@ -301,7 +418,8 @@ def test_gateway_connection():
301418
return False
302419
303420
if __name__ == "__main__":
304-
test_gateway_connection()
421+
token = get_oidc_token() # Your OIDC token retrieval
422+
test_gateway_connection(token)
305423
----
306424

307425
=== Test multiple models
@@ -348,7 +466,7 @@ Configure Claude Code to use AI Gateway:
348466
[source,bash]
349467
----
350468
claude mcp add --transport http redpanda-aigateway ${REDPANDA_GATEWAY_URL}/mcp \
351-
--header "Authorization: Bearer ${REDPANDA_API_KEY}"
469+
--header "Authorization: Bearer ${AUTH_TOKEN}"
352470
----
353471
+
354472
Or edit `~/.claude/config.json`:
@@ -361,7 +479,7 @@ Or edit `~/.claude/config.json`:
361479
"transport": "http",
362480
"url": "<your-gateway-endpoint>/mcp",
363481
"headers": {
364-
"Authorization": "Bearer your-api-key"
482+
"Authorization": "Bearer <oidc-access-token>"
365483
}
366484
}
367485
}
@@ -385,7 +503,7 @@ Edit `~/.continue/config.json`:
385503
"provider": "openai",
386504
"model": "openai/gpt-5.2",
387505
"apiBase": "<your-gateway-endpoint>",
388-
"apiKey": "your-redpanda-api-key"
506+
"apiKey": "<oidc-access-token>"
389507
}
390508
]
391509
}
@@ -425,7 +543,7 @@ Store configuration in environment variables, not hardcoded in code:
425543
base_url = os.getenv("REDPANDA_GATEWAY_URL")
426544
427545
# Bad
428-
base_url = "https://gw.ai.panda.com" # Don't hardcode
546+
base_url = "https://gw.ai.panda.com" # Don't hardcode URLs or credentials
429547
----
430548

431549
=== Implement retry logic
@@ -498,8 +616,9 @@ Problem: 401 Unauthorized
498616

499617
Solutions:
500618

501-
* Verify your API token is correct and not expired
502-
* Check that the token has access to the specified gateway
619+
* Check that your OIDC token has not expired and refresh it if necessary
620+
* Verify the audience is set to `cloudv2-production.redpanda.cloud`
621+
* Check that the service account has access to the specified gateway
503622
* Ensure the `Authorization` header is formatted correctly: `Bearer <token>`
504623

505624
=== "Model not found"

0 commit comments

Comments
 (0)