mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-16 23:41:43 +00:00
fix(auth_v2): require scim:write auth on all SCIM routes
The SCIM router mounted /scim/v2/* with no security dependency, so any caller could create or delete users and groups unauthenticated. Guard the whole router with Security(get_current_principal, scopes=["scim:write"]) per design 03 §11, so provisioning callers authenticate with the same bearer token or API key as every other route and the scope gates them: unauthenticated requests now 401, an authenticated principal without scim:write gets 403 insufficient_scope. Also document two deployment facts uncovered alongside this: uvicorn's --proxy-headers rewrites request.client from X-Forwarded-For before this module's trusted_proxy_cidrs check runs and silently bypasses it (install_auth docstring), and the scheme_order precedence where HTTP precedes openIdConnect so a bearer JWT is labeled bearer_jwt rather than oidc (both verify identically).
This commit is contained in:
parent
03faccea17
commit
d3878ee9a9
3 changed files with 20 additions and 2 deletions
|
|
@ -84,6 +84,10 @@ class SamlConfig(BaseModel):
|
|||
|
||||
|
||||
class AuthConfig(BaseModel):
|
||||
# First-match-wins precedence. HTTP precedes OPENID_CONNECT, so a bearer JWT
|
||||
# is claimed by HttpAuthenticator (auth_method=bearer_jwt) and OidcAuthenticator
|
||||
# never runs; both share the same JwtVerifiers and verify identically, so this
|
||||
# only changes the auth_method label. Reorder if openIdConnect labeling matters.
|
||||
scheme_order: List[SecuritySchemeType] = Field(
|
||||
default_factory=lambda: [
|
||||
SecuritySchemeType.API_KEY,
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ from __future__ import annotations
|
|||
|
||||
from typing import Any, Dict, Optional, Type, TypeVar
|
||||
|
||||
from fastapi import APIRouter, Request, Response, status
|
||||
from fastapi import APIRouter, Request, Response, Security, status
|
||||
from fastapi.responses import JSONResponse
|
||||
from pydantic import ValidationError
|
||||
from scim2_models import (
|
||||
|
|
@ -23,6 +23,7 @@ from scim2_models import (
|
|||
)
|
||||
|
||||
from .resolver import ProvisioningStore
|
||||
from .security import get_current_principal
|
||||
|
||||
R = TypeVar("R", bound=Resource)
|
||||
|
||||
|
|
@ -63,7 +64,11 @@ def _dump(resource: Resource, ctx: Context) -> Dict[str, Any]:
|
|||
|
||||
|
||||
def build_scim_router() -> APIRouter:
|
||||
router = APIRouter(prefix="/scim/v2", tags=["scim"])
|
||||
router = APIRouter(
|
||||
prefix="/scim/v2",
|
||||
tags=["scim"],
|
||||
dependencies=[Security(get_current_principal, scopes=["scim:write"])],
|
||||
)
|
||||
|
||||
@router.post("/Users", status_code=status.HTTP_201_CREATED)
|
||||
async def create_user(request: Request) -> Response:
|
||||
|
|
|
|||
|
|
@ -31,6 +31,15 @@ def install_auth(
|
|||
mount_oidc: bool = True,
|
||||
mount_saml: bool = True,
|
||||
) -> AuthContext:
|
||||
"""Wire the authenticators, resolver and optional routers onto the app.
|
||||
|
||||
Deployment requirement for trusted-proxy IP resolution: uvicorn's
|
||||
``--proxy-headers`` (enabled by default) overwrites ``request.client`` from
|
||||
``X-Forwarded-For`` before this module's ``trusted_proxy_cidrs`` check runs,
|
||||
which silently bypasses it. Run uvicorn with ``--no-proxy-headers`` and let
|
||||
this module resolve the client IP, or leave ``trusted_proxy_cidrs`` empty and
|
||||
rely on uvicorn's own ``--forwarded-allow-ips``. Do not enable both.
|
||||
"""
|
||||
ctx = AuthContext(config, build_authenticators(config), resolver)
|
||||
app.state.auth_v2 = ctx
|
||||
if mount_scim:
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue