ایمن‌سازی برنامه با OpenID Connect و JWT

ایمن‌سازی برنامه با OpenID Connect و JWT

ایمن‌سازی برنامه با OpenID Connect و JWT

منبع: Coding Clean, Reliable, and Safe REST APIs with ASP.NET Core 8 — Anthony Giretti

اعتبار ترجمه: ترجمه با کمک هوش مصنوعی

فصل ۱۰ — ایمن‌سازی برنامه با OpenID Connect

تقریباً هر برنامه به سازوکاری برای شناسایی کاربری که قصد انجام Action دارد نیاز دارد. این فرایند Authentication (احراز هویت) است و نباید با Authorization (مجوزدهی) اشتباه گرفته شود. Authorization پس از احراز هویت تعیین می‌کند کاربر چه Permission یا Roleهایی دارد و مجاز به انجام چه عملیات‌هایی است.

موضوعات فصل:

  • مقدمه‌ای بر OpenID Connect (OIDC)
  • پیکربندی Authentication و Authorization در ASP.NET Core
  • ارسال JSON Web Token (JWT) در Request و خواندن هویت کاربر
تصویر منبع — صفحهٔ 395Visual واقعی استخراج‌شده از PDF، مربوط به صفحهٔ 395.

مقدمه‌ای بر OpenID Connect

OpenID Connect (OIDC) یک استاندارد هویتی است که روی OAuth 2.0، پروتکل Authorization، قرار می‌گیرد. در OIDC مسئولیت Authentication به یک سرویس Third-party یا Identity Provider واگذار می‌شود. برنامهٔ محافظت‌شده لازم نیست منطق Login و اعتبارسنجی Credential را خودش پیاده‌سازی کند؛ Provider هویت کاربر را تأیید می‌کند و نتیجه را در قالب Token قابل اعتماد به Client و Resource ارائه می‌دهد.

وقتی این سرویس هویتی مستقل از یک برنامه باشد، می‌توان آن را برای چند برنامه به‌صورت مشترک استفاده کرد و Single Sign-On (SSO) ساخت. سه Actor اصلی عبارت‌اند از:

  1. Client، برای مثال Web App.
  2. Identity Provider.
  3. Protected Resource، مانند ASP.NET Core API.
شکل ۱۰-۱ — ارتباط سه Actor در OpenID Connect

Client ابتدا نزد Provider احراز هویت می‌شود. Provider یک JSON Web Token صادر می‌کند و Client آن را برای دسترسی به Protected Resource ارسال می‌کند. Resource Metadata و Signing Key Provider را دریافت می‌کند تا Issuer و Signature Token را اعتبارسنجی کند. پس از دریافت Metadata، اعتبارسنجی Token می‌تواند محلی انجام شود.

JWT شامل سه بخش Header، Payload و Signature است که به‌شکل Base64Url Encoding شده و با نقطه از هم جدا می‌شوند. Signature برای تشخیص دستکاری Token استفاده می‌شود. استاندارد JWT در RFC 7519 تعریف شده است.

هدف فصل آموزش کامل OIDC نیست؛ فقط حداقل مفاهیم لازم برای استفاده از آن در ASP.NET Core است. Providerهای مشهور شامل Google، Facebook، Apple و Microsoft هستند.

شکل ۱۰-۲ — استفادهٔ Canva.com از Google، Facebook و Apple به‌عنوان Provider

نمونه‌های کتاب از Microsoft Authentication Platform مبتنی بر Azure Active Directory استفاده می‌کنند، اما Configuration API از نظر اصل کار به Provider خاصی وابسته نیست. فرض ادامهٔ فصل این است که یک access_token معتبر در اختیار دارید و آن را به‌عنوان Bearer Token استفاده می‌کنید.

پیکربندی Authentication و Authorization در ASP.NET Core

ابتدا Package زیر نصب می‌شود:

Microsoft.AspNetCore.Authentication.JwtBearer

Configuration شامل این اجزاست:

  • AddAuthentication Scheme پیش‌فرض Authentication و Challenge را روی JwtBearerDefaults.AuthenticationScheme قرار می‌دهد.
  • AddJwtBearer پارامترهای OIDC/JWT را تنظیم می‌کند؛ Authority آدرس Identity Provider و Audience شناسهٔ Application هدف Token است.
  • ValidateLifetime و ValidateIssuer برای اعتبارسنجی زمان و صادرکننده فعال می‌شوند.
  • ClockSkew اختلاف ساعت مجاز بین Issuer و API را در مثال روی پنج دقیقه قرار می‌دهد.
  • AddAuthorization زیرساخت Policy/Role Authorization را ثبت می‌کند.

در Pipeline نیز Middlewareهای UseAuthentication و UseAuthorization فعال می‌شوند.

