CodeGym /행동 /ChatGPT Apps /MCP 인가 아키텍처: MCP Client, MCP Server, MCP Auth Server

MCP 인가 아키텍처: MCP Client, MCP Server, MCP Auth Server

ChatGPT Apps
레벨 10 , 레슨 1
사용 가능

1. 이 강의에서 다루는 것과 다루지 않는 것

이번 강의에서는 다음을 다룹니다:

  • MCP Client, MCP Server, MCP Auth Server 사이의 “신뢰의 삼각형”을 머릿속에 그립니다 — 그리고 이 삼각형 “위”에 자원 소유자로 서 있는 인간 사용자까지 포함합니다;
  • flow를 정리합니다: 누가 누구에게 토큰을 보내는지, 사용자가 어디에서 로그인하는지, 그리고 왜 MCP 서버는 절대 비밀번호를 보지 않는지;
  • 이를 우리의 Next.js/MCP 백엔드 및 앞으로 설정할 Keycloak/Auth0와 연결합니다.

오늘 하지 않는 것:

  • Keycloak에서 체크박스를 클릭하며 특정 IdP를 직접 설정하지 않습니다;
  • 완전한 JWT 검증이나 introspection을 구현하지 않습니다 — 이는 다음 강의(Auth Server 및 보호된 리소스로서의 MCP Server) 주제입니다.

지금의 목표는 — 종이에 ChatGPT, 여러분의 서버, Auth0/Keycloak 사이의 화살표를 그려가며 “어디가 로그인이고, 어디가 토큰이며, 데이터는 어디에 있는지”를 막힘없이 설명할 수 있게 되는 것입니다.

2. 신뢰의 삼각형: MCP Client, MCP Server, MCP Auth Server

등장인물부터 정리해 봅시다. 기술적인 “신뢰의 삼각형”은 MCP Client, MCP Server, MCP Auth Server가 구성합니다. 사용자(User)는 별도의 역할이며, 리소스 소유자로서 이 삼각형 “위”에 서서 접근에 동의합니다. MCP 및 Apps SDK의 맥락에서는 이 아키텍처가 꽤 명확하게 형식화되어 있습니다.

User (리소스 소유자)

화면 반대편의 사람입니다. 사용자는:

  • ChatGPT에 들어옵니다;
  • “내 주문/내 선물 목록을 보여줘”와 같은 요청을 보냅니다;
  • 여러분의 서비스 계정을 ChatGPT에 “연결”하는 데 동의합니다.

핵심: 리소스(주문 내역, 프로필, 선물 목록)의 소유자는 사용자이며, 접근에 대한 동의도 사용자만이 줄 수 있습니다.

MCP Client

여기서 MCP Client는 다음을 의미합니다:

  • Apps SDK가 있는 ChatGPT;
  • 때로는 MCP Jam Inspector(디버깅 시).

MCP Client는 다음을 할 수 있습니다:

  • 여러분의 MCP 서버 메타데이터를 읽습니다(.well-known 경유);
  • 사용자 브라우저에서 OAuth flow를 시작합니다;
  • 토큰을 저장하고 MCP 도구 호출에 첨부합니다.

중요한 점은 MCP Client가 public client라는 것입니다. client_secret을 저장하지 않으므로 Auth Server와는 공개 SPA 애플리케이션처럼 통신합니다: Authorization Code + PKCE.

MCP Server (Resource Server)

여러분의 MCP 구현 백엔드입니다:

  • ChatGPT와 연결을 수립합니다;
  • 도구(tools), 리소스, 프롬프트를 선언합니다;
  • 각 도구 호출에서 Authorization: Bearer <token> 헤더를 확인합니다;
  • 토큰(서명, exp, aud, scope)을 검증하고, 이상이 없으면 비즈니스 로직을 수행합니다.

원칙적으로 MCP 서버는 로그인을 담당하지 않습니다. 비밀번호를 보지 않으며, 로그인 폼을 그리지 않고, “이메일을 확인하세요”라는 메일을 보내지 않습니다. Auth Server가 암호학적으로 서명한 토큰만 신뢰합니다.

MCP Auth Server (Authorization Server / IdP)

이는 인증과 인가를 담당하는 별도의 서비스입니다: Keycloak, Auth0, Ory Hydra+Kratos, Okta, Cognito, Azure AD 등.

역할은 다음과 같습니다:

  • 로그인 UI(email/비밀번호, SSO, 2FA);
  • 사용자 계정 저장;
  • 토큰 발급(access token, refresh token);
  • OAuth/OIDC 메타데이터 공개(/authorize, /token, jwks_uri, /registration 등).

MCP 관점에서는 public client를 위한 OAuth 2.1(PKCE S256, dynamic client registration 등)을 지원해야 합니다.

역할 요약 표

주체 하는 일 하지 않는 일
User 로그인/비밀번호 입력, 데이터 접근 동의 MCP Server와 직접 통신하지 않음
MCP Client (ChatGPT/Jam) OAuth를 시작하고 토큰을 저장하며 MCP 도구 호출 비밀번호나 토큰 서명을 검증하지 않음
MCP Server 토큰 검증, 도구의 비즈니스 로직 수행 로그인 폼을 그리지 않고 비밀번호를 저장하지 않음
MCP Auth Server 사용자 로그인 처리, 토큰 발급 여러분의 MCP 도구와 비즈니스 로직을 알지 못함

지금까지 머릿속에서 모든 것이 “모든 걸 다 하는 큰 서버”로 섞여 있었다면 — 이제 분리할 때입니다.

3. Flow: ‘토큰 없음’에서 보호된 도구 호출까지

이제 메시지 흐름을 살펴봅시다. MCP 사양에서는 이 과정을 “The Flow”라고 부릅니다: discovery → redirect → code → token → authorized calls.

0단계. 토큰 없이 보호된 도구 호출 시도

사용자가 말합니다: “내가 저장해 둔 선물 아이디어를 보여줘”.

MCP Client인 ChatGPT는 “이를 위해서는 우리 MCP 서버의 getUserGiftLists 도구를 호출해야 한다”고 판단합니다. 아직 사용자가 로그인하지 않았으므로 토큰 없이 호출합니다.

여러분의 MCP 서버는:

  • Authorization 헤더가 없거나 잘못되었음을 봅니다;
  • 401 Unauthorized로 응답하고, WWW-Authenticate: Bearer resource_metadata="https://api.giftgenius.com/.well-known/oauth-protected-resource" 헤더를 추가하여 보호된 리소스의 메타데이터(resource metadata, 아래에서 설명)를 제공합니다.

대략 다음과 같습니다(로직만, 전체 HTTP 아님):

HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer resource_metadata="https://api.giftgenius.com/.well-known/oauth-protected-resource"

ChatGPT는 이 헤더를 보고 “아하, 이 리소스는 OAuth로 보호되므로 OAuth flow를 수행해 계정을 연결해야겠군”이라고 이해합니다.

Discovery: .well-known/oauth-protected-resource

이어 MCP Client가 서버에 메타데이터를 요청합니다:

GET /.well-known/oauth-protected-resource

서버는 리소스 식별자와, 토큰을 받을 권한 서버 목록이 담긴 JSON 문서를 응답합니다.

최소 예시는 다음과 같습니다(자세한 설정은 나중에, 여기서는 개념이 중요합니다):

{
  "resource": "https://api.giftgenius.com",
  "authorization_servers": [
    "https://auth.giftgenius.com"
  ],
  "scopes_supported": ["gifts.read", "gifts.write"]
}

설명:

  • resource — 여러분 리소스의 표준 ID입니다. 나중에 토큰 발급 시 audience 또는 resource로 사용되어야 합니다;
  • authorization_servers — ChatGPT가 토큰을 요청할 수 있는 Auth Server 목록;
  • scopes_supported — MCP 서버가 이해하는 권한 범위입니다.

Authorization Request: Auth Server로 리다이렉트

메타데이터를 받은 MCP Client는 Auth Server로 이동합니다. 사용자 브라우저에서 다음 주소를 엽니다:

GET https://auth.giftgenius.com/authorize
    ?response_type=code
    &client_id=chatgpt-giftgenius
    &redirect_uri=... (MCP Client의 콜백 URL)
    &code_challenge=...
    &code_challenge_method=S256
    &scope=openid gifts.read
    &resource=https://api.giftgenius.com

사용자는:

  • 익숙한 로그인 화면(예: Keycloak 또는 Auth0)을 봅니다;
  • 로그인/비밀번호를 입력하고 2FA를 통과합니다;
  • ChatGPT가 자신의 선물 목록을 읽을 수 있도록 동의합니다(scope gifts.read).

Code → Token: PKCE로 코드를 토큰으로 교환

로그인이 성공하면 Auth Server는 code와 함께 사용자를 MCP Client로 리다이렉트합니다. MCP Client는:

  • /token에 POST 요청을 보냅니다;
  • code와, 위 단계의 code_challenge에 대응하는 code_verifier를 전송합니다.

