diff --git a/gateway/examples/anthropic-openai-proxy.yaml b/gateway/examples/anthropic-openai-proxy.yaml index 674c9b6b7b..c9c17e2bae 100644 --- a/gateway/examples/anthropic-openai-proxy.yaml +++ b/gateway/examples/anthropic-openai-proxy.yaml @@ -25,7 +25,8 @@ # set on upstream.auth (the policy does NOT inject the Anthropic key). # # The provider example's api-key-auth policy expects the proxy to send the -# issued provider loopback key in X-API-Key. +# issued provider loopback key in x-api-key (HTTP header names are +# case-insensitive, matching the provider's own client-facing convention). # -------------------------------------------------------------------- apiVersion: gateway.api-platform.wso2.com/v1 @@ -42,7 +43,7 @@ spec: id: anthropic-provider auth: type: api-key - header: X-API-Key + header: x-api-key value: REPLACE_WITH_ANTHROPIC_PROVIDER_LOOPBACK_KEY policies: - name: openai-to-anthropic diff --git a/gateway/examples/anthropic-provider.yaml b/gateway/examples/anthropic-provider.yaml index 2a653d354f..c34ccb6639 100644 --- a/gateway/examples/anthropic-provider.yaml +++ b/gateway/examples/anthropic-provider.yaml @@ -45,7 +45,7 @@ spec: - path: /v1/messages methods: [POST] params: - key: X-API-Key + key: x-api-key in: header accessControl: mode: deny_all diff --git a/gateway/examples/azure-openai-provider.yaml b/gateway/examples/azure-openai-provider.yaml index 6e4686e47e..d9239a69d1 100644 --- a/gateway/examples/azure-openai-provider.yaml +++ b/gateway/examples/azure-openai-provider.yaml @@ -45,22 +45,22 @@ spec: - path: /openai/deployments/{deployment}/chat/completions methods: [POST] params: - key: X-API-Key + key: api-key in: header - path: /openai/deployments/{deployment}/completions methods: [POST] params: - key: X-API-Key + key: api-key in: header - path: /openai/deployments/{deployment}/embeddings methods: [POST] params: - key: X-API-Key + key: api-key in: header - path: /openai/models methods: [GET] params: - key: X-API-Key + key: api-key in: header accessControl: mode: deny_all diff --git a/gateway/examples/azure-openai-proxy.yaml b/gateway/examples/azure-openai-proxy.yaml index f77e7e0ee3..cc9df58a9c 100644 --- a/gateway/examples/azure-openai-proxy.yaml +++ b/gateway/examples/azure-openai-proxy.yaml @@ -40,7 +40,7 @@ spec: id: azure-openai-provider auth: type: api-key - header: X-API-Key + header: api-key value: REPLACE_WITH_AZURE_OPENAI_PROVIDER_LOOPBACK_KEY policies: - name: openai-to-azure-openai diff --git a/gateway/examples/bedrock-openai-proxy.yaml b/gateway/examples/bedrock-openai-proxy.yaml index 4d50b7388e..e8f1d68331 100644 --- a/gateway/examples/bedrock-openai-proxy.yaml +++ b/gateway/examples/bedrock-openai-proxy.yaml @@ -24,7 +24,9 @@ # The provider must already be deployed (see bedrock-provider.yaml) with its # Bearer key on upstream.auth (the policy does NOT inject the Bedrock key). # The provider example's api-key-auth policy expects the proxy to send the -# issued provider loopback key in X-API-Key. +# issued provider loopback key as "Authorization: Bearer " -- matching +# the provider's own client-facing convention. LLMUpstreamAuth has no +# separate valuePrefix field, so "Bearer " must be included in value here. # -------------------------------------------------------------------- apiVersion: gateway.api-platform.wso2.com/v1 @@ -39,8 +41,8 @@ spec: id: bedrock-provider auth: type: api-key - header: X-API-Key - value: REPLACE_WITH_BEDROCK_PROVIDER_LOOPBACK_KEY + header: Authorization + value: Bearer REPLACE_WITH_BEDROCK_PROVIDER_LOOPBACK_KEY policies: - name: openai-to-bedrock-transformer version: v1 diff --git a/gateway/examples/bedrock-provider.yaml b/gateway/examples/bedrock-provider.yaml index 0413337109..2a94b491e9 100644 --- a/gateway/examples/bedrock-provider.yaml +++ b/gateway/examples/bedrock-provider.yaml @@ -53,13 +53,15 @@ spec: - path: /model/{modelId}/converse methods: [POST] params: - key: X-API-Key + key: Authorization in: header + valuePrefix: "Bearer " - path: /model/{modelId}/converse-stream methods: [POST] params: - key: X-API-Key + key: Authorization in: header + valuePrefix: "Bearer " accessControl: mode: deny_all exceptions: diff --git a/gateway/examples/gemini-provider.yaml b/gateway/examples/gemini-provider.yaml index d2fb4b4240..a25482f93a 100644 --- a/gateway/examples/gemini-provider.yaml +++ b/gateway/examples/gemini-provider.yaml @@ -40,12 +40,12 @@ spec: - path: /v1beta/models/{model}:generateContent methods: [POST] params: - key: X-API-Key + key: x-goog-api-key in: header - path: /v1beta/models/{model}:streamGenerateContent methods: [POST] params: - key: X-API-Key + key: x-goog-api-key in: header accessControl: mode: deny_all diff --git a/gateway/examples/mistral-openai-proxy.yaml b/gateway/examples/mistral-openai-proxy.yaml index ae16a53115..16bacc0058 100644 --- a/gateway/examples/mistral-openai-proxy.yaml +++ b/gateway/examples/mistral-openai-proxy.yaml @@ -26,7 +26,9 @@ # Mistral key). # # The provider example's api-key-auth policy expects the proxy to send the -# issued provider loopback key in X-API-Key. +# issued provider loopback key as "Authorization: Bearer " -- matching +# the provider's own client-facing convention. LLMUpstreamAuth has no +# separate valuePrefix field, so "Bearer " must be included in value here. # -------------------------------------------------------------------- apiVersion: gateway.api-platform.wso2.com/v1 @@ -43,8 +45,8 @@ spec: id: mistral-provider auth: type: api-key - header: X-API-Key - value: REPLACE_WITH_MISTRAL_PROVIDER_LOOPBACK_KEY + header: Authorization + value: Bearer REPLACE_WITH_MISTRAL_PROVIDER_LOOPBACK_KEY policies: - name: openai-to-mistral version: v1 diff --git a/gateway/examples/mistral-provider.yaml b/gateway/examples/mistral-provider.yaml index 7f55201aa0..9a6d53bfe7 100644 --- a/gateway/examples/mistral-provider.yaml +++ b/gateway/examples/mistral-provider.yaml @@ -43,18 +43,21 @@ spec: - path: /v1/chat/completions methods: [POST] params: - key: X-API-Key + key: Authorization in: header + valuePrefix: "Bearer " - path: /v1/embeddings methods: [POST] params: - key: X-API-Key + key: Authorization in: header + valuePrefix: "Bearer " - path: /v1/models methods: [GET] params: - key: X-API-Key + key: Authorization in: header + valuePrefix: "Bearer " accessControl: mode: deny_all exceptions: diff --git a/gateway/examples/openai-multi-provider-proxy.yaml b/gateway/examples/openai-multi-provider-proxy.yaml index 5ab5e12e21..a86beb9217 100644 --- a/gateway/examples/openai-multi-provider-proxy.yaml +++ b/gateway/examples/openai-multi-provider-proxy.yaml @@ -40,8 +40,8 @@ spec: id: openai-provider auth: type: api-key - header: X-API-Key - value: REPLACE_WITH_OPENAI_PROVIDER_LOOPBACK_KEY + header: Authorization + value: Bearer REPLACE_WITH_OPENAI_PROVIDER_LOOPBACK_KEY additionalProviders: - id: anthropic-provider # Optional alias, for when the provider id is not readable enough to use @@ -52,7 +52,7 @@ spec: # as: anthropic-upstream auth: type: api-key - header: X-API-Key + header: x-api-key value: REPLACE_WITH_ANTHROPIC_PROVIDER_LOOPBACK_KEY transformer: type: openai-to-anthropic @@ -62,7 +62,7 @@ spec: - id: azure-openai-provider auth: type: api-key - header: X-API-Key + header: api-key value: REPLACE_WITH_AZURE_OPENAI_PROVIDER_LOOPBACK_KEY transformer: type: openai-to-azure-openai @@ -73,8 +73,8 @@ spec: - id: mistral-provider auth: type: api-key - header: X-API-Key - value: REPLACE_WITH_MISTRAL_PROVIDER_LOOPBACK_KEY + header: Authorization + value: Bearer REPLACE_WITH_MISTRAL_PROVIDER_LOOPBACK_KEY transformer: type: openai-to-mistral version: v1 @@ -83,7 +83,7 @@ spec: - id: gemini-provider auth: type: api-key - header: X-API-Key + header: x-goog-api-key value: REPLACE_WITH_GEMINI_PROVIDER_LOOPBACK_KEY transformer: type: openai-to-gemini @@ -94,8 +94,8 @@ spec: - id: bedrock-provider auth: type: api-key - header: X-API-Key - value: REPLACE_WITH_BEDROCK_PROVIDER_LOOPBACK_KEY + header: Authorization + value: Bearer REPLACE_WITH_BEDROCK_PROVIDER_LOOPBACK_KEY transformer: type: openai-to-bedrock-transformer version: v1 @@ -108,8 +108,9 @@ spec: - path: /chat/completions methods: [POST] params: - key: X-API-Key + key: Authorization in: header + valuePrefix: "Bearer " - name: llm-header-router version: v1 paths: @@ -145,9 +146,11 @@ spec: # backend: memory --- # Client API key for this proxy. The api-key-auth policy above validates -# the X-API-Key header against the key the gateway issues for this resource. -# apiKey is omitted here, so the gateway generates one. Read it from the -# create response (POST .../llm-proxies/openai-multi/api-keys). +# an "Authorization: Bearer " header against the key the gateway +# issues for this resource -- the same convention an OpenAI SDK already +# uses, so no custom header is required. apiKey is omitted here, so the +# gateway generates one. Read it from the create response +# (POST .../llm-proxies/openai-multi/api-keys). apiVersion: gateway.api-platform.wso2.com/v1 kind: ApiKey metadata: diff --git a/gateway/examples/openai-provider.yaml b/gateway/examples/openai-provider.yaml index 569b45bb4b..9b3d89d8fd 100644 --- a/gateway/examples/openai-provider.yaml +++ b/gateway/examples/openai-provider.yaml @@ -43,28 +43,33 @@ spec: - path: /chat/completions methods: [POST] params: - key: X-API-Key + key: Authorization in: header + valuePrefix: "Bearer " - path: /completions methods: [POST] params: - key: X-API-Key + key: Authorization in: header + valuePrefix: "Bearer " - path: /embeddings methods: [POST] params: - key: X-API-Key + key: Authorization in: header + valuePrefix: "Bearer " - path: /models methods: [GET] params: - key: X-API-Key + key: Authorization in: header + valuePrefix: "Bearer " - path: /models/{modelId} methods: [GET] params: - key: X-API-Key + key: Authorization in: header + valuePrefix: "Bearer " accessControl: mode: deny_all exceptions: diff --git a/portals/ai-workspace/src/pages/appShell/appShellPages/serviceProvider/ServiceProviderNew.tsx b/portals/ai-workspace/src/pages/appShell/appShellPages/serviceProvider/ServiceProviderNew.tsx index 8610dcc647..fe4f6707b2 100644 --- a/portals/ai-workspace/src/pages/appShell/appShellPages/serviceProvider/ServiceProviderNew.tsx +++ b/portals/ai-workspace/src/pages/appShell/appShellPages/serviceProvider/ServiceProviderNew.tsx @@ -386,12 +386,22 @@ export default function ServiceProviderNew() { }, }; + // Match the client-facing header to the upstream vendor's own convention + // only when we're actually configuring upstream auth with it — otherwise + // there's no vendor convention to match, so keep the neutral default. const security = { enabled: true, apiKey: { enabled: true, - key: 'X-API-Key', + key: + !isNoCredentialsAuthType && hasCredential + ? formState.upstreamAuthHeader.trim() || 'X-API-Key' + : 'X-API-Key', in: 'header' as const, + valuePrefix: + !isNoCredentialsAuthType && hasCredential + ? formState.valuePrefix || '' + : '', }, }; diff --git a/portals/ai-workspace/src/utils/tmpSPRequest.ts b/portals/ai-workspace/src/utils/tmpSPRequest.ts index b4eb236497..256dde996e 100644 --- a/portals/ai-workspace/src/utils/tmpSPRequest.ts +++ b/portals/ai-workspace/src/utils/tmpSPRequest.ts @@ -127,7 +127,9 @@ export function buildFullProviderRequest( modelProviders: request.modelProviders ?? buildModelProvidersFromTemplate(request.template), rateLimiting: {}, - security: { + // Honor a security config the caller already resolved. + // only fall back to the generic default when none was supplied. + security: request.security ?? { enabled: true, apiKey: { enabled: true,