MCP에서 Authorization 이해하기

원문: https://modelcontextprotocol.io/docs/tutorials/security/authorization

MCP(Model Context Protocol)에서 Authorization은 MCP 서버가 노출하는 민감한 리소스와 작업에 대한 접근을 보호합니다. 서버가 사용자 데이터나 관리 작업을 다룬다면, 어떤 사용자가 어떤 엔드포인트에 접근할 수 있는지 명확히 통제해야 합니다. MCP는 특정 인증 체계에 종속되지 않고 OAuth 2.1 관례를 따르는 표준적인 흐름을 사용합니다.

언제 Authorization을 써야 하나?

Authorization은 선택 사항이지만, 다음과 같은 경우에는 강하게 권장됩니다.

로컬 MCP 서버의 경우

STDIO 전송을 사용하는 로컬 MCP 서버는 브라우저 기반 OAuth 흐름 대신 환경 변수 기반 자격 증명이나 서버 내부 라이브러리를 통한 인증 정보를 사용할 수 있습니다. 반대로 HTTP 기반 원격 서버는 OAuth 흐름을 사용하는 것이 일반적입니다.

Authorization 흐름 단계별 이해

  1. 초기 핸드셰이크

    클라이언트가 보호된 MCP 서버에 처음 접속하면 서버는 401 Unauthorized와 함께 인증 정보를 찾을 위치를 WWW-Authenticate 헤더로 알려줍니다.

    HTTP/1.1 401 Unauthorized
    WWW-Authenticate: Bearer realm="mcp",
      resource_metadata="https://your-server.com/.well-known/oauth-protected-resource"
  2. Protected Resource Metadata(PRM) 조회

    클라이언트는 PRM 문서를 가져와 어떤 Authorization Server를 써야 하는지, 어떤 scope를 지원하는지 확인합니다.

    {
      "resource": "https://your-server.com/mcp",
      "authorization_servers": ["https://auth.your-server.com"],
      "scopes_supported": ["mcp:tools", "mcp:resources"]
    }
  3. Authorization Server 메타데이터 조회

    다음으로 클라이언트는 Authorization Server의 메타데이터를 조회해 authorization_endpoint, token_endpoint, registration_endpoint 같은 실제 엔드포인트를 파악합니다.

    {
      "issuer": "https://auth.your-server.com",
      "authorization_endpoint": "https://auth.your-server.com/authorize",
      "token_endpoint": "https://auth.your-server.com/token",
      "registration_endpoint": "https://auth.your-server.com/register"
    }
  4. 클라이언트 등록

    클라이언트가 이미 사전 등록되어 있지 않다면 Dynamic Client Registration(DCR)로 자신을 등록할 수 있습니다. DCR을 지원하지 않고 사전 등록도 없다면, 사용자가 필요한 클라이언트 정보를 직접 입력할 수 있어야 합니다.

    {
      "client_name": "My MCP Client",
      "redirect_uris": ["http://localhost:3000/callback"],
      "grant_types": ["authorization_code", "refresh_token"],
      "response_types": ["code"]
    }
  5. 사용자 승인

    클라이언트는 브라우저를 열어 /authorize 엔드포인트로 보냅니다. 사용자가 로그인하고 권한을 승인하면 authorization code가 발급되고, 클라이언트는 이를 access token과 refresh token으로 교환합니다.

    {
      "access_token": "eyJhbGciOiJSUzI1NiIs...",
      "refresh_token": "def502...",
      "token_type": "Bearer",
      "expires_in": 3600
    }
  6. 인증된 요청 수행

    이후 클라이언트는 Authorization: Bearer ... 헤더를 사용해 MCP 서버에 요청합니다. 서버는 토큰이 유효하고 필요한 권한을 만족하는지 검증해야 합니다.

    GET /mcp HTTP/1.1
    Host: your-server.com
    Authorization: Bearer eyJhbGciOiJSUzI1NiIs...

구현 예시: Keycloak 사용

원문은 로컬 개발 환경에서 Docker로 Keycloak을 띄워 MCP 서버의 Authorization Server로 사용하는 예시를 보여줍니다. 테스트용으로는 다음 명령으로 Keycloak을 시작할 수 있습니다.

docker run -p 127.0.0.1:8080:8080 -e KC_BOOTSTRAP_ADMIN_USERNAME=admin -e KC_BOOTSTRAP_ADMIN_PASSWORD=admin quay.io/keycloak/keycloak start-dev
위 설정은 테스트와 실험용입니다. 운영 환경에서는 그대로 사용하면 안 되며, 별도의 보안 설정과 고가용성 구성이 필요합니다.

Keycloak에서는 다음 사항을 추가로 설정합니다.

MCP 서버 측 핵심 포인트

원문 예시는 TypeScript, Python, C# SDK 중 하나를 사용해 간단한 MCP 서버를 만들고, Keycloak이 발급한 토큰을 검증해 두 개의 도구에 대한 접근을 보호하는 방식으로 설명합니다. 중요한 점은 MCP Authorization 규격을 따르는 것만으로 끝나지 않고, 서버가 실제로 토큰 유효성 검증과 scope 검사를 수행해야 한다는 것입니다.

토큰 검증, 권한 판단, 세션 처리 같은 보안 로직은 직접 구현하기보다 검증된 라이브러리와 Authorization Server 기능을 활용하는 편이 안전합니다.

자주 하는 실수와 피해야 할 점

관련 문서