From 7630745b9179af6d1c5950c7e0ccd79a6a750d9a Mon Sep 17 00:00:00 2001 From: micheleRP Date: Tue, 10 Mar 2026 13:04:40 -0600 Subject: [PATCH 1/5] 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 --- .../builders/connect-your-agent.adoc | 136 +++++++++++++++++- 1 file changed, 133 insertions(+), 3 deletions(-) diff --git a/modules/ai-agents/pages/ai-gateway/builders/connect-your-agent.adoc b/modules/ai-agents/pages/ai-gateway/builders/connect-your-agent.adoc index 635ac8a6..59cc8fcd 100644 --- a/modules/ai-agents/pages/ai-gateway/builders/connect-your-agent.adoc +++ b/modules/ai-agents/pages/ai-gateway/builders/connect-your-agent.adoc @@ -22,7 +22,9 @@ After completing this guide, you will be able to: + If not, see xref:ai-gateway/builders/discover-gateways.adoc[]. -* You have a Redpanda Cloud API token with access to the gateway. +* You have one of the following authentication credentials: +** *Quick start*: A Redpanda Cloud API token with access to the gateway. +** *Production*: A service account with OIDC client credentials. See xref:security:cloud-authentication.adoc[]. * You have a development environment with your chosen programming language. == Integration overview @@ -30,7 +32,7 @@ If not, see xref:ai-gateway/builders/discover-gateways.adoc[]. Connecting to AI Gateway requires two configuration changes: . *Change the base URL*: Point to the gateway endpoint instead of the provider's API. The gateway ID is embedded in the endpoint URL. -. *Add authentication*: Use your Redpanda Cloud token instead of provider API keys +. *Add authentication*: Use your Redpanda Cloud API token (quick start) or an OIDC service account token (production) instead of provider API keys. For production workloads, <>. == Quickstart @@ -42,9 +44,10 @@ Set these environment variables for consistent configuration: ---- export REDPANDA_GATEWAY_URL="" export REDPANDA_API_KEY="your-redpanda-cloud-token" +export REDPANDA_GATEWAY_ID="" ---- -Replace with your actual gateway endpoint and API token. +Replace with your actual gateway endpoint, API token, and gateway ID. [tabs] ==== @@ -125,6 +128,7 @@ For testing or shell scripts: curl ${REDPANDA_GATEWAY_URL}/chat/completions \ -H "Authorization: Bearer ${REDPANDA_API_KEY}" \ -H "Content-Type: application/json" \ + -H "rp-aigw-id: ${REDPANDA_GATEWAY_ID}" \ -d '{ "model": "openai/gpt-5.2-mini", "messages": [{"role": "user", "content": "Hello, AI Gateway!"}], @@ -133,6 +137,130 @@ curl ${REDPANDA_GATEWAY_URL}/chat/completions \ ---- ==== +[[authenticate-with-oidc]] +== Authenticate with OIDC + +For production workloads, use OIDC (OAuth 2.0 `client_credentials` grant) with a service account instead of a static API key. + +=== Step 1: Create a service account + +. In the Redpanda Cloud UI, go to https://cloud.redpanda.com/organization-iam?tab=service-accounts[*Organization IAM* > *Service account*^]. +. Create a new service account and note the *Client ID* and *Client Secret*. + +For details, see xref:security:cloud-authentication.adoc#_authenticate_to_the_cloud_api[Authenticate to the Cloud API]. + +=== Step 2: Configure your OIDC client + +Use the following OIDC configuration: + +[cols="1,2", options="header"] +|=== +|Parameter |Value + +|Discovery URL +|`\https://auth.prd.cloud.redpanda.com/.well-known/openid-configuration` + +|Token endpoint +|`\https://auth.prd.cloud.redpanda.com/oauth/token` + +|Audience +|`cloudv2-production.redpanda.cloud` + +|Grant type +|`client_credentials` +|=== + +[tabs] +==== +cURL:: ++ +[source,bash] +---- +AUTH_TOKEN=$(curl -s --request POST \ + --url 'https://auth.prd.cloud.redpanda.com/oauth/token' \ + --header 'content-type: application/x-www-form-urlencoded' \ + --data grant_type=client_credentials \ + --data client_id= \ + --data client_secret= \ + --data audience=cloudv2-production.redpanda.cloud | jq -r .access_token) +---- + +Replace `` and `` with your service account credentials. + +Python (authlib):: ++ +[source,python] +---- +from authlib.integrations.requests_client import OAuth2Session + +client = OAuth2Session( + client_id="", + client_secret="", +) + +token = client.fetch_token( + "https://auth.prd.cloud.redpanda.com/oauth/token", + grant_type="client_credentials", + audience="cloudv2-production.redpanda.cloud", +) + +access_token = token["access_token"] +---- + +`authlib` automatically handles token caching and refresh. + +Node.js (openid-client):: ++ +[source,javascript] +---- +import { Issuer } from 'openid-client'; + +const issuer = await Issuer.discover( + 'https://auth.prd.cloud.redpanda.com' +); + +const client = new issuer.Client({ + client_id: '', + client_secret: '', +}); + +const tokenSet = await client.grant({ + grant_type: 'client_credentials', + audience: 'cloudv2-production.redpanda.cloud', +}); + +const accessToken = tokenSet.access_token; +---- +==== + +=== Step 3: Make authenticated requests + +Requests require two headers: + +* `Authorization: Bearer ` - your OIDC access token +* `rp-aigw-id: ` - your AI Gateway ID + +[source,bash] +---- +curl ${REDPANDA_GATEWAY_URL}/chat/completions \ + -H "Authorization: Bearer ${AUTH_TOKEN}" \ + -H "Content-Type: application/json" \ + -H "rp-aigw-id: ${REDPANDA_GATEWAY_ID}" \ + -d '{ + "model": "openai/gpt-5.2-mini", + "messages": [{"role": "user", "content": "Hello, AI Gateway!"}], + "max_tokens": 100 + }' +---- + +=== Token lifecycle management + +IMPORTANT: Your agent is responsible for refreshing tokens before they expire. OIDC tokens have a limited TTL and are not automatically renewed. + +* Proactively refresh tokens at approximately 80% of the token's TTL to avoid failed requests. +* `authlib` (Python) handles token refresh automatically when you use `OAuth2Session`. +* For other languages, cache the token and its expiry time, then request a new token before the current one expires. + == Model naming convention When making requests through AI Gateway, use the `vendor/model_id` format for the model parameter: @@ -499,6 +627,8 @@ Problem: 401 Unauthorized Solutions: * Verify your API token is correct and not expired +* If using OIDC, check that your token has not expired and refresh it if necessary +* If using OIDC, verify the audience is set to `cloudv2-production.redpanda.cloud` * Check that the token has access to the specified gateway * Ensure the `Authorization` header is formatted correctly: `Bearer ` From 2292aaa98d06ad39db59bc3fbdaa59de732eea09 Mon Sep 17 00:00:00 2001 From: micheleRP Date: Tue, 10 Mar 2026 13:25:37 -0600 Subject: [PATCH 2/5] fixes --- .../pages/ai-gateway/builders/connect-your-agent.adoc | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/modules/ai-agents/pages/ai-gateway/builders/connect-your-agent.adoc b/modules/ai-agents/pages/ai-gateway/builders/connect-your-agent.adoc index 59cc8fcd..d7906e2d 100644 --- a/modules/ai-agents/pages/ai-gateway/builders/connect-your-agent.adoc +++ b/modules/ai-agents/pages/ai-gateway/builders/connect-your-agent.adoc @@ -147,7 +147,7 @@ For production workloads, use OIDC (OAuth 2.0 `client_credentials` grant) with a . In the Redpanda Cloud UI, go to https://cloud.redpanda.com/organization-iam?tab=service-accounts[*Organization IAM* > *Service account*^]. . Create a new service account and note the *Client ID* and *Client Secret*. -For details, see xref:security:cloud-authentication.adoc#_authenticate_to_the_cloud_api[Authenticate to the Cloud API]. +For details, see xref:security:cloud-authentication.adoc#authenticate-to-the-cloud-api[Authenticate to the Cloud API]. === Step 2: Configure your OIDC client @@ -174,6 +174,7 @@ Use the following OIDC configuration: ==== cURL:: + +-- [source,bash] ---- AUTH_TOKEN=$(curl -s --request POST \ @@ -186,9 +187,11 @@ AUTH_TOKEN=$(curl -s --request POST \ ---- Replace `` and `` with your service account credentials. +-- Python (authlib):: + +-- [source,python] ---- from authlib.integrations.requests_client import OAuth2Session @@ -208,6 +211,7 @@ access_token = token["access_token"] ---- `authlib` automatically handles token caching and refresh. +-- Node.js (openid-client):: + From c78692d5f6d6ae41afa100cbd90f05a829718837 Mon Sep 17 00:00:00 2001 From: micheleRP Date: Tue, 10 Mar 2026 15:03:55 -0600 Subject: [PATCH 3/5] 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 --- .../builders/connect-your-agent.adoc | 216 ++++++++---------- 1 file changed, 96 insertions(+), 120 deletions(-) diff --git a/modules/ai-agents/pages/ai-gateway/builders/connect-your-agent.adoc b/modules/ai-agents/pages/ai-gateway/builders/connect-your-agent.adoc index d7906e2d..fd70c7b0 100644 --- a/modules/ai-agents/pages/ai-gateway/builders/connect-your-agent.adoc +++ b/modules/ai-agents/pages/ai-gateway/builders/connect-your-agent.adoc @@ -22,9 +22,7 @@ After completing this guide, you will be able to: + If not, see xref:ai-gateway/builders/discover-gateways.adoc[]. -* You have one of the following authentication credentials: -** *Quick start*: A Redpanda Cloud API token with access to the gateway. -** *Production*: A service account with OIDC client credentials. See xref:security:cloud-authentication.adoc[]. +* You have a service account with OIDC client credentials. See xref:security:cloud-authentication.adoc[]. * You have a development environment with your chosen programming language. == Integration overview @@ -32,115 +30,12 @@ If not, see xref:ai-gateway/builders/discover-gateways.adoc[]. Connecting to AI Gateway requires two configuration changes: . *Change the base URL*: Point to the gateway endpoint instead of the provider's API. The gateway ID is embedded in the endpoint URL. -. *Add authentication*: Use your Redpanda Cloud API token (quick start) or an OIDC service account token (production) instead of provider API keys. For production workloads, <>. - -== Quickstart - -=== Environment variables - -Set these environment variables for consistent configuration: - -[source,bash] ----- -export REDPANDA_GATEWAY_URL="" -export REDPANDA_API_KEY="your-redpanda-cloud-token" -export REDPANDA_GATEWAY_ID="" ----- - -Replace with your actual gateway endpoint, API token, and gateway ID. - -[tabs] -==== -Python (OpenAI SDK):: -+ -[source,python] ----- -import os -from openai import OpenAI - -# Configure client to use AI Gateway -client = OpenAI( - base_url=os.getenv("REDPANDA_GATEWAY_URL"), - api_key=os.getenv("REDPANDA_API_KEY"), -) - -# Make a request (same as before) -response = client.chat.completions.create( - model="openai/gpt-5.2-mini", # Note: vendor/model_id format - messages=[{"role": "user", "content": "Hello, AI Gateway!"}], - max_tokens=100 -) - -print(response.choices[0].message.content) ----- - -Python (Anthropic SDK):: -+ -The Anthropic SDK can also route through AI Gateway using the OpenAI-compatible endpoint: -+ -[source,python] ----- -import os -from anthropic import Anthropic - -client = Anthropic( - base_url=os.getenv("REDPANDA_GATEWAY_URL"), - api_key=os.getenv("REDPANDA_API_KEY"), -) - -# Make a request -message = client.messages.create( - model="anthropic/claude-sonnet-4.5", - max_tokens=100, - messages=[{"role": "user", "content": "Hello, AI Gateway!"}] -) - -print(message.content[0].text) ----- - -Node.js (OpenAI SDK):: -+ -[source,javascript] ----- -import OpenAI from 'openai'; - -const openai = new OpenAI({ - baseURL: process.env.REDPANDA_GATEWAY_URL, - apiKey: process.env.REDPANDA_API_KEY, -}); - -// Make a request -const response = await openai.chat.completions.create({ - model: 'openai/gpt-5.2-mini', - messages: [{ role: 'user', content: 'Hello, AI Gateway!' }], - max_tokens: 100 -}); - -console.log(response.choices[0].message.content); ----- - -cURL:: -+ -For testing or shell scripts: -+ -[source,bash] ----- -curl ${REDPANDA_GATEWAY_URL}/chat/completions \ - -H "Authorization: Bearer ${REDPANDA_API_KEY}" \ - -H "Content-Type: application/json" \ - -H "rp-aigw-id: ${REDPANDA_GATEWAY_ID}" \ - -d '{ - "model": "openai/gpt-5.2-mini", - "messages": [{"role": "user", "content": "Hello, AI Gateway!"}], - "max_tokens": 100 - }' ----- -==== +. *Add authentication*: Use an OIDC access token from your service account instead of provider API keys. [[authenticate-with-oidc]] == Authenticate with OIDC -For production workloads, use OIDC (OAuth 2.0 `client_credentials` grant) with a service account instead of a static API key. +AI Gateway uses OIDC (OAuth 2.0 `client_credentials` grant) with a service account for authentication. === Step 1: Create a service account @@ -244,6 +139,86 @@ Requests require two headers: * `Authorization: Bearer ` - your OIDC access token * `rp-aigw-id: ` - your AI Gateway ID +Set these environment variables for consistent configuration: + +[source,bash] +---- +export REDPANDA_GATEWAY_URL="" +export REDPANDA_GATEWAY_ID="" +---- + +[tabs] +==== +Python (OpenAI SDK):: ++ +[source,python] +---- +import os +from openai import OpenAI + +# Configure client to use AI Gateway with OIDC token +client = OpenAI( + base_url=os.getenv("REDPANDA_GATEWAY_URL"), + api_key=access_token, # OIDC access token from Step 2 +) + +# Make a request +response = client.chat.completions.create( + model="openai/gpt-5.2-mini", # Note: vendor/model_id format + messages=[{"role": "user", "content": "Hello, AI Gateway!"}], + max_tokens=100 +) + +print(response.choices[0].message.content) +---- + +Python (Anthropic SDK):: ++ +The Anthropic SDK can also route through AI Gateway using the OpenAI-compatible endpoint: ++ +[source,python] +---- +import os +from anthropic import Anthropic + +client = Anthropic( + base_url=os.getenv("REDPANDA_GATEWAY_URL"), + api_key=access_token, # OIDC access token from Step 2 +) + +# Make a request +message = client.messages.create( + model="anthropic/claude-sonnet-4.5", + max_tokens=100, + messages=[{"role": "user", "content": "Hello, AI Gateway!"}] +) + +print(message.content[0].text) +---- + +Node.js (OpenAI SDK):: ++ +[source,javascript] +---- +import OpenAI from 'openai'; + +const openai = new OpenAI({ + baseURL: process.env.REDPANDA_GATEWAY_URL, + apiKey: accessToken, // OIDC access token from Step 2 +}); + +// Make a request +const response = await openai.chat.completions.create({ + model: 'openai/gpt-5.2-mini', + messages: [{ role: 'user', content: 'Hello, AI Gateway!' }], + max_tokens: 100 +}); + +console.log(response.choices[0].message.content); +---- + +cURL:: ++ [source,bash] ---- curl ${REDPANDA_GATEWAY_URL}/chat/completions \ @@ -256,6 +231,7 @@ curl ${REDPANDA_GATEWAY_URL}/chat/completions \ "max_tokens": 100 }' ---- +==== === Token lifecycle management @@ -328,7 +304,7 @@ from openai import OpenAI, OpenAIError client = OpenAI( base_url=os.getenv("REDPANDA_GATEWAY_URL"), - api_key=os.getenv("REDPANDA_API_KEY"), + api_key=access_token, # OIDC access token ) try: @@ -412,11 +388,11 @@ Compare responses, latency, and cost to determine the best model for your use ca import os from openai import OpenAI -def test_gateway_connection(): +def test_gateway_connection(access_token): """Test basic connectivity to AI Gateway""" client = OpenAI( base_url=os.getenv("REDPANDA_GATEWAY_URL"), - api_key=os.getenv("REDPANDA_API_KEY"), + api_key=access_token, # OIDC access token ) try: @@ -433,7 +409,8 @@ def test_gateway_connection(): return False if __name__ == "__main__": - test_gateway_connection() + token = get_oidc_token() # Your OIDC token retrieval + test_gateway_connection(token) ---- === Test multiple models @@ -480,7 +457,7 @@ Configure Claude Code to use AI Gateway: [source,bash] ---- claude mcp add --transport http redpanda-aigateway ${REDPANDA_GATEWAY_URL}/mcp \ - --header "Authorization: Bearer ${REDPANDA_API_KEY}" + --header "Authorization: Bearer ${AUTH_TOKEN}" ---- + Or edit `~/.claude/config.json`: @@ -493,7 +470,7 @@ Or edit `~/.claude/config.json`: "transport": "http", "url": "/mcp", "headers": { - "Authorization": "Bearer your-api-key" + "Authorization": "Bearer " } } } @@ -517,7 +494,7 @@ Edit `~/.continue/config.json`: "provider": "openai", "model": "openai/gpt-5.2", "apiBase": "", - "apiKey": "your-redpanda-api-key" + "apiKey": "" } ] } @@ -557,7 +534,7 @@ Store configuration in environment variables, not hardcoded in code: base_url = os.getenv("REDPANDA_GATEWAY_URL") # Bad -base_url = "https://gw.ai.panda.com" # Don't hardcode +base_url = "https://gw.ai.panda.com" # Don't hardcode URLs or credentials ---- === Implement retry logic @@ -630,10 +607,9 @@ Problem: 401 Unauthorized Solutions: -* Verify your API token is correct and not expired -* If using OIDC, check that your token has not expired and refresh it if necessary -* If using OIDC, verify the audience is set to `cloudv2-production.redpanda.cloud` -* Check that the token has access to the specified gateway +* Check that your OIDC token has not expired and refresh it if necessary +* Verify the audience is set to `cloudv2-production.redpanda.cloud` +* Check that the service account has access to the specified gateway * Ensure the `Authorization` header is formatted correctly: `Bearer ` === "Model not found" From 704229ecfd99798b4e9545872344ed32a90f32c4 Mon Sep 17 00:00:00 2001 From: micheleRP Date: Tue, 10 Mar 2026 15:12:09 -0600 Subject: [PATCH 4/5] DOC-2034: Fix remaining API token references to say OIDC token Co-Authored-By: Claude Opus 4.6 --- .../pages/ai-gateway/builders/connect-your-agent.adoc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/ai-agents/pages/ai-gateway/builders/connect-your-agent.adoc b/modules/ai-agents/pages/ai-gateway/builders/connect-your-agent.adoc index fd70c7b0..faf92528 100644 --- a/modules/ai-agents/pages/ai-gateway/builders/connect-your-agent.adoc +++ b/modules/ai-agents/pages/ai-gateway/builders/connect-your-agent.adoc @@ -318,7 +318,7 @@ except OpenAIError as e: if e.status_code == 400: print("Bad request - check model name and parameters") elif e.status_code == 401: - print("Authentication failed - check API token") + print("Authentication failed - check OIDC token") elif e.status_code == 404: print("Model not found - check available models") elif e.status_code == 429: @@ -332,7 +332,7 @@ except OpenAIError as e: Common error codes: * *400*: Bad request (invalid parameters, malformed JSON) -* *401*: Authentication failed (invalid or missing API token) +* *401*: Authentication failed (invalid or expired OIDC token) * *403*: Forbidden (no access to this gateway) * *404*: Model not found (model not enabled in gateway) * *429*: Rate limit exceeded (too many requests) From 1b6050863fb10dd1cfecaadd7b49afe82ac2d929 Mon Sep 17 00:00:00 2001 From: micheleRP Date: Thu, 12 Mar 2026 13:56:00 -0600 Subject: [PATCH 5/5] 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 --- .../builders/connect-your-agent.adoc | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/modules/ai-agents/pages/ai-gateway/builders/connect-your-agent.adoc b/modules/ai-agents/pages/ai-gateway/builders/connect-your-agent.adoc index faf92528..fbd553e0 100644 --- a/modules/ai-agents/pages/ai-gateway/builders/connect-your-agent.adoc +++ b/modules/ai-agents/pages/ai-gateway/builders/connect-your-agent.adoc @@ -35,7 +35,7 @@ Connecting to AI Gateway requires two configuration changes: [[authenticate-with-oidc]] == Authenticate with OIDC -AI Gateway uses OIDC (OAuth 2.0 `client_credentials` grant) with a service account for authentication. +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. === Step 1: Create a service account @@ -65,6 +65,8 @@ Use the following OIDC configuration: |`client_credentials` |=== +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. + [tabs] ==== cURL:: @@ -96,8 +98,15 @@ client = OAuth2Session( client_secret="", ) +# Discover token endpoint from OIDC metadata +import requests +metadata = requests.get( + "https://auth.prd.cloud.redpanda.com/.well-known/openid-configuration" +).json() +token_endpoint = metadata["token_endpoint"] + token = client.fetch_token( - "https://auth.prd.cloud.redpanda.com/oauth/token", + token_endpoint, grant_type="client_credentials", audience="cloudv2-production.redpanda.cloud", ) @@ -105,7 +114,7 @@ token = client.fetch_token( access_token = token["access_token"] ---- -`authlib` automatically handles token caching and refresh. +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. -- Node.js (openid-client):: @@ -235,10 +244,10 @@ curl ${REDPANDA_GATEWAY_URL}/chat/completions \ === Token lifecycle management -IMPORTANT: Your agent is responsible for refreshing tokens before they expire. OIDC tokens have a limited TTL and are not automatically renewed. +IMPORTANT: Your agent is responsible for refreshing tokens before they expire. OIDC tokens have a limited TTL and are not automatically renewed by the AI Gateway. * Proactively refresh tokens at approximately 80% of the token's TTL to avoid failed requests. -* `authlib` (Python) handles token refresh automatically when you use `OAuth2Session`. +* `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. * For other languages, cache the token and its expiry time, then request a new token before the current one expires. == Model naming convention