MCP 서버에 OAuth2와 JWT를 적용하려면 핵심은 MCP 서버를 OAuth2 리소스 서버(Resource Server)로 두고, 외부 인증 서버가 발급한 JWT 액세스 토큰을 헤더에서 검증하도록 구성하는 것입니다. 실무에서는 여기에 scope 기반 권한 제어와 필요 시 authorization_code + client_credentials 혼합 전략을 함께 붙이는 방식이 가장 많이 쓰입니다.
적용 구조
MCP 최신 보안 흐름에서는 서버가 직접 인증 서버 역할까지 맡지 않아도 되고, 외부 OAuth2/OIDC 서버가 발급한 액세스 토큰을 MCP 서버가 검증하면 됩니다. 즉, Keycloak·Auth0·Okta·Cognito 같은 인증 서버가 토큰을 발급하고, MCP 서버는 Authorization: Bearer ... 헤더의 JWT를 issuer, audience, exp 기준으로 검증한 뒤 요청을 허용합니다.
기본 순서
구현 순서는 보통 4단계입니다: 리소스 식별자와 scope 설계, 보호 리소스 메타데이터 공개, JWT 검증, scope 기반 인가입니다. 예를 들어 mcp:tools:sales-data:read, mcp:tools:crm:write 같은 scope를 먼저 정의해 두면, 이후 도구별 접근 제어를 명확하게 나눌 수 있습니다.
OAuth 메타데이터
MCP 서버는 클라이언트가 OAuth 구성을 자동 발견할 수 있도록 /.well-known/oauth-protected-resource 같은 메타데이터 엔드포인트를 제공할 수 있습니다. 이 엔드포인트에는 resource, authorization_servers, bearer_methods_supported, scopes_supported 등을 내려주며, 클라이언트가 어떤 인증 서버와 어떤 scope를 써야 하는지 자동으로 이해하게 만듭니다.
app.get('/.well-known/oauth-protected-resource', (req, res) => {
res.json({
resource: 'https://api.example.com',
authorization_servers: ,
bearer_methods_supported: ,
scopes_supported: });
});
Node.js JWT 검증
Node.js 계열에서는 jose 라이브러리로 원격 JWKS를 읽어 JWT를 검증하는 방식이 일반적입니다. 아래 예시는 Bearer 토큰이 없으면 401을 반환하고, 있으면 issuer와 audience를 확인한 뒤 scope, client_id, sub를 req.auth에 저장하는 패턴입니다.
import express from 'express';
import { jwtVerify, createRemoteJWKSet } from 'jose';
const app = express();
const JWKS = createRemoteJWKSet(
new URL('https://auth.example.com/.well-known/jwks')
);
async function validateToken(req, res, next) {
const token = req.headers.authorization?.split(' ') ;
if (!token) {
return res.status(401).json({ error: 'Bearer token required' });
}
try {
const { payload } = await jwtVerify(token, JWKS, {
issuer: 'https://auth.example.com',
audience: 'https://api.example.com'
});
req.auth = {
userId: payload.sub,
clientId: payload.client_id,
scopes: payload.scope?.split(' ') || ,
expiresAt: payload.exp
};
next();
} catch (e) {
return res.status(401).json({ error: 'Invalid or expired token' });
}
}
app.use('/mcp', validateToken);
Scope 인가
토큰 검증만으로는 충분하지 않고, 실제 도구 호출 시에는 scope 기반 인가가 필요합니다. 예를 들어 읽기 도구는 mcp:tools:read, 쓰기 도구는 mcp:tools:write를 요구하게 만들면 JWT가 유효해도 scope가 부족한 사용자는 403으로 차단할 수 있습니다.
const requireScope = (requiredScope) => (req, res, next) => {
const scopes = req.auth?.scopes || ;
const ok = scopes.some(scope =>
scope === requiredScope ||
(scope.endsWith(':*') && requiredScope.startsWith(scope.slice(0, -1)))
);
if (!ok) {
return res.status(403).json({
error: 'insufficient_scope',
required_scope: requiredScope
});
}
next();
};
app.post(
'/mcp/tools/send-message',
requireScope('mcp:tools:write'),
(req, res) => {
res.json({ ok: true });
}
);
Spring 서버 적용
Spring 기반 MCP 서버라면 spring-boot-starter-oauth2-resource-server를 넣고, issuer-uri만 설정해도 JWT 리소스 서버 구성이 가능합니다. 이 경우 모든 MCP 요청은 JWT가 필요해지고, Spring Security가 발급자 기준으로 토큰을 자동 검증합니다.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>
server.port=8090
spring.security.oauth2.resourceserver.jwt.issuer-uri=http://localhost:9000
이 구성만으로도 MCP 서버는 외부 인증 서버가 발급한 JWT를 받는 보호된 리소스 서버가 됩니다.
인증 서버 구성
Spring 예시에서는 인증 서버 쪽에 authorization_code, client_credentials, refresh_token grant를 함께 열어 두는 방식이 소개됩니다. 이 패턴은 사용자 브라우저가 개입하는 실제 도구 실행은 authorization_code, 서버 초기화나 tools/list 같은 머신 간 호출은 client_credentials로 나누기 좋습니다.
클라이언트 이중 전략
MCP 클라이언트 쪽에서는 초기 세션 수립·initialize·tools/list는 client_credentials, 실제 사용자 권한이 필요한 tools/call은 authorization_code로 나누는 설계가 실무적으로 매우 중요합니다. Spring 팀도 이 이유로 클라이언트 등록을 2개 두는 예시를 제시하고 있으며, 사용자 요청이 없는 부팅 단계에서는 authorization_code를 쓸 수 없기 때문에 client_credentials가 필요하다고 설명합니다.
게이트웨이 적용
게이트웨이 계층을 두는 경우에는 JWT를 검증한 뒤 토큰 교환(Token Exchange) 또는 tool 이름별 권한 매핑까지 넣을 수 있습니다. Red Hat 예시에서는 x-mcp-toolname 헤더와 JWT claims를 연결해, 특정 툴에 대해 허용된 role이 있는 경우에만 호출되도록 정책을 구성합니다.
실무 팁
프로덕션에서는 audience를 MCP 서버 URL로 고정하고, 액세스 토큰 수명을 5분~1시간 수준으로 짧게 두는 것이 권장됩니다. 또한 JWT는 항상 JWKS 기반 서명 검증, scope 최소 권한, 401과 403 분리, 감사 로그 기록을 함께 운영해야 안전합니다.
최소 구성 예시
가장 현실적인 최소 구성은 다음과 같습니다:
- 인증 서버: Keycloak/Auth0/Okta/Cognito 중 하나
- MCP 서버: OAuth2 Resource Server로 설정
- JWT 검증:
issuer+audience+exp+ JWKS 검증 - 권한 제어: scope 기반
read/write/admin분리 - 클라이언트:
client_credentials+authorization_code혼합
원하시면 다음 답변에서 Node.js용 완성 예제나 Keycloak 기준 설정 파일 예시로 바로 이어서 정리해드릴게요.