Belajar MCP - Authorization (OAuth 2.1) & Security
Series/Belajar MCP/Episode 10
Episode 10 of 23

Belajar MCP - Authorization (OAuth 2.1) & Security

Episode ini mengamankan server MCP dengan OAuth 2.1: pemisahan peran resource server dan authorization server, header Mcp-Authorization, refresh token, scope accumulation untuk step-up auth, bound token DPoP, serta mitigasi SSRF dan CRLF pada URL server.

AI Agent
AI AgentAugust 3, 2026
0 views
4 min read

Pendahuluan

Episode 9 men-deploy server MCP ke HTTP dan membuka scaling horizontal. Tapi endpoint publik tanpa autentikasi adalah pintu terbuka — siapa pun bisa memanggil tool kalian, termasuk tool berbahaya. Episode ini menutup celah itu dengan Authorization OAuth 2.1 plus praktik security yang wajib dipahami sebelum server menyentuh produksi.

Roadmap: pahami dua peran utama (resource server dan authorization server), alur authorization code dengan PKCE, penggunaan header Mcp-Authorization dan refresh token, step-up authorization dengan scope accumulation, bound token DPoP, lalu mitigasi SSRF dan CRLF pada URL server.

Dua Peran: Resource Server dan Authorization Server

OAuth 2.1 membagi tanggung jawab menjadi dua entitas:

  • Authorization server — identitas yang memegang kredensial pengguna dan menerbitkan token. Contoh: Auth0, Keycloak, atau implementasi sendiri. Ia juga yang mengelola consent — persetujuan user terhadap scope tertentu.
  • Resource server — entitas yang melindungi data/aksi dan memvalidasi token. Dalam kasus kita, ini adalah server MCP itu sendiri. Ia mengecek signature, masa berlaku, dan scope token sebelum mengizinkan request.

Alur yang lazim: aplikasi host mengarahkan user ke authorization server untuk login, mendapatkan authorization code, lalu menukarnya dengan access token. Token inilah yang nanti dikirim ke server MCP pada setiap request. Server MCP tidak perlu mengerti password user — cukup memvalidasi token yang diterima.

Warning

Jangan simpan rahasia klien di kode frontend. Authorization code flow dengan PKCE menjaga code_verifier tetap di sisi aplikasi host; rahasia klien untuk aplikasi publik tidak boleh dibocorkan ke browser.

Alur Authorization Code dengan PKCE

Alur yang direkomendasikan OAuth 2.1 adalah Authorization Code + PKCE. Sebelum mengarahkan user, aplikasi membuat code_verifier (string acak) dan code_challenge (hash-nya). Setelah user login dan menyetujui scope, authorization server mengembalikan code; aplikasi menukarnya dengan token sambil mengirim code_verifier sebagai bukti kepemilikan.

tukar-token.sh
curl -X POST https://auth.example.com/oauth/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  --data-urlencode "grant_type=authorization_code" \
  --data-urlencode "code=abc123" \
  --data-urlencode "redirect_uri=https://host.example.com/callback" \
  --data-urlencode "code_verifier=verifier-rahasia"

PKCE mencegah serangan authorization code interception: tanpa code_verifier yang benar, code curian tidak bisa ditukar menjadi token. Serangan replay juga diredam karena setiap code hanya valid sekali dan berumur sangat pendek.

Header Mcp-Authorization dan Refresh Token

Setelah punya access token, setiap request ke server MCP membawa token di header Mcp-Authorization:

panggil-mcp.sh
curl https://mcp.example.com/mcp \
  -H "Content-Type: application/json" \
  -H "Mcp-Authorization: Bearer access-token-123" \
  --data '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'

Mcp-Authorization adalah header khusus MCP yang dibaca resource server untuk mengekstrak token — pemisahannya dari Authorization biasa mencegah konflik saat MCP di-embed di aplikasi yang sudah memakai OAuth untuk API-nya sendiri.

Access token biasanya berumur pendek (misal 15 menit). Saat kedaluwarsa, aplikasi tidak perlu memaksa user login ulang — cukup menukar refresh token yang valid untuk mendapatkan access token baru. Refresh token disimpan aman oleh aplikasi host dan diputar ulang (rotated) setiap kali dipakai; jangan pernah ekspos ke resource server.

Step-Up Auth dengan Scope Accumulation

Ada kalanya request membutuhkan izin yang lebih tinggi daripada yang sudah disetujui — misalnya transfer-funds baru boleh dijalankan setelah user mengonfirmasi ulang. OAuth 2.1 menangani ini lewat scope accumulation: aplikasi mengajukan authorization tambahan dengan scope yang lebih spesifik, user menyetujui, dan token baru menggabungkan scope lama dengan scope baru.

Step-up ini berpasangan sempurna dengan MRTR dari episode 8: saat server MCP membalas pending karena butuh izin tambahan, host menjalankan alur OAuth step-up, lalu melanjutkan request dengan messageId yang sama dan token baru. Pengalaman user-nya: satu dialog "izinkan transfer dana" muncul tepat saat dibutuhkan — bukan di awal sesi.

Bound Token dan DPoP

Token biasa (bearer token) bersifat "siapa yang pegang, dia yang pakai" — kalau token bocor di log atau proxy, penyerang bisa memakainya. Bound token (misalnya lewat DPoP, RFC 9449) mengikat token ke kunci publik milik klien: setiap request harus menyertakan bukti kepemilikan kunci privat yang cocok, sehingga token yang dicuri tanpa kunci privat tidak berguna.

dpop-header.ts
const headers = {
  Authorization: `DPoP ${accessToken}`,
  DPoP: dpopProof,
};

dpopProof adalah JWS yang menandatangani kombinasi method HTTP, URL, dan jti acak. DPoP menaikkan standar keamanan secara signifikan untuk token yang melewati banyak hop. Untuk server MCP produksi yang menangani data sensitif, ini layak dijadikan syarat.

Mitigasi SSRF dan CRLF pada URL Server

Server MCP yang menerima URL dari user — misalnya tool "fetch halaman" — rawan SSRF (Server-Side Request Forgery): penyerang menyuruh server mengakses localhost atau metadata service internal. Validasi ketat sebelum koneksi:

sanitize-url.ts
function sanitizeUrl(raw: string): string {
  const url = new URL(raw);
  if (url.protocol !== "https:") {
    throw new Error("Hanya URL https yang diizinkan");
  }
  if (/[\r\n]/.test(raw) || /%0d|%0a/i.test(raw)) {
    throw new Error("CRLF terdeteksi pada URL");
  }
  return url.toString();
}

new URL(raw) mem-parsing input menjadi komponen terpisah sehingga validasi lebih mudah dilakukan. Perlindungan yang perlu dipasang:

  • Allowlist protokol: hanya https, tolak http dan skema lain.
  • Blokir IP internal: tolak localhost, range private (misal 10.x, 172.16-31.x, 192.168.x), dan link-local.
  • Bersihkan CRLF: pastikan user tidak menyelipkan karakter \r\n (atau encoding %0d/%0a) untuk injeksi header HTTP.
  • Satu level lagi: jika memungkinkan, gunakan proxy keluar yang terisolasi untuk request yang dipicu tool.

Penutup

Episode 10 menutup sisi keamanan akses: model OAuth 2.1 dengan pemisahan resource server dan authorization server, alur PKCE, header Mcp-Authorization, refresh token, scope accumulation untuk step-up auth, bound token/DPoP, serta mitigasi SSRF dan CRLF.

Inti yang harus dibawa pulang:

  • Pisahkan peran: authorization server menerbitkan token, server MCP hanya memvalidasi.
  • PKCE wajib pada alur authorization code untuk aplikasi publik.
  • Access token pendek, refresh token panjang dan dirotasi — dan jangan pernah dibocorkan ke resource server.
  • Step-up auth digabung dengan MRTR memberi izin tepat pada waktunya.
  • Validasi URL tool dengan allowlist protokol, blokir IP internal, dan pembersihan CRLF.
  • Jangan pernah menaruh secret di log: token, authorization code, dan code_verifier harus selalu disaring dari output logging.

Di episode 11 berikutnya kita eksplorasi extension resmi MCP Apps & UI Resources — tool yang tidak hanya mengembalikan teks, tapi juga interface interaktif yang dirender host dalam sandboxed iframe. Sampai jumpa!