커스텀 인증

커스텀 인증 (Custom Auth)

기본 api key 인증을 재정의할 수 있어요.

출처: 문서

본문

커스텀 인증으로 강제하기 (Enforcement with custom auth)

기본적으로 커스텀 인증은 반환된 객체에 설정한 요율 제한만 강제해요. 예산과 모델 액세스는 플래그가 필요해요. 아래 표는 각 제어에 대해 어디서 구성하고 어떤 플래그가 필요한지 보여줘요.

무엇이 강제되는가 (What gets enforced)

목표 설정 위치 필요한 플래그
키 / 사용자 / 팀 / 최종 사용자 요율 제한 반환된 객체 (rpm_limit, team_tpm_limit, …) 없음
모델별 요율 제한, 키 / 팀 범위 반환된 객체의 metadata / team_metadata 없음
모델별 요율 제한, 프로젝트 범위 프로젝트 레코드 (model_tpm_limit / model_rpm_limit) custom_auth_run_common_checks
팀 / 사용자 / 프로젝트 예산 팀 / 사용자 / 프로젝트 레코드 custom_auth_run_common_checks
팀 / 사용자 / 프로젝트 모델 허용 목록 팀 / 사용자 / 프로젝트 레코드 custom_auth_run_common_checks
최종 사용자 예산 최종 사용자 레코드 custom_auth_run_common_checks 또는 enable_post_custom_auth_checks
키 모델 허용 목록 (models) 반환된 객체 두 플래그 모두
키 모델별 예산 (model_max_budget) 반환된 객체 enable_post_custom_auth_checks
키 만료 (expires) 반환된 객체 enable_post_custom_auth_checks
키 스칼라 예산 (max_budget / soft_budget) 지원되지 않음. 범위별 예산 사용 n/a

참고: 프로젝트 모델별 한도는 플래그가 꺼져 있을 때 객체의 project_metadata에 들어가지만, 플래그가 켜지면 DB 프로젝트 레코드가 이를 재정의하므로 그곳에 설정하세요. (팀 모델별은 team_metadata로 항상 객체에 머물러요.)

예시는 Enforce budgets and model access와 Key-level enforcement를 보세요.

사용법 (Usage)

1. 커스텀 auth 파일 만들기

응답 타입이 UserAPIKeyAuth pydantic 객체를 따르도록 하세요. 이는 그 사용자 키에 특화된 사용량을 기록하는 데 사용돼요.

from fastapi import Request
from litellm.proxy._types import UserAPIKeyAuth

async def user_api_key_auth(request: Request, api_key: str) -> UserAPIKeyAuth:
    try:
        modified_master_key = "«redacted:sk-…»"
        if api_key == modified_master_key:
            return UserAPIKeyAuth(api_key=api_key)
        raise Exception
    except:
        raise Exception

2. 파일 경로 전달 (config.yaml 기준 상대 경로)

파일 경로를 config.yaml에 전달하세요.

예를 들어 둘 다 같은 디렉토리에 있다면 - ./config.yaml./custom_auth.py - 다음과 같아요:

model_list:
  - model_name: "openai-model"
    litellm_params:
      model: "gpt-5.6-luna"
litellm_settings:
  drop_params: True
  set_verbose: True
general_settings:
  custom_auth: custom_auth.user_api_key_auth

구현 코드 (Implementation Code)

3. 프록시 시작

$ litellm --config /path/to/config.yaml

UserAPIKeyAuth 필드 참조 (UserAPIKeyAuth Fields Reference)

이 필드들은 반환된 객체에서 직접 읽어 플래그 없이 강제돼요. 예산과 모델 액세스는 플래그 뒤에서 강제돼요 (아래 참고).

신원 (Identity)

요청이 속하는 대상. *_id 필드는 custom_auth_run_common_checks: true일 때 LiteLLM이 어떤 DB 레코드를 로드할지도 알려줘요.

def UserAPIKeyAuth(
    api_key: Optional[str] = None,                    # The API key (will be hashed automatically)
    token: Optional[str] = None,                      # Hashed token for internal use
    key_alias: Optional[str] = None,                  # Key alias for identification
    user_id: Optional[str] = None,                    # User identifier (also used to load the user record)
    user_email: Optional[str] = None,                 # User email address
    user_role: Optional[LitellmUserRoles] = None,     # User role (PROXY_ADMIN, INTERNAL_USER, etc.)
    team_id: Optional[str] = None,                    # Team identifier (also used to load the team record)
    org_id: Optional[str] = None,                     # Organization identifier (also used to load the org record)
    end_user_id: Optional[str] = None,                # End-user identifier (also used to load the end-user record)
): ...

요율 제한 (Rate limits)

아래 모든 범위는 반환된 객체에서 직접 강제돼요. 플래그가 필요 없어요.

def UserAPIKeyAuth(
    # Key
    tpm_limit: Optional[int] = None,
    rpm_limit: Optional[int] = None,
    # User
    user_tpm_limit: Optional[int] = None,
    user_rpm_limit: Optional[int] = None,
    # Team
    team_tpm_limit: Optional[int] = None,
    team_rpm_limit: Optional[int] = None,
    # Per team-member
    team_member_tpm_limit: Optional[int] = None,
    team_member_rpm_limit: Optional[int] = None,
    # Per end-user
    end_user_tpm_limit: Optional[int] = None,
    end_user_rpm_limit: Optional[int] = None,
    # Per-model (key / team scoped)
    metadata: Dict = {},          # e.g. {"model_tpm_limit": {...}, "model_rpm_limit": {...}}
    team_metadata: Optional[Dict] = None,  # same keys, team scoped
): ...

note

모델별 요율 제한은 metadata(키)와 team_metadata(팀)에서 모델 이름을 키로 읽어요. 모델 키는 요청의 model 문자열과 정확히 같아야 해요. 아니면 한도가 조용히 건너뛰어져요. rpm_limit_per_model / tpm_limit_per_model은 객체에 존재하지만 무활동이에요. 대신 metadata / team_metadata를 사용하거나, 프로젝트 레코드를 사용하세요 (아래 참고).

고급 (Advanced)

def UserAPIKeyAuth(
    max_parallel_requests: Optional[int] = None,      # Concurrent request limit
    allowed_model_region: Optional[AllowedModelRegion] = None,  # Geographic restrictions
    blocked: Optional[bool] = None,                   # Whether the key is blocked
    config: Dict = {},                                # Configuration settings
): ...

객체 권한 (Object permission, MCP, agents 등)

from litellm.proxy._experimental.mcp_server.mcp_server_manager import (
    global_mcp_server_manager,
)

def _server_id(name: str) -> str:
    server = global_mcp_server_manager.get_mcp_server_by_name(name)
    if not server:
        raise ValueError(f"Unknown MCP server '{name}'")
    return server.server_id

object_permission = LiteLLM_ObjectPermissionTable(
    mcp_servers=[_server_id("deepwiki"), _server_id("everything")], # MCP servers this key is allowed to use
    mcp_tool_permissions={"deepwiki": ["search", "read_doc"]},      # optional per-server tool allow-list
)

UserAPIKeyAuth(
    object_permission=object_permission,
)

예산과 모델 액세스 강제하기 (Enforce budgets and model access)

custom_auth_run_common_checks: true를 설정해 커스텀 인증과 함께 예산 및 모델 액세스를 강제하세요:

general_settings:
  custom_auth: custom_auth.user_api_key_auth
  custom_auth_run_common_checks: true

핸들러가 ID를 반환하면, 예산과 허용 목록은 일치하는 DB 레코드(/team/new, /user/new, /project/new, /customer/new, 또는 UI)에 있으며, LiteLLM이 이를 로드해 강제해요.

예를 들어 예산과 모델 허용 목록이 있는 팀:

curl -X POST 'http://0.0.0.0:4000/team/new' \
  -H 'Authorization: Bearer ***' \
  -H 'Content-Type: application/json' \
  -d '{
    "team_id": "eng-team",
    "max_budget": 100,
    "models": ["gpt-5.6-luna", "claude-sonnet-5"]
  }'
# ...then return that team_id from custom auth:
return UserAPIKeyAuth(api_key=api_key, team_id="eng-team")

프로젝트 모델별 요율 제한의 경우 프로젝트 레코드에 model_tpm_limit / model_rpm_limit(모델 이름 키)을 설정하고 그 project_id를 반환하세요:

# On the project record (via /project/new or the UI):
#   model_tpm_limit = {"gpt-5.6-terra": 100000, "claude-sonnet-5": 50000}
#   model_rpm_limit = {"gpt-5.6-terra": 100,    "claude-sonnet-5": 200}

note

프로젝트 레코드의 metadata는 반환된 객체에 설정한 project_metadata를 대체하므로, 프로젝트 모델별 한도는 객체가 아니라 프로젝트 레코드에 구성하세요.

모델별 요율 제한의 경우 모델 키는 요청의 model 문자열과 정확히 같아야 해요. 아니면 한도가 조용히 건너뛰어져요. 이는 실제 Expedia 실패 모드였어요.

키 모델 vs 프로젝트 모델 (Key models vs project models)

이들은 별개의 제어예요:

필드 강제 위치 진실 소스
UserAPIKeyAuth의 models 키 수준 허용 목록 커스텀 인증에서 반환하는 값
UserAPIKeyAuth의 project_id 프로젝트 수준 허용 목록 LiteLLM DB의 프로젝트 레코드의 models

models 목록([])은 제한 없음을 의미해요. 이름은 콘피그의 모델 그룹과 일치해야 해요 (와일드카드 지원). Project Management와 Config Settings 참고.

키 수준 강제 (Key-level enforcement)

다음은 반환된 객체에서 강제되지만, litellm.enable_post_custom_auth_checks: true도 설정됐을 때만 그래요:

general_settings:
  custom_auth: custom_auth.user_api_key_auth
  custom_auth_run_common_checks: true   # required for the key models allowlist
litellm_settings:
  enable_post_custom_auth_checks: true
from datetime import datetime, timedelta, timezone

return UserAPIKeyAuth(
    api_key=api_key,
    models=["gpt-5.6-luna"],                                   # key model allowlist (needs both flags)
    model_max_budget={"gpt-5.6-terra": {"budget_limit": 100, "time_period": "30d"}},  # key per-model budget
    expires=datetime.now(timezone.utc) + timedelta(days=30),  # key expiry
)

이 경로는 end_user_id가 설정될 때 최종 사용자 예산과 모델별 최종 사용자 예산도 강제해요.

✨ LiteLLM 가상 키 + 커스텀 인증 지원 (Support LiteLLM Virtual Keys + Custom Auth)

v1.72.2+부터 지원

Enterprise 기능

LiteLLM 가상 키와 함께 커스텀 인증을 사용하려면 LiteLLM Enterprise 라이선스가 필요해요. 무료 30일 체험판을 시작하거나 데모를 예약하세요. Enterprise에 포함된 것 보기.

사용법 (Usage)

커스텀 auth 파일 설정

"""Example custom auth function.
This will allow all keys starting with "my-custom-key" to pass through."""
from typing import Union
from fastapi import Request
from litellm.proxy._types import UserAPIKeyAuth

async def user_api_key_auth(
    request: Request, api_key: str) -> Union[UserAPIKeyAuth, str]:
    try:
        if api_key.startswith("my-custom-key"):
            return "«redacted:sk-…»"
        else:
            raise Exception("Invalid API key")
    except Exception:
        raise Exception("Invalid API key")

config.yaml 설정

키 변경 mode: auto 설정. 이는 litellm api key auth와 custom auth를 모두 확인해요.

model_list:
  - model_name: "openai-model"
    litellm_params:
      model: "gpt-5.6-luna"
      api_key: os.environ/OPENAI_API_KEY
general_settings:
  custom_auth: custom_auth_auto.user_api_key_auth
  custom_auth_settings:
    mode: "auto" # can be 'on', 'off', 'auto' - 'auto' checks both litellm api key auth + custom auth

흐름 (Flow):

  1. 커스텀 인증 먼저 확인
  2. 커스텀 인증 실패 시 litellm api key auth 확인
  3. 둘 다 실패하면 401 반환

테스트!

curl -L -X POST 'http://0.0.0.0:4000/v1/chat/completions' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer ***' \
-d '{
    "model": "openai-model",
    "messages": [
          {
            "role": "user",
            "content": "Hey! My name is John"
          }
        ]
}'

커스텀 예외 버블업 (Bubble up custom exceptions)

커스텀 예외를 버블업하려면 ProxyException을 발생시키면 돼요.

"""Example custom auth function.
This will allow all keys starting with "my-custom-key" to pass through."""
from typing import Union
from fastapi import Request
from litellm.proxy._types import UserAPIKeyAuth, ProxyException

async def user_api_key_auth(
    request: Request, api_key: str) -> Union[UserAPIKeyAuth, str]:
    try:
        if api_key.startswith("my-custom-key"):
            return "«redacted:sk-…»"
        if api_key == "invalid-api-key":
            # raise a custom exception back to the client
            raise ProxyException(
                message="Invalid API key",
                type="invalid_request_error",
                param="api_key",
                code=401,
            )
        else:
            raise Exception("Invalid API key")
    except Exception:
        raise Exception("Invalid API key")