본문 바로가기
C.W.K.
Stream
Lesson 01 of 05 · published

OAuth 2.1 — Authorization Framework

~26 min · oauth, resource-server, scopes, tokens

Level 0호기심 많은 독자
0 XP0/48 lessons0/14 achievements
0/100 XP to next level100 XP to go0% complete

HTTP transport 를 쓰는 MCP server 의 authorization 은 OAuth 2.1 로 해. Spec 은 MCP server 를 OAuth 2.1 Resource Server 로 봐. Access token 을 받아서 검증하고, token 의 scope 로 tool 호출 하나하나를 허가하는 역할이지. MCP client 는 바깥의 Authorization Server 에서 token 을 받아오는 OAuth client 고. 핵심은 이거야. MCP server 는 auth 를 발행하는 장사를 하지 않아 — token 을 받아 쓰기만 하고 만들지는 않아.

Session 하나가 흘러가는 모습은 이래. MCP client 를 굴리는 host 가 user 를 authorization server 의 OAuth 흐름으로 안내해서 access token (과 refresh token) 을 받아 저장해. 그 다음부터는 모든 MCP request 에 access token 을 Bearer header 로 실어 보내. MCP server 는 token 을 검증하고 (introspection 이든 JWT 검증이든), scope 를 읽어서 요청을 허가해. Token 을 갱신하는 건 host 의 몫이고, MCP server 는 이미 발행된 Bearer token 만 보게 돼.

MCP 만의 세부 사항이 둘 있어:

  1. Authorization Server 찾기. Token 없이 (또는 잘못된 걸로) 호출이 들어오면, MCP server 는 authorization 정보가 어디 있는지 가리키는 WWW-Authenticate header 를 내려줘. Client 는 그 정보를 받아 issuer 와 scope, PKCE 요구사항을 알아낸 다음 OAuth 흐름을 돌려. OpenID Connect 가 일반 신원 확인에서 그랬듯, MCP server 도 자기 auth 를 스스로 설명하는 셈이야.
  2. Client ID Metadata Document (CIMD) — 2025-11-25 revision 에서 들어왔어. Server 마다 client 를 따로 등록시키는 대신, client 가 널리 알려진 URL 에 metadata 문서를 올려두고 server 가 필요할 때 가져가는 방식이야. '서비스마다 client 등록하기' 라는 마찰이 규모가 커지면 도입 자체를 죽이는데, MCP 는 이걸로 그 마찰을 피해.

Code

MCP server 에 Bearer-token request·text
POST /mcp HTTP/1.1
Authorization: Bearer eyJhbGciOiJSUzI1NiIs...
Content-Type: application/json

{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{...}}
Token 빠진 거에 WWW-Authenticate discovery·text
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer resource_metadata="https://api.example.com/.well-known/oauth-protected-resource"

# Client 가 URL 따라가서 issuer · scope · PKCE 요구사항 학습,
# OAuth 2.1 authorization-code flow 돌리고 retry.
Server 에서 token 검증 (FastMCP / FastAPI 스타일)·python
from fastapi import HTTPException, Depends
from authlib.integrations.starlette_client import OAuth

async def require_scope(scope: str, token: str = Depends(extract_bearer)):
    claims = verify_jwt(token, jwks=AUTH_SERVER_JWKS, audience="https://api.example.com")
    if scope not in claims.get("scope", "").split():
        raise HTTPException(403, "insufficient scope")
    return claims

External links

Exercise

MCP authorization spec 을 끝까지 읽어. 요구사항 하나하나를 셋 중 하나로 분류해봐. 'client 가 할 일', 'server 가 할 일', 'auth server 가 할 일'. 그 분류가 네가 실제로 떠안아야 하는 OAuth 부분과, 이미 있는 인프라에 맡겨도 되는 부분을 갈라줘.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.