JWT → 가상 키 매핑

JWT → 가상 키 매핑 (JWT → Virtual Key Mapping)

JWT → 가상 키 매핑은 LiteLLM Enterprise 라이선스가 필요해요. 30일 무료 체험판을 시작하거나 데모를 예약하세요.

JWT 토큰을 LiteLLM 가상 키에 매핑해, 모든 JWT 클라이언트가 가상 키와 동일한 세밀한 제어(모델 제한, 지출 한도, 레이트 리밋, 가드레일, 완전한 지출 추적)를 받게 해요.

이것이 중요한 이유: 표준 JWT 인증은 JWT를 팀에 매핑해요. 이는 공유 경계이므로 팀 아래의 모든 클라이언트가 같은 제한을 공유해요. JWT → 가상 키 매핑에서는 각 개별 JWT 클라이언트(client_id, azp, sub 같은 클레임으로 식별)가 자신의 가상 키에 매핑돼요. 사용자에게 API 키를 발급하지 않고 클라이언트별 책임을 얻을 수 있어요.

일반적인 사용 사례: 회사가 SSO/OIDC를 사용해요. 개발자가 신원 토큰으로 Claude Code를 사용해요. 각 개발자에게 LiteLLM API 키를 주지 않고 개발자별 모델 접근과 지출 한도를 강제하고 싶어요.

출처: 문서

본문

동작 방식 (How It Works)

OIDC 프로바이더가 JWT를 발급하면 클라이언트(Claude Code/API)가 Authorization: Bearer <JWT>로 프록시에 요청해요. 프록시는 JWT 서명을 검증하고 클레임(예: client_id = "[email protected]")을 추출한 뒤 (claim_name, claim_value)를 조회해 key = sk-abc123을 찾아요. 그러면 가상 키 권한(모델, 예산, 레이트 리밋)을 적용해 200 OK를 반환해요. 매핑이 없으면 fallback_team_mapping(팀 JWT 인증으로 대체), reject(403), 또는 auto_register(새 가상 키+매핑 생성) 중 하나로 처리해요.

설정 (Setup)

사전 요구사항 (Prerequisites)

먼저 OIDC JWT 인증 설정을 완료해요. JWT_PUBLIC_KEY_URL이 구성되고 프록시 config에 enable_jwt_auth: True가 필요해요.

1단계: 매핑할 JWT 클레임 구성

litellm_jwtauth config에 virtual_key_claim_field를 추가해요. 이것이 LiteLLM이 조회 키로 사용하는 JWT 클레임이에요.

general_settings:
  master_key: os.environ/LITELLM_MASTER_KEY
  enable_jwt_auth: True
  litellm_jwtauth:
    team_id_jwt_field: "team_id"          # existing team mapping (optional)
    user_id_jwt_field: "sub"
    virtual_key_claim_field: "client_id"  # claim used as the key-mapping lookup
    unregistered_jwt_client_behavior: "fallback_team_mapping"  # see below

virtual_key_claim_field는 이전에 jwt_client_id_field로 명명되었으며, 옛 이름도 하위 호환 별칭으로 여전히 동작해요.

unregistered_jwt_client_behavior는 JWT에 등록된 매핑이 없을 때 일어나는 일을 제어해요.

동작
fallback_team_mapping 팀 기반 JWT 인증으로 대체 (기본값 — 하위 호환)
reject 매핑이 없으면 403 반환
auto_register 최초 마주침 시 가상 키 + 매핑 자동 생성

auto_register를 사용하면 새 클레임 값이 담긴 첫 요청이 관리자 호출 없이 즉석에서 가상 키와 매핑을 프로비저닝해요. 키는 JWT가 전체 정책(서명, RBAC/scope, custom_validate, user_allowed_email_domain)을 통과한 뒤에만 생성되며, 토큰이 어떤 검사에도 실패하면 요청이 거부되고 아무것도 생성되지 않아요. 새 키는 검증된 JWT에서 해석된 팀·사용자·조직을 상속하므로 해당 클레임이 구성되어 있는지 확인하세요. 프록시 관리자로 해석되는 토큰은 관리자가 이미 전체 접근을 가지므로 자동 등록되지 않아요. auto_register는 데이터베이스 연결이 필요해요.

2단계: JWT 클라이언트 → 가상 키 매핑 등록

권장: auto_register에 맡기세요. 1단계에서 unregistered_jwt_client_behavior: "auto_register"를 설정하면 각 새 클레임 값의 첫 요청이 관리자 호출 없이 자동으로 자신의 키를 프로비저닝해요. 모든 클라이언트가 같은 기본값에서 시작해야 할 때 사용해요.

수동: 특정 제한으로 키를 등록해요. 클라이언트가 자신의 예산이나 모델 세트가 필요하면 먼저 가상 키를 만든 뒤 클레임 값을 그 키에 매핑해요. 단일 원자적 엔드포인트는 없으며 두 번의 호출이에요.

# 1. Create a virtual key with the limits you want
curl -X POST 'http://0.0.0.0:4000/key/generate' \
  -H 'Authorization: Bearer <PROXY...KEY>' \
  -H 'Content-Type: application/json' \
  -d '{
    "models": ["claude-sonnet-5", "claude-opus-5"],
    "max_budget": 50.0,
    "budget_duration": "30d",
    "rpm_limit": 100,
    "tpm_limit": 50000,
    "team_id": "engineering"
  }'
# -> {"key": "sk-abc123...", ...}

# 2. Map a JWT claim value to that key
curl -X POST 'http://0.0.0.0:4000/jwt/key/mapping/new' \
  -H 'Authorization: Bearer <PROXY...KEY>' \
  -H 'Content-Type: application/json' \
  -d '{
    "jwt_claim_name": "client_id",
    "jwt_claim_value": "dev-alice",
    "key": "sk-abc123...",
    "description": "dev-alice"
  }'

3단계: 테스트

# Get a JWT from your OIDC provider (must have client_id: dev-alice)
JWT_TOKEN="eyJhbG..."

curl -X POST 'http://0.0.0.0:4000/v1/chat/completions' \
  -H "Authorization: Bearer ***" \
  -H 'Content-Type: application/json' \
  -d '{
    "model": "claude-sonnet-5",
    "messages": [{"role": "user", "content": "Hello"}]
  }'

이제 요청은 dev-alice의 가상 키에 대해 추적되며, 지출·레이트 리밋·모델 접근이 클라이언트별로 강제돼요.

워크스루: 관리자가 세밀한 접근을 부여하고 팀이 Claude Code 사용

회사 SSO로 Claude Code를 사용하는 엔지니어링 팀의 전체 흐름이에요.

관리자 설정

  1. 엔지니어링 팀 생성
curl -X POST 'http://0.0.0.0:4000/team/new' \
  -H 'Authorization: Bearer ***' \
  -H 'Content-Type: application/json' \
  -d '{
    "team_alias": "engineering",
    "models": ["claude-sonnet-5", "claude-opus-5"]
  }'
  1. 각 개발자에게 자신의 키와 지출 한도 등록

각 개발자는 가상 키 + 그들의 JWT 클레임에서 해당 키로의 매핑이에요. /key/generate로 키를 만들고 /jwt/key/mapping/new로 클레임 값을 매핑해요.

# Alice: senior eng, higher budget
ALICE_KEY=$(curl -s -X POST 'http://0.0.0.0:4000/key/generate' \
  -H 'Authorization: Bearer ***' -H 'Content-Type: application/json' \
  -d '{"team_id": "engineering", "models": ["claude-sonnet-5", "claude-opus-5"], "max_budget": 200.0, "budget_duration": "30d", "rpm_limit": 200}' \
  | jq -r '.key')

curl -X POST 'http://0.0.0.0:4000/jwt/key/mapping/new' \
  -H 'Authorization: Bearer ***' -H 'Content-Type: application/json' \
  -d "{\"jwt_claim_name\": \"client_id\", \"jwt_claim_value\": \"[email protected]\", \"key\": \"$ALICE_KEY\", \"description\": \"[email protected]\"}"

# Bob: contractor, tighter limits
BOB_KEY=$(curl -s -X POST 'http://0.0.0.0:4000/key/generate' \
  -H 'Authorization: Bearer ***' -H 'Content-Type: application/json' \
  -d '{"team_id": "engineering", "models": ["claude-sonnet-5"], "max_budget": 20.0, "budget_duration": "30d", "rpm_limit": 30}' \
  | jq -r '.key')

curl -X POST 'http://0.0.0.0:4000/jwt/key/mapping/new' \
  -H 'Authorization: Bearer ***' -H 'Content-Type: application/json' \
  -d "{\"jwt_claim_name\": \"client_id\", \"jwt_claim_value\": \"[email protected]\", \"key\": \"$BOB_KEY\", \"description\": \"[email protected]\"}"

모든 사람이 같은 기본값에서 시작하는 팀에서는 개발자별 호출을 건너뛰고 unregistered_jwt_client_behavior: "auto_register"로 설정하세요.

  1. Claude Code가 프록시를 사용하도록 구성

팀의 Claude Code 설정에서 API base를 프록시로 설정해요.

# Point Claude Code at the LiteLLM proxy instead of Anthropic directly.
# ANTHROPIC_API_KEY here is the bearer token sent to the proxy — set it to
# the user's SSO/OIDC JWT token (obtained from your IdP at login).
export ANTHROPIC_API_KEY="<user-sso-jwt-token>"
export ANTHROPIC_BASE_URL="http://your-litellm-proxy:4000"

또는 ~/.claude/settings.json:

{
  "env": {
    "ANTHROPIC_BASE_URL": "http://your-litellm-proxy:4000"
  }
}
  1. 개발자는 평소처럼 SSO로 인증

Alice가 Claude Code를 실행하면 그녀의 JWT(client_id: [email protected]으로 발급)가 프록시로 가요. LiteLLM은 매핑을 조회해 그녀의 가상 키를 찾고 그녀의 구체적 제한(월 $200 예산, 200 RPM 상한, Sonnet과 Opus만)을 강제해요.

Bob의 토큰은 자신의 키에 매핑됩니다: 월 $20, Sonnet만, 30 RPM.

API 키 배포 없음. 공유 제한 없음. LiteLLM 대시보드에서 완전한 개발자별 지출 가시성.

매핑 관리 (Managing mappings)

모든 매핑은 id를 가져요(생성 시 반환). info, update, delete 엔드포인트가 그 id를 키로 사용하므로 list부터 시작해서 찾아요.

매핑 목록:

curl 'http://0.0.0.0:4000/jwt/key/mapping/list?page=1&size=50' \
  -H 'Authorization: Bearer ***'

id로 매핑 하나 보기:

curl 'http://0.0.0.0:4000/jwt/key/mapping/info?id=<mapping-id>' \
  -H 'Authorization: Bearer ***'

응답은 매핑 자체의 메타데이터예요. 클레임 이름·값, 설명, is_active, 타임스탬프, 생성/마지막 업데이트한 사람. 연결된 키나 그 설정은 포함하지 않으며, 해시된 키는 절대 반환되지 않아요. 키의 모델·예산·지출을 검사하려면 /key/info를 사용하세요.

매핑 업데이트:

update는 매핑 자신을 바꿔요. 다른 키를 가리키거나, 설명을 편집하거나, is_active를 토글해요. 예산이나 모델 접근을 바꾸려면 /key/update로 기본 키를 업데이트해요. 매핑은 클레임, 연결된 키, 설명, 활성 플래그만 저장해요.

curl -X POST 'http://0.0.0.0:4000/jwt/key/mapping/update' \
  -H 'Authorization: Bearer ***' \
  -H 'Content-Type: application/json' \
  -d '{
    "id": "<mapping-id>",
    "key": "sk-newkey...",
    "description": "rotated key",
    "is_active": true
  }'

매핑 삭제:

curl -X POST 'http://0.0.0.0:4000/jwt/key/mapping/delete' \
  -H 'Authorization: Bearer ***' \
  -H 'Content-Type: application/json' \
  -d '{"id": "<mapping-id>"}'

보안 (Security)

다중 신원 공급자 (Multiple identity providers)

매핑은 (jwt_claim_name, jwt_claim_value)에만 키가 매겨지며 발급자별 차원이 없어요. 두 신원 공급자가 같은 클레임 값을 방출할 수 있다면(예: 둘 다 sub: user-123 전송) 그 토큰들은 같은 매핑으로 해석되어 충돌해요. 공급자 간 전역적으로 고유한 클레임(예: email)에 매핑하거나, 발급자 바인딩 JWT 규칙으로 발급자별 검증을 구성해 각 공급자의 신원이 고유 클레임 값에 들어가게 하세요.

JWT 클라이언트가 가상 키 대비 할 수 있는 것과 없는 것

기능 가상 키 JWT → 키 매핑
클라이언트별 모델 접근
클라이언트별 지출 예산
클라이언트별 RPM/TPM 제한
팀 멤버십
대시보드 지출 추적
가드레일
키 로테이션 ✅ (관리자만)
키 만료
API 키 배포 불필요
기존 SSO/OIDC 사용

더 알아보기 (Learn more)