احراز هویت به وسیله توکن JWT در Asp.net Core Identity

معرفی کتابخانه Identity.JwtAuhentication

  • ایمان محمدی
  • ‫۱ سال قبل، سه شنبه ۶ آبان ۱۳۹۹، ساعت ۲۲:۰۰
  • برنامه نویسی سمت سرور

امروزه استفاده از نرم افزارهای SPA و اپلیکیشن های موبایلی بر بستر Restful-API's و HTTP API's بسیار فراگیر شده است این در حالی است که سیستم احراز هویت موجود در کتابخانه .Net یعنی Asp.net Core Identity به صورت پیش فرض راه حلی برای آنها ارائه نداده است و این کتابخانه محدود به یک اپلیکیشن و صرفا از طریق کوکی کار می کند. برای استفاده از سیستم های احراز هویت بستر API به دلایل مختلف توصیه شده است از یک Identity provider استاندارد استفاده کنید که نمونه موفق و رایگان (البته تا پایان پشتیبانی نسخه فعلی) پروژه IdentityServer می باشد. اما اگر نیاز ما محدود است و بخواهیم از Identity در پروژه های مکمل وب سایت همانند یک اپ موبایل و یا نرم افزار های SPA مرتبط استفاده کنیم چکار باید کرد؟ ما در کتابخانه JwtAuhentication که به عنوان یک افزونه برای Identity طراحی شده است پیاده سازی محدودی از OAuth 2.0 در نظر گرفتیم که توسط آن می توانیم از قابلیت توکن JWT در Asp.Net Core Identity استفاده کرد.


نصب و راه اندازی Identity.JwtAuhentication

ابتدا ASP.Net Identity را بر روی پروژه نصب و راه اندازی می کنیم، سپس با دستور زیر کتابخانه JwtAuhentication را نصب می کنیم.

Install-Package Honamic.Identity.JwtAuthentication

مطابق با کد زیر و معرفی کلاس کاربری که در Identity ایجاد کرده ایم، سرویس های مورد نیاز کتابخانه را اضافه می کنیم:

services.AddAuthentication().AddJwtAuthentication<IdentityUser>(Configuration);

در فایل پیکربندی پروژه، تنظیمات این کتابخانه را وارد می کنیم در صورتیکه که نمی خواهید توکن رمزنگاری شود مقدار EncrtyptKey را خالی بگذارید.


...
  "JwtAuthenticationOptions": {
    "SigningKey": "1234567890123456",
    "EncrtyptKey": "1234567890123456",
    "Issuer": "https://localhost:5001/",
    "Audience": "Any",
    "AccessTokenExpirationMinutes": 30,
    "RefreshTokenExpirationMinutes": 1440
  },


 در ادامه با استفاده از کلاس AuthController و کلاس کاربری که در Identity ایجاد کردیم یک کنترلر احراز هویت ایجاد می کنیم :


    [Route("api/[controller]")]
    public class AuthController : AuthController<IdentityUser>
    {
        public AuthController(JwtSignInManager JwtSignInManager<IdentityUser> ,
            UserManager<IdentityUser> userManager,
              ILogger<AuthController<IdentityUser>> logger
            ) :
            base(jwtSignInManager, userManager, logger)
        {

        }

        protected override Task<bool> SendTwoFactureCodeAsync(IdentityUser user, string code, string provider)
        {
            return Task.FromResult(true);
        }
    } 


با توجه به مسیریابی تعریف شده، از مسیر های زیر می توانیم به اکشن های مور نیاز احراز هویت دسترسی پیدا کنیم :

POST api/Auth/Login {"email":"user@example.com","password":"ABC#1234"}

POST api/Auth/RefreshToken {"refreshToken":"tokenValue"}


قابلیت ورود دو عاملی یا Two Facture:

در صورتیکه این قابلیت برای کاربر فعال شده باشد پس از ورود موفقیت آمیز یک توکن با نام TwoFactorToken دریافت می کنیم که با استفاده از آن می توانیم در مسیر TwoFactorProviders لیست ارایه دهنده های عامل ورود دوم برای این کاربر را دریافت کنیم و و پس از انتخاب یکی از آنها از مسیر SendCode در خواست ارسال کد داشته باشید و در مرحله آخر با کد دریافت شده، از مسیر TwoFactorSignIn ورود را نهایی کنیم و توکن احراز هویت را دریافت کنیم. البته بدیهی است برای ارایه دهنده نوع Authenticator نیازی به مرحله ارسال کد نداریم.

احتمالا در نسخه های بعدی روش ورود عامل دوم تغییر خواهد کرد و از رفت و برگشت های آن کاسته خواهد شد.


JwtAuhentication چگونه کار می کند؟

در کتابخانه Identity همان طور که در مقاله معماری Asp.Net Identity Core بررسی کردیم کلاس SigninManager به عنوان یک کلاس مکمل برای UserManager استفاده شده است که تمامی موضوعات مرتبط با احراز هویت توسط کوکی را به عهده دارد. ما نیز به تبعیت از این موضوع کلاس JwtSigninManager را ایجاد کردیم و تمام فرآیندهای مرتبط با احراز هویت به روش توکن JWT را در آن نوشتیم. با استفاده کلاس JwtSigninManager می توانیم تمام کارهای لازم جهت احراز هویت به روش توکن را مشابه کلاس های پیش فرض Identity در کوکی برای توکن انجام دهیم. برای سادگی کار یک کلاس AuthController تعریف کردیم که می توان با ارث بری از آن کنترلر احراز هویت را ایجاد کرد. البته اجباری در این کار نیست و می توان تمام اکشن های مرتبط با احراز هویت را با دو کلاس UserManager و JwtSigninManager به راحتی و مشابه کوکی مستقلاً پیاده سازی کنیم.


فرق کوکی و توکن در JwtAuthentication

یکی از نکاتی که به رعایت آن در حین طراحی کتابخانه اصرار داشتیم همسو بودن آن با حالت استفاده از کوکی است. بنابراین بجز موارد مرتبط با نحوه استفاده و اعتبار سنجی کوکی و توکن هیچ گونه فرقی از نظر رفتاری با همدیگر ندارند.

  • همانند روش کوکی بواسطه تغییرات مهم کاربر و یا تغییر مستقیم فیلد SecurityStamp توکن از اعتبار خارج می شود.
  • اگر در Identity نقش ها را ایجاد کرده باشید همانند کوکی این نقش ها به صورت خودکار به توکن اضافه می شود.
  • همانند روش کوکی با استفاده از پالیسی زیر می توانیم اکشن های مورد نظر رو منوط به ورود به روش دو عاملی احراز هویت کنیم.

    services.AddAuthorization(options =>

        options.AddPolicy("TwoFactorEnabled",

            x => x.RequireClaim("amr", "mfa")));


قابلیت تغییر و توسعه در JwtAuthentication

همانند کتابخانه Identity همه چیز قابل تغییر و تنظیم است. می توانیم نمونه سفارشی سازی شده توسط خودمان از کلاس JwtSigninManager را ایجاد کنیم و متدهای آن را بازنویسی کنیم. به کدهای کلاس AddJwtAuthentication مراجعه کنید و مطابق با خواسته و نیاز خود در این تعاریف تغییر ایجاد کنید.


چگونه Claim های شخصی رو به توکن اضافه کنیم؟

همانطور که گفتیم سعی کردیم همه چیز مشابه کوکی باشد از این رو اگر شما از روش استاندارد افزودن Claim های شخصی استفاده کرده باشید آنها به توکن نیز اضافه می شوند. برای افزودن آنها برای کوکی و توکن باید کلاس UserClaimsPrincipalFactory را بازنویسی کنیم و پس از افزودن Claim های مورد نظر آن را به سیستم وابستگی ها معرف کنیم.


با تشکر از وحید نصیری از بعضی از کدهای پروژه ASPNETCore2JwtAuthentication در این کتابخانه استفاده شده است.

آدرس مخزن پروژه Identity.JwtAuthentication

منبع: تحقیق و توسعه راهکارهای نرم افزاری هنامیک