Unified · Split
| @@ -384,22 +384,20 @@ func (h *Handler) resolveMetaToken(ctx context.Context, auth *coreauth.Auth, req · 383 unmodified lines | ||
| 384 | 384 | } |
| 385 | 385 | |
| 386 | 386 | var dcaToken string |
| 387 | − if auth.Metadata != nil { | |
| 388 | − if d, ok := auth.Metadata["dca_token"].(string); ok && strings.TrimSpace(d) != "" { | |
| 389 | − dcaToken = strings.TrimSpace(d) | |
| 390 | − } else if t, ok := auth.Metadata["access_token"].(string); ok && strings.HasPrefix(strings.TrimSpace(t), "dca:") { | |
| 391 | − dcaToken = strings.TrimSpace(t) | |
| 392 | − } else if k, ok := auth.Metadata["api_key"].(string); ok && strings.HasPrefix(strings.TrimSpace(k), "dca:") { | |
| 393 | − dcaToken = strings.TrimSpace(k) | |
| 394 | − } | |
| 395 | − } | |
| 396 | − if dcaToken == "" && auth.Attributes != nil { | |
| 397 | − if d := strings.TrimSpace(auth.Attributes["dca_token"]); d != "" { | |
| 398 | − dcaToken = d | |
| 399 | − } else if t := strings.TrimSpace(auth.Attributes["access_token"]); strings.HasPrefix(t, "dca:") { | |
| 400 | − dcaToken = t | |
| 401 | − } else if k := strings.TrimSpace(auth.Attributes["api_key"]); strings.HasPrefix(k, "dca:") { | |
| 402 | − dcaToken = k | |
| 387 | + if !coreauth.IsConfigAPIKeyAuth(auth) { | |
| 388 | + if auth.Metadata != nil { | |
| 389 | + if d, ok := auth.Metadata["dca_token"].(string); ok && strings.TrimSpace(d) != "" { | |
| 390 | + dcaToken = strings.TrimSpace(d) | |
| 391 | + } else if t, ok := auth.Metadata["access_token"].(string); ok && strings.HasPrefix(strings.TrimSpace(t), "dca:") { | |
| 392 | + dcaToken = strings.TrimSpace(t) | |
| 393 | + } | |
| 394 | + } | |
| 395 | + if dcaToken == "" && auth.Attributes != nil { | |
| 396 | + if d := strings.TrimSpace(auth.Attributes["dca_token"]); d != "" { | |
| 397 | + dcaToken = d | |
| 398 | + } else if t := strings.TrimSpace(auth.Attributes["access_token"]); strings.HasPrefix(t, "dca:") { | |
| 399 | + dcaToken = t | |
| 400 | + } | |
| 403 | 401 | } |
| 404 | 402 | } |
| 405 | 403 | |
| @@ -9,13 +9,13 @@ func TestMetaConfigDropsUnusableKeys(t *testing.T) { · 8 unmodified lines | ||
| 9 | 9 | - base-url: "https://api.meta.ai/v1" |
| 10 | 10 | - headers: {X-Trace: placeholder} |
| 11 | 11 | - api-key: " LLM|valid " |
| 12 | − - api-key: "dca:recoverable" | |
| 12 | + - api-key: "dca:requires-oauth-storage" | |
| 13 | 13 | `)) |
| 14 | 14 | if err != nil { |
| 15 | 15 | t.Fatal(err) |
| 16 | 16 | } |
| 17 | − if len(cfg.MetaKey) != 2 { | |
| 18 | − t.Fatalf("got %d keys, want only the two usable credentials", len(cfg.MetaKey)) | |
| 17 | + if len(cfg.MetaKey) != 1 { | |
| 18 | + t.Fatalf("got %d keys, want only the 1 valid API key (DCA tokens require OAuth storage)", len(cfg.MetaKey)) | |
| 19 | 19 | } |
| 20 | 20 | if cfg.MetaKey[0].APIKey != "LLM|valid" || cfg.MetaKey[0].BaseURL != "https://api.meta.ai/v1" { |
| 21 | 21 | t.Fatalf("valid key not normalized: %#v", cfg.MetaKey[0]) |
| @@ -210,8 +210,8 @@ func sanitizeMetaKeyEntries(entries []MetaKey) []MetaKey { · 209 unmodified lines | ||
| 210 | 210 | for i := range entries { |
| 211 | 211 | e := entries[i] |
| 212 | 212 | e.APIKey = strings.TrimSpace(e.APIKey) |
| 213 | − // The native Meta executor requires an API key or a DCA token in this field. | |
| 214 | − if e.APIKey == "" { | |
| 213 | + // meta-api-key requires a valid API key. DCA tokens require OAuth storage (auths/*.json). | |
| 214 | + if e.APIKey == "" || strings.HasPrefix(e.APIKey, "dca:") { | |
| 215 | 215 | continue |
| 216 | 216 | } |
| 217 | 217 | e.Prefix = normalizeModelPrefix(e.Prefix) |
| @@ -254,7 +254,7 @@ func (e *MetaExecutor) Refresh(ctx context.Context, auth *cliproxyauth.Auth) (*c · 253 unmodified lines | ||
| 254 | 254 | |
| 255 | 255 | // ShouldPrepareRequestAuth reports true when a Meta auth has a DCA token but no usable API key. |
| 256 | 256 | func (e *MetaExecutor) ShouldPrepareRequestAuth(auth *cliproxyauth.Auth) bool { |
| 257 | − if auth == nil { | |
| 257 | + if auth == nil || cliproxyauth.IsConfigAPIKeyAuth(auth) { | |
| 258 | 258 | return false |
| 259 | 259 | } |
| 260 | 260 | _, token := metaCreds(auth) |
| @@ -291,6 +291,12 @@ func (e *MetaExecutor) ensureAuth(ctx context.Context, auth *cliproxyauth.Auth) · 30 unmodified lines | ||
| 291 | 291 | } |
| 292 | 292 | |
| 293 | 293 | if token == "" { |
| 294 | + if cliproxyauth.IsConfigAPIKeyAuth(auth) { | |
| 295 | + return nil, statusErr{ | |
| 296 | + code: http.StatusUnauthorized, | |
| 297 | + msg: "meta executor: meta-api-key requires a valid API key (DCA tokens require OAuth storage)", | |
| 298 | + } | |
| 299 | + } | |
| 294 | 300 | return nil, statusErr{ |
| 295 | 301 | code: http.StatusUnauthorized, |
| 296 | 302 | msg: "meta executor: missing API key or access token", |
| @@ -326,7 +332,7 @@ func (e *MetaExecutor) enrichAuth(auth *cliproxyauth.Auth) *cliproxyauth.Auth { · 29 unmodified lines | ||
| 326 | 332 | } |
| 327 | 333 | |
| 328 | 334 | func extractDCAToken(a *cliproxyauth.Auth) string { |
| 329 | − if a == nil { | |
| 335 | + if a == nil || cliproxyauth.IsConfigAPIKeyAuth(a) { | |
| 330 | 336 | return "" |
| 331 | 337 | } |
| 332 | 338 | if a.Attributes != nil { |
| @@ -336,9 +342,6 @@ func extractDCAToken(a *cliproxyauth.Auth) string { · 3 unmodified lines | ||
| 336 | 342 | if t := strings.TrimSpace(a.Attributes["access_token"]); strings.HasPrefix(t, "dca:") { |
| 337 | 343 | return t |
| 338 | 344 | } |
| 339 | − if k := strings.TrimSpace(a.Attributes["api_key"]); strings.HasPrefix(k, "dca:") { | |
| 340 | − return k | |
| 341 | − } | |
| 342 | 345 | } |
| 343 | 346 | if a.Metadata != nil { |
| 344 | 347 | if d, ok := a.Metadata["dca_token"].(string); ok && strings.TrimSpace(d) != "" { |
| @@ -347,9 +350,6 @@ func extractDCAToken(a *cliproxyauth.Auth) string { · 2 unmodified lines | ||
| 347 | 350 | if t, ok := a.Metadata["access_token"].(string); ok && strings.HasPrefix(strings.TrimSpace(t), "dca:") { |
| 348 | 351 | return strings.TrimSpace(t) |
| 349 | 352 | } |
| 350 | − if k, ok := a.Metadata["api_key"].(string); ok && strings.HasPrefix(strings.TrimSpace(k), "dca:") { | |
| 351 | − return strings.TrimSpace(k) | |
| 352 | − } | |
| 353 | 353 | } |
| 354 | 354 | if a.Storage != nil { |
| 355 | 355 | if ms, ok := a.Storage.(*metaauth.MetaTokenStorage); ok && ms != nil { |
| @@ -572,6 +572,17 @@ func TestMetaExecutor_RequestAuthPreparer(t *testing.T) { · 571 unmodified lines | ||
| 572 | 572 | t.Errorf("ShouldPrepareRequestAuth should be false when dca_token is missing") |
| 573 | 573 | } |
| 574 | 574 | |
| 575 | + authConfigDCA := &cliproxyauth.Auth{ | |
| 576 | + Provider: "meta", | |
| 577 | + Attributes: map[string]string{ | |
| 578 | + "api_key": "dca:invalid-for-config", | |
| 579 | + cliproxyauth.AttributeSource: "config:meta[0]", | |
| 580 | + }, | |
| 581 | + } | |
| 582 | + if exec.ShouldPrepareRequestAuth(authConfigDCA) { | |
| 583 | + t.Errorf("ShouldPrepareRequestAuth should be false for config API key auths") | |
| 584 | + } | |
| 585 | + | |
| 575 | 586 | authDCAOnly := &cliproxyauth.Auth{ |
| 576 | 587 | ID: "meta-prep-test", |
| 577 | 588 | Provider: "meta", |