AWS가 [MCP 클라이언트와 Bedrock AgentCore Gateway를 OAuth 2.0 인증 코드 흐름으로 연결하는 가이드](https://aws.amazon.com/blogs/machine-learning/building-a-secure-auth-code-flow-setup-using-agentcore-gateway-with-mcp-clients/)를 공개했다. Cursor, Claude Desktop, 자체 구현 클라이언트와 Gateway를 연결하는 방법을 잘 설명한 튜토리얼이다. 하지만 실제 설계에서 더 어려운 질문은 따로 있다. **언제 사용자 위임 OAuth가 필요하고, 클라이언트 자격 증명으로 처리하면 무엇이 달라지는가?**
내부 도구를 외부 MCP 클라이언트에 노출하려고 하면 이 질문이 가장 먼저 등장한다. 이 글은 튜토리얼을 따라 하는 방법이 아니라, 인증 방식을 고르는 기준을 정리한다.
## 구성 요약
AgentCore Gateway는 MCP 클라이언트와 Lambda 기반 도구 사이에서 리소스 서버 역할을 한다.
```
MCP 클라이언트 (Cursor / Claude Desktop / 자체 구현)
│ (Bearer 토큰을 포함한 HTTP+SSE 기반 MCP)
▼
AgentCore Gateway ──► Cognito / Okta / Auth0 (토큰 검증)
│
▼ (서명된 호출)
Lambda 도구 ──► DynamoDB / Salesforce / Jira / 기타 서비스
```
Gateway는 OAuth 리소스 서버이고, ID 공급자(IdP)는 토큰을 발급하며, MCP 클라이언트는 그 토큰을 제시한다. 여기에는 두 가지 방식이 있다.
1. **클라이언트 자격 증명(M2M)**: MCP 클라이언트 자체가 서비스 계정으로 인증한다. 토큰은 하나이며, 사용자 정체성이나 사용자별 권한 범위가 없다.
2. **인증 코드 흐름 + PKCE**: 사용자가 브라우저 리디렉션을 통해 한 번 로그인한다. MCP 클라이언트는 발급받은 액세스 토큰과 갱신 토큰을 저장하고, 도구 호출 때마다 액세스 토큰을 제시한다.
AWS의 가이드는 두 번째 방식을 다룬다. 중요한 것은 왜 이 방식을 선택해야 하는지 판단하는 일이다.
## 클라이언트 자격 증명으로 충분한 경우
다음 조건이 모두 참이라면 인증 코드 흐름이 필요하지 않다.
- 도구가 **사용자별로 분리되지 않은 공유 데이터**를 다룬다. 예를 들어 공개 지식 기반, 내부 지표 집계, 읽기 전용 카탈로그다.
- 모든 호출자가 신뢰할 수 있는 내부 영역에 있다. 즉, 사내 에이전트 플랫폼이지 제3자 MCP 클라이언트의 마켓플레이스가 아니다.
- 감사 로그에서 호출을 특정 사용자에게 귀속할 필요가 없다.
이 경우 MCP 클라이언트 종류별로 `client_id`와 `client_secret`을 발급하고, 비밀 값을 순환 교체하며, 도구 권한 범위를 제한하면 된다.
## 인증 코드 흐름이 필요한 경우
다음 중 하나라도 해당되면 M2M 방식은 한계에 부딪힌다.
| 조건 | M2M이 적합하지 않은 이유 |
|---|---|
| 도구가 **사용자별 데이터**를 조회한다. 예: 특정 사용자의 Salesforce 레코드, Jira 티켓, S3 접두사 | 공유 서비스 계정은 모든 데이터를 보게 되어 과도한 권한을 갖거나, 아무것도 보지 못하게 된다. 중간 지점을 만들려면 도구 안에 인증 체계를 다시 구현해야 한다. |
| **멀티 테넌트 SaaS**와 연동한다 | 도구는 사용자를 대신해 하위 API를 호출해야 한다. 그렇지 않으면 도구에 사용자별 자격 증명을 보관하고 가장해야 하는데, OAuth가 해결하려는 문제를 다시 만드는 셈이다. |
| **감사 또는 컴플라이언스**에서 호출 주체를 요구한다 | 토큰의 `sub` 클레임이 감사 기록이 된다. M2M 토큰의 `sub`은 서비스 식별자이지 사람이 아니다. |
| **동의 화면**이 제품 요구사항이다 | 토큰이 발급되기 전에 “이 MCP 클라이언트가 사용자의 특정 데이터를 읽으려 합니다”라는 동의를 보여 줄 수 있는 방식은 인증 코드 흐름이다. |
내부 에이전트가 플랫폼 작업을 수행하는 도구 게이트웨이라면 M2M으로 충분할 수 있다. 반면 사용자가 자신의 Cursor를 Gateway에 연결해 자신의 권한으로 작업하게 한다면 인증 코드 흐름이 필요하다. 이를 어설프게 절충하면 결국 자체 인증 시스템을 만들게 된다.
## 튜토리얼에서 충분히 다루지 않는 세 가지
튜토리얼은 정상 흐름을 설명한다. 실제 운영에서는 다음 세 가지를 더 신경 써야 한다.
### 1. MCP 전송 방식과 브라우저 리디렉션은 자연스럽게 결합되지 않는다
OAuth 인증 코드 흐름은 브라우저를 전제로 한다. 하지만 MCP 클라이언트는 대개 **stdio**(Cursor, Claude Desktop) 또는 **SSE**로 동작한다. stdio에는 브라우저가 없고, SSE 환경에서도 MCP 서버가 브라우저를 직접 제어하지 않는다.
MCP 사양의 해법은 클라이언트가 유효한 토큰이 없을 때 브라우저로 인증 엔드포인트를 열고, 리디렉션을 수신해 인증 코드를 교환한 뒤 결과를 저장하는 방식이다. Gateway는 브라우저를 다루지 않는다. 이 방식이 의미하는 바는 다음과 같다.
- MCP 클라이언트마다 토큰 캐시와 갱신 로직을 각자 구현해야 한다. Cursor와 Claude Desktop의 방식이 다르고, 자체 클라이언트도 별도의 구현이 필요하다.
- 데스크톱 클라이언트의 현실적인 `redirect_uri`는 `http://localhost:<random-port>`다. IdP에서 와일드카드 또는 충분한 포트 범위를 허용하지 않으면 새 포트가 생길 때마다 등록해야 한다.
### 2. 동적 클라이언트 등록은 아직 널리 쓰기 어렵다
[RFC 7591](https://datatracker.ietf.org/doc/html/rfc7591)은 MCP 클라이언트가 연결 시점에 IdP에 자신을 등록해 사용자가 `client_id`를 복사해 붙여 넣지 않도록 하는 표준이다. MCP 0.6도 이를 참조한다. 그러나 Cognito는 지원하지 않고, Okta는 엔터프라이즈 요금제에서만 제공하며, Auth0는 테넌트별로 기능을 켜야 한다.
당분간은 IdP에 클라이언트를 미리 만들고, 사용자에게 `client_id`를 제공해 MCP 클라이언트 설정에 입력하게 하는 방식이 현실적이다. IdP의 지원이 성숙하기 전까지 동적 등록에 의존해서는 안 된다.
### 3. 토큰 수명과 에이전트 세션 수명은 다르다
장시간 실행되는 에이전트 세션은 몇 시간에 걸칠 수 있지만, 기본 액세스 토큰 수명은 대개 60분이다. 인증 코드 흐름은 갱신 토큰을 제공하지만 다음 사항을 고려해야 한다.
- **stdio 기반 MCP에서 토큰 갱신은 자동이 아니다.** 클라이언트가 Gateway의 401 응답을 감지하고 갱신 요청을 수행한 뒤 원래 요청을 재시도해야 한다. 대부분의 기본 MCP 클라이언트 라이브러리는 이를 아직 자동으로 처리하지 않는다.
- **갱신 토큰은 Bearer 비밀 값이다.** `~/.config/` 아래의 JSON 파일이 아니라 OS 키체인에 보관해야 한다. macOS에서는 Keychain, Windows에서는 Credential Manager를 사용한다. Cursor와 Claude Desktop도 이 방식을 사용하며, 자체 클라이언트도 마찬가지여야 한다.
- **유휴 만료와 절대 만료를 함께 설계해야 한다.** 4시간짜리 에이전트 작업을 지원하면서도 토큰이 무기한 유지되지 않도록 정책을 정한다. 절대 만료 30일, 유휴 만료 24시간은 합리적인 시작점이다.
## 스코프 설계: 과소평가하기 쉬운 부분
스코프는 크게 두 방식으로 설계할 수 있다.
**도구별 스코프**: `tool:read-account`, `tool:create-opportunity`, `tool:run-report`처럼 세분화한다. 권한을 정밀하게 통제하고 감사에도 유리하지만, 동의 화면이 너무 많은 선택지로 채워지고 스코프 이름 관리가 어려워진다.
**도메인별 스코프**: `crm:read`, `crm:write`, `analytics:read`처럼 묶는다. 동의 화면은 사람이 이해하기 쉬워지지만, 하나의 스코프가 여러 도구에 대한 접근을 허용한다. 도구 하나가 손상됐을 때 수평 이동 범위가 넓어질 수 있다.
대개는 **동의 화면에는 도메인별 스코프를 사용하고, Gateway 내부에서는 도구별 권한을 적용하는 방식**이 적합하다. 액세스 토큰은 Gateway에 들어올 수 있는 권한을 부여하고, Gateway 정책은 실제로 호출할 수 있는 Lambda를 결정한다. 사용자는 적은 수의 이해하기 쉬운 선택지를 보면서도, 운영 측에서는 세분화된 통제를 유지할 수 있다.
## 주의할 함정
- **하나의 Cognito 리소스 서버에 M2M 클라이언트와 사용자 클라이언트를 함께 넣는 경우.** 동작은 하지만 스코프가 서로 섞이고, 동의 화면에 M2M 전용 스코프가 사용자에게 노출된다. 처음부터 리소스 서버를 분리하는 편이 낫다.
- **비프로덕션 환경에서 `http://localhost` 리디렉션 URI 등록을 빼먹는 경우.** Cognito, Okta, Auth0 모두 등록할 수 있다. 누락하면 로컬 개발 환경이 실패하고 오류 메시지는 종종 잘못된 계층을 가리킨다.
- **IdP의 기본 `aud` 클레임을 그대로 신뢰하는 경우.** AgentCore Gateway는 특정 대상(audience)을 기대한다. 별도 리소스 서버를 구성했다면 Cognito 사용자 풀 토큰의 기본 대상인 앱 클라이언트 ID가 Gateway가 기대하는 값과 다를 수 있다. 양쪽에서 대상을 명시적으로 설정해야 한다.
- **기밀 클라이언트라는 이유로 PKCE를 생략하는 경우.** 모든 데스크톱 MCP 클라이언트는 공개 클라이언트다. PKCE 없는 인증 코드 흐름은 다운그레이드 공격에 취약하다. PKCE는 비용이 없으므로 반드시 사용해야 한다.
- **CloudWatch에 액세스 토큰을 남기는 경우.** Gateway 요청 로그를 디버그 수준으로 설정하면 `Authorization` 헤더가 포함될 수 있다. 영구 로그로 전송하기 전에 반드시 제거해야 한다.
## 한 문단으로 정리하는 결정 기준
도구가 사용자별 데이터를 읽거나 쓰거나, 호출한 사용자를 식별해야 한다면 PKCE를 포함한 인증 코드 흐름을 사용해야 한다. 기존 IdP가 없다면 Cognito를, 이미 Okta나 Auth0를 사용 중이라면 해당 IdP를 활용하면 된다. 동의 화면에는 도메인별 스코프를 사용하고 Gateway 안에서는 도구별 정책을 적용한다. 동적 등록이 성숙할 때까지는 MCP 클라이언트 유형별 OAuth 클라이언트를 미리 만든다. 갱신 토큰은 OS 키체인에 보관하고, 토큰 수명은 IdP 기본값이 아니라 에이전트 세션의 특성에 맞춰 조정한다. M2M과 사용자 클라이언트는 같은 리소스 서버에 두지 말고, 운영 환경을 테스트하기 전에 로컬 개발 리디렉션 경로부터 확인한다.
AWS 튜토리얼은 인증 방식을 결정한 뒤 구현할 때 좋은 안내서다. 어떤 방식을 선택할지는 각 시스템의 데이터 경계와 책임 모델을 기준으로 직접 판단해야 한다.
내부 도구를 외부 MCP 클라이언트에 노출하려고 하면 이 질문이 가장 먼저 등장한다. 이 글은 튜토리얼을 따라 하는 방법이 아니라, 인증 방식을 고르는 기준을 정리한다.
구성 요약
AgentCore Gateway는 MCP 클라이언트와 Lambda 기반 도구 사이에서 리소스 서버 역할을 한다.
MCP 클라이언트 (Cursor / Claude Desktop / 자체 구현)
│ (Bearer 토큰을 포함한 HTTP+SSE 기반 MCP)
▼
AgentCore Gateway ──► Cognito / Okta / Auth0 (토큰 검증)
│
▼ (서명된 호출)
Lambda 도구 ──► DynamoDB / Salesforce / Jira / 기타 서비스
Gateway는 OAuth 리소스 서버이고, ID 공급자(IdP)는 토큰을 발급하며, MCP 클라이언트는 그 토큰을 제시한다. 여기에는 두 가지 방식이 있다.
클라이언트 자격 증명(M2M): MCP 클라이언트 자체가 서비스 계정으로 인증한다. 토큰은 하나이며, 사용자 정체성이나 사용자별 권한 범위가 없다.
인증 코드 흐름 + PKCE: 사용자가 브라우저 리디렉션을 통해 한 번 로그인한다. MCP 클라이언트는 발급받은 액세스 토큰과 갱신 토큰을 저장하고, 도구 호출 때마다 액세스 토큰을 제시한다.
AWS의 가이드는 두 번째 방식을 다룬다. 중요한 것은 왜 이 방식을 선택해야 하는지 판단하는 일이다.
클라이언트 자격 증명으로 충분한 경우
다음 조건이 모두 참이라면 인증 코드 흐름이 필요하지 않다.
도구가 사용자별로 분리되지 않은 공유 데이터를 다룬다. 예를 들어 공개 지식 기반, 내부 지표 집계, 읽기 전용 카탈로그다.
모든 호출자가 신뢰할 수 있는 내부 영역에 있다. 즉, 사내 에이전트 플랫폼이지 제3자 MCP 클라이언트의 마켓플레이스가 아니다.
감사 로그에서 호출을 특정 사용자에게 귀속할 필요가 없다.
이 경우 MCP 클라이언트 종류별로 client_id와 client_secret을 발급하고, 비밀 값을 순환 교체하며, 도구 권한 범위를 제한하면 된다.
인증 코드 흐름이 필요한 경우
다음 중 하나라도 해당되면 M2M 방식은 한계에 부딪힌다.
조건
M2M이 적합하지 않은 이유
도구가 사용자별 데이터를 조회한다. 예: 특정 사용자의 Salesforce 레코드, Jira 티켓, S3 접두사
공유 서비스 계정은 모든 데이터를 보게 되어 과도한 권한을 갖거나, 아무것도 보지 못하게 된다. 중간 지점을 만들려면 도구 안에 인증 체계를 다시 구현해야 한다.
멀티 테넌트 SaaS와 연동한다
도구는 사용자를 대신해 하위 API를 호출해야 한다. 그렇지 않으면 도구에 사용자별 자격 증명을 보관하고 가장해야 하는데, OAuth가 해결하려는 문제를 다시 만드는 셈이다.
감사 또는 컴플라이언스에서 호출 주체를 요구한다
토큰의 sub 클레임이 감사 기록이 된다. M2M 토큰의 sub은 서비스 식별자이지 사람이 아니다.
동의 화면이 제품 요구사항이다
토큰이 발급되기 전에 “이 MCP 클라이언트가 사용자의 특정 데이터를 읽으려 합니다”라는 동의를 보여 줄 수 있는 방식은 인증 코드 흐름이다.
내부 에이전트가 플랫폼 작업을 수행하는 도구 게이트웨이라면 M2M으로 충분할 수 있다. 반면 사용자가 자신의 Cursor를 Gateway에 연결해 자신의 권한으로 작업하게 한다면 인증 코드 흐름이 필요하다. 이를 어설프게 절충하면 결국 자체 인증 시스템을 만들게 된다.
튜토리얼에서 충분히 다루지 않는 세 가지
튜토리얼은 정상 흐름을 설명한다. 실제 운영에서는 다음 세 가지를 더 신경 써야 한다.
1. MCP 전송 방식과 브라우저 리디렉션은 자연스럽게 결합되지 않는다
OAuth 인증 코드 흐름은 브라우저를 전제로 한다. 하지만 MCP 클라이언트는 대개 stdio(Cursor, Claude Desktop) 또는 SSE로 동작한다. stdio에는 브라우저가 없고, SSE 환경에서도 MCP 서버가 브라우저를 직접 제어하지 않는다.
MCP 사양의 해법은 클라이언트가 유효한 토큰이 없을 때 브라우저로 인증 엔드포인트를 열고, 리디렉션을 수신해 인증 코드를 교환한 뒤 결과를 저장하는 방식이다. Gateway는 브라우저를 다루지 않는다. 이 방식이 의미하는 바는 다음과 같다.
MCP 클라이언트마다 토큰 캐시와 갱신 로직을 각자 구현해야 한다. Cursor와 Claude Desktop의 방식이 다르고, 자체 클라이언트도 별도의 구현이 필요하다.
데스크톱 클라이언트의 현실적인 redirect_uri는 http://localhost:<random-port>다. IdP에서 와일드카드 또는 충분한 포트 범위를 허용하지 않으면 새 포트가 생길 때마다 등록해야 한다.
2. 동적 클라이언트 등록은 아직 널리 쓰기 어렵다
RFC 7591은 MCP 클라이언트가 연결 시점에 IdP에 자신을 등록해 사용자가 client_id를 복사해 붙여 넣지 않도록 하는 표준이다. MCP 0.6도 이를 참조한다. 그러나 Cognito는 지원하지 않고, Okta는 엔터프라이즈 요금제에서만 제공하며, Auth0는 테넌트별로 기능을 켜야 한다.
당분간은 IdP에 클라이언트를 미리 만들고, 사용자에게 client_id를 제공해 MCP 클라이언트 설정에 입력하게 하는 방식이 현실적이다. IdP의 지원이 성숙하기 전까지 동적 등록에 의존해서는 안 된다.
3. 토큰 수명과 에이전트 세션 수명은 다르다
장시간 실행되는 에이전트 세션은 몇 시간에 걸칠 수 있지만, 기본 액세스 토큰 수명은 대개 60분이다. 인증 코드 흐름은 갱신 토큰을 제공하지만 다음 사항을 고려해야 한다.
stdio 기반 MCP에서 토큰 갱신은 자동이 아니다. 클라이언트가 Gateway의 401 응답을 감지하고 갱신 요청을 수행한 뒤 원래 요청을 재시도해야 한다. 대부분의 기본 MCP 클라이언트 라이브러리는 이를 아직 자동으로 처리하지 않는다.
갱신 토큰은 Bearer 비밀 값이다.~/.config/ 아래의 JSON 파일이 아니라 OS 키체인에 보관해야 한다. macOS에서는 Keychain, Windows에서는 Credential Manager를 사용한다. Cursor와 Claude Desktop도 이 방식을 사용하며, 자체 클라이언트도 마찬가지여야 한다.
유휴 만료와 절대 만료를 함께 설계해야 한다. 4시간짜리 에이전트 작업을 지원하면서도 토큰이 무기한 유지되지 않도록 정책을 정한다. 절대 만료 30일, 유휴 만료 24시간은 합리적인 시작점이다.
스코프 설계: 과소평가하기 쉬운 부분
스코프는 크게 두 방식으로 설계할 수 있다.
도구별 스코프: tool:read-account, tool:create-opportunity, tool:run-report처럼 세분화한다. 권한을 정밀하게 통제하고 감사에도 유리하지만, 동의 화면이 너무 많은 선택지로 채워지고 스코프 이름 관리가 어려워진다.
도메인별 스코프: crm:read, crm:write, analytics:read처럼 묶는다. 동의 화면은 사람이 이해하기 쉬워지지만, 하나의 스코프가 여러 도구에 대한 접근을 허용한다. 도구 하나가 손상됐을 때 수평 이동 범위가 넓어질 수 있다.
대개는 동의 화면에는 도메인별 스코프를 사용하고, Gateway 내부에서는 도구별 권한을 적용하는 방식이 적합하다. 액세스 토큰은 Gateway에 들어올 수 있는 권한을 부여하고, Gateway 정책은 실제로 호출할 수 있는 Lambda를 결정한다. 사용자는 적은 수의 이해하기 쉬운 선택지를 보면서도, 운영 측에서는 세분화된 통제를 유지할 수 있다.
주의할 함정
하나의 Cognito 리소스 서버에 M2M 클라이언트와 사용자 클라이언트를 함께 넣는 경우. 동작은 하지만 스코프가 서로 섞이고, 동의 화면에 M2M 전용 스코프가 사용자에게 노출된다. 처음부터 리소스 서버를 분리하는 편이 낫다.
비프로덕션 환경에서 http://localhost 리디렉션 URI 등록을 빼먹는 경우. Cognito, Okta, Auth0 모두 등록할 수 있다. 누락하면 로컬 개발 환경이 실패하고 오류 메시지는 종종 잘못된 계층을 가리킨다.
IdP의 기본 aud 클레임을 그대로 신뢰하는 경우. AgentCore Gateway는 특정 대상(audience)을 기대한다. 별도 리소스 서버를 구성했다면 Cognito 사용자 풀 토큰의 기본 대상인 앱 클라이언트 ID가 Gateway가 기대하는 값과 다를 수 있다. 양쪽에서 대상을 명시적으로 설정해야 한다.
기밀 클라이언트라는 이유로 PKCE를 생략하는 경우. 모든 데스크톱 MCP 클라이언트는 공개 클라이언트다. PKCE 없는 인증 코드 흐름은 다운그레이드 공격에 취약하다. PKCE는 비용이 없으므로 반드시 사용해야 한다.
CloudWatch에 액세스 토큰을 남기는 경우. Gateway 요청 로그를 디버그 수준으로 설정하면 Authorization 헤더가 포함될 수 있다. 영구 로그로 전송하기 전에 반드시 제거해야 한다.
한 문단으로 정리하는 결정 기준
도구가 사용자별 데이터를 읽거나 쓰거나, 호출한 사용자를 식별해야 한다면 PKCE를 포함한 인증 코드 흐름을 사용해야 한다. 기존 IdP가 없다면 Cognito를, 이미 Okta나 Auth0를 사용 중이라면 해당 IdP를 활용하면 된다. 동의 화면에는 도메인별 스코프를 사용하고 Gateway 안에서는 도구별 정책을 적용한다. 동적 등록이 성숙할 때까지는 MCP 클라이언트 유형별 OAuth 클라이언트를 미리 만든다. 갱신 토큰은 OS 키체인에 보관하고, 토큰 수명은 IdP 기본값이 아니라 에이전트 세션의 특성에 맞춰 조정한다. M2M과 사용자 클라이언트는 같은 리소스 서버에 두지 말고, 운영 환경을 테스트하기 전에 로컬 개발 리디렉션 경로부터 확인한다.
AWS 튜토리얼은 인증 방식을 결정한 뒤 구현할 때 좋은 안내서다. 어떤 방식을 선택할지는 각 시스템의 데이터 경계와 책임 모델을 기준으로 직접 판단해야 한다.