From f3aaaa1256adaaf4e3d657111195029c137904d3 Mon Sep 17 00:00:00 2001 From: Hadi Khazaee Asl Date: Sat, 10 Oct 2026 22:15:12 +0330 Subject: [PATCH] adding xApiKey Valaidaion Issue Resolve Documentation ... --- .../Adding xApiKey Validation 3.html | 920 ++++++++++++++++++ 1 file changed, 920 insertions(+) create mode 100644 Documents/Docs/Developer/Adding xApiKey Validation 3.html diff --git a/Documents/Docs/Developer/Adding xApiKey Validation 3.html b/Documents/Docs/Developer/Adding xApiKey Validation 3.html new file mode 100644 index 0000000..bc2956d --- /dev/null +++ b/Documents/Docs/Developer/Adding xApiKey Validation 3.html @@ -0,0 +1,920 @@ + + + + + + رفع مشکل تست XApiKey در Swagger | مستند فنی + + + +
+
+
+
+
شماره مستند: XIDS-SWAGGER-APIKEY-002
+
نسخه: ۲.۰
+
تاریخ تهیه: ۲۰ مهر ۱۴۰۵
+
طبقه‌بندی: داخلی — فنی
+
+
+
+

رفع مشکل تست XApiKey در Swagger UI

+

تحلیل ریشه‌ای و راه‌حل گام‌به‌گام برای فعال‌سازی کامل

+
+
+ +
+ + +
+
+
۱
+
+

خلاصه مشکل شناسایی‌شده

+
Problem Summary
+
+
+
+

+ پس از بررسی دقیق کدهای پروژه xIds، مشخص شد که اگرچه زیرساخت + XApiKeyAuthenticationHandler و Policyهای مربوطه به‌درستی پیاده‌سازی شده‌اند، + اما چهار مشکل معماری مانع از کارکرد صحیح احراز هویت با API Key در Swagger می‌شوند. +

+ +
+ 🔴 مشکلات شناسایی‌شده: +
    +
  1. ترتیب ثبت Schemeها: XApiKey اول ثبت شده و به‌عنوان DefaultScheme انتخاب می‌شود — در نتیجه درخواست‌های Bearer Token هم به آن حواله می‌شوند و شکست می‌خورند.
  2. +
  3. عدم تنظیم DefaultScheme: در AddAuthentication() هیچ DefaultScheme مشخصی تعیین نشده است.
  4. +
  5. محدودیت LocalApi Policy: Policy IdentityServerConstants.LocalApi.PolicyName فقط Scheme IdentityServerAccessToken را قبول می‌کند.
  6. +
  7. عدم وجود PolicyScheme هوشمند: هیچ مکانیزمی برای انتخاب پویای Scheme بر اساس Header درخواست وجود ندارد.
  8. +
+
+
+
+ + +
+
+
۲
+
+

تحلیل جریان فعلی احراز هویت

+
Current Authentication Flow Analysis
+
+
+
+

+ در کد فعلی XDIHelperExtension.AddXIdentityServerAuthentication، ترتیب ثبت به این صورت است: +

+ +
// xIds/DI/XDIHelperExtension.cs — کد فعلی +var authBuilder = services.AddAuthentication(); // ← DefaultScheme تنظیم نشده! + +// ۱. ثبت XApiKey (اول) +if (addApiKeyAuthentication) +{ + authBuilder.AddScheme<..., XApiKeyAuthenticationHandler>( + "XApiKey", options => { }); +} + +// ۲. ثبت JwtBearer +authBuilder.AddJwtBearer(options => { + options.ForwardDefaultSelector = context => { ... }; +}) +// ۳. ثبت LocalApi +.AddLocalApi();
+ +
+ 💡 نکته کلیدی: + وقتی AddAuthentication() بدون پارامتر فراخوانی می‌شود و سپس اولین Scheme (XApiKey) ثبت می‌گردد، + ASP.NET Core به‌صورت خودکار XApiKey را به‌عنوان DefaultScheme در نظر می‌گیرد. + این یعنی تمام درخواست‌ها (حتی آن‌هایی که Bearer Token دارند) ابتدا به XApiKeyAuthenticationHandler می‌روند + و چون Header X-Api-Key وجود ندارد، NoResult برمی‌گردانند. +
+ +
+
جریان فعلی (معیوب):
+ درخواست با Bearer Token + → + DefaultScheme = XApiKey + → + NoResult (بدون Header) + → + 401 Unauthorized ❌ +
+ +
+
جریان مطلوب (پس از اصلاح):
+ درخواست + → + PolicyScheme (هوشمند) + → + XApiKey یا LocalApi + → + 200 OK ✓ +
+
+
+ + +
+
+
۳
+
+

