WorkOS AuthKit으로 최신 MCP 인증 붙이기

원격 MCP 서버에 OAuth를 붙일 때 의외로 까다로운 부분은 로그인 화면이 아닙니다. 처음 만난 MCP 클라이언트를 인가 서버(Authorization Server)에 어떻게 등록할지가 더 큰 문제인데요. 데스크톱 앱, 코드 편집기, 에이전트 플랫폼을 인가 서버에 일일이 미리 등록할 수는 없기 때문입니다.
2025년 11월 25일 MCP 스펙은 이 문제를 풀기 위해 클라이언트 ID 메타데이터 문서(Client ID Metadata Documents, CIMD)를 권장 등록 방식으로 추가했습니다. 이전 스펙에서 사용하던 동적 클라이언트 등록(Dynamic Client Registration, DCR)도 호환성을 위해 남겨 두었고요. 하지만 인가 서버가 이 두 방식을 모두 구현해야 최신 클라이언트와 기존 클라이언트를 함께 받을 수 있습니다.
WorkOS AuthKit은 CIMD와 DCR을 한 환경에서 모두 지원하는 몇 안 되는 신원 제공자(Identity Provider, IdP)입니다. PKCE, 자원 지시자(Resource Indicator), 보호 자원 메타데이터(Protected Resource Metadata)처럼 MCP가 요구하는 OAuth 구성 요소도 WorkOS Connect 위에서 제공합니다. 이 글에서는 일반적인 MCP 인증 원리를 반복하지 않고 WorkOS의 지원 기능을 어떻게 켜고 확인하는지 살펴보겠습니다. 전체 인증 흐름이 먼저 궁금하다면 MCP Authentication을 읽고 오셔도 좋습니다.
WorkOS는 MCP에서 무엇을 맡을까요?
WorkOS AuthKit은 MCP 서버 앞에 붙는 로그인 미들웨어가 아니라 액세스 토큰(access token)을 발급하는 인가 서버입니다. MCP 서버는 토큰을 받아 검증하는 자원 서버(Resource Server)이고, MCP 클라이언트는 AuthKit에서 토큰을 받아 서버에 제시하는 OAuth 클라이언트(OAuth Client)입니다.
flowchart TB
accTitle: WorkOS AuthKit을 사용하는 MCP의 OAuth 역할
accDescr: MCP 클라이언트는 OAuth 클라이언트 역할로 사용자 브라우저를 열고 WorkOS AuthKit에 인가를 요청한다. 사용자가 브라우저에서 로그인하고 동의하면 AuthKit이 클라이언트에 액세스 토큰을 발급한다. 클라이언트는 이 토큰을 MCP 자원 서버에 제시한다.
client["MCP 클라이언트<br/>OAuth 클라이언트"]
browser["사용자 브라우저"]
authkit["WorkOS AuthKit<br/>인가 서버"]
server["MCP 서버<br/>자원 서버"]
client -. 브라우저 열기 .-> browser
browser -->|"로그인과 동의"| authkit
client -->|"인가 요청과 토큰 교환"| authkit
authkit -->|"액세스 토큰"| client
client -->|"Bearer 액세스 토큰"| server
classDef clientRole stroke:#0284c7,stroke-width:2px
classDef authorizationRole stroke:#7c3aed,stroke-width:2px
classDef resourceRole stroke:#16a34a,stroke-width:2px
class client clientRole
class authkit authorizationRole
class server resourceRole
WorkOS의 MCP 지원은 WorkOS Connect를 기반으로 합니다. AuthKit 도메인은 인가 엔드포인트(authorization endpoint), 토큰 엔드포인트(token endpoint), 등록 엔드포인트(registration endpoint), JWKS 엔드포인트(JWKS endpoint)를 제공합니다. 따라서 MCP 서버가 /authorize, /token, /register를 직접 만들 필요가 없습니다.
MCP 서버에 남는 책임은 두 가지입니다. 클라이언트가 AuthKit을 발견하도록 표준 메타데이터를 제공하고, AuthKit이 발급한 토큰을 검증하면 됩니다. 401 응답과 토큰 검증 규칙 자체는 특정 제품에 종속되지 않으므로 MCP 자원 서버 구현 방법으로 분리했습니다.
CIMD와 DCR을 왜 모두 지원해야 할까요?
MCP 클라이언트는 연결하려는 모든 서버와 사전 관계를 맺기 어렵습니다. OAuth에서 흔히 쓰는 고정된 client_id만 허용한다면 서버 운영자가 Claude, VS Code, 사내 에이전트 같은 클라이언트를 하나씩 등록해야 하죠.
DCR은 이 문제를 자동화합니다. 클라이언트가 인가 서버의 registration_endpoint에 자신의 이름과 리디렉션 URI(redirect URI)를 보내면 인가 서버가 새 client_id를 발급합니다. 초기 MCP 스펙에서 사용한 방식이라 아직 DCR에 의존하는 클라이언트가 있습니다. 다만 클라이언트가 만나는 인가 서버마다 등록 레코드가 생기고, 발급받은 정보를 따로 보관해야 한다는 부담이 있습니다.
CIMD는 접근법이 다릅니다. MCP 클라이언트가 자신의 메타데이터를 HTTPS URL에 게시하고 그 URL 자체를 client_id로 사용합니다. 인가 서버별 등록 레코드를 만들지 않고도 같은 클라이언트 신원을 재사용할 수 있습니다.
최신 MCP 스펙에서 권장하는 선택 순서는 사전 등록, CIMD, DCR, 사용자 입력 순입니다. CIMD를 이해하는 클라이언트는 인가 서버 메타데이터의 client_id_metadata_document_supported를 보고 URL형 client_id를 사용합니다. 그렇지 않은 클라이언트는 registration_endpoint를 보고 DCR로 내려갑니다. WorkOS에서 두 기능을 모두 켜 두면 클라이언트 세대가 달라도 같은 AuthKit 도메인에 연결할 수 있는 셈입니다.
CIMD 활성화하고 사용하기
CIMD는 WorkOS에서 기본적으로 꺼져 있습니다. WorkOS 대시보드의 Connect → Configuration으로 이동해 CIMD를 활성화해야 합니다. 최신 MCP 클라이언트를 받을 계획이라면 사실상 필수 설정입니다.
활성화한 다음에는 AuthKit의 인가 서버 메타데이터에서 지원 신호를 확인합니다. authkit_domain에는 WorkOS에서 사용하는 AuthKit 도메인을 넣으세요.
curl https://authkit_domain/.well-known/oauth-authorization-server \
| jq '.client_id_metadata_document_supported'
결과가 true라면 AuthKit이 URL형 client_id를 받을 준비가 된 것입니다. 이제 MCP 클라이언트가 다음과 같은 메타데이터 문서를 공개해야 합니다.
{
"client_id": "https://client.example.com/oauth/client-metadata.json",
"client_name": "Example MCP Client",
"client_uri": "https://client.example.com",
"redirect_uris": ["http://127.0.0.1:3000/callback"],
"grant_types": ["authorization_code"],
"response_types": ["code"],
"token_endpoint_auth_method": "none"
}
메타데이터 필드별 의미와 AuthKit이 확인해야 할 보안 조건은 CIMD의 동작 원리에서 더 자세히 다뤘습니다.
문서는 client_id에 적힌 HTTPS URL에서 실제로 조회할 수 있어야 하고, 문서 안의 client_id도 그 URL과 정확히 같아야 합니다. MCP 클라이언트는 별도의 등록 요청 없이 이 URL을 AuthKit 인가 요청의 client_id로 보냅니다.
curl https://authkit_domain/oauth2/authorize -G \
--data-urlencode \
"client_id=https://client.example.com/oauth/client-metadata.json" \
--data-urlencode "redirect_uri=http://127.0.0.1:3000/callback" \
-d "response_type=code" \
--data-urlencode "scope=openid profile email" \
-d "nonce=RANDOM_NONCE" \
-d "state=RANDOM_STATE" \
-d "code_challenge=PKCE_CODE_CHALLENGE" \
-d "code_challenge_method=S256" \
--data-urlencode "resource=https://mcp.example.com"
AuthKit은 인가 요청을 받으면 client_id URL에서 문서를 가져옵니다. 문서 안의 client_id와 요청의 redirect_uri를 검증한 뒤 로그인과 동의 화면을 보여 주죠. 사용자가 동의하면 클라이언트는 인가 코드(authorization code)를 받아 code_verifier와 함께 토큰으로 교환합니다. 실제로는 MCP 클라이언트가 이 과정을 자동으로 처리하므로 서버 운영자가 각 클라이언트를 WorkOS에 등록할 필요가 없습니다.
DCR 활성화하고 사용하기
DCR도 WorkOS 대시보드의 Connect → Configuration에서 활성화합니다. DCR 관리 화면에서는 동적으로 등록되는 클라이언트에 기본으로 허용할 스코프도 선택할 수 있습니다. 최신 스펙의 우선 방식은 아니지만 CIMD를 아직 구현하지 않은 MCP 클라이언트를 지원하려면 함께 켜 두는 편이 좋습니다.
설정을 저장한 뒤 인가 서버 메타데이터의 registration_endpoint를 확인합니다.
curl https://authkit_domain/.well-known/oauth-authorization-server \
| jq '.registration_endpoint'
WorkOS AuthKit에서는 이 값이 https://authkit_domain/oauth2/register를 가리킵니다. DCR을 지원하는 MCP 클라이언트는 RFC 7591 형식으로 이 엔드포인트에 등록 요청을 보냅니다.
curl -X POST https://authkit_domain/oauth2/register \
-H "Content-Type: application/json" \
-d '{
"client_name": "Example MCP Client",
"redirect_uris": ["http://127.0.0.1:3000/callback"],
"grant_types": ["authorization_code", "refresh_token"],
"response_types": ["code"],
"token_endpoint_auth_method": "none"
}'
AuthKit은 등록에 성공하면 새 client_id와 등록 정보를 응답합니다. 공개 클라이언트(public client)는 이 값을 안전하게 보관하고 다음 인가 요청부터 CIMD의 URL형 client_id 대신 발급받은 ID를 사용합니다.
curl https://authkit_domain/oauth2/authorize -G \
-d "client_id=WORKOS_ISSUED_CLIENT_ID" \
--data-urlencode "redirect_uri=http://127.0.0.1:3000/callback" \
-d "response_type=code" \
--data-urlencode "scope=openid profile email" \
-d "nonce=RANDOM_NONCE" \
-d "state=RANDOM_STATE" \
-d "code_challenge=PKCE_CODE_CHALLENGE" \
-d "code_challenge_method=S256" \
--data-urlencode "resource=https://mcp.example.com"
DCR에서는 등록과 인가가 분리됩니다. /oauth2/register는 클라이언트 ID를 얻는 단계이고, 사용자 로그인과 동의는 그다음 /oauth2/authorize에서 진행됩니다. 등록 요청만으로 액세스 토큰이 발급되지는 않습니다.
두 기능을 모두 켜도 같은 클라이언트가 둘을 동시에 사용하지는 않습니다. 최신 클라이언트는 client_id_metadata_document_supported: true를 보고 CIMD를 선택하고, 이전 클라이언트는 registration_endpoint를 따라 DCR을 사용합니다. 둘 중 어느 쪽이든 PKCE의 S256과 MCP 서버를 가리키는 resource 파라미터는 그대로 필요합니다.
MCP 서버 URL을 자원 지시자로 등록하기
다음으로 WorkOS 대시보드의 Resource Indicator 항목에 MCP 엔드포인트 URL을 추가합니다. MCP 서버가 https://mcp.example.com이라면 대시보드와 서버 메타데이터에서 같은 값을 사용해야 합니다.
WORKOS_ISSUER=https://auth.example.com
MCP_RESOURCE=https://mcp.example.com
MCP 클라이언트는 인가 요청과 토큰 요청에 resource=https://mcp.example.com을 보냅니다. WorkOS는 요청된 자원과 일치하는 aud 클레임을 액세스 토큰에 넣고, MCP 서버는 이 값을 수신 대상(audience)으로 검증합니다.
이 설정에서 URL은 비슷하면 되는 값이 아니라 식별자입니다. https://mcp.example.com, https://mcp.example.com/, https://mcp.example.com/mcp는 서로 다른 값이 될 수 있으므로 대시보드, 보호 자원 메타데이터, 토큰 검증 코드에서 정확히 맞춰야 합니다. 개발, 스테이징, 프로덕션 서버가 따로 있다면 WorkOS에 자원 지시자를 여러 개 추가할 수 있습니다.
자원 지시자를 하나도 등록하지 않으면 WorkOS는 환경별 기본값을 aud로 사용하고 클라이언트가 보낸 resource 파라미터를 무시합니다. 토큰을 특정 MCP 서버에 묶으려는 목적이 사라지므로 MCP 연동에서는 서버 URL을 명시적으로 등록하는 편이 안전합니다.
MCP 서버에서 AuthKit을 가리키기
WorkOS 설정을 마쳤다면 MCP 서버의 보호 자원 메타데이터에서 AuthKit 발급자(issuer)를 알려 줍니다. Bun에서는 다음처럼 최소 엔드포인트를 열 수 있습니다.
const issuer = Bun.env.WORKOS_ISSUER!;
const resource = Bun.env.MCP_RESOURCE!;
export function getProtectedResourceMetadata() {
return Response.json({
resource,
authorization_servers: [issuer],
bearer_methods_supported: ["header"],
});
}
import { getProtectedResourceMetadata } from "./resource-metadata";
Bun.serve({
routes: {
"/.well-known/oauth-protected-resource": {
GET: getProtectedResourceMetadata,
},
},
fetch() {
return new Response("Not Found", { status: 404 });
},
});
클라이언트는 이 문서의 authorization_servers를 따라 AuthKit 메타데이터를 조회합니다. 최신 클라이언트라면 여기서 CIMD 지원 여부를 확인하고, 이전 클라이언트라면 DCR 등록 엔드포인트를 사용합니다. MCP 서버가 클라이언트 등록 방식을 직접 판별할 필요는 없습니다.
토큰 검증에서는 WorkOS AuthKit 도메인의 /oauth2/jwks를 사용합니다. WorkOS API 키는 필요하지 않습니다.
bun add jose
import { createRemoteJWKSet, jwtVerify } from "jose";
const issuer = Bun.env.WORKOS_ISSUER!;
const audience = Bun.env.MCP_RESOURCE!;
const jwks = createRemoteJWKSet(new URL(`${issuer}/oauth2/jwks`));
export async function verifyWorkosToken(token: string) {
const { payload } = await jwtVerify(token, jwks, {
issuer,
audience,
});
return payload;
}
WorkOS가 서명했다는 사실만 확인해서는 부족합니다. 대시보드에 등록한 자원 지시자와 aud가 일치해야 다른 서비스용 토큰이 MCP 서버에서 재사용되는 일을 막을 수 있습니다. 토큰이 없거나 잘못됐을 때 반환할 WWW-Authenticate 헤더와 도구별 권한 검사는 Bun 기반 MCP 자원 서버 예제에서 이어서 다룹니다.
이전 메타데이터 발견 방식도 받아 주기
일부 MCP 클라이언트는 보호 자원 메타데이터를 먼저 읽지 않고 MCP 서버 자체에서 /.well-known/oauth-authorization-server를 찾습니다. WorkOS는 이런 클라이언트를 위해 AuthKit 메타데이터를 프록시하는 호환성 방법도 안내합니다.
export async function proxyAuthorizationServerMetadata() {
const upstream = await fetch(
`${Bun.env.WORKOS_ISSUER}/.well-known/oauth-authorization-server`,
);
return new Response(upstream.body, {
status: upstream.status,
headers: {
"Content-Type":
upstream.headers.get("content-type") ?? "application/json",
},
});
}
이 프록시는 CIMD와 DCR 사이를 중계하는 기능이 아닙니다. 보호 자원 메타데이터를 이해하지 못하는 클라이언트가 AuthKit의 인가 서버 메타데이터에 도달하도록 돕는 장치입니다. 실제 클라이언트 등록과 토큰 발급은 여전히 WorkOS가 처리합니다.
기존 로그인 시스템은 그대로 유지하기
이미 자체 로그인과 사용자 데이터베이스를 운영하고 있다면 AuthKit으로 모두 옮겨야 할까요? WorkOS의 Standalone MCP Auth를 사용하면 기존 인증 시스템을 유지하면서 MCP용 OAuth 기능만 AuthKit에 맡길 수 있습니다.
이 구성에서는 MCP 클라이언트가 AuthKit에서 인가 흐름을 시작하지만, WorkOS 로그인 화면 대신 애플리케이션의 로그인 URI(Login URI)로 이동합니다. 애플리케이션이 사용자를 인증하고 AuthKit 완료 API를 호출하면 WorkOS가 동의와 토큰 발급을 마무리합니다. 애플리케이션의 로그인 URI는 AuthKit이 전달한 external_auth_id도 처리해야 합니다.
CIMD와 DCR, 메타데이터 발견, 토큰 검증 방법은 일반 AuthKit 연동과 같습니다. 사용자 인증 화면과 세션은 기존 시스템에 남기고 변화가 빠른 MCP OAuth 호환 계층만 WorkOS에 맡길 수 있습니다.
Cross App Access까지 확장하기
WorkOS는 교차 앱 접근(Cross App Access, XAA)도 얼리 액세스로 제공합니다. 같은 조직에서 서로 다른 두 애플리케이션에 SSO로 로그인한 사용자가 일반적인 OAuth 동의 흐름을 다시 거치지 않고 다른 애플리케이션의 MCP 서버에 접근하도록 만드는 기능입니다.
AuthKit의 MCP 인증은 ID 토큰 JWT 인가 승인(ID Token JWT Authorization Grant, ID-JAG) 교환을 기본으로 지원합니다. XAA가 활성화된 환경에서는 한 애플리케이션의 토큰을 대상 MCP 서버용 액세스 토큰으로 교환할 수 있습니다. 여러 SaaS 제품과 에이전트를 조직 단위로 연결하는 경우에 눈여겨볼 만한 기능이죠.
다만 XAA는 현재 얼리 액세스라 WorkOS에 활성화를 요청해야 하며, Standalone MCP Auth와는 함께 사용할 수 없습니다. 일반적인 MCP OAuth 연결을 먼저 안정화한 뒤 필요한 환경에서 검토하는 편이 좋습니다.
배포 전에 확인할 것
WorkOS 연동에서 문제가 생기면 다음 순서로 확인해 보세요.
- WorkOS 대시보드의 Connect → Configuration에서 CIMD가 켜져 있는가?
- 이전 MCP 클라이언트도 지원한다면 DCR도 켜져 있는가?
- 인가 서버 메타데이터에
client_id_metadata_document_supported: true와registration_endpoint가 모두 보이는가? - WorkOS에 등록한 자원 지시자와 서버의
resource, 토큰 검증의audience가 정확히 같은가? - 보호 자원 메타데이터의
authorization_servers가 실제 AuthKit 발급자를 가리키는가? - JWKS를 AuthKit 도메인의
/oauth2/jwks에서 읽고iss,aud, 만료 시간을 검증하는가? - 최신 CIMD 클라이언트와 DCR에 의존하는 이전 클라이언트를 각각 시험했는가?
WorkOS API 키를 MCP 클라이언트에 넣어 해결하려고 해서는 안 됩니다. 클라이언트가 필요한 것은 표준 OAuth 흐름이고, MCP 서버의 토큰 검증도 공개 JWKS로 처리할 수 있습니다. API 키는 사용자나 조직을 관리하는 서버 사이드 WorkOS API 호출에만 사용하세요.
마치며
WorkOS AuthKit의 장점은 로그인 화면보다 MCP 호환성에서 더 잘 드러납니다. 최신 클라이언트가 선택하는 CIMD와 이전 클라이언트가 기대하는 DCR을 함께 지원하고, PKCE와 자원 지시자, 표준 메타데이터를 한 인가 서버에서 제공합니다.
새 연동이라면 CIMD를 기본으로 켜고 DCR은 하위 호환성 경로로 함께 열어 두세요. 그다음 MCP 서버 URL을 자원 지시자로 등록하고 메타데이터에서 두 등록 방식이 실제로 노출되는지 확인하면 됩니다. MCP 서버는 AuthKit을 가리키는 보호 자원 메타데이터와 iss, aud를 확인하는 토큰 검증에 집중할 수 있고요.
대시보드 항목과 현재 지원 범위는 WorkOS AuthKit MCP 공식 문서에서, CIMD와 DCR의 선택 규칙은 MCP Authorization 공식 스펙에서 확인할 수 있습니다.
This work is licensed under CC BY 4.0