apache / apache/shenyu

[BUG] AI proxy key mode can reuse a cached client with the wrong upstream API key

Open
#6,471 1 comment 0 reactions 0 assignees View on GitHub
Dominant language
Java
Stars
8.8k
Forks
3.1k
Avg merge
7d 1h
Merged PRs (30d)
85

Description

### Current Behavior

When AI proxy key mode is enabled, the request path resolves the real upstream API key from the incoming `X-API-KEY` header and writes it into `primaryConfig`:

```java
final String realKey = AiProxyApiKeyCache.getInstance().getRealApiKey(selector.getId(), proxyApiKey);
if (Objects.nonNull(realKey)) {
primaryConfig.setApiKey(realKey);
}
```

But the main chat client cache key explicitly excludes `apiKey`:

```java
return Objects.hash(
config.getProvider(),
config.getBaseUrl(),
config.getModel(),
config.getTemperature(),
config.getMaxTokens(),
config.getStream()
// Explicitly exclude apiKey
);
```

The cached `ChatClient` is reused by selector/config hash, while the Spring AI model factory embeds the API key when the model is created. This means the first request that warms the cache can determine the upstream API key used by later requests with different proxy keys but the same selector/model settings.

### Expected Behavior

Proxy-key mode should not reuse a cached upstream client across different resolved real API keys, or the request-time credential should be injected in a way that is not captured by a shared cached model.

### Impact

Different tenants or callers using different Shenyu proxy keys can be routed to the wrong upstream AI account/key after the first cache warm-up. That can cause incorrect billing, quota usage, and authorization isolation issues.

### Code Location

- `shenyu-plugin/shenyu-plugin-ai/shenyu-plugin-ai-proxy/src/main/java/org/apache/shenyu/plugin/ai/proxy/enhanced/AiProxyPlugin.java`
- `proxyApiKey` is resolved and assigned to `primaryConfig.apiKey`.
- `generateConfigCacheKey()` excludes `apiKey`.
- `createMainChatClient()` reuses the cached client by selector/config hash.
- `shenyu-plugin/shenyu-plugin-ai/shenyu-plugin-ai-common/src/main/java/org/apache/shenyu/plugin/ai/common/spring/ai/factory/OpenAiModelFactory.java`
- `shenyu-plugin/shenyu-plugin-ai/shenyu-plugin-ai-common/src/main/java/org/apache/shenyu/plugin/ai/common/spring/ai/factory/DeepSeekModelFactory.java`
- factories build model instances with the API key from config.

Contributor guide

No contributing guide indexed for this repository

Research direction

Start in AiProxyPlugin.java by tracing proxyApiKey resolution, generateConfigCacheKey(), and createMainChatClient(), then inspect OpenAiModelFactory.java and DeepSeekModelFactory.java to confirm where the API key is captured. Done means requests resolving different upstream keys cannot share a cached client, or the credential is no longer captured by a shared cached model.

Written by the indexing model from the issue text.

Assessment

Tech stack
java, spring
Domain
api, authorization, backend, security
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
55/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.