[Feature] Support multiple api keys in server (#18548)

Signed-off-by: Yan Pashkovsky <yanp.bugz@gmail.com>
This commit is contained in:
Yan Pashkovsky 2025-07-30 15:03:23 +01:00 committed by GitHub
parent da3e0bd6e5
commit bf668b5bf5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 30 additions and 29 deletions

View File

@ -126,6 +126,7 @@ curl http://localhost:8000/v1/models
``` ```
You can pass in the argument `--api-key` or environment variable `VLLM_API_KEY` to enable the server to check for API key in the header. You can pass in the argument `--api-key` or environment variable `VLLM_API_KEY` to enable the server to check for API key in the header.
You can pass multiple keys after `--api-key`, and the server will accept any of the keys passed, this can be useful for key rotation.
### OpenAI Completions API with vLLM ### OpenAI Completions API with vLLM

View File

@ -1239,9 +1239,9 @@ class AuthenticationMiddleware:
2. The request path doesn't start with /v1 (e.g. /health). 2. The request path doesn't start with /v1 (e.g. /health).
""" """
def __init__(self, app: ASGIApp, api_token: str) -> None: def __init__(self, app: ASGIApp, tokens: list[str]) -> None:
self.app = app self.app = app
self.api_token = api_token self.api_tokens = {f"Bearer {token}" for token in tokens}
def __call__(self, scope: Scope, receive: Receive, def __call__(self, scope: Scope, receive: Receive,
send: Send) -> Awaitable[None]: send: Send) -> Awaitable[None]:
@ -1255,7 +1255,7 @@ class AuthenticationMiddleware:
headers = Headers(scope=scope) headers = Headers(scope=scope)
# Type narrow to satisfy mypy. # Type narrow to satisfy mypy.
if url_path.startswith("/v1") and headers.get( if url_path.startswith("/v1") and headers.get(
"Authorization") != f"Bearer {self.api_token}": "Authorization") not in self.api_tokens:
response = JSONResponse(content={"error": "Unauthorized"}, response = JSONResponse(content={"error": "Unauthorized"},
status_code=401) status_code=401)
return response(scope, receive, send) return response(scope, receive, send)
@ -1512,8 +1512,8 @@ def build_app(args: Namespace) -> FastAPI:
status_code=HTTPStatus.BAD_REQUEST) status_code=HTTPStatus.BAD_REQUEST)
# Ensure --api-key option from CLI takes precedence over VLLM_API_KEY # Ensure --api-key option from CLI takes precedence over VLLM_API_KEY
if token := args.api_key or envs.VLLM_API_KEY: if tokens := [key for key in (args.api_key or [envs.VLLM_API_KEY]) if key]:
app.add_middleware(AuthenticationMiddleware, api_token=token) app.add_middleware(AuthenticationMiddleware, tokens=tokens)
if args.enable_request_id_headers: if args.enable_request_id_headers:
app.add_middleware(XRequestIdMiddleware) app.add_middleware(XRequestIdMiddleware)

View File

@ -85,9 +85,9 @@ class FrontendArgs:
"""Allowed methods.""" """Allowed methods."""
allowed_headers: list[str] = field(default_factory=lambda: ["*"]) allowed_headers: list[str] = field(default_factory=lambda: ["*"])
"""Allowed headers.""" """Allowed headers."""
api_key: Optional[str] = None api_key: Optional[list[str]] = None
"""If provided, the server will require this key to be presented in the """If provided, the server will require one of these keys to be presented in
header.""" the header."""
lora_modules: Optional[list[LoRAModulePath]] = None lora_modules: Optional[list[LoRAModulePath]] = None
"""LoRA modules configurations in either 'name=path' format or JSON format """LoRA modules configurations in either 'name=path' format or JSON format
or JSON list format. Example (old format): `'name=path'` Example (new or JSON list format. Example (old format): `'name=path'` Example (new