Files
xSaherelmWorkspace/Documents/Docs/Developer/Adding xApiKey Validation 2.html
T
2026-10-10 16:24:11 +03:30

394 lines
16 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="fa" dir="rtl">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>مستندات فنی پیاده‌سازی احراز هویت XApiKey در xIds</title>
<style>
@import url('https://fonts.googleapis.com/css2?family=Vazirmatn:wght@300;400;500;600;700;800&display=swap');
:root {
--primary: #1a365d;
--primary-light: #2c5282;
--accent: #c9a961;
--accent-dark: #a8893f;
--bg: #faf9f6;
--card-bg: #ffffff;
--text: #1a202c;
--text-muted: #4a5568;
--border: #e2e8f0;
--success: #38a169;
--danger: #e53e3e;
--warning: #dd6b20;
--shadow: 0 4px 6px rgba(0,0,0,0.05), 0 10px 30px rgba(26,54,93,0.08);
--code-bg: #f7fafc;
}
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
font-family: 'Vazirmatn', Tahoma, sans-serif;
background: var(--bg);
color: var(--text);
line-height: 1.8;
font-size: 15px;
padding: 30px 15px;
}
.document-container {
max-width: 1000px;
margin: 0 auto;
background: var(--card-bg);
box-shadow: var(--shadow);
border-radius: 12px;
overflow: hidden;
}
.document-header {
background: linear-gradient(135deg, var(--primary) 0%, var(--primary-light) 100%);
color: white;
padding: 40px;
position: relative;
border-bottom: 4px solid var(--accent);
}
.header-top {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
.doc-meta {
text-align: left;
font-size: 13px;
opacity: 0.9;
}
.doc-meta div { margin-bottom: 4px; }
.document-title h1 {
font-size: 26px;
font-weight: 800;
margin-bottom: 8px;
}
.document-title h2 {
font-size: 16px;
font-weight: 400;
opacity: 0.9;
}
.document-body {
padding: 40px;
}
.section {
margin-bottom: 40px;
page-break-inside: avoid;
}
.section-header {
display: flex;
align-items: center;
gap: 15px;
padding-bottom: 12px;
border-bottom: 2px solid var(--border);
margin-bottom: 20px;
}
.section-number {
width: 40px; height: 40px;
background: linear-gradient(135deg, var(--primary), var(--primary-light));
color: white;
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
font-weight: 800;
font-size: 18px;
flex-shrink: 0;
}
.section-title h3 {
font-size: 18px;
font-weight: 700;
color: var(--primary);
}
.section-content p {
margin-bottom: 15px;
text-align: justify;
}
.code-block {
background: var(--code-bg);
border: 1px solid var(--border);
border-right: 4px solid var(--accent);
border-radius: 6px;
padding: 15px;
margin: 15px 0;
font-family: 'Consolas', 'Monaco', monospace;
font-size: 13px;
overflow-x: auto;
direction: ltr;
text-align: left;
}
.highlight-box {
background: #fffaf0;
border: 1px solid #f6e05e;
border-right: 4px solid var(--accent);
padding: 15px;
border-radius: 6px;
margin: 20px 0;
}
.success-box {
background: #f0fff4;
border: 1px solid #9ae6b4;
border-right: 4px solid var(--success);
padding: 15px;
border-radius: 6px;
margin: 20px 0;
}
.warning-box {
background: #fff5f5;
border: 1px solid #feb2b2;
border-right: 4px solid var(--danger);
padding: 15px;
border-radius: 6px;
margin: 20px 0;
}
table {
width: 100%;
border-collapse: collapse;
margin: 20px 0;
}
th, td {
padding: 12px;
border: 1px solid var(--border);
text-align: right;
}
th {
background: var(--primary);
color: white;
}
tr:nth-child(even) { background: #f8fafc; }
.document-footer {
background: var(--primary);
color: white;
padding: 20px;
text-align: center;
font-size: 12px;
}
.footer-brand {
font-weight: 700;
color: var(--accent);
margin-bottom: 5px;
}
</style>
</head>
<body>
<div class="document-container">
<header class="document-header">
<div class="header-top">
<div class="doc-meta">
<div>شماره مستند: TECH-XIDS-APIKEY-001</div>
<div>نسخه: ۱.۰</div>
<div>تاریخ تهیه: ۲۰ مهر ۱۴۰۵</div>
<div>طبقه‌بندی: داخلی - فنی</div>
</div>
</div>
<div class="document-title">
<h1>راهنمای جامع پیاده‌سازی و عیب‌یابی احراز هویت XApiKey</h1>
<h2>پروژه xIds (Identity Server) | ماژول مدیریت Application و API Key</h2>
</div>
</header>
<div class="document-body">
<div class="section">
<div class="section-header">
<div class="section-number">۱</div>
<div class="section-title">
<h3>مقدمه و بررسی وضعیت فعلی</h3>
</div>
</div>
<div class="section-content">
<p>
در پروژه <strong>xIds</strong>، زیرساخت کاملی برای مدیریت <code>XApplication</code> و <code>XApiKey</code> پیاده‌سازی شده است.
کلاس‌های <code>XApplicationProvider</code> و <code>XApiKeyHelper</code> وظیفه تولید، هش کردن و اعتبارسنجی کلیدها را بر عهده دارند.
با این حال، چالش اصلی در لایه <strong>Authentication Middleware</strong> و نحوه اتصال آن به Policyهای موجود بود.
</p>
<div class="highlight-box">
<strong>مشکل شناسایی شده:</strong> درخواست‌های ارسالی با هدر <code>X-Api-Key</code> توسط ASP.NET Core نادیده گرفته می‌شدند، زیرا Scheme پیش‌فرض روی JWT Bearer تنظیم شده بود و Handler مخصوص ApiKey فراخوانی نمی‌شد.
</div>
</div>
</div>
<div class="section">
<div class="section-header">
<div class="section-number">۲</div>
<div class="section-title">
<h3>راه‌حل فنی: اصلاح زنجیره احراز هویت</h3>
</div>
</div>
<div class="section-content">
<p>برای رفع مشکل، دو تغییر اساسی در فایل <code>xIds/DI/XDIHelperExtension.cs</code> اعمال شد:</p>
<h4>الف) ثبت Scheme اختصاصی برای XApiKey</h4>
<p>قبل از ثبت JwtBearer، Scheme مربوط به ApiKey ثبت می‌شود تا در لیست Authentication Schemes وجود داشته باشد.</p>
<h4>ب) استفاده از ForwardDefaultSelector هوشمند</h4>
<p>به جای استفاده ثابت از <code>ForwardDefault</code>، از یک Delegate استفاده می‌کنیم که Header درخواست را بررسی می‌کند.</p>
<div class="code-block">
// xIds/DI/XDIHelperExtension.cs
public static void AddXIdentityServerAuthentication(
this IServiceCollection services,
IConfiguration configuration,
bool addApiKeyAuthentication = false)
{
services.AddXIdentityResourceConfiguration(configuration);
var authBuilder = services.AddAuthentication();
// 1. ثبت Scheme برای XApiKey (اولویت اول)
if (addApiKeyAuthentication)
{
authBuilder.AddScheme&lt;AuthenticationSchemeOptions, XApiKeyAuthenticationHandler&gt;(
XAuthenticationScheme.XApiKey.GetStringValue(), // "XApiKey"
options => { });
}
// 2. ثبت JwtBearer با Selector هوشمند
authBuilder.AddJwtBearer(options =>
{
options.SaveToken = true;
options.RequireHttpsMetadata = false;
// انتخاب دینامیک Scheme بر اساس هدر درخواست
options.ForwardDefaultSelector = context =>
{
var apiKeyHeader = XHeader.ApiKey.GetStringValue(); // "X-Api-Key"
if (context.Request.Headers.ContainsKey(apiKeyHeader))
{
return XAuthenticationScheme.XApiKey.GetStringValue();
}
// در غیر این صورت، از احراز هویت استاندارد IdentityServer استفاده کن
return XAuthentication.IDENTITY_SERVER_LOCAL_API;
};
})
.AddLocalApi();
}
</div>
</div>
</div>
<div class="section">
<div class="section-header">
<div class="section-number">۳</div>
<div class="section-title">
<h3>اصلاح Claims تولید شده در Handler</h3>
</div>
</div>
<div class="section-content">
<p>
در فایل <code>xIds/Providers/XApiKeyAuthenticationHandler.cs</code>، متد <code>HandleAuthenticateAsync</code> باید Claimهای صحیح را تولید کند تا با Policyهای تعریف شده در <code>XAuthorizationHelper</code> همخوانی داشته باشد.
</p>
<div class="warning-box">
<strong>نکته مهم:</strong> Policyهای ApiKey بر اساس Scope (مانند read, write, admin) کار می‌کنند، نه Role. بنابراین Claim از نوع <code>JwtClaimTypes.Scope</code> ضروری است.
</div>
<div class="code-block">
protected override async Task&lt;AuthenticateResult&gt; HandleAuthenticateAsync()
{
// ... استخراج و اعتبارسنجی ApiKey ...
var validationResult = await applicationProvider.ValidateApiKey(apiKey, clientIP);
if (validationResult.Errors.HasChild())
return AuthenticateResult.Fail(string.Join(", ", validationResult.Errors));
var claims = new List&lt;Claim&gt;
{
new Claim(ClaimTypes.Name, validationResult.OwnerId),
new Claim(JwtClaimTypes.Subject, validationResult.OwnerId),
new Claim("application_id", validationResult.ApplicationId.ToString()),
new Claim("auth_type", "apikey"),
};
// افزودن Scopeهای مجاز به Claims
if (validationResult.Scopes != null)
{
foreach (var scope in validationResult.Scopes)
{
claims.Add(new Claim(JwtClaimTypes.Scope, scope));
}
}
var identity = new ClaimsIdentity(claims, Scheme.Name);
var principal = new ClaimsPrincipal(identity);
var ticket = new AuthenticationTicket(principal, Scheme.Name);
return AuthenticateResult.Success(ticket);
}
</div>
</div>
</div>
<div class="section">
<div class="section-header">
<div class="section-number">۴</div>
<div class="section-title">
<h3>نحوه استفاده در Controllerها</h3>
</div>
</div>
<div class="section-content">
<p>برای محافظت از Endpointها با ApiKey، از Attributeهای زیر استفاده کنید:</p>
<div class="code-block">
[RequireXPowered]
[Authorize(Policy = XPolicies.ApiKeyAccess)] // دسترسی کلی
public async Task&lt;ActionResult&gt; MySecureAction() { ... }
[Authorize(Policy = XPolicies.ApiKeyReadAccess)] // فقط خواندن
public async Task&lt;ActionResult&gt; GetItems() { ... }
</div>
<div class="success-box">
<strong>تست موفقیت‌آمیز:</strong> اکنون با ارسال هدر <code>X-Api-Key: xapp_...</code> به Endpointهای فوق، احراز هویت انجام شده و دسترسی اعطا می‌شود.
</div>
</div>
</div>
<div class="section">
<div class="section-header">
<div class="section-number">۵</div>
<div class="section-title">
<h3>چک‌لیست نهایی استقرار</h3>
</div>
</div>
<div class="section-content">
<table>
<thead>
<tr>
<th>ردیف</th>
<th>اقدام</th>
<th>وضعیت</th>
</tr>
</thead>
<tbody>
<tr>
<td>۱</td>
<td>بررسی ثبت <code>AddXIdentityServerAuthentication</code> در Startup با پارامتر <code>true</code></td>
<td>✅ انجام شد</td>
</tr>
<tr>
<td>۲</td>
<td>اطمینان از وجود Policyهای <code>ApiKeyAccess</code> در <code>XAuthorizationHelper</code></td>
<td>✅ موجود است</td>
</tr>
<tr>
<td>۳</td>
<td>تست ایجاد ApiKey جدید از طریق <code>/Applications/{id}/ApiKeys/Create</code></td>
<td>✅ تست شد</td>
</tr>
<tr>
<td>۴</td>
<td>تست دسترسی به <code>/Account/Test/HiApiKeyAccess</code> با کلید معتبر</td>
<td>✅ پاس شد</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<footer class="document-footer">
<div class="footer-brand">تهیه شده توسط: هادی خزاعی اصل | شرکت فن آوران ساحر علم</div>
<div>تاریخ انتشار: ۲۰ مهر ۱۴۰۵</div>
</footer>
</div>
</body>
</html>