Auth Server는 PKCE를 검증합니다: code_verifier를 해시하고 초기 code_challenge와 비교합니다. 모두 정상이고 flow를 시작한 클라이언트가 동일하다면:

  • 짧은 수명의 access_token(보통 JWT)을 발급하고;
  • 그 안에 다음을 기록합니다:
    • sub — Auth Server에서의 사용자 ID;
    • aud 또는 resource — 여러분의 MCP 서버;
    • scope — 허용된 동작(gifts.read, openid 등).

Authenticated Request: 토큰을 포함해 MCP 도구 호출

이제 MCP Client는 다음과 같은 헤더로 도구를 다시 호출합니다:

Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...

MCP 서버는:

  • Auth Server의 JWK로 토큰 서명을 검증하거나 introspection을 수행하고;
  • 만료 시간(exp)을 확인하며;
  • aud / resource를 확인하여 토큰이 실제로 https://api.giftgenius.com용으로 발급되었는지 검증하고;
  • scope를 보고 getUserGiftLists 호출이 가능한지 결정합니다.

그 후 DB에서 어떤 userId에 해당하는지 찾아 개인 선물 목록을 반환합니다.

지금까지는 토큰이 어떻게 발급되어 MCP 서버에 도착하는지에 관한 네트워크 흐름만 이야기했습니다. 다음으로, 토큰의 sub 및 기타 claims가 어떻게 여러분의 DB에서 특정 userId로 바뀌는지 이해하는 것이 중요합니다 — 여기서 identity bridge가 등장합니다.

4. Identity Bridge: ChatGPT의 user가 우리 DB의 userId로 바뀌는 과정

가장 흥미로운 아키텍처 조각은 “정체성 브리지(identity bridge)”입니다. MCP 사양은 MCP 서버가 ChatGPT의 사용자를 직접 알지 못하며, Auth Server의 토큰에 담긴 데이터에 의존함을 분명히 강조합니다.

대략적인 구조는 다음과 같습니다:

flowchart TD
  User[ChatGPT의 User] -->|Login/SSO| Auth[Auth Server]
  Auth -->|JWT: sub, email, tenant| MCP[MCP Server]
  MCP -->|userId/tenantId| DB[(여러분의 DB)]

단계별로 보면 다음과 같습니다.

첫째, Auth Server는 내부적으로 자신의 사용자를 알고 있습니다: user, email, id, 그리고 경우에 따라 tenant, roles와 같은 엔터티를 가집니다. 로그인에 성공하면 이 정보를 토큰의 claims에 넣습니다:

{
  "sub": "auth0|abc123",
  "email": "user@example.com",
  "given_name": "Alice",
  "https://giftgenius.com/tenant": "tenant-42",
  "scope": "openid gifts.read",
  "aud": "https://api.giftgenius.com"
}

둘째, MCP Server는 토큰을 검증할 때 이 claims를 꺼내 자신 세계의 사용자와 매핑합니다. 예를 들어:

  • sub가 이미 User.authProviderId 테이블에 있다면 연결된 userId를 사용합니다;
  • 없다면 로컬 레코드를 생성(on‑the‑fly provisioning)하고 연결합니다.

MCP 서버 쪽의 전형적인 TypeScript 코드(단순화, 서명 검증 생략)는 다음과 같을 수 있습니다:

type TokenClaims = {
  sub: string;
  email?: string;
  scope?: string;
};

async function mapClaimsToUserId(claims: TokenClaims): Promise<string> {
  const user = await db.user.findUnique({ where: { authSub: claims.sub } });
  if (user) return user.id;

  const created = await db.user.create({
    data: { authSub: claims.sub, email: claims.email ?? null }
  });
  return created.id;
}

셋째, MCP 서버는 자체 userId로 선물 목록, 주문 내역, 설정, 요금제 등 필요한 모든 것을 조회합니다.

결국 Auth Server는 외부 세계(ChatGPT, Google, SSO)와 여러분의 내부 세계(DB의 customer_id)를 잇는 “다리”가 됩니다.

5. 왜 Auth Server와 MCP Server를 분리해야 하는가

“차라리 내 MCP 서버가 직접 로그인도 보여주고 토큰도 발급하면 되지 않을까?”라는 유혹이 생길 수 있습니다. 형식상 가능은 합니다(내부에 미니 IdP를 심는 방식). 하지만 아키텍처적으로는 좋지 않은 생각입니다. 이유는 매우 현실적입니다.