var builder = WebApplication.CreateBuilder(args);

    ...

    builder.Services.AddAuthentication(options =>
    {
        options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
        options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
    }).AddJwtBearer(options =>
    {
        options.Authority = "https://login.microsoftonline.com/136544d9-xxxx-xxxx-xxxx-10accb370679/v2.0";
        options.Audience = "257b6c36-xxxx-xxxx-xxxx-6f2cd81cec43";
        options.TokenValidationParameters.ValidateLifetime = true;
        options.TokenValidationParameters.ValidateIssuer = true;
        options.TokenValidationParameters.ClockSkew = TimeSpan.FromMinutes(5);
    });

    builder.Services.AddAuthorization();

    ...

    var app = builder.Build();

    ...
    app.UseCors(); // Before Authentication and Authorization middlewares
    app.UseMiddleware<YourMiddleware>(); // Before Authentication and Authorization middlewares

    app.UseAuthentication();
    app.UseAuthorization();

    ...

    app.Run();
Listing 10-1 — پیکربندی و فعال‌سازی Authentication/Authorization مبتنی بر JWT

الزام Authentication روی Endpoint

با RequireAuthorization می‌توان یک Minimal Endpoint را فقط برای کاربر احراز هویت‌شده در دسترس قرار داد.

app.MapGet("/authenticated", () =>
    {
        return Results.Ok("Authenticated !");
    }).RequireAuthorization();
Listing 10-2 — GET /authenticated با RequireAuthorization

این نمونه فقط Authentication را الزام می‌کند. برای سطح دسترسی دقیق‌تر، Role یا Policy استفاده می‌شود.

Authorization بر اساس Role و Policy

Identity Provider می‌تواند Roleهای کاربر را در Token قرار دهد. برنامه بر اساس قوانین تجاری خودش Policy تعریف می‌کند. مثال زیر Policy با نام SurveyCreator را می‌سازد که وجود Role همنام را الزامی می‌کند و سپس آن را روی Endpoint اعمال می‌کند.

builder.Services
        .AddAuthorization(options =>
            options.AddPolicy("SurveyCreator",
            policy => policy.RequireRole("SurveyCreator")
    ));

    ...

    var app = builder.Build();

    ...

    app.UseAuthentication();
    app.UseAuthorization();

    ...

    app.MapGet("/authorized", () =>
    {
        return Results.Ok("Authorized !");
    }).RequireAuthorization("SurveyCreator");

    ...

    app.Run();
Listing 10-3 — GET /authorized با Policy نوع SurveyCreator

اگر Request به Endpoint محافظت‌شده بدون JWT معتبر برسد، ASP.NET Core پاسخ 401 Unauthorized می‌دهد. اگر کاربر Authenticate شده باشد اما Requirement مربوط به Authorization، مثلاً Role لازم، را نداشته باشد، پاسخ 403 Forbidden است.

تصویر منبع — صفحهٔ 403Visual واقعی استخراج‌شده از PDF، مربوط به صفحهٔ 403.

ساختار JWT و Debug آن

JWT را می‌توان Decode کرد تا Header و Payload آن دیده شود. بخش Payload اطلاعاتی مانند Issuer، Expiration، شناسهٔ User و Roleها را در Claimها نگه می‌دارد. Decode کردن Token هنگام Debug خطاهایی مانند Expiration نامناسب، Audience اشتباه یا Roleهای نادرست مفید است؛ اما Decode کردن به‌تنهایی به معنی معتبر بودن Signature نیست و API همچنان باید Validation کامل Token را انجام دهد.

شکل ۱۰-۳ — نمونهٔ JWT Decode‌شده با Role نوع SurveyCreator

ارسال JWT در Request و دریافت هویت کاربر

در HTTP، JWT معمولاً در Header با نام Authorization و Scheme نوع Bearer ارسال می‌شود:

Authorization: Bearer {YourJWT}

جریان معمول OIDC این است که UI یا Client نزد Provider احراز هویت کند، JWT بگیرد و سپس آن را به API Back-end بفرستد. مثال‌های این بخش روی Debug و تست Back-end تمرکز دارند و فرض می‌کنند Token قبلاً تولید شده است.

فعال کردن Bearer Token در Swagger

Swagger UI را می‌توان طوری تنظیم کرد که JWT را از کاربر بگیرد و خودکار در Requestهای API قرار دهد.

builder.Services.AddSwaggerGen(c =>
    {
        c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme()
        {
            Name = "Authorization",
            Scheme = "Bearer",
            BearerFormat = "JWT",
            In = ParameterLocation.Header,
            Description = "JWT Authorization header required"

        });
        c.AddSecurityRequirement(new OpenApiSecurityRequirement {
            {
                new OpenApiSecurityScheme {
                    Reference = new OpenApiReference {
                        Type = ReferenceType.SecurityScheme,
                        Id = "Bearer"
                    }
                },
                new string[] {}
            }
        });
    });
Listing 10-4 — پیکربندی SwaggerGen برای دریافت JWT

پس از این Configuration دکمهٔ Authorize در Swagger UI ظاهر می‌شود.

شکل ۱۰-۴ — دکمهٔ Authorize در Swagger

در Dialog مربوط، Token به همراه Scheme نوع Bearer وارد می‌شود.

