메인 콘텐츠로 건너뛰기
버전: 0.2.0-beta.1

MCP 서버에서 Bearer 인증 (Authentication) 구성하기

최신 MCP 명세에 따르면, MCP 서버는 보호된 리소스에 대한 액세스 토큰 (Access token) 을 검증하는 리소스 서버 (Resource Server) 역할을 합니다. MCP Auth는 Bearer 인가 (Authorization) 를 구성하는 다양한 방법을 제공합니다:

  • JWT (JSON Web Token) 모드: 클레임 (Claim) 검증을 통해 JWT를 검증하는 내장 인가 방식입니다.
  • 커스텀 모드: 직접 인가 로직을 구현할 수 있습니다.

Bearer 인증 (Authentication) 미들웨어는 이제 엔드포인트가 속한 리소스를 지정해야 하며, 이를 통해 구성된 인가 서버에 대해 적절한 토큰 검증이 가능합니다.

JWT 모드로 Bearer 인증 (Authentication) 구성하기

OAuth / OIDC 제공자가 인가를 위해 JWT를 발급하는 경우, MCP Auth의 내장 JWT 모드를 사용할 수 있습니다. 이 모드는 JWT의 서명, 만료, 그리고 지정한 기타 클레임 (Claim) 을 검증한 후, 인증 (Authentication) 정보를 요청 컨텍스트에 채워 MCP 구현에서 추가 처리를 할 수 있도록 합니다.

스코프 (Scope) 검증

다음은 기본적인 스코프 (Scope) 검증 예시입니다:

from mcpauth import MCPAuth
from starlette.applications import Starlette
from starlette.middleware import Middleware
from starlette.routing import Mount
from mcp.server.fastmcp import FastMCP

mcp = FastMCP("MyMCPServer")
mcp_auth = MCPAuth(
    # Initialize with your auth server config
)
bearer_auth = mcp_auth.bearer_auth_middleware("jwt", 
    resource="https://api.example.com",  # 이 엔드포인트가 속한 리소스를 지정하세요
    audience="https://api.example.com",  # 보안을 위해 대상 (Audience) 검증 활성화
    required_scopes=["read", "write"] 
)

app = Starlette(
    routes=[Mount('/', app=mcp.sse_app(), middleware=[Middleware(bearer_auth)])]
)

위 예시에서 JWT에 readwrite 스코프 (Scope) 가 필요함을 지정했습니다. JWT에 이 중 하나라도 포함되어 있지 않으면, 요청은 403 Forbidden 오류로 거부됩니다.

대상 (Audience) 검증 (RFC 8707)

보안 토큰 검증을 위해서는 항상 audience 파라미터를 지정하여 대상 (Audience) 검증을 포함해야 합니다. 이는 JWT의 aud (대상) 클레임 (Claim) 을 검증하여 토큰이 해당 MCP 서버 리소스에 대해 발급되었는지 확인합니다.

Audience Validation

audience 파라미터는 보안 토큰 검증을 위해 OAuth 2.0 명세에서 필수입니다. 그러나, 아직 리소스 식별자를 지원하지 않는 인가 서버와의 호환성을 위해 현재는 선택 사항입니다. 보안을 위해 가능한 경우 항상 audience 파라미터를 포함하세요. 향후 버전에서는 명세 준수를 위해 audience 검증이 필수로 적용될 예정입니다.

대상 (Audience) 값은 일반적으로 리소스 식별자와 일치해야 합니다:

bearer_auth = mcp_auth.bearer_auth_middleware(
    "jwt",
    resource="https://api.example.com",  # 이 엔드포인트가 속한 리소스를 지정하세요
    audience="https://api.example.com",  # 보안을 위해 대상 (Audience) 검증 활성화
    required_scopes=["read", "write"]
)

위 예시에서 MCP Auth는 JWT의 aud 클레임 (Claim) 과 필요한 스코프 (Scope) 를 모두 검증합니다.

JWT 검증에 커스텀 옵션 제공하기

기본 JWT 검증 라이브러리에 커스텀 옵션을 제공할 수도 있습니다:

Python SDK에서는 JWT 검증을 위해 PyJWT를 사용합니다. 다음 옵션을 사용할 수 있습니다:

  • leeway: JWT 만료 시간 검증 시 허용할 여유 시간 (초 단위). 기본값은 60초입니다.
bearer_auth = mcp_auth.bearer_auth_middleware(
    "jwt",
    resource="https://api.example.com",
    audience="https://api.example.com",
    required_scopes=["read", "write"],
    leeway=10,  # 10초의 여유 시간을 허용하여 시계 오차를 줄임
)

커스텀 검증으로 Bearer 인증 (Authentication) 구성하기

OAuth / OIDC 제공자가 JWT를 발급하지 않거나, 직접 인가 로직을 구현하고 싶은 경우, MCP Auth는 커스텀 검증 함수를 만들 수 있도록 지원합니다:

정보

Bearer 인증 (Authentication) 미들웨어는 발급자 (iss), 대상 (aud), 그리고 필요한 스코프 (scope) 를 검증 결과와 비교합니다. 따라서 커스텀 검증 함수에서 이 검증을 직접 구현할 필요가 없습니다. 토큰의 유효성 (예: 서명, 만료 등) 검증과 인증 (Authentication) 정보 객체 반환에 집중하면 됩니다.

from mcpauth.exceptions import MCPAuthJwtVerificationException, MCPAuthJwtVerificationExceptionCode
from mcpauth.types import AuthInfo

async def custom_verification(token: str) -> AuthInfo:
    # 여기서 커스텀 검증 로직을 구현하세요
    info = await verify_token(token)
    if not info:
        raise MCPAuthJwtVerificationException(
            MCPAuthJwtVerificationExceptionCode.JWT_VERIFICATION_FAILED
        )
    return info  # 인증 (Authentication) 정보 객체 반환

bearer_auth = mcp_auth.bearer_auth_middleware(
    custom_verification,
    resource="https://api.example.com",
    audience="https://api.example.com",  # 보안을 위해 대상 (Audience) 검증 활성화
    required_scopes=["read", "write"]
)

MCP 서버에 Bearer 인증 (Authentication) 적용하기

MCP 서버를 Bearer 인증 (Authentication) 으로 보호하려면, MCP 서버 인스턴스에 Bearer 인증 (Authentication) 미들웨어를 적용해야 합니다.

bearer_auth = mcp_auth.bearer_auth_middleware("jwt", 
    resource="https://api.example.com",
    audience="https://api.example.com",  # 보안을 위해 대상 (Audience) 검증 활성화
    required_scopes=["read", "write"]
)
app = Starlette(
    routes=[Mount('/', app=mcp.sse_app(), middleware=[Middleware(bearer_auth)])]
)

이렇게 하면 모든 들어오는 요청이 구성된 Bearer 인증 (Authentication) 설정에 따라 인증 (Authentication) 및 인가 (Authorization) 되며, 인증 (Authentication) 정보가 요청 컨텍스트에서 사용할 수 있게 됩니다.

이후 MCP 서버 구현에서 해당 정보를 사용할 수 있습니다:

@mcp.tool()
async def whoami() -> dict:
    # `mcp_auth.auth_info`는 현재 요청의 컨텍스트 객체입니다
    auth_info = mcp_auth.auth_info
    print(f"Authenticated user: {auth_info.subject}")
    return {"subject": auth_info.subject}