وقتی ابزارها و منابع را از طریق پروتکل زمینهی مدل (MCP) روی بستر HTTP و بهصورت راهدور در اختیار عاملهای هوشمند میگذارید، موضوع امنیت و کنترل دسترسی دیگر یک ویژگی اختیاری نیست. با فراخوانی هر ابزار، دادههای حساس خوانده شده یا عملیات پرهزینهای اجرا میشوند؛ بنابراین سیستم باید دقیقاً بداند چه کسی، با چه مجوزی و برای کدام منبع درخواست را فرستاده است.
در نسخههای جدید MCP، نشستهای مانا و پروتکلهای بدون وضعیت جای خود را به کنترلهای دقیقتر دادهاند. با بهرهگیری از اسناد فرادادهی شناسهی کلاینت (CIMD)، جایگزین کردن روشهای منسوخ مثل DCR و مقیدسازی کامل توکن به سرور صادرکننده و منبع نهایی، راه نفوذ و سواستفاده از توکنهای متفرقه بسته میشود.
در این درس یاد میگیرید چطور کشف خودکار سرورهای مجوزدهی را با استانداردهای RFC 9728 و RFC 8707 پیادهسازی کنید، جریان PKCE را در سطح کلاینت به کار ببرید و در صورت نیاز به دسترسیهای بالاتر، دامنهها را بدون قطعی ارتباط ارتقا دهید.
یک درخواست MCP راهدور بیوضعیت است، اما مجوزدهی آن ناشناس نیست. هر فقره اعتبارنامه را به صادرکنندهای مقید کنید که آن را ایجاد کرده و هر توکن را به منبعی که دریافتش میکند.
اهداف یادگیری
- کشف سرورهای مجوزدهی از طریق فرادادهی منبع حفاظتشده.
- ترجیح دادن اسناد فرادادهی شناسهی کلاینت (Client ID Metadata Documents) به ثبت پویای کلاینتِ (Dynamic Client Registration) منسوخشده.
- تعیین
application_typeصحیح در مواقعی که استفاده از مسیر سازگاری DCR ناگزیر است. - اعتبارسنجی
issدر پاسخ مجوزدهی و جداسازی اعتبارنامهها بر اساس صادرکننده. - بهکارگیری PKCE، نشانگرهای منبع، اعتبارسنجی مخاطب (audience) و دامنههای افزایشی.
- ارسال درخواستهای دارای مجوز MCP 2026-07-28 بدون نشستهای پروتکل.
صورت مسئله
یک سرور MCP راهدور ممکن است رکوردهای محرمانه را بخواند، روی سیستمهای خارجی بنویسد یا پردازشهای پرهزینهای راه بیندازد. احراز هویت مشخص میکند چه کسی اعتبارنامه را ارائه داده است. مجوزدهی باید به این پرسشها نیز پاسخ دهد:
- کدام سرور مجوزدهی این اعتبارنامه را صادر کرده است؟
- این توکن برای کدام منبع MCP است؟
- کدام کلاینت و URI بازهدایت این فرایند را کامل کردهاند؟
- کاربر با کدام عملیاتها موافقت کرده است؟
- آیا این درخواستِ مشخص همچنان با آن موافقت همخوانی دارد؟
پروفایل مجوزدهی 2026-07-28، فرایند ثبت کلاینت و کار با صادرکننده را سختگیرانهتر میکند. این پروفایل اسناد فرادادهی شناسهی کلاینت را ترجیح میدهد، ثبت پویای کلاینت را منسوخ میداند، تعیین application_type درست را در DCR الزامی میکند، پاسخهای صادرکننده را بر اساس RFC 9207 میسنجد و استفادهی مجدد از اعتبارنامه میان صادرکنندگان مختلف را ممنوع میشمارد.
این قواعد مکمل هستهی بیوضعیت هستند و قرار نیست دستدادن اصلی پروتکل یا Mcp-Session-Id را بازگردانند.
مفاهیم اصلی
سه نقش اصلی را بشناسید
- کلاینت MCP: درخواستها را به نمایندگی از مالک منبع ارسال میکند.
- سرور منبع MCP: توکن دسترسی را میپذیرد و به اندپوینت MCP سرویس میدهد.
- سرور مجوزدهی: هویت مالک منبع را احراز میکند، رضایت او را میگیرد و توکنها را صادر میکند.
سرور منبع و سرور مجوزدهی میتوانند توسط یک مجموعهی مشترک اداره شوند، اما شناسهها و وظایف اعتبارسنجی آنها را از هم تفکیک کنید.
مجوزدهی روی HTTP اعمال میشود
مشخصات مجوزدهی MCP برای بسترهای انتقال مبتنی بر HTTP کاربرد دارد. یک سرور محلی stdio درون مرز امنیتیِ پردازه و سیستمعامل اجرا میشود؛ صرفاً برای حفظ تقارن ظاهری، فرایند ساختگی OAuth در مرورگر را به stdio اضافه نکنید.
برای Streamable HTTP راهدور، توکن حامل (bearer token) را در تمام درخواستها درون هدر Authorization بفرستید. هرگز آن را در URL نگذارید.
کار را با فرادادهی منبع حفاظتشده آغاز کنید
سرور منبع فرادادهی RFC 9728 را منتشر میکند:
{
"resource": "https://notes.example.com/mcp",
"authorization_servers": ["https://auth.example.com"],
"scopes_supported": ["notes:delete", "notes:read", "notes:write"]
}
کلاینت کارش را از URL منبع MCP شروع میکند، این سند را واکشی میکند، یکی از سرورهای مجوزدهیِ معرفیشده را برمیگزیند و سپس فرادادهی OAuth یا OpenID Connect آن سرور را میگیرد.
هنگام ساخت URL استاندارد (well-known) طبق RFC 9728، مسیر منبع را دستنخورده نگه دارید. برای منبع https://notes.example.com/mcp، این درس از https://notes.example.com/.well-known/oauth-protected-resource/mcp استفاده میکند. حذف پسوند /mcp ممکن است به انتخاب فرادادهی منبع حفاظتشدهی دیگری روی همان مبدأ (origin) منجر شود.
سرور مجوزدهی را از روی نام میزبان حدس نزنید. دنبال صادرکنندهای که از یک بدنهی خطای اعتبارسنجینشده به دست آمده نروید. همواره سیاستی مشخص داشته باشید که تعیین کند کلاینت به کدام صادرکنندگان اعتماد دارد.
فرادادهی سرور مجوزدهی را اعتبارسنجی کنید
فراداده باید اندپوینتها و کنترلهای پشتیبانیشده را در اختیارتان بگذارد:
{
"issuer": "https://auth.example.com",
"authorization_endpoint": "https://auth.example.com/authorize",
"token_endpoint": "https://auth.example.com/token",
"code_challenge_methods_supported": ["S256"],
"authorization_response_iss_parameter_supported": true,
"client_id_metadata_document_supported": true
}
استفاده از S256 را برای PKCE اجباری کنید. رشتهی دقیق صادرکننده را ذخیره کنید؛ چون دقیقاً همین مقدار، کلید ثبتنام و ذخیرهسازی توکن خواهد شد.
اولویت ثبتنام را رعایت کنید
اگر کلاینت از قبل ارتباط مشخصی با صادرکنندهی انتخابی دارد، از اطلاعات کلاینتِ از پیش ثبتشده استفاده کنید. در غیر این صورت، چنانچه سرور مجوزدهی اعلام پشتیبانی کرده باشد، اسناد فرادادهی شناسهی کلاینت (Client ID Metadata Documents یا CIMD) را در اولویت بگذارید. از DCR تنها بهعنوان راهکار پشتیبان و سازگاری با سیستمهای قدیمی استفاده کنید؛ اگر هیچکدام از این سازوکارها در دسترس نبود، اطلاعات کلاینت را از کاربر بگیرید (prompt).
اسناد فرادادهی شناسهی کلاینت را ترجیح دهید
یک سند فرادادهی شناسهی کلاینت (CIMD)، یک URL با پروتکل HTTPS به سرور مجوزدهی میدهد که هم شناسهی کلاینت است و هم نشانیِ فرادادهی آن:
{
"client_id": "https://client.example.com/oauth/metadata.json",
"client_name": "Notes desktop client",
"application_type": "native",
"redirect_uris": ["http://127.0.0.1:8765/callback"],
"grant_types": ["authorization_code"],
"response_types": ["code"]
}
سرور مجوزدهی سند را واکشی و اعتبارسنجی میکند. مقدار client_id باید یک HTTPS URL دارای مسیر باشد و مقدارِ داخل سند هم دقیقاً با آن URL برابری کند. فیلدهای الزامی سند شامل client_id، client_name و redirect_uris هستند. مقدار application_type هرچند در این مثال آمده، جزو شروط CIMD نیست؛ الزام جدید آن صرفاً به مسیر DCR مربوط میشود.
واکشی سند را یک عملیات حساس به جعل درخواست سمت سرور (SSRF) در نظر بگیرید. مقصد را به IP تبدیل و اعتبارسنجی کنید؛ آدرسهای loopback، خصوصی، link-local و دیگر نشانیهای غیرمجاز را رد کنید؛ پس از بازهدایتها و تغییرات DNS دوباره همهچیز را بررسی کنید؛ برای بازهدایتها، حجم بایتها و زمان محدودیت بگذارید؛ فرمت JSON را اجباری کنید و کشکردن را فقط بر اساس کنترلهای معتبر کش HTTP انجام دهید. با client_name و سایر فیلدهای نمایشی مانند متنی نامطمئن رفتار کنید.
روش CIMD نیاز به صدور یک شناسهی پویای تازه در هر تماس اولیه را برطرف میکند. با این حال، نیاز به اعتبارسنجی URI بازهدایت، سیاستهای صادرکننده یا رضایت کاربر را از بین نمیبرد.
DCR یک مسیر سازگاری است
ثبت پویای کلاینت (DCR) همچنان برای سرورهای مجوزدهی قدیمی در دسترس است، اما برای پیادهسازیهای جدید MCP منسوخ به شمار میرود.
هنگام استفاده از DCR، مقدار application_type را تعیین کنید:
{
"client_name": "Notes desktop client",
"application_type": "native",
"redirect_uris": ["http://127.0.0.1:8765/callback"],
"grant_types": ["authorization_code"],
"response_types": ["code"]
}
- کلاینتهای دسکتاپ، موبایل، خط فرمان و loopback از
nativeاستفاده میکنند. - برنامههای تحت وب میزبانیشده روی مرورگر از
webو بازهدایتهای HTTPS راهدور بهره میبرند.
حذف این فیلد در پیادهسازی ثبتنام OpenID Connect میتواند مقدار پیشفرض را روی web قرار دهد و باعث شکست یک بازهدایت معتبر loopback شود.
کد DCR را مشروط به یک تصمیم صریح برای fallback نگه دارید. بعد از هر خطای اعتبارسنجی تصادفی در CIMD، بیصدا به DCR سوییچ نکنید؛ این کار ممکن است یک اشکال امنیتی را به مسیری ضعیفتر برای ثبت کلاینت تبدیل کند.
اعتبارنامهها را به صادرکننده مقید کنید
دادههای ثبت صادرشده توسط سرور را دقیقاً ذیل همان صادرکننده ذخیره کنید:
issuer_credentials[issuer] = pre_registered_or_dcr_client
tokens[(issuer, resource)] = access_token
اگر کشف منبع حفاظتشده از https://auth-one.example به https://auth-two.example تغییر کرد، میزان اعتماد را دوباره بسنجید. هرگز client secret، شناسهی کلاینتِ DCR، توکن دسترسیِ ثبتنام، رفرش توکن یا توکن دسترسیِ صادرکنندهی اول را برای دومی نفرستید. کلاینتهای از پیش ثبتشده و DCR باید از اعتبارنامههایی استفاده کنند که برای همان صادرکنندهی جدید ساخته شدهاند.
شناسهی کلاینت CIMD فرق دارد؛ چون یک HTTPS URL تحت میزبانی خود کلاینت است، نه اعتبارنامهای که سرور مجوزدهی صادر کرده باشد. همان URL در CIMD قابلحمل است: صادرکنندهی مورد اعتماد جدید، سند را بدون نیاز به ثبتنام مجدد در DCR واکشی و اعتبارسنجی میکند. با این حال، پاسخهای مجوزدهی و توکنها همچنان ذیل صادرکنندهی جدید اعتبارسنجی و ذخیره میشوند.
کد مجوزدهی بههمراه PKCE
فرایند تعاملی به این صورت است:
- یک
code_verifierبا آنتروپی بالا بسازید. - مقدار
code_challengeرا با S256 از آن تولید کنید. - درخواست مجوزدهی را با مقادیر دقیق
client_id،redirect_uri،scope،code_challengeوresourceبفرستید. - پاسخ مجوزدهی حاوی
codeو (در صورت ارسال)issرا دریافت کنید. - پیش از استفاده از هر فیلدی در پاسخ،
issرا با صادرکنندهی دقیقاً ثبتشده مطابقت دهید. - کد را بههمراه
code_verifier، همان URI بازهدایت و همانresourceمبادله کنید. - توکن نهایی را ذیل
(issuer, resource)ذخیره کنید.
پارامتر resource از استاندارد RFC 8707 در هر دو درخواست مجوزدهی و توکن میآید. این پارامتر URI رسمی و استاندارد سرور MCP را مشخص میکند.
فیلد iss را دقیقاً اعتبارسنجی کنید
استاندارد RFC 9207 از اشتباه گرفته شدن پاسخ مجوزدهی یک صادرکننده با پاسخی از سوی صادرکنندهی دیگر جلوگیری میکند.
وقتی iss در پاسخ وجود دارد، آن را بدون تغییر بزرگی و کوچکی حروف، بدون تغییر اسلش انتهایی، بدون حذف پورت پیشفرض و بدون نرمالسازی کدگذاری درصدی، با صادرکنندهی ذخیرهشده مقایسه کنید. در صورت مغایرت، کاری با کد دریافتی نداشته باشید و حتی جزئیات خطای پاسخ را که ممکن است توسط مهاجم دستکاری شده باشد به کاربر نمایش ندهید.
سرور مجوزدهی که iss را بازمیگرداند، مقدار authorization_response_iss_parameter_supported: true را در فرادادهاش اعلام میکند. کلاینتهای امروزی حتی اگر این اعلان وجود نداشته باشد، در صورت بازگشت iss حتماً آن را اعتبارسنجی میکنند.
مخاطب را در سرور MCP اعتبارسنجی کنید
سرور منبع فقط توکنهایی را میپذیرد که برای خود آن صادر شده باشند:
token.issuer == configured_authorization_server
token.audience == canonical_mcp_resource
توکنهای نامعتبر، منقضیشده، با صادرکنندهی اشتباه یا با مخاطب نادرست، خطای 401 میگیرند. سرور MCP نباید توکنی را که برای سرویس دیگری صادر شده بپذیرد یا انتقال دهد.
کمترین دامنهی مورد نیاز فعلی را درخواست کنید
کار را با دامنهای آغاز کنید که در حال حاضر لازم دارید. اگر ابزاری در ادامهی کار به دامنهی بیشتری نیاز داشت، سرور خطای 403 را همراه با یک چالش دامنهی رسمی و معتبر بازمیگرداند:
WWW-Authenticate: Bearer error="insufficient_scope",
scope="notes:delete",
resource_metadata="https://notes.example.com/.well-known/oauth-protected-resource/mcp"
کلاینت مجوز جدید را برای کاربر شرح میدهد، رضایت او را میگیرد، فرایند مجوزدهی جدیدی را با مجموعهی دامنههای ترکیبشده اجرا میکند و درخواست MCP را با یک id جدید برای JSON-RPC دوباره میفرستد.
فرض نکنید دامنهی خواستهشده در چالش حتماً زیرمجموعهای از scopes_supported است؛ این چالش برای عملیات فعلی اعتبار قطعی دارد.
مجوزدهی و ارتباطات بیوضعیت MCP
یک فراخوانی ابزارِ دارای مجوز همچنان بستهی کامل درخواست فعلی را با خود حمل میکند:
POST /mcp
Authorization: Bearer <access-token>
MCP-Protocol-Version: 2026-07-28
Mcp-Method: tools/call
Mcp-Name: notes.delete
{
"jsonrpc": "2.0",
"id": 12,
"method": "tools/call",
"params": {
"name": "notes.delete",
"arguments": {"id": "note-7"},
"_meta": {
"io.modelcontextprotocol/protocolVersion": "2026-07-28",
"io.modelcontextprotocol/clientCapabilities": {},
"io.modelcontextprotocol/clientInfo": {
"name": "oauth-lesson-client",
"version": "1.0.0"
}
}
}
}
توکن، هویتِ عامل را مجاز میکند و فرادادهی درخواست، رفتار پروتکل را تعیین مینماید؛ هیچکدام جایگزین دیگری نیستند.
پیامهای شبکه را با ترتیبی مشخص اعتبارسنجی کنید: نوع دادههای JSON-RPC و فراداده، برابری هدر و بدنه، و در نهایت پشتیبانی از پروتکل. مغایرت در مسیریابی یا هدر نسخه، خطای HTTP 400 با کد -32020 برمیگرداند. اگر هدر و بدنه روی نسخهای توافق دارند که پشتیبانی نمیشود، خطای HTTP 400 با کد -32022 و بخش data دقیقاً به شکل {"supported":["2026-07-28"],"requested":"<actual>"} برگردانید. یک متد ناشناخته، HTTP 404 با کد -32601 دریافت میکند.
تمام خطاهای درخواست، از جمله توکن نامعتبر 401 و دامنهی ناکافی 403، در قالب یک بستهی خطای JSON-RPC با همان id درخواست اصلی برمیگردند. اطلاعات ساختاریافته برای بازیابی در فیلد اختیاری data در خطا قرار میگیرد؛ هدر WWW-Authenticate هم همچنان یک هدر پاسخ HTTP باقی میماند. اعلانها (notifications) بدون id هستند، بنابراین هیچ بدنهی JSON-RPC نمیگیرند. یک اعلان HTTP پذیرفتهشده، پاسخ 202 با بدنهی خالی برمیگرداند.
چون سرور متد server/discover را پیادهسازی کرده و ابزارها را اعلام میکند، باید متد الزامی tools/list را هم پیادهسازی کند. توصیفگرهای ابزار آن نامهای پایدار، توضیحات و مقادیر inputSchema دارند که ریشهی آنها آبجکت است. خروجی این فهرست قطعی (deterministic) است و resultType، فرادادهی هویت سرور، یک ttlMs محدود و cacheScope را برمیگرداند. فرایند کشف و فهرست ابزارهای ناوابسته به کاربر میتوانند پیش از مجوزدهی در دسترس باشند. اگر هرکدام بر اساس هویت کاربر فرق میکنند، سیاستهای معمولی و کشینگ خصوصی را اعمال کنید.
عدم پاس دادن مستقیم توکن
سرور MCP نباید توکن دسترسی MCP کلاینت را به یک API پاییندستی بفرستد. یک توکن جداگانه برای سرویس پاییندستی با مخاطب درست بگیرید یا از الگوی صریح تبادل توکن (token exchange) استفاده کنید. اعتبارسنجی مخاطب فقط وقتی کارآمد است که سرویسها توکنهای صادرشده برای دیگران را پس بزنند.
رفرش توکنها
رفرش توکنها اختیاری هستند. اگر صادر شدند، آنها را به صورت محرمانه نگه دارید و بر اساس صادرکننده و منبع ذخیره کنید. فرض نکنید این توکنها همیشه حضور دارند. در صورتی که سرور مجوزدهی از چرخش توکن (rotation) پشتیبانی میکند، آنها را بچرخانید و استفادهی مجدد از مقادیر باطلشده را شناسایی کنید.
پیادهسازی عملی
کد code/main.py یک شبیهساز درونپردارشی برای پروتکل و مجوزدهی است. این کد کشف منبع حفاظتشده، فرادادهی سرور مجوزدهی، ثبتنام CIMD، راهکار جایگزین DCR بر اساس نسخه، بررسی نوع برنامه، PKCE، اعتبارسنجی صادرکننده، توکنهای مقید به منبع، ارتقای دامنه، server/discover، متد tools/list و درخواست بیوضعیت ابزار را پیادهسازی میکند.
این مدل، بدنههای پردازششدهی درخواست و هدرهای مسیریابی را تحویل میگیرد. این کد یک تطبیقدهندهی کامل HTTP نیست و هدرهای Content-Type یا Accept را پردازش نمیکند. آن را به تطبیقدهندهی Streamable HTTP در درس 09 وصل کنید که به Content-Type: application/json و مقدار Accept شامل هر دو مورد application/json و text/event-stream نیاز دارد.
آن را اجرا کنید:
cd phases/13-tools-and-protocols/16-mcp-security-oauth-2-1
python3 code/main.py
python3 -m unittest discover code/tests -v
خروجی برنامه ابتدا فرایند کشف، سپس ثبتنام CIMD، یک خواندن ساده، دو مرحله ارتقای دامنهی مجزا و ذخیرهسازی اعتبارنامهها بر اساس صادرکننده را نمایش میدهد.
استفاده در عمل
اشیاء شبیهساز را به مؤلفههای محیط عملیاتی متصل کنید:
- بخش
ResourceServer.protected_resource_metadataتبدیل به اندپوینت RFC 9728 میشود. - بخش
AuthorizationServer.metadataبه فرایند کشف در RFC 8414 یا OpenID Connect بدل میشود. - بخش
Client.enrollبه پردازش CIMD بههمراه یک branch صریح برای سازگاری با DCR تبدیل میشود. - اعتبارنامههای کلاینتِ صادرشده توسط سرور و
tokens_by_issuer_resourceبه رکوردهای رمزنگاریشده تبدیل میشوند. نشانی CIMD میتواند قابلحمل بماند، در حالی که نتایج مجوزدهی آن همچنان مقید به صادرکننده میمانند. - بخش
ResourceServer.handleتبدیل به میانافزاری میشود که پیش از هدایت درخواست، هدرهای کنونی MCP، توکن و دامنهی ابزار را اعتبارسنجی میکند و تمام خطاهای درخواست را در یک بستهی متناظر JSON-RPC نگه میدارد.
تحویل پروژه
این درس فایل outputs/skill-oauth-scope-planner.md را ارائه میدهد. این فایل اکنون ساختار اولویتبندی ثبتنام، ذخیرهی اعتبارنامههای مقید به صادرکننده، نوع برنامه، PKCE، نشانگرهای منبع، چالشهای دامنه و محدودهی درخواستهای بیوضعیت فعلی را طراحی میکند.
تمرینها
- چرخش رفرش توکن را اضافه کنید و در صورت استفادهی مجدد از رفرش توکن قبلی، آن را پس بزنید.
- یک لیست مجاز (allowlist) برای صادرکننده اضافه کنید. در صورت تغییر صادرکننده، فقط نشانیِ قابلحمل CIMD را دوباره به کار ببرید و تمام اعتبارنامهها و توکنهای صادرشدهی قبلی را رد کنید.
- برای کدهای مجوزدهی زمان انقضا بگذارید و بررسی کنید که مبادلهی دیرهنگام آنها ناموفق باشد.
- نسخهای تحت وب از کلاینت با بازهدایتِ HTTPS راهدور بسازید و فرادادهی DCR آن را با کلاینتِ native مقایسه کنید.
- منبع دومی ذیل همان صادرکننده اضافه کنید. مطمئن شوید توکن دسترسیِ آن منبع در منبع اول کار نمیکند.
اصطلاحات کلیدی
| اصطلاح | معنی |
|---|---|
| فرادادهی منبع حفاظتشده | سند RFC 9728 که منبع و سرورهای مجوزدهی را مشخص میکند |
| CIMD | سند فراداده با HTTPS که نشانی آن همان شناسهی کلاینت OAuth است |
| DCR | ثبت پویای کلاینت که منسوخ شده و فقط برای سازگاری نگه داشته میشود |
application_type |
مقدار native یا web که برای اعتبارسنجی قوانین URI بازهدایت به کار میرود |
| PKCE | مدل اعتبارسنج و چالش S256 برای محافظت از کد مجوزدهیِ رهگیریشده |
iss |
شناسهی صادرکننده در پاسخ مجوزدهی طبق RFC 9207 |
| نشانگر منبع | پارامتر RFC 8707 که درخواست توکن را به یک منبع MCP مقید میکند |
| مخاطب (Audience) | منبعی که توکن برای آن معتبر است |
| ارتقای دامنه (Step-up) | جلب رضایت جدید و صدور توکن برای دامنهی اضافهی مورد نیاز در عملیات فعلی |
| اعتبارنامههای مقید به صادرکننده | رکوردهای ثبتنام و توکن که دقیقاً بر اساس صادرکنندهی سرور مجوزدهی تفکیک شدهاند |
