MCP OAuth 패스스루

MCP OAuth 패스스루 (OAuth Passthrough)

일부 MCP 서버는 자체 OAuth issuer를 실행하고 클라이언트(Claude Code, Cursor, ChatGPT 등)가 그것에 직접 인증하길 기대해요. 그런 서버에는 LiteLLM이 자체적으로 발행·저장·새로고침하지 않고 클라이언트 자신의 업스트림 토큰이 통과하도록 할 수 있어요.

auth_type 값이 이를 다뤄요. 이 둘은 한 가지가 달라요: LiteLLM이 여전히 자체 경계에서 호출자를 인증하는지 여부예요.

모드 LiteLLM 입장 업스트림으로 전달되는 자격 증명 지출 / 속도 제한 / 감사 사용 시점
true_passthrough 없음; LiteLLM 레이어에서 익명 클라이언트의 Authorization 그대로 기록되지 않음 LiteLLM이 인증을 전혀 추가하지 않고 업스트림이 유일한 게이트여야 할 때
oauth_delegate 필수(LiteLLM 키 / SSO / JWT) 입장과 함께 호출자가 보내는 별개의 업스트림 베어러 기록되며 입장 신원 기준 업스트림이 도구 권한을 소유하면서도 LiteLLM이 경로를 계속 게이팅·관찰하게 하고 싶을 때

두 모드 모두 디스커버리 중 업스트림의 보호된 리소스 메타데이터를 그대로 반환하므로 클라이언트는 항상 실제 업스트림 issuer에 대해 권한을 부여해요.

둘 다 호출자가 보낸 그대로 토큰을 전달해요. LiteLLM은 디코드하지 않고, audience나 스코프를 확인하지 않으며, 교환하지 않아요. 업스트림 MCP 서버만이 검증하는 유일한 당사자예요. 호출자 토큰이 다른 리소스용으로 발급되었다면 두 모드 모두 작동시킬 수 없고, oauth2_token_exchange가 필요해요.

둘 다 직교 dcr_bridge 플래그를 받는데, 이는 OAuth 전용 클라이언트가 authorization server를 어디서 발견하는지 바꿔요. 업스트림 IdP에 등록할 수 없거나 두 개의 별도 자격 증명을 보낼 수 없는 클라이언트(OpenCode, Claude Code, Cursor, Claude Desktop)에 대해 켜세요. Gateway-hosted sign-in (DCR bridge) 참고.

출처: 문서

본문

Audience 고정 토큰: 패스스루 또는 토큰 교환 (Audience-locked tokens: passthrough or token exchange)

OAuth 액세스 토큰은 audience(aud, 요청된 리소스)를 담아요. 올바르게 동작하는 업스트림 MCP 서버는 다른 것을 가리키는 audience의 토큰을 거부하는데, 예를 들어 에이전트가 SaaS API용으로 얻은 토큰을 MCP 서버에 제시한 경우예요. true_passthroughoauth_delegate는 토큰을 그대로 전달하므로 그 거부는 업스트림의 자체 401로 클라이언트에 중계돼요. 이는 올바른 confused-deputy-safe 결과예요. 게이트웨이가 한 리소스용으로 발급된 토큰을 다른 것에 대한 접근으로 세탁하지 않은 것이고, 파일링할 LiteLLM 버그가 아니에요.

실제 보유한 토큰에서 모드를 고르세요:

호출자 토큰이 발급된 대상 사용 LiteLLM이 그것으로 하는 일
업스트림 MCP 서버 자체(클라이언트가 그 서버의 issuer에 대해 OAuth 실행) true_passthrough 또는 oauth_delegate 그대로 전달; 업스트림이 audience·스코프 검증
다른 리소스(자신의 IdP, 내부 API, SaaS API)이고 IdP가 RFC 8693 또는 Entra On-Behalf-Of 지원 oauth2_token_exchange IdP에 subject_token으로 보내고, audience가 MCP 서버인 토큰을 받아(audience: ... 서버 구성) 캐시하고 교환된 토큰만 전달. MCP OBO Auth 참고
LiteLLM 가상 키, SSO 세션, IdP JWT(게이트웨이에만 신원을 증명) 패스스루 모드 둘 다 아님 입장 자격 증명은 업스트림으로 전달되지 않음. 대신 서버 측 자격 증명(oauth2 클라이언트 자격 증명, 정적 authentication_token, 또는 토큰 교환) 구성

실용 테스트: 업스트림이 받아들이도록 IdP에 토큰 audience를 넓히게 요청해야 한다면 멈추고 토큰 교환을 쓰세요. audience를 넓히면 하나의 베어러가 여러 리소스의 키가 되며, 패스스루 모드는 정확히 게이트웨이가 호출자를 대신해 그렇게 하지 않도록 존재해요.

true_passthrough

LiteLLM은 투명 프록시로 작동해요. 입장 검사 없음, 발행·저장 없음, 클라이언트의 Authorization이 변경 없이 전달. 접근의 진실 소스가 업스트림이고 LiteLLM이 경로를 두 번 게이팅하기 싫을 때 사용하세요.

설정 (Setup)

config.yaml:

mcp_servers:
  notion_passthrough:
    url: "https://mcp.notion.com/mcp"
    auth_type: true_passthrough

그게 전체 구성이에요. LiteLLM이 토큰 교환에 참여하지 않으므로 클라이언트 자격 증명이나 토큰 엔드포인트가 없어요.

동작 방식 (How It Works)

  • 클라이언트가 LiteLLM API 키 없이 MCP 요청을 보냅니다.
  • 아직 업스트림 토큰이 없으면 LiteLLM이 업스트림의 자체 401WWW-Authenticate를 중계해요.
  • 클라이언트는 업스트림 issuer에 대해 OAuth를 직접 실행해요.
  • 클라이언트가 Authorization: Bearer <upstr...n>으로 재시도하면 LiteLLM이 손대지 않고 전달해요.

Fail-Closed 동작 (Fail-Closed Behavior)

투명 경로는 모든 대상이 true_passthrough로 해석될 때만 작동해요. 다음 경우에는 정상 LiteLLM 입장으로 폴백해요:

  • 서버의 auth_type이 다른 것일 때.
  • 요청이 여러 서버(x-mcp-servers: a,b)를 대상으로 하고 그중 하나가 true_passthrough가 아닐 때.
  • 대상 서버를 URL 경로나 x-mcp-servers 헤더에서 해결할 수 없을 때.

보안 트레이드오프 (Security Trade-offs)

  • MCP 경로가 LiteLLM 레이어에서 인증 없는 인그레스가 돼요.
  • 지출 추적, 키별 속도 제한, user_api_key_auth.user_id에 의존하는 가드레일이 실행되지 않아요.
  • LiteLLM은 호출자가 누구인지 알 수 없으므로 사용자별 감사는 업스트림 서버 로그에서 나와야 해요.
  • available_on_public_internet: false는 여기서 인증을 추가하지 않아요. 주로 IP 기반 디스커버리를 제어해요(가이드 참고).
  • 액세스 제어를 강제할 것을 신뢰하는 업스트림 OAuth issuer를 가진 서버에서만 활성화하세요.

구성 참조 (Config Reference)

필드 필수 설명
auth_type true_passthrough여야 함.
url 업스트림 MCP 서버 URL.
allowed_tools 아니오 서버 레벨 도구 허용 목록. 호출자 신원이 없으므로 키·팀별 도구 권한이 없고, 이 목록이 유일한 도구 제한이며 모든 호출자에게 동일하게 적용.

config.yaml:

mcp_servers:
  notion_passthrough:
    url: "https://mcp.notion.com/mcp"
    auth_type: true_passthrough
    allowed_tools:
      - search
      - fetch

oauth_delegate

LiteLLM은 여전히 호출자(LiteLLM API 키, SSO, JWT)를 입장시킨 다음, 호출자가 공급하는 별도 업스트림 베어러를 전달해요. LiteLLM은 아무것도 발행하지 않고 입장 자격 증명을 업스트림으로 전달하지 않아요. 업스트림이 도구 레벨 권한을 소유하지만 여전히 LiteLLM이 경로를 게이팅하고 지출·속도 제한·감사 귀속을 유지하길 원할 때 사용하세요.

설정 (Setup)

config.yaml:

mcp_servers:
  notion_delegate:
    url: "https://mcp.notion.com/mcp"
    auth_type: oauth_delegate

true_passthrough와 같은 이유로 클라이언트 자격 증명이 없어요. 바뀌는 것은 요청이에요. 호출자가 두 자격 증명을 보냅니다.

동작 방식 (How It Works)

  • 호출자가 x-litellm-api-key의 LiteLLM 자격 증명으로 입장.
  • 업스트림 토큰은 Authorization(또는 집계 요청의 x-mcp-<alias>-authorization)에 실림.
  • LiteLLM이 입장을 검증한 다음 업스트림 베어러만 전달하고 입장 자격 증명은 절대 전달하지 않아요.
  • 아직 업스트림 토큰이 없으면 LiteLLM이 게이트웨이의 oauth-protected-resource well-known을 가리키는 401을 반환하며, 이는 업스트림 메타데이터를 그대로 프록시해요.

두 자격 증명을 별도 헤더에 두세요 — 호출자가 x-litellm-api-key 없이 단일 자격 증명을 Authorization에 보내면 LiteLLM은 그것을 입장 자격 증명(가상 키, IdP JWT, SSO 세션 토큰)으로 취급하고 업스트림으로 전달하지 않아요. 이것이 LiteLLM·IdP 토큰이 제3자 MCP 서버에 도달하지 못하게 하는 유출 방어예요.

보안 트레이드오프 (Security Trade-offs)

  • 입장이 항상 실행되므로 익명 인그레스가 없어요.
  • 지출, 속도 제한, 감사가 입장 신원에 대해 해석돼요.
  • LiteLLM이 업스트림 토큰을 검사하지 않고 전달하므로, 업스트림이 여전히 도구 레벨 권한과 토큰 검증을 소유해요.

구성 참조 (Config Reference)

필드 필수 설명
auth_type oauth_delegate여야 함.
url 업스트림 MCP 서버 URL.

요청 시점: 입장은 x-litellm-api-key, 업스트림 토큰은 Authorization: Bearer ***(또는 집계 요청에서 특정 서버용 x-mcp-<alias>-authorization).

입장이 실행되므로 업스트림이 강제하는 것 위에 전체 LiteLLM 권한 모델이 적용돼요. 키·팀별 도구 권한, 서버 레벨 allowed_tools 목록, 키별 속도 제한, 입장된 신원 아래의 모든 도구 호출 지출 로깅.

다중 서버 집계 요청 (Multi-server aggregate requests)

집계 /mcp 엔드포인트(또는 x-mcp-servers: a,b를 가진 요청)에 대한 요청은 여러 업스트림으로 팬아웃되지만 요청은 Authorization 헤더 하나만 담을 수 있어요. 그 업스트림 중 둘이 모두 호출자 토큰을 전달한다면 그 하나의 헤더를 둘 다에 보내는 것은 관련 없는 리소스에 걸쳐 단일 베어러를 재생(replay)하는 것(RFC 9700이 경고하는 교차 리소스 재생)이 돼요. 따라서 LiteLLM은 집계 범위 안의 true_passthrough·oauth_delegate 서버에 두 규칙을 적용해요.

하나의 업스트림 토큰을 x-mcp-{alias}-authorization으로 한 서버에 바인딩하세요. 별칭은 소문자화되고 a-z0-9_ 밖의 문자는 _이 되므로 Jira Cloud로 별칭된 서버는 x-mcp-jira_cloud-authorization으로 다뤄져요. 값은 그대로 전달되므로 스킴을 포함하세요(Bearer <token>). 서버별 헤더는 각각 정확히 한 수신자를 지정하므로 어떤 동작에서도 보류되지 않아요.

목록 팬아웃(tools/list, 프롬프트·리소스 목록) 동안 같은 범위의 다른 서버도 그것을 소비할 때마다 요청 전체 Authorization은 클라이언트 전달 서버에서 보류돼요. 그 서버는 업스트림 자격 증명 없이 나열되므로 자격 증명이 필요한 서버는 그 401을 반환하고 집계가 그것을 흡수해요(아래 참고). 이름 있는 도구의 tools/call, /{server_name}/mcp 같은 단일 서버 경로, 또는 하나의 서버만 호출자 토큰을 전달하는 집계 범위 같은 명시적으로 주소 지정된 동작은 영향받지 않아요. 클라이언트가 단일 수신자를 지정했으므로 요청 전체 헤더가 그것에 전달돼요.

실제로 이것이 여러 업스트림에 유효한 단일 베어러가 집계 엔드포인트를 통해 도구를 실행할 수 있는데도 집계 tools/list에는 나타나지 않는 이유예요. 도구 호출은 한 서버를 이름 지시고 목록은 그렇게 하지 않기 때문이에요. 해결책은 요청 전체 대신 서버별로 토큰을 보내는 거예요.

서버별 토큰이 있는 집계 tools/list:

curl -X POST "https://litellm.example.com/mcp" \
  -H "Content-Type: application/json" \
  -H "x-litellm-api-key: *** $LITELLM_API_KEY" \
  -H "x-mcp-servers: jira,confluence" \
  -H "x-mcp-jira-authorization: Bearer ***" \
  -H "x-mcp-confluence-authorization: Bearer <token...nce>" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'

두 서버별 헤더에 같은 토큰 값을 보내는 것은 서버별로 명시적으로 내리는 결정이며 LiteLLM이 이를 존중해요. LiteLLM이 거부하는 것은 주소가 없는 Authorization을 팬아웃해서 당신을 위해 그 결정을 내리는 거예요.

true_passthrough에는 추가 제약이 있어요. 투명 입장 경로는 범위의 모든 서버가 true_passthrough일 때만 작동해요. true_passthrough 서버를 다른 모드와 집계에 섞으면 정상 LiteLLM 입장으로 폴백하므로 호출자가 그 요청에 LiteLLM 자격 증명이 필요해요.

관리 UI에서 도구 미리보기 (Previewing tools in the Admin UI)

LiteLLM은 이 서버들에 대해 업스트림 토큰을 보유하지 않으므로 생성·편집 폼이 자체적으로 도구를 나열할 수 없어요. 두 폼 모두 true_passthrough·oauth_delegate에 "Authorize & Fetch Tools (browser-only)" 버튼을 보여줘요. 이는 관리자 브라우저에서 업스트림 OAuth 흐름을 실행하고 결과 토큰을 브라우저 세션에만 유지하며, 도구 미리보기와 allowed_tools 구성용으로 서버별 전달해요. 토큰은 서버 행, 사용자별 자격 증명 저장소, 어떤 캐시에도 쓰이지 않아요. 탭을 닫으면 폐기돼요.

그 버튼 옆의 선택적 OAuth Client ID와 Client Secret은 달라요. 선언된 구성으로 서버와 함께 저장돼요. 업스트림 issuer가 동적 클라이언트 등록을 지원하지 않고 모든 관리자가 하나의 사전 등록된 앱을 통해 권한을 부여해야 할 때 설정하세요.

의도적 한계 (Intentional limits)

다음은 토큰을 검사하지 않고 그대로 전달하는 것의 결과이며 결함이 아니라 설계에 의한 것이에요.

다중 서버 집계에는 서버별 "재인증 필요" 신호가 없어요. 단일 서버 경로는 업스트림의 401WWW-Authenticate를 진실하게 중계하지만, 집계는 한 서버의 인증 실패를 그 서버의 빈 목록에 흡수해 나머지 서버가 여전히 나열되게 해요. 어떤 업스트림이 토큰을 거부했는지 봐야 한다면 서버별 헤더 또는 단일 서버 경로를 쓰세요.

발신자 제한 토큰(DPoP, RFC 9449, 또는 mTLS 바운드 토큰 RFC 8705)은 중계될 수 없어요. 바인딩은 요청이 토큰을 얻은 TLS 클라이언트 또는 키 보유자에게서 왔음을 증명하며, 레이어-7 프록시는 어느 쪽도 아니에요. 업스트림이 그것을 거부할 것이므로, MCP 서버용 일반 베어러를 얻거나 토큰 교환을 쓰세요.

철회된 토큰은 연결 시점에 감지되지 않아요. LiteLLM은 전달된 토큰에 대해 상태를 유지하지 않고 인트로스펙트하지 않으므로, issuer에서 철회된 토큰은 전달되고 그것을 사용하는 요청에서 업스트림이 거부해요. 클라이언트는 그 시점에 업스트림의 401을 봅니다. 업스트림에 직접 말하는 것과 정확히 같아요.

게이트웨이 호스팅 로그인 (DCR 브리지) (Gateway-hosted sign-in (DCR bridge))

OAuth 전용 MCP 클라이언트(OpenCode, Claude Code, Cursor, Claude Desktop)는 디스커버리 메타데이터가 광고하는 어떤 authorization server에 대해 단일 Dynamic Client Registration(RFC 7591) + PKCE 흐름을 실행해 연결해요. 이들은:

  • 업스트림 IdP에 사전 프로비저닝된 클라이언트 자격 증명을 보유하지 않음.
  • 업스트림 토큰과 함께 별도의 LiteLLM 자격 증명을 보낼 수 없음.

그래서 일반 true_passthrough 요청이나 두 헤더 oauth_delegate 요청이 이들에게 맞지 않아요. dcr_bridge 플래그가 그 간극을 메워요:

  • 켬: LiteLLM이 디스커버리 중 자신을 authorization server로 광고하고 /{server_name}/register, /{server_name}/authorize, /{server_name}/token을 호스팅해요. 클라이언트는 게이트웨이를 통해 등록·로그인하며 LiteLLM이 그 엔드포인트 뒤에서 업스트림 OAuth를 실행해요.
  • 끔: LiteLLM이 업스트림 서버 자신의 OAuth 메타데이터를 그대로 중계해요. 이미 업스트림 IdP에 등록되었거나 그것에 대해 DCR을 직접 실행할 수 있는 클라이언트에 적합.

어디에 설정하는가:

  • true_passthrough·oauth_delegate에서만 유효하며, 다른 auth_type에서는 생성·업데이트·config 로드 시 거부돼요.
  • 관리 UI에서는 MCP 서버 폼의 "Gateway-hosted sign-in (DCR bridge)" 스위치이며, 두 모드에서 기본 켜짐.
  • config에서는 불리언 필드.

브리지가 있는 true_passthrough (true_passthrough with the bridge)

config.yaml:

mcp_servers:
  miro:
    url: "https://mcp.miro.com/"
    transport: http
    auth_type: true_passthrough
    dcr_bridge: true

OAuth 전용 클라이언트를 위한 투명 옵션:

  • 클라이언트가 게이트웨이를 자신의 authorization server로 발견하고 POST /{server_name}/register로 등록.
  • GET /{server_name}/authorizePOST /{server_name}/token에 대해 PKCE 실행.
  • 업스트림이 DCR을 지원하면 LiteLLM이 클라이언트 등록을 그것에 중계.
  • 서버에 저장된 client_id가 없으면 LiteLLM이 일시적(ephemeral) 클라이언트를 발행하고 아무것도 유지하지 않음.
  • LiteLLM 로그인은 개입되지 않으며 호출자 신원·지출도 기록되지 않음.

브리지가 있는 oauth_delegate (oauth_delegate with the bridge)

config.yaml:

mcp_servers:
  miro:
    url: "https://mcp.miro.com/"
    transport: http
    auth_type: oauth_delegate
    dcr_bridge: true

이 조합은 OAuth 전용 클라이언트에 대해 LiteLLM을 관측성 경로에 유지하므로 지출, 속도 제한, 감사, 도구 호출별 귀속이 모두 해석돼요. 호출자가 어떤 서버를 사용했는지, 어떤 도구를 호출했는지 LiteLLM에서 보고 싶을 때 사용하세요.

OAuth 전용 클라이언트는 LiteLLM 키를 인라인으로 제시할 수 없으므로 신원은 LiteLLM 브라우저 세션에서 나와요:

  • authorize 단계에서 LiteLLM이 LiteLLM UI 세션 쿠키를 찾음.
  • 쿠키가 없으면 먼저 LiteLLM 로그인(/sso/key/generate)으로 리다이렉트.
  • 로그인 후 사용자가 연결을 다시 시작.
  • LiteLLM이 신원과 업스트림 토큰을 게이트웨이 바운드 자격 증명에 봉인.
  • 클라이언트가 그 자격 증명을 저장하고 이후 모든 요청에서 재생; 입장·지출·감사가 그것에 대해 해석.

두 가지 사전 요구사항:

  • 게이트웨이가 작동하는 브라우저 로그인이 필요해요(SSO 또는 사용자 이름/비밀번호). 그게 없으면 바인딩할 신원이 없고 authorize 단계가 진행될 수 없어요. 게이트웨이 로그인이 없으면 oauth_delegate 브리지 연결이 로그인 페이지에서 멈추는 일반적인 이유예요.
  • 클라이언트의 OAuth 흐름이 대화형 브라우저 세션에서 실행되어야 해요. OpenCode, Claude Code, Cursor, Claude Desktop 모두 그렇게 해요.

완전히 스크립트된 비대화형 위임을 위해 dcr_bridge를 끄고 두 헤더 oauth_delegate 요청을 쓰세요.

플래그 선택 (Choosing the flag)

상황 dcr_bridge
업스트림 client_id를 보유하지 않고 두 자격 증명을 보낼 수 없는 OAuth 전용 클라이언트(OpenCode, Claude Code, Cursor, Claude Desktop, ChatGPT) true
이미 업스트림 IdP에 등록되었거나 업스트림에 대해 DCR을 직접 실행하는 클라이언트 false
x-litellm-api-key + 업스트림 Authorization 베어러를 보낼 수 있는 스크립트된 비대화형 호출자 false, auth_type: oauth_delegate(두 헤더 형태)

클라이언트 연결 (Connecting a client)

  • 클라이언트를 https://<gateway-host>/<server_name>/mcp에 지정하고 거기서 OAuth를 발견하게 하세요.
  • 클라이언트에 client_id나 시크릿을 구성하지 마세요. 게이트웨이가 등록과 토큰 교환을 처리해요.
  • true_passthrough 브리지 서버에서 브라우저 흐름은 업스트림으로만 권한을 부여해요.
  • oauth_delegate 브리지 서버에서는 먼저 LiteLLM에 로그인한 다음 업스트림으로 권한을 부여해요.

정확한 필드 이름은 시간이 지나며 바뀌므로 각 클라이언트의 MCP 문서를 따르세요.

Claude Code:

claude mcp add --transport http miro https://<gateway-host>/miro/mcp

Cursor:

{
  "mcpServers": {
    "miro": {
      "url": "https://<gateway-host>/miro/mcp"
    }
  }
}

OpenCode:

{
  "mcp": {
    "miro": {
      "type": "remote",
      "url": "https://<gateway-host>/miro/mcp",
      "enabled": true
    }
  }
}

구성 참조 (Config Reference)

필드 필수 설명
auth_type true_passthrough 또는 oauth_delegate여야 함. dcr_bridge는 다른 값에서는 거부.
url 업스트림 MCP 서버 URL.
dcr_bridge true면 OAuth 전용 클라이언트의 로그인을 게이트웨이가 호스팅. 끄면 업스트림 자신의 OAuth 메타데이터를 중계.

업스트림으로 인증 위임(PKCE 패스스루) (Delegate Auth to Upstream (PKCE Passthrough))

더 이상 사용하지 않음delegate_auth_to_upstream은 투명 패스스루의 원래 플래그 기반 형태이며 폐기 예정이에요. auth_type: true_passthrough의 직접 전신이고 같은 방식으로 동작하며(동작 방식·fail-closed·보안 트레이드오프 동일), 새 서버는 true_passthrough를 사용해야 해요. 아래 섹션은 기존 config를 위해 유지돼요.

클라이언트가 이미 업스트림 서버 자신의 OAuth issuer에 직접 인증하는 OAuth2 MCP 서버의 경우 경로를 업스트림 위임 인증으로 옵트인할 수 있어요. LiteLLM이 자체 API 키/SSO 확인을 멈추고 클라이언트의 PKCE 흐름이 업스트림 MCP 서버와 종단 간 실행되게 해요.

설정 (Setup)

config.yaml:

mcp_servers:
  notion_mcp:
    url: "https://mcp.notion.com/mcp"
    auth_type: oauth2
    oauth2_flow: authorization_code
    delegate_auth_to_upstream: true

위임된 서버는 대화형이므로 oauth2_flow: authorization_code를 받아요. 플래그는 auth_type: oauth2일 때만 적용되며, 다른 인증 타입에 설정하면 조용히 무시돼요.

내부 전용(available_on_public_internet: false) 및 업스트림 PKCE 위임 — 상호작용 서버(non-oauth2_flow: client_credentials)에서 **available_on_public_internet: false**와 **delegate_auth_to_upstream: true**를 함께 사용해도 여전히 익명 호출자가 LiteLLM API 키 세션 없이 일치하는 MCP 경로에 대해 업스트림 OAuth2 /authorize 흐름에 도달하고 PKCE를 완료할 수 있어요. 내부 전용 플래그는 주로 IP 기반 디스커버리와 관련 동작을 제어하며(가이드) 이 위임 우회를 비활성화하지 않아요. 해야 할 일: 업스트림 IdP와 네트워크 경계에서 접근을 강제하세요. LiteLLM UI는 두 설정이 모두 활성화되면 경고를 표시하고, 프록시는 서버가 config나 데이터베이스에서 로드될 때 경고를 기록해요.

동작 방식 (How It Works)

  1. 클라이언트가 x-litellm-api-key 없이(MCP 요청에 Authorization 헤더 선택) LiteLLM에 MCP 요청을 보냅니다.
  2. LiteLLM이 요청의 모든 대상 서버가 auth_type: oauth2 AND delegate_auth_to_upstream: true임을 감지하고 자체 API 키/SSO 검사를 건너뜁니다.
  3. LiteLLM이 선제적 401도 건너뛰므로 업스트림 MCP 서버 자신의 401 + WWW-Authenticate가 클라이언트로 흘러갑니다.
  4. 클라이언트가 업스트림 OAuth issuer와 PKCE를 직접 완료해요.
  5. 클라이언트가 Authorization: Bearer <upstr...n>으로 재시도합니다. LiteLLM이 손대지 않고 전달해요.

Fail-Closed 동작 (Fail-Closed Behavior)

우회는 모든 대상이 옵트인할 때만 작동해요. 다음 경우에는 닫히고(fail closed) 정상 LiteLLM 인증을 실행해요:

  • 서버의 auth_typeoauth2가 아닐 때.
  • delegate_auth_to_upstream이 명시적으로 true가 아닐 때.
  • 요청이 여러 서버(x-mcp-servers: a,b)를 대상으로 하고 그중 하나가 위임되지 않았을 때.
  • 대상 서버를 URL 경로나 x-mcp-servers 헤더에서 해결할 수 없을 때.

보안 트레이드오프 (Security Trade-offs)

  • MCP 경로가 LiteLLM 레이어에서 인증 없는 인그레스가 돼요. 지출 추적, 키별 속도 제한, user_api_key_auth.user_id에 의존하는 가드레일이 실행되지 않아요.
  • LiteLLM은 설계상 호출자가 누구인지 알 수 없으므로 사용자별 감사는 업스트림 MCP 서버 자신의 로그에서 나와야 해요.
  • 액세스 제어를 강제할 것을 신뢰하는 업스트림 OAuth issuer를 가진 서버에서만 활성화하세요.

구성 참조 (Config Reference)

필드 필수 설명
auth_type oauth2여야 함. 그렇지 않으면 플래그 무시.
oauth2_flow authorization_code로 설정. 위임은 클라이언트의 대화형 PKCE 흐름을 업스트림 서버로 통과시킴.
delegate_auth_to_upstream 이 서버를 PKCE 패스스루에 옵트인하려면 true 설정.

더 알아보기 (Learn more)