شکل ۱۰-۵ — قرار دادن JWT Bearer در Swagger

اگر Token معتبر و دارای Role لازم باشد، Endpoint محافظت‌شده با موفقیت پاسخ می‌دهد.

شکل ۱۰-۶ — پاسخ موفق GET /authorized در Swagger
تصویر منبع — صفحهٔ 407Visual واقعی استخراج‌شده از PDF، مربوط به صفحهٔ 407.

استفاده از JWT در Postman

در Postman می‌توان Authorization Type را روی Bearer Token قرار داد و خود JWT را Paste کرد؛ Postman Scheme نوع Bearer را به Header اضافه می‌کند.

شکل ۱۰-۷ — پاسخ GET /authorized در Postman با JWT معتبر

دسترسی به Claims کاربر

در Minimal Endpoint می‌توان ClaimsPrincipal را مستقیماً دریافت کرد و Claimهای هویت جاری را خواند. Claimها داده‌هایی هستند که Identity Provider دربارهٔ User در Token صادر کرده است؛ مانند Name و Role.

شکل ۱۰-۸ — دسترسی به هویت کاربر و Claims

برای خواناتر کردن مصرف Claims، کتاب یک Service به نام UserProfile می‌سازد که IUserProfile را پیاده‌سازی و Name و Roles را از HttpContext.User استخراج می‌کند.

using System.Security.Claims;

    namespace AspNetCore8MinimalApis.Identity;

    public class UserProfile : IUserProfile
    {
        private readonly IHttpContextAccessor _context;
        public UserProfile(IHttpContextAccessor context)
        {
            _context = context;
        }

        public string Name => _context.HttpContext.User?.Claims
            .FirstOrDefault(x => x.Type == "name")?.Value;

        public IEnumerable<string> Roles => _context.HttpContext.User?.Claims
            .Where(x => x.Type == ClaimTypes.Role).Select(x => x.Value);
    }
Listing 10-5 — کلاس UserProfile

در یک Service سفارشی نمی‌توان مثل پارامتر Minimal Endpoint فرض کرد ClaimsPrincipal مستقیم Inject می‌شود؛ برای این کار IHttpContextAccessor استفاده می‌شود. لازم است IUserProfile با Implementation خود و AddHttpContextAccessor در DI ثبت شوند.

شکل ۱۰-۹ — خواندن هویت کاربر از طریق IUserProfile
تصویر منبع — صفحهٔ 410Visual واقعی استخراج‌شده از PDF، مربوط به صفحهٔ 410.

جمع‌بندی فصل

این فصل تفاوت Authentication و Authorization، نقش OIDC و JWT، پیکربندی JwtBearer در ASP.NET Core، Policyهای Role-based، وضعیت‌های ۴۰۱/۴۰۳، تست Token در Swagger و Postman و خواندن Claimهای User را بررسی کرد. با پایان این فصل تقریباً همهٔ اجزای یک API با پیچیدگی متوسط پوشش داده شده‌اند؛ موضوع نهایی کتاب، Testing API است.

تصاویر منبع مرتبط با این بخش

تصویر منبع — صفحهٔ 397Visual واقعی استخراج‌شده از PDF، مربوط به صفحهٔ 397.
تصویر منبع — صفحهٔ 406Visual واقعی استخراج‌شده از PDF، مربوط به صفحهٔ 406.
تصویر منبع — صفحهٔ 406Visual واقعی استخراج‌شده از PDF، مربوط به صفحهٔ 406.
تصویر منبع — صفحهٔ 408Visual واقعی استخراج‌شده از PDF، مربوط به صفحهٔ 408.
تصویر منبع — صفحهٔ 408Visual واقعی استخراج‌شده از PDF، مربوط به صفحهٔ 408.

منبع: Coding Clean, Reliable, and Safe REST APIs with ASP.NET Core 8 — Anthony Giretti.

فروش یا انتشار این ترجمه منوط به داشتن مجوز لازم از صاحب حقوق اثر است.

امتیاز کاربران به این مقاله

☆☆☆☆☆

0 نفر امتیاز داده اند. میانگین: 0.0 از 5

 

0 نظر

نظر محترم شما در مورد مقاله های وب سایت برنامه نویسی و پایگاه داده

نظرات محترم شما در خدمات رسانی بهتر ما را یاری می نمایند. لطفا اگر مایل بودید یک نظر ما را مهمان فرمائید. آدرس ایمیل و وب سایت شما نمایش داده نخواهد شد.

0 / 500

اطلاعات تماس

  • آدرس:اصفهان-خیابان ام کلثوم غربی - بعد خیابان تخم چی - بیست متر بعد از پیتزا ننه شب - کوچه تعمیر گاه سمار زغالی - پلاک 354 - درب مشکی - طبقه هفتم
  • آدرس ایمیل:najafzade@gmail.com
  • وب سایت:http://www.a00b.com/
  • تلفن ثابت:(+98)9131253620
  • تلفن همراه:09131253620