첫째, 보안과 확장성입니다. Auth Server는 2FA, 소셜 로그인, 비밀번호 정책, 계정 잠금, 접근 복구, 로그인 감사, 심지어 인증 관련 컴플라이언스까지 갖춘 무거운 기계입니다. 이를 각 마이크로서비스(각 MCP 서버)마다 매번 새로 구현하는 것은 지옥과 PCI‑DSS로 가는 길입니다. Keycloak/Auth0에 이를 위임하고 토큰만 검증하는 편이 훨씬 쉽습니다.

둘째, 클라이언트의 교체 가능성입니다. 오늘은 ChatGPT뿐일 수 있지만, 내일은 Claude Desktop, 여러분의 Next.js 웹 프런트엔드, 모바일 앱이 추가될 수 있습니다. 이들 모두 동일한 Auth Server와 동일한 OAuth 2.1 스킴을 사용할 수 있으며, MCP 서버는 토큰 검증만 계속하면 됩니다. 새로운 클라이언트가 추가될 때 비즈니스 로직을 다시 작성할 필요가 없습니다.

셋째, 코드의 단순성입니다. 이상적인 MCP Server는:

  • /.well-known/oauth-protected-resource를 공개할 줄 알고;
  • Bearer 토큰을 검증해 그 안에서 userId, scopes, tenant를 꺼낼 줄 알며;
  • 비즈니스 도구(orders, gifts, profiles)를 구현합니다.

로그인 UI 로직 — 폼, 레이아웃, 소셜 로그인 — 은 Auth Server에 두고 백엔드를 어지럽히지 않습니다.

6. 우리 학습용 애플리케이션 GiftGenius에서는 어떻게 보이는가

코스 전반에서 함께 가져갈 앱으로 돌아가 봅시다. 다음이 있다고 가정합시다:

  • 선물 추천을 하는 위젯이 있는 ChatGPT App “GiftGenius”(Apps SDK);
  • 도구를 제공하는 Node/Next.js 기반 MCP 서버:
    • searchGifts — 익명, 로그인 불필요;
    • getSavedGiftLists — 개인용, 인증 필요;
  • 각 사용자에게 계정이 있는 Auth Server(나중에 Keycloak/Auth0).

익명 사용자 vs 로그인 사용자 시나리오

사용자가 “동생에게 줄 선물 추천해줘. 30살이고 보드게임을 좋아해”라고만 말한다면, 우리 App은:

  • 익명 도구 searchGifts를 호출하고;
  • 인터페이스에 추천을 표시할 수 있습니다.

이 경우에는:

  • 토큰이 필요하지 않으며;
  • MCP 서버는 단순히 요청을 수행합니다(예: 여러분의 카탈로그나 외부 API).

사용자가 “이걸 내 목록에 저장해” 또는 “내가 저장해 둔 아이디어를 보여줘”라고 말하는 순간, 모델은 보호된 도구 getSavedGiftLists를 호출하기로 결정합니다. 서버는 401 + WWW-Authenticateresource_metadata로 응답합니다. ChatGPT는 “Link GiftGenius account”라는 OAuth 마법사를 띄우고, 사용자를 로그인 흐름으로 안내해 토큰을 받습니다.

그 다음, 각 보호된 호출마다:

  • MCP Server는 Authorization: Bearer ...를 확인하고;
  • 토큰에서 userId를 꺼내며;
  • userId로 데이터를 필터링합니다.

바로 이것 덕분에 우리는 다음을 할 수 있습니다:

  • 사용자별 데이터 분리;
  • 주문 내역, 즐겨찾기 등의 개인정보를 안전하게 표시;
  • 커머스 기능 구현(코스 후반에 다룸).

백엔드 아키텍처: middleware + 도구 핸들러

실제 Node/Next.js 코드에서는 보통 “인증 미들웨어 → 도구의 비즈니스 핸들러”라는 체인으로 구성합니다. 도구 핸들러 구현 강의에서 이미 컨텍스트(user_id, 토큰, 설정)를 넘겨야 한다고 강조했습니다.

코드 조각은 다음과 같을 수 있습니다:

// auth-context.ts
export type AuthContext = {
  userId: string | null;    // 익명 호출의 경우 null
  scopes: string[];
};

모든 MCP 엔드포인트에 적용되는 미들웨어:

// mcp-auth-middleware.ts
export async function buildAuthContext(req: Request): Promise<AuthContext> {
  const header = req.headers.authorization || "";
  const token = header.replace(/^Bearer\s+/i, "");

  if (!token) return { userId: null, scopes: [] }; // 익명 사용자

  const claims = await verifyAndDecodeToken(token); // 토큰 검증
  const userId = await mapClaimsToUserId(claims);
  const scopes = (claims.scope || "").split(" ");
  return { userId, scopes };
}

도구 핸들러는 이 컨텍스트를 받습니다:

// tools/getSavedGiftLists.ts
export async function getSavedGiftLists(_args: {}, ctx: AuthContext) {
  if (!ctx.userId) throw new Error("User must be authenticated");

  return db.giftList.findMany({
    where: { ownerId: ctx.userId }
  });
}

핵심은, 도구 핸들러가 OAuth나 PKCE를 전혀 알 필요가 없다는 점입니다. 그저 “자명한” userId로만 작업하면 됩니다. 모든 OAuth 마법은 그 이전 단계 — MCP 클라이언트와 Auth 미들웨어 — 에 숨어 있습니다.

7. 시각적 다이어그램: Client, Server, Auth의 상호작용

3장에서 이미 흐름을 단계별로 살펴봤습니다. 하지만 때로는 백 문장보다 한 번 그려보는 게 더 쉽습니다. 같은 상호작용을 두 가지 다이어그램으로 보여드리겠습니다.

상호작용의 골격(The Triangle of Trust)

flowchart TD
  U[User] -->|1. Login / Consent| A[MCP Auth Server]
  U -->|2. 채팅| C["MCP Client (ChatGPT)"]
  C -->|3. OAuth Flow| A
  C -->|4. Bearer Token| S[MCP Server]
  S -->|5. Data| C

도표는 이렇게 읽습니다.

먼저 사용자가 Auth Server를 통해 로그인하고, Auth Server는 사용자의 신원을 확인해 토큰을 발급합니다. MCP Client가 이 과정을 주도하고, 이후 토큰을 이용해 MCP 서버에 요청합니다. MCP 서버는 로그인/비밀번호를 보지 않으며, 오직 토큰만 보고 허용 여부를 결정합니다.

요청부터 응답까지의 흐름

sequenceDiagram
  participant User
  participant ChatGPT as MCP Client
  participant Auth as Auth Server
  participant MCP as MCP Server

  User->>ChatGPT: "내 선물 목록을 보여줘"
  ChatGPT->>MCP: callTool(getSavedGiftLists) (토큰 없음)
  MCP-->>ChatGPT: 401 + WWW-Authenticate (resource_metadata)
  ChatGPT->>Auth: /authorize + PKCE
  User->>Auth: 로그인/비밀번호 입력, consent 승인
  Auth-->>ChatGPT: redirect + code
  ChatGPT->>Auth: /token + code_verifier
  Auth-->>ChatGPT: access_token (JWT)
  ChatGPT->>MCP: callTool(getSavedGiftLists) + Authorization: Bearer ...
  MCP-->>ChatGPT: 개인 목록이 담긴 JSON
  ChatGPT-->>User: 위젯에 렌더링된 목록

이 다이어그램은 이 모듈이 끝날 때쯤 눈 감고도 설명할 수 있어야 합니다.

8. 더 깊게: 여러 리소스, 여러 클라이언트, DCR

이러한 아키텍처의 장점은 확장성입니다.

첫째, MCP 서버가 여러 개일 수 있습니다(예: 하나는 선물, 다른 하나는 주문). 그리고 단일 Auth Server가 서로 다른 aud/resource를 가진 토큰을 발급합니다. 각 리소스 서버는 토큰이 진짜 자신을 위한 것인지 반드시 확인해야 합니다. 그렇지 않으면 같은 Auth Server가 발급한 다른 서비스용 토큰을 받아들이는 “confused deputy” 문제가 발생합니다.

둘째, 클라이언트가 많아질 수 있습니다:

  • ChatGPT App;
  • 여러분의 자체 웹 프런트엔드;
  • 모바일 앱;
  • MCP Gateway를 통한 파트너 통합.

이들 모두는 다음을 수행합니다:

  • /.well-known/oauth-protected-resource를 읽고;
  • Auth Server의 위치를 알아내며;
  • OAuth 2.1 flow를 수행하고;
  • 토큰을 받아 MCP 서버를 호출합니다.

셋째, 최신 Auth Server는 Dynamic Client Registration (DCR)을 점점 더 많이 지원합니다 — API로 클라이언트를 동적으로 등록하는 기능입니다. MCP 사양은 이를 전제로 합니다: 클라이언트(ChatGPT/Jam)는 Auth Server의 registration_endpoint를 통해 스스로를 자동 등록할 수 있습니다.

이 모듈에서 중요한 포인트:

  • MCP Client, MCP Server, Auth Server는 표준화된 discovery 문서와 토큰으로 상호작용한다;
  • 백엔드 코드에 모든 클라이언트를 “하드코딩”할 필요가 없다;
  • 기존 인가 모델을 깨지 않고 생태계를 확장할 수 있다.

9. MCP 인가 아키텍처에서 흔한 오해

오류 №1: “MCP 서버가 사용자 로그인을 직접 처리해야 한다”.
일부 개발자는 로그인 폼을 MCP 서버에 직접 넣고, 도구를 통해 로그인/비밀번호를 보내려 합니다. 이는 OAuth의 기본 아이디어를 깨뜨립니다. MCP 서버는 어떠한 상황에서도 비밀번호를 보아서는 안 됩니다. 로그인과 consent는 Auth Server의 책임입니다. MCP 서버는 토큰과 그 claims만 다룹니다.

오류 №2: MCP Client와 MCP Server의 혼동.
가끔 ChatGPT를 “내 백엔드의 일부”처럼 여겨 비밀을 보관하게 하거나, 권한 검증을 ChatGPT가 해줄 것이라 기대합니다. 실제로 MCP Client는 OAuth를 시작하고 토큰을 첨부할 뿐입니다. 토큰과 권한 검증은 ChatGPT가 아니라 MCP 서버의 역할입니다.

오류 №3: .env의 “API 키”로 OAuth를 대체.
전형적인 안티 패턴: 커다란 SERVICE_API_KEY 하나를 만들어 MCP 서버의 .env에 넣고 문제를 해결했다고 생각하는 것입니다. 이 방식에는 사용자별 권한 분리가 없고, 개인정보를 안전하게 보여줄 수도, 구매 같은 행위를 안전하게 수행할 수도 없습니다. 모든 것이 “서비스의 이름으로” 수행되어 사용자의 이름으로가 아닙니다. 이는 ChatGPT Apps에서의 인가 목적에 정면으로 반합니다.

오류 №4: audienceresource 무시.
MCP 서버가 서명이 맞는 임의의 JWT를 받아들이고 aud/resource를 확인하지 않는다면, 같은 Auth Server가 다른 서비스용으로 발급한 토큰을 사용해 여러분의 도구를 호출할 수 있게 됩니다. 이는 OAuth 보안 모델을 정면으로 위반하는 것입니다. 서버는 토큰이 자신의 resource를 위해 발급되었는지 반드시 확인해야 합니다.

오류 №5: 인증/인가 로직과 비즈니스 로직의 혼합.
가끔 도구 핸들러에 토큰 파싱, 서명 검증, JWK 처리 등을 모두 밀어 넣습니다. 그 결과 코드는 깨지기 쉽고 유지보수가 어려워집니다. userId 매핑까지의 “토큰 검증 레이어”(미들웨어)와 “도구 자체의 로직” 레이어를 명확히 분리해, 핸들러는 이미 이해하기 쉬운 AuthContext만 받도록 하는 것이 훨씬 올바릅니다.

오류 №6: .well-known 없이 ChatGPT가 “다 해줄 것”이라 기대.
올바른 /.well-known/oauth-protected-resource 엔드포인트가 없으면 MCP 클라이언트는 여러분의 Auth Server가 어디에 있고 어떤 scopes가 필요한지 알 수 없습니다. 결과적으로 챗은 조용히 “로그인할 줄 모르는” 상태가 되고, 개발자는 텅 빈 로그만 바라보게 됩니다. 올바른 접근: MCP 서버가 .well-known을 통해 인가 요구사항을 명확히 선언하고, 클라이언트가 이를 읽어 flow를 구성하도록 하십시오.

오류 №7: 비즈니스 로직에서 사용자를 잊어버림.
OAuth와 토큰→userId 매핑을 올바르게 설정해 놓고도, DB 쿼리에서 이를 사용하지 않는 경우가 있습니다. 예를 들어, ownerId = userId로 필터링하는 것을 잊는 것입니다. 그러면 인증된 누구나 타인의 데이터를 볼 수 있습니다. 토큰이 있다는 것은 첫걸음일 뿐입니다. 두 번째 걸음은 항상 비즈니스 코드에서 userIdscope를 올바르게 사용하는 것입니다.

코멘트
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION