• mcp
  • mcp-auth

MCP 서버 인증 구현 가이드: 최신 사양 활용하기

2025-06-18 사양을 준수하는 MCP 서버 인증 구현의 주요 포인트를 제공합니다.

Yijun
Yijun
Developer

사용자 인증에 몇 주를 낭비하지 마세요
Logto로 더 빠르게 안전한 앱을 출시하세요. 몇 분 만에 사용자 인증을 통합하고 핵심 제품에 집중하세요.
시작하기
Product screenshot

며칠 전 (2025년 6월 18일), MCP (Model Context Protocol) 팀이 최신 MCP 사양 (2025-06-18)을 공개했습니다. 이번 업데이트에서는 인증 사양에 중요한 변경 사항이 포함되었습니다. 이제 MCP 서버는 더 이상 Authorization Server로서 직접 액세스 토큰을 발급하지 않습니다. 대신, 서버는 액세스 토큰을 받아 Resource Server로서 리소스를 제공합니다.

MC​P Auth (플러그 앤 플레이 MCP 서버 인증 라이브러리) 유지자로서, 이 프로젝트에 최신 MCP 인증 사양을 이미 적용하였습니다. 실전 경험을 바탕으로, 최신 사양을 준수하는 MCP 서버의 인증 기능을 구현하는 방법을 단계별로 설명합니다.

이 글을 통해 여러분은 다음을 할 수 있습니다:

  • 새로운 MCP 인증 사양 하에서 MCP 인증의 동작 원리 이해
  • MCP 인증 사양 요구사항에 따라 Resource Server로서 MCP 서버가 구현해야 할 사항 명확화
  • 최신 MCP 인증 사양을 만족하는 인증 구현 가이드 제공
  • 사양 구현 시 잘 놓치는 포인트와 보안 이슈 식별

참고사항:

  • 이 글은 JWT (JSON Web Token)의 기본 개념을 알고 있다는 전제로 작성되었습니다. JWT 구조, 서명 검증 등의 기초 개념은 다루지 않으니 자세한 내용은 Auth Wiki - JWT를 참고하세요.
  • MCP 인증 사양에서 의존하는 여러 RFC에 대해서는 깊게 소개하지 않습니다. 오직 이들 RFC 요구사항을 따르는 구현만 설명합니다.
  • MCP Client 및 Authorization 관련 구현·상호작용 세부 사항은 다루지 않습니다. 이는 LLM 클라이언트와 MCP를 지원하는 인증 서버 제공자가 MCP 사양에 따라 구현하는 영역입니다. MCP 서버 개발자가 개입하거나 관여하지 않습니다. 자세한 정보는 OAuth ClientAuthorization Server를 참고하세요.
  • 모든 액세스 토큰은 JWT 포맷(현재 시장에서 가장 널리 쓰임)으로 가정합니다. 별도 포맷이 필요한 경우, 해당 인증 서버 제공업체의 문서를 참고하세요.

MCP 인증은 어떻게 작동할까?

최신 MCP 인증 사양에 따라 MCP 인증 플로우는 다음 시퀀스 다이어그램과 같습니다:

  1. MCP 클라이언트가 https://github-tools.com에 리소스를 요청합니다. 이 시점에서 인증 헤더에 액세스 토큰이 없어 사용자가 아직 로그인하지 않은 상황입니다.

  2. MCP 서버는 클라이언트 요청의 인증 헤더에서 액세스 토큰을 찾을 수 없으므로, HTTP 401 에러를 반환합니다. 이 응답에는 WWW-Authenticate 헤더가 포함되며, 여기에는 MCP 서버의 resource metadata(= resource_metadata 필드 값) URL이 실려있습니다.

  3. 클라이언트는 받은 WWW-Authenticate 헤더에서 resource_metadata 값을 추출합니다 (예: https://github-tools.com/.well-known/oauth-protected-resource). 그 후, 클라이언트는 MCP 서버가 Resource Server로 제공하는 resource metadata 엔드포인트에 요청을 보냅니다. 해당 metadata에는 authorization_servers, scopes_supported와 같은 정보가 포함되어 있습니다. 이를 통해 MCP 클라이언트는 MCP 서버 접근에 필요한 액세스 토큰을 어떤 인증 서버에서, 어떤 권한(scope)으로 발급받아야 할지 파악합니다.

4-8. 클라이언트는 resource metadata에서 얻은 인증 서버 metadata URL을 바탕으로 인증 서버 metadata를 요청하고, OAuth 2.1 인증 플로우를 거쳐 액세스 토큰을 발급받습니다.

  1. 클라이언트는 인증 서버에서 받은 액세스 토큰을 담아, MCP 서버에 다시 리소스 요청을 보냅니다.

  2. MCP 서버는 토큰의 유효성 검증 후 요청 리소스를 반환합니다. 그 뒤로 MCP 클라이언트와 MCP 서버는 유효한 토큰으로 계속 통신합니다.

이제부터 MCP 인증 워크플로우에 맞춰 MCP 서버 인증 메커니즘의 구현 방법을 단계별로 살펴보겠습니다.

인증 실패 요청 처리: 401 에러 및 WWW-Authenticate 헤더 반환

앞서 본 플로우대로 MCP 클라이언트가 액세스 토큰 없이 요청을 보내면, MCP 서버는 HTTP 401 Unauthorized 에러와 WWW-Authenticate 헤더(내부에 resource metadata 엔드포인트 URL 포함)를 반환해야 합니다.

MCP 인증 사양의 오류 처리에 따르면, MCP 클라이언트의 요청에 액세스 토큰이 없을 때뿐 아니라, MCP 서버가 유효하지 않은 액세스 토큰을 받을 때에도 반드시 401 에러와 함께 WWW-Authenticate 헤더를 반환해야 합니다.

언제 401 에러를 반환할지 알았다면, 그 다음은 실제 반환할 WWW-Authenticate 헤더의 구성입니다.

RFC9728 5.1절에 따라, WWW-Authenticate 헤더는 보호된 리소스 메타데이터의 URL을 나타내는 resource_metadata 파라미터를 반드시 포함해야 합니다.

기본 형식은 다음과 같습니다:

여기에서 Bearer는 인증 스킴을 의미하며, OAuth 2.0 인증이 필요한 보호 리소스임을 나타냅니다 (참고: MDN 문서). 뒤따르는 resource_metadata 파라미터의 값은 MCP 서버가 제공하는 리소스 메타데이터 엔드포인트의 전체 URL이어야 합니다.

실제 코드 예시는 다음과 유사합니다:

MCP 클라이언트가 이러한 401 에러를 수신하면, 클라이언트는 WWW-Authenticate 헤더의 resource_metadata 값을 통해 MCP 서버의 리소스 메타데이터에 접근합니다. 이후 리소스 메타데이터가 제공하는 정보를 바탕으로 지정된 인증 서버에 대한 인증 요청을 수행해 MCP 서버 접근에 사용할 수 있는 액세스 토큰을 획득합니다.

이제 언제 401 에러와 resource_metadata URL을 반환해야 하는지 알았습니다. 다음 단계는 해당 리소스 메타데이터 URL의 생성 방식과 실제 메타데이터 구조에 대한 설명입니다.

리소스 메타데이터 탐색 메커니즘 구현

MCP 인증 플로우에 따르면, MCP 클라이언트는 401 에러 수신 후 곧바로 MCP 서버의 리소스 메타데이터 엔드포인트에 액세스를 시도합니다. 즉, Resource Server 역할의 MCP 서버는 반드시 리소스 메타데이터 탐색 기능을 구현해야 합니다.

메타데이터 엔드포인트의 URL 경로 설계

OAuth 시스템에서는 URI를 통해 Resource 주소(= resource indicator)를 명시합니다. RFC9728에 따라 리소스 메타데이터는 반드시 지정된 /.well-known 경로 아래에 호스팅해야 합니다.

만약 MCP 서버(https://github-tools.com 등)가 단일 서비스만을 제공한다면, 메타데이터 엔드포인트는 다음과 같이 설정될 수 있습니다:

하나의 호스트에서 여러 MCP 서비스를 제공한다면, 각 서비스별로 독립된 메타데이터 엔드포인트를 가져야 합니다. 예를 들어, 기업 플랫폼 https://api.acme-corp.com이 다음과 같은 서비스를 제공한다고 가정합니다:

  • https://api.acme-corp.com/github - GitHub 연동 서비스
  • https://api.acme-corp.com/slack - Slack 연동 서비스
  • https://api.acme-corp.com/database - DB 조회 서비스

이때 메타데이터 엔드포인트는 각각 다음과 같습니다:

  • https://api.acme-corp.com/.well-known/oauth-protected-resource/github
  • https://api.acme-corp.com/.well-known/oauth-protected-resource/slack
  • https://api.acme-corp.com/.well-known/oauth-protected-resource/database

이렇게 하면 각 서비스별로 권한 범위(scope) 또는 인증 서버 구성을 다르게 설정할 수 있습니다:

  • GitHub 서비스: GitHub OAuth 사용, github:read, github:write 권한 필요
  • Slack 서비스: Slack OAuth 사용, slack:channels:read, slack:messages:write 권한 필요
  • Database 서비스: 사내 인증 서버 사용, db:query 권한 필요

정리하면, 메타데이터 엔드포인트 URL 패턴은 다음과 같습니다:

리소스 인디케이터로부터 메타데이터 엔드포인트 URL을 추출하는 방식은 아래와 같습니다:

리소스 메타데이터 응답 구성

엔드포인트의 URL 경로를 정했다면, 이제 해당 엔드포인트가 RFC9728에 맞는 JSON 형식의 메타데이터를 반환해야 합니다.

실제 구현에서 가장 중요한 필드는 네 가지입니다.

첫 번째는 resource 필드로, 리소스 인디케이터이며 MCP 클라이언트가 접근하려는 리소스 주소와 일치해야 합니다.

다음으로 authorization_servers 필드가 있습니다. MCP 클라이언트가 액세스 토큰을 발급받기 위해 어느 인증 서버에 접근해야 할지 명시하는 배열입니다. OAuth 2.0 보호 리소스 메타데이터(RFC 9728) 기준 이 필드는 선택 사항이지만, MCP 인증 사양에선 필수입니다.

세 번째는 scopes_supported 필드로, 해당 리소스 서버가 지원하는 모든 권한(scope) 목록입니다.

마지막으로 bearer_methods_supported 필드는 MCP 클라이언트가 액세스 토큰을 어떤 방식으로 전달해야 하는지를 명시합니다. 보통 ["header"] 값으로, MCP 클라이언트는 액세스 토큰을 HTTP Authorization 헤더로 전달해야 함을 뜻합니다.

예를 들어, https://github-tools.com MCP 서버용 resource metadata은 다음과 같습니다:

이 값은 MCP 클라이언트에게 다음 사실을 전달합니다: 접근하려는 리소스는 https://github-tools.com이고, 액세스 토큰은 https://auth.github-tools.com 인증 서버에서 받아야 하며, 신청 가능한 권한은 github:read, github:write, repo:admin이고, 받은 토큰은 HTTP Authorization 헤더로 전달해야 함을 의미합니다.

대부분의 환경에선 이 네 개의 필드만으로 MCP 클라이언트가 정상 동작하는데 충분합니다. 더 복잡한 구성이 필요하다면 RFC9728의 전체 필드 목록을 참고하세요.

액세스 토큰 검증

MCP 클라이언트가 인증 서버에서 액세스 토큰을 받아오면, 해당 토큰을 담아 MCP 서버에 요청을 보냅니다.

MCP 서버가 액세스 토큰을 검증할 때 주의해야 할 점은 다음과 같습니다:

  1. 토큰 기본 검증 시 반드시 MCP 서버 자체 설정을 근거로 해야 하며, 토큰 자체가 포함한 인증 서버 정보를 맹신하지 않아야 합니다.
  2. 토큰의 audience가 MCP 서버 본인인지(즉, 이 토큰이 실제로 우리 MCP 서버를 위해 발급된 것인지) 검증해야 합니다.
  3. scope 검증을 올바르게 처리해야 합니다.

MCP 서버 설정을 사용해 토큰 검증하기

MCP 서버는 액세스 토큰을 받을 때, 이 토큰 자체에 인증 서버 정보(issuer)가 들어 있단 점에 현혹되기 쉽습니다. 특히 MCP 서버 리소스 메타데이터에 복수의 인증 서버가 설정된 경우, 예를 들어 아래와 같이 메타데이터가 구성되었다면:

개발자는 흔히 토큰에서 issuer를 추출해 해당 인증 서버의 정보를 참조해 검증을 수행하려 합니다. 그러나 공격자가 임의로 제작한 악성 인증 서버에서 MCP 서버를 대상으로 허위 액세스 토큰을 발급할 수도 있습니다. 이런 경우, 토큰에서 추출한 issuer가 실제 MCP 서버 설정에 등록된 인증 서버가 아니라면 바로 인증을 거부해야 합니다.

따라서 올바른 검증 방법은 다음과 같습니다:

  • 인증 서버가 1개밖에 없다면: MCP 서버의 백엔드에 구성된 인증 서버 정보만 사용해 무조건 그 인증 서버로만 검증
  • 인증 서버가 복수라면: 토큰에서 issuer 값을 추출한 뒤, MCP 서버 설정에 있는 인증 서버 목록에서 해당 값을 검색한 후 일치하는 항목이 있을 때만 검증
  • 그 외에는 무조건 토큰 인증 거부

물론, 토큰 검증 시에는 issuer 값의 엄격한 검증이 필수입니다.

예시: jose JWT 검증 라이브러리를 이용한 코드

토큰 audience 검증하기

MCP 인증 사양의 토큰 audience 바인딩 및 검증 절에 따르면, 클라이언트가 인증 서버에 액세스 토큰을 신청할 때, 이 토큰이 실제로 어떤 Resource Server에 사용될 것인지 명시해야 합니다. 마찬가지로 MCP 서버는 받은 토큰의 audience 값이 진짜 본인을 가리키는지 엄격히 확인해야 합니다.

이 메커니즘은 Resource Indicators for OAuth 2.0(RFC 8707) 표준에 기초합니다. 요약하면 다음과 같은 흐름입니다:

  1. 클라이언트가 인증 서버에 액세스 토큰 요청 시, resource 파라미터에 토큰 적용 대상 리소스 서버(예: https://github-tools.com)를 명시
  2. 인증 서버가 토큰 발급 시 토큰의 payload(aud 항목)에 해당 resource 값을 기입
  3. MCP 서버는 수신한 토큰의 aud가 본인 리소스 인디케이터와 정확하게 일치하는지 검증

https://github-tools.com MCP 서버 예시:

audience 검증은 토큰 오용을 막는 핵심 보안 메커니즘입니다. 만약 이 검증을 빼먹는다면, 공격자가 다른 서비스를 위해 발급받은 액세스 토큰을 MCP 서버에 제시할 수 있으며, 서버가 audience 검증 없이 받아버릴 경우 원래 허용되지 않는 요청도 처리할 수 있게 됩니다.

scope 검증

MCP 서버가 내부 리소스별로 접근 권한을 따로 관리하는 경우, 사용자마다 보유한 scope가 다를 수 있습니다.

따라서 액세스 토큰이 정상적으로 검증되었다면, 최종적으로 현재 리소스에 필요한 권한(scope)이 토큰의 scope claim에 실제로 있는지도 체크해야 합니다:

MCP 인증 사양에 따르면, 요구하는 scope가 토큰에 없으면 403 Forbidden을 반환해야 합니다.

마무리

이 글을 통해 최신 사양(MCP 2025-06-18)에 맞춰 MCP 서버의 인증 기능을 어떻게 구현하는지 익혔을 것입니다. 핵심은 토큰 검증을 안전하게 하고, 메타데이터를 올바르게 구성하며, audience를 반드시 엄격 검증하는 것입니다.

MCP 서버를 구축 중이라면, 이 글에 설명된 내용을 모두 구현한 MCP Auth 라이브러리를 사용해 빠르고 안정적인 인증 연동을 경험해 보세요.

궁금한 점은 언제든 GitHub에서 질문 및 토론해 주세요. 함께 MCP 생태계를 발전시켜나갑시다.