From e9beb2889753d2cc6bdfb6b6c6b6211db1b761a3 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Tue, 17 Feb 2026 07:33:56 +0000 Subject: [PATCH] fix: validate API keys at provider init to prevent 403 'authorization missing' When NVIDIA_NIM_API_KEY or OPENROUTER_API_KEY is empty or not set, the proxy forwarded requests without a valid Authorization header, causing providers to return 403 with 'Header of type authorization was missing'. Now fail fast with HTTP 503 and a clear message telling users to add the key to .env, with links to obtain keys. Fixes #29 Co-authored-by: Ali Khokhar --- api/dependencies.py | 23 +++++++++++++++++ tests/api/test_dependencies.py | 45 ++++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+) diff --git a/api/dependencies.py b/api/dependencies.py index 636c8264..1a1fb594 100644 --- a/api/dependencies.py +++ b/api/dependencies.py @@ -2,6 +2,7 @@ from typing import Optional +from fastapi import HTTPException from loguru import logger from config.settings import Settings, get_settings as _get_settings, NVIDIA_NIM_BASE_URL @@ -24,6 +25,17 @@ def get_provider() -> BaseProvider: settings = get_settings() if settings.provider_type == "nvidia_nim": + if ( + not settings.nvidia_nim_api_key + or not settings.nvidia_nim_api_key.strip() + ): + raise HTTPException( + status_code=503, + detail=( + "NVIDIA_NIM_API_KEY is not set. Add it to your .env file. " + "Get a key at https://build.nvidia.com/settings/api-keys" + ), + ) from providers.nvidia_nim import NvidiaNimProvider config = ProviderConfig( @@ -38,6 +50,17 @@ def get_provider() -> BaseProvider: _provider = NvidiaNimProvider(config, nim_settings=settings.nim) logger.info("Provider initialized: %s", settings.provider_type) elif settings.provider_type == "open_router": + if ( + not settings.open_router_api_key + or not settings.open_router_api_key.strip() + ): + raise HTTPException( + status_code=503, + detail=( + "OPENROUTER_API_KEY is not set. Add it to your .env file. " + "Get a key at https://openrouter.ai/keys" + ), + ) from providers.open_router import OpenRouterProvider config = ProviderConfig( diff --git a/tests/api/test_dependencies.py b/tests/api/test_dependencies.py index 2aafdb7e..89d56aad 100644 --- a/tests/api/test_dependencies.py +++ b/tests/api/test_dependencies.py @@ -1,4 +1,5 @@ import pytest +from fastapi import HTTPException from unittest.mock import AsyncMock, MagicMock, patch from api.dependencies import get_provider, get_settings, cleanup_provider from providers.lmstudio import LMStudioProvider @@ -143,6 +144,50 @@ async def test_get_provider_passes_http_timeouts_from_settings(): assert timeout.connect == 5.0 +@pytest.mark.asyncio +async def test_get_provider_nvidia_nim_missing_api_key(): + """NVIDIA NIM with empty API key raises HTTPException 503.""" + with patch("api.dependencies.get_settings") as mock_settings: + mock_settings.return_value = _make_mock_settings(nvidia_nim_api_key="") + + with pytest.raises(HTTPException) as exc_info: + get_provider() + + assert exc_info.value.status_code == 503 + assert "NVIDIA_NIM_API_KEY" in exc_info.value.detail + assert "build.nvidia.com" in exc_info.value.detail + + +@pytest.mark.asyncio +async def test_get_provider_nvidia_nim_whitespace_only_api_key(): + """NVIDIA NIM with whitespace-only API key raises HTTPException 503.""" + with patch("api.dependencies.get_settings") as mock_settings: + mock_settings.return_value = _make_mock_settings(nvidia_nim_api_key=" ") + + with pytest.raises(HTTPException) as exc_info: + get_provider() + + assert exc_info.value.status_code == 503 + assert "NVIDIA_NIM_API_KEY" in exc_info.value.detail + + +@pytest.mark.asyncio +async def test_get_provider_open_router_missing_api_key(): + """OpenRouter with empty API key raises HTTPException 503.""" + with patch("api.dependencies.get_settings") as mock_settings: + mock_settings.return_value = _make_mock_settings( + provider_type="open_router", + open_router_api_key="", + ) + + with pytest.raises(HTTPException) as exc_info: + get_provider() + + assert exc_info.value.status_code == 503 + assert "OPENROUTER_API_KEY" in exc_info.value.detail + assert "openrouter.ai" in exc_info.value.detail + + @pytest.mark.asyncio async def test_get_provider_unknown_type(): """Test that unknown provider_type raises ValueError."""