گام ۱ — اصلاح AddXIdentityServerAuthentication با PolicyScheme

+
Step 1: Fix Authentication Registration with PolicyScheme
+
+
+
+
+
حیاتی — فایل: xIds/DI/XDIHelperExtension.cs
+

توضیح:

+

+ باید از AddPolicyScheme استفاده کنیم تا یک Scheme هوشمند بسازیم که بر اساس + وجود Header X-Api-Key در درخواست، Scheme مناسب را انتخاب کند. + همچنین DefaultScheme و DefaultChallengeScheme باید به‌درستی تنظیم شوند. +

+
+ +

کد اصلاح‌شده کامل:

+
// xIds/DI/XDIHelperExtension.cs +// متد: AddXIdentityServerAuthentication + +public static void AddXIdentityServerAuthentication( + this IServiceCollection services, + IConfiguration configuration, + bool addApiKeyAuthentication = false +) +{ + // افزودنی‌های مورد نیاز + services.AddXIdentityResourceConfiguration(configuration); + + // ═══════════════════════════════════════════════════════ + // ثبت XApiKey Scheme (قبل از هر چیز دیگر) + // ═══════════════════════════════════════════════════════ + if (addApiKeyAuthentication) + { + services.AddAuthentication() + .AddScheme<AuthenticationSchemeOptions, XApiKeyAuthenticationHandler>( + XAuthenticationScheme.XApiKey.GetStringValue(), + options => { }); + } + + // ═══════════════════════════════════════════════════════ + // تعریف PolicyScheme هوشمند برای مسیریابی درخواست‌ها + // ═══════════════════════════════════════════════════════ + var smartSchemeName = "SmartAuthScheme"; + + services.AddAuthentication(options => + { + // DefaultScheme = PolicyScheme هوشمند + options.DefaultScheme = smartSchemeName; + options.DefaultAuthenticateScheme = smartSchemeName; + options.DefaultChallengeScheme = smartSchemeName; + }) + .AddPolicyScheme(smartSchemeName, "Smart Authentication Scheme", options => + { + options.ForwardDefaultSelector = context => + { + // بررسی وجود Header X-Api-Key + var apiKeyHeader = XHeader.ApiKey.GetStringValue(); + if (context.Request.Headers.ContainsKey(apiKeyHeader)) + { + return XAuthenticationScheme.XApiKey.GetStringValue(); + } + + // در غیر این صورت، از LocalApi (IdentityServer) استفاده کن + return XAuthentication.IDENTITY_SERVER_LOCAL_API; + }; + }) + .AddJwtBearer(XAuthenticationScheme.XToken.GetStringValue(), options => + { + options.Authority = configuration + .GetSection("IdentityResourceConfiguration:Authority").Value; + options.TokenValidationParameters.ValidateAudience = false; + options.TokenValidationParameters.ValidTypes = new[] { "at+jwt" }; + }) + .AddOAuth2Introspection(XAuthenticationScheme.XIntrospection.GetStringValue(), options => + { + var identityConfig = configuration.GetXIdentityResourceConfiguration(); + options.Authority = identityConfig.Authority; + options.ClientId = identityConfig.ApiName; + options.ClientSecret = identityConfig.ClientSecret; + }) + .AddLocalApi(); +}
+ +
+ ✅ چرا این راه‌حل کار می‌کند؟ +
    +
  • PolicyScheme به‌عنوان DefaultScheme عمل می‌کند و بر اساس Header تصمیم می‌گیرد.
  • +
  • اگر X-Api-Key وجود داشته باشد → به XApiKeyAuthenticationHandler حواله می‌دهد.
  • +
  • در غیر این صورت → به LocalApi (IdentityServer) حواله می‌دهد.
  • +
  • هیچ‌گاه Scheme اشتباه انتخاب نمی‌شود.
  • +
+
+
+
+ + +
+
+
۴
+
+

گام ۲ — اصلاح LocalApi Policy برای پذیرش XApiKey

+
Step 2: Fix LocalApi Policy to Accept XApiKey
+
+
+
+
+
مهم — فایل: xIds/DI/XDIHelperExtension.cs
+

توضیح:

+

+ در متد AddXAuthorization، Policy LocalApi.PolicyName + فقط Scheme IdentityServerAccessToken را قبول می‌کند. + باید این Policy را اصلاح کنیم تا XApiKey را هم بپذیرد. +

+
+ +
// xIds/DI/XDIHelperExtension.cs +// متد: AddXAuthorization + +public static void AddXAuthorization( + this IServiceCollection services, + IDictionary<string, AuthorizationPolicy> policies = null +) +{ + services.AddAuthorization(options => + { + // ... کدهای قبلی برای policies و xPolicies ... + + // ═══════════════════════════════════════════════════════ + // اصلاح LocalApi Policy برای پذیرش هر دو Scheme + // ═══════════════════════════════════════════════════════ + options.AddPolicy(IdentityServerConstants.LocalApi.PolicyName, policy => + { + policy.AddAuthenticationSchemes(new[] + { + IdentityServerConstants.LocalApi.AuthenticationScheme, + XAuthenticationScheme.XApiKey.GetStringValue() // ← اضافه شد + }); + policy.RequireAuthenticatedUser(); + }); + }); + + services.AddSingleton<IAuthorizationHandler, RequiredRolesHandler>(); +}
+ +
+ 💡 نکته: + با این اصلاح، Endpointهایی که [Authorize(LocalApi.PolicyName)] دارند، + هم با Bearer Token و هم با X-Api-Key قابل دسترسی خواهند بود. +
+
+
+ + +
+
+
۵
+
+

گام ۳ — بررسی و اصلاح Swagger Security Definition

+
Step 3: Verify Swagger Security Configuration
+
+
+
+
+
بررسی — فایل: xCommons/Extensions/DIExtensions.cs
+

توضیح:

+

+ در کد فعلی AddXSwaggerGenOptions، Security Definition برای XApiKey + به‌درستی تعریف شده است. اما باید مطمئن شویم که addXApiKeyAuthorization + در فراخوانی AddXSwagger مقدار true دارد. +

+
+ +

بررسی Startup.cs:

+
// xIds/Startup.cs — ConfigureServices + +// ثبت Swagger — مطمئن شوید پارامترها درست هستند +var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml"; +var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile); + +services.AddXSwagger( + Configuration, + xmlFilePath: xmlPath, + addRequiredXPoweredFilter: true, + addXTokenAuthorization: true, + addXApiKeyAuthorization: true, // ← باید true باشد + addDefaultValueFilter: true +);
+ +

خروجی مورد انتظار در Swagger UI:

+
+ 🔑 در Swagger UI باید دو دکمه "Authorize" ببینید: +
    +
  • accessToken: برای Bearer Token (مقدار: Bearer {token})
  • +
  • X-Api-Key: برای API Key (مقدار: xapp_xxxxxxxxxxxx)
  • +
+
+
+
+ + +
+
+
۶
+
+

گام ۴ — اصلاح XApiKeyAuthenticationHandler

+
Step 4: Verify XApiKeyAuthenticationHandler
+
+
+
+
+
بررسی — فایل: xIds/Providers/XApiKeyAuthenticationHandler.cs
+

توضیح:

+

+ Handler فعلی به‌درستی پیاده‌سازی شده است. فقط یک نکته مهم: + مطمئن شوید که ValidateApiKey در XApplicationProvider + مقدار requiredScope را به‌درستی بررسی می‌کند. +

+
+ +

کد فعلی Handler (صحیح است):

+
// xIds/Providers/XApiKeyAuthenticationHandler.cs +protected override async Task<AuthenticateResult> HandleAuthenticateAsync() +{ + // ۱. استخراج API Key از Header + var apiKeyHeader = HeaderName; // "X-Api-Key" + if (!Request.Headers.ContainsKey(apiKeyHeader)) + return AuthenticateResult.NoResult(); + + var apiKey = Request.Headers[apiKeyHeader].ToString(); + if (apiKey.IsNullOrEmpty()) + return AuthenticateResult.NoResult(); + + // ۲. استخراج Client IP + var clientIP = Request.HttpContext.Connection.RemoteIpAddress?.ToString(); + + // ۳. اعتبارسنجی API Key + var validationResult = await applicationProvider.ValidateApiKey( + apiKey: apiKey, + clientIP: clientIP + ); + + if (validationResult.Errors.HasChild()) + { + var message = validationResult.Errors.ToListString('\n'); + return AuthenticateResult.Fail(message); + } + + // ۴. ساخت Claims + var claims = new List<Claim> { + new(ClaimTypes.Name, validationResult.OwnerId), + new(JwtClaimTypes.Subject, validationResult.OwnerId), + new(XCustomClaims.ApplicationId, validationResult.ApplicationId.ToString()), + new(XCustomClaims.AuthType, "apikey"), + }; + + // ۵. اضافه کردن Scopeها + if (validationResult.Scopes != null) + { + foreach (var scope in validationResult.Scopes) + { + claims.Add(new Claim(JwtClaimTypes.Scope, scope)); + } + } + + // ۶. ساخت Principal و Ticket + var identity = new ClaimsIdentity(claims, Scheme.Name); + var principal = new ClaimsPrincipal(identity); + var ticket = new AuthenticationTicket(principal, Scheme.Name); + + return AuthenticateResult.Success(ticket); +}
+ +
+ ✅ وضعیت Handler: + کد فعلی Handler صحیح است و نیازی به تغییر ندارد. + فقط مطمئن شوید که validationResult.Scopes مقادیر صحیح + (read, write, admin, manage) + را برمی‌گرداند. +
+
+
+ + +
+
+
۷
+
+

گام ۵ — راهنمای تست در Swagger UI

+
Step 5: Testing Guide in Swagger UI
+
+
+
+

مراحل تست گام‌به‌گام:

+ +
+
تست ۱
+

ساخت Application و API Key

+

ابتدا یک Application بسازید و سپس یک API Key برای آن ایجاد کنید:

+
# ۱. ساخت Application (با Bearer Token احراز هویت کنید) +POST /Applications +Content-Type: application/json +Authorization: Bearer {access_token} +X-PoweredBy: {xPoweredValue} + +{ + "name": "TestApp", + "description": "Test Application", + "type": 0 +} + +# ۲. ساخت API Key (با Bearer Token) +POST /Applications/{applicationId}/ApiKeys/Create +Content-Type: application/json +Authorization: Bearer {access_token} +X-PoweredBy: {xPoweredValue} + +{ + "scopes": ["read", "write", "manage"], + "rateLimit": 60, + "expirationMinutes": 43200, + "allowedIPs": ["127.0.0.1", "::1"] +} + +# پاسخ شامل plainKey است — آن را ذخیره کنید! +# مثال: xapp_abc123def456...
+
+ +
+
تست ۲
+

استفاده از API Key در Swagger UI

+

در Swagger UI:

+
    +
  1. روی دکمه "Authorize" (بالا سمت راست) کلیک کنید.
  2. +
  3. در بخش "X-Api-Key"، کلید API دریافتی را وارد کنید.
  4. +
  5. روی "Authorize" و سپس "Close" کلیک کنید.
  6. +
  7. Endpoint GET /Account/Test/HiApiKeyAccess را پیدا کنید.
  8. +
  9. روی "Try it out" و سپس "Execute" کلیک کنید.
  10. +
+
+ +
+
تست ۳
+

نتایج مورد انتظار

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
سناریوپاسخ مورد انتظاروضعیت
API Key معتبر + Scope کافی200 OK — "Hi ApiKey Access, is Passed ..."✓ صحیح
API Key نامعتبر401 Unauthorized — "ApiKey not found"✓ صحیح
API Key منقضی‌شده401 — "ApiKey is Expired"✓ صحیح
Scope ناکافی403 Forbidden✓ صحیح
IP غیرمجاز401 — "Client IP not Allowed"✓ صحیح
Rate Limit رد شده401 — "ApiKey Rate Limit Reached"✓ صحیح
+
+ +
+
تست ۴
+

تست با cURL (برای اطمینان بیشتر)

+
# تست مستقیم با cURL +curl -X GET "https://localhost:5001/Account/Test/HiApiKeyAccess" \ + -H "X-Api-Key: xapp_your_api_key_here" \ + -H "X-PoweredBy: your_powered_value" \ + -k + +# پاسخ مورد انتظار: +# "Hi ApiKey Access, is Passed ..."
+
+
+
+ + +
+
+
۸
+
+

چک‌لیست نهایی اصلاحات

+
Final Checklist
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ردیففایلتغییراولویت
۱xIds/DI/XDIHelperExtension.csبازنویسی AddXIdentityServerAuthentication با PolicyScheme🔴 حیاتی
۲xIds/DI/XDIHelperExtension.csاصلاح AddXAuthorization — افزودن XApiKey به LocalApi Policy🔴 حیاتی
۳xIds/Startup.csبررسی addXApiKeyAuthorization: true در AddXSwagger🟡 بررسی
۴xIds/Providers/XApiKeyAuthenticationHandler.csتأیید صحت کد (نیاز به تغییر ندارد)🟢 تأیید
۵xIdentityHelper/XAuthorizationHelper.csتأیید Policyهای ApiKey (نیاز به تغییر ندارد)🟢 تأیید
+
+
+ + +
+
+
۹
+
+

جمع‌بندی و نکات پایانی

+
Conclusion
+
+
+
+

+ ریشه اصلی مشکل تست XApiKey در Swagger، نحوه ثبت Authentication Schemes + و عدم استفاده از PolicyScheme هوشمند بود. با اعمال اصلاحات گام ۱ و ۲، + سیستم احراز هویت به‌صورت خودکار بر اساس Header درخواست، Scheme مناسب را انتخاب می‌کند + و Swagger UI می‌تواند به‌درستی با API Key احراز هویت نماید. +

+ +
+ 🎯 نتیجه نهایی: +
    +
  • درخواست‌های دارای Header X-Api-Key → به XApiKeyAuthenticationHandler هدایت می‌شوند.
  • +
  • درخواست‌های دارای Header Authorization: Bearer → به LocalApi (IdentityServer) هدایت می‌شوند.
  • +
  • Swagger UI هر دو روش احراز هویت را پشتیبانی می‌کند.
  • +
  • Policyهای ApiKeyAccess, ApiKeyReadAccess و غیره به‌درستی کار می‌کنند.
  • +
+
+ +
+ ⚠️ نکته مهم: + پس از اعمال تغییرات، حتماً پروژه را Clean و Rebuild کنید + و سپس Swagger UI را با Ctrl+F5 رفرش نمایید تا تغییرات Security Definition + به‌درستی اعمال شوند. +
+
+
+ +
+ + +
+ + \ No newline at end of file