احراز هویت و مجوز (Authentication، Authorization) در Xamarin

چهارشنبه 11 بهمن 1396

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

احراز هویت و مجوز (Authentication، Authorization) در Xamarin

روش‌های متعددی برای ادغام احراز هویت و مجوز درون برنامه Xamarin.Forms وجود دارد که با برنامه وب ASP.NET MVC ارتباط برقرار می‌کند، ازجمله استفاده از ASP.NET Core Identity، ایجادکننده‌های (provider) احراز هویت خارجی مثل مایکروسافت، گوگل، فیس‌بوک، توییتر، و میان‌افزارهای (middleware) احراز هویت. برنامه‌های موبایل eShopOnContainers، احراز هویت و مجوز را با میکروسرویس‌های containerized identity که در IdentityServer 4 استفاده می‌شوند، انجام می‌دهند. برنامه‌های موبایل توکن (کلمه رمزی) امنیتی را از IdentityServer، برای اعتبارسنجی کاربر یا برای دسترسی به منبع درخواست می‌کنند. برای اینکه IdentityServer توکنی را صادر کند، کاربر باید وارد IdentityServer شود. با این حال، IdentityServer رابط کاربری یا پایگاه داده‌ای را برای احراز هویت فراهم نمی‌کند. بنابراین در برنامه مرجع eShopOnContainers، ASP.NET Core Identity برای این کار استفاده می‌شود.

احراز هویت (Authentication)

احراز هویت زمانی لازم است که برنامه‌ باید هویت کاربر جاری را شناسایی کند. مکانیسم اولیه ASP.NET Core برای شناسایی کاربران، سیستم عضویت ASP.NET Core Identity است، که اطلاعات کاربر را در یک مخزن داده تنظیم شده توسط توسعه‌دهنده ذخیره می‌کند. معمولا، این مخزن داده یک مخزن EntityFramework خواهد بود، هرچند مخازن سفارشی یا بسته‌های شخص ثالث می‌توانند برای ذخیره اطلاعات شناسایی در مخزن Azure، DocumentDB و یا دیگر مکان‌ها استفاده شوند.

برای سناریوهای احراز هویت که معمولا از مخزن داده کاربر محلی استفاده کرده و پایداری اطلاعات شناسایی بین درخواست‌ها را از طریق کوکی‌ها ایجاد می‌کند، ASP.NET Core Identity راه‌حل خوبی است. با این حال، کوکی‌ها همیشه شیوه بدیهی برای حفظ و انتقال داده‌ها نیستند. مثلا یک برنامه وب ASP.NET Core که endpointهای RESTful را نمایش می‌دهد، از برنامه موبایلی که نیاز به استفاده از توکن‌های حامل احراز هویت دارد قابل دسترسی هستند، نظر به اینکه کوکی‌ها در این سناریو قابل استفاده نیستند. توکن‌های حامل را می‌توان به راحتی بازیابی کرد و در هدر مجوز درخواست‌های وب ساخته شده از برنامه موبایل قرار داد.

صدور توکن‌های حامل با استفاده از IdentityServer 4

IdentityServer 4 چارچوب اپن سورس OAuth 2.0 و OpenID Connect برای ASP.NET Core است که می‌تواند برای بسیاری از سناریوهای احراز هویت و مجوز مورد استفاده قرار گیرد، ازجمله صدور توکن‌های امنیتی برای کاربران محلی ASP.NET Core Identity.

نکته: OpenID Connect و OAuth 2.0 بسیار شبیه هم هستند، در حالی که وظایف مختلفی دارند.

OpenID Connect یک لایه احراز هویت در بالای پروتکل OAuth 2.0 است. OAuth 2.0 پروتکلی است که به برنامه‌ها اجازه می‌دهد تا دسترسی توکن‌ها را از سرویس امنیتی توکن درخواست کرده و از آن‌ها برای ارتباط با APIها استفاده کنند. این‌ها پیچیدگی را در برنامه‌های کلاینت و APIها کاهش می‌دهند، زیرا احراز هویت و مجوز می‌توانند متمرکز شوند.

ترکیب OpenID Connect و OAuth 2.0 دو امنیت اساسی مربوط به احراز هویت و دسترسی به API را ترکیب می‌کند، و IdentityServer 4 پیاده‌سازیی از این پروتکل‌ها است.
در برنامه‌هایی که ارتباط مستقیم بین کلاینت و میکروسرویس‌ها وجود دارد، مانند برنامه مرجع eShopOnContainers، میکروسرویس اختصاصی احراز هویت که به عنوان سرویس امنیتی توکن (STS) ایفای نقش می‌کند، می‌تواند برای احراز هویت کاربران استفاده شود، که در شکل زیر نشان داده شده است.

برنامه موبایل eShopOnContainers با میکروسرویس identity ارتباط برقرار می‌کند که از IdentityServer 4 برای اجرای احراز هویت و کنترل دسترسی به APIها استفاده می‌کند. بنابراین، برنامه‌های موبایل توکن‌ها را از IdentityServer، برای احراز هویت کاربر یا برای دسترسی به یک منبع درخواست می‌کنند:

احراز هویت کاربران با IdentityServer توسط درخواست توکن‌ شناسایی برنامه موبایل، که نتیجه فرآیند احراز هویت است، به دست می‌آید و شامل یک شناسه برای کاربر و اطلاعاتی در مورد نحوه و زمان احراز هویت کاربر می‌باشد. همچنین می‌تواند شامل اطلاعات اضافی دیگری برای شناسایی باشد.

دسترسی به منبع با IdentityServer توسط درخواست توکن دسترسی برنامه موبایل، که اجازه دسترسی به منبع API را می‌دهد، به دست می‌آید. کلاینت‌ها دسترسی به توکن‌ها را درخواست داده و آن‌ها را به API ارسال می‌کنند. توکن‌های دسترسی شامل اطلاعاتی در مورد کلاینت و کاربر (اگر موجود باشد) می‌باشد. سپس APIها از این اطلاعات برای مجوز دسترسی به داده‌هایشان استفاده می‌کنند.

نکته: کلاینت قبل از اینکه بتواند توکن‌ها را درخواست دهد، باید با IdentityServer ثبت‌نام شود.

افزودن IdentityServer به برنامه وب

برای اینکه یک برنامه وب ASP.NET Core از IdentityServer 4 استفاده کند، باید به سولوشن ویژوال استودیو برنامه وب اضافه شود.

هنگامی که IdentityServer در سولوشن ویژوال استودیو برنامه وب قرار می‌گیرد، باید به خط لوله (Pipleline) پردازش درخواست HTTP برنامه وب اضافه شود، تا بتواند درخواست‌ها را برای OprnID Connect و OAuth 2.0 به کار گیرد. این عمل در متد Configure در کلاس Startup برنامه وب انجام می‌شود، همانند زیر:

public void Configure(  
    IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)  
{  
    ...  
    app.UseIdentity();  
    ...  
}

ترتیب در pipeline پردازش درخواست HTTP برنامه وب اهمیت دارد. بنابراین، IdentityServer باید قبل از فریم‌ورک UI که صفحه ورود را اجرا می‌کند، اضافه شود.

پیکربندی IdentityServer

IdentityServer باید در متد ConfigureServices در کلاس Startup برنامه وب با فراخوانی سرویس‌ها تنظیم شود. همانطور که در مثال زیر از برنامه مرجع eShopOnContainers نشان داده شده است:

public void ConfigureServices(IServiceCollection services)  
{  
    ...  
    services.AddIdentityServer(x => x.IssuerUri = "null")  
        .AddSigningCredential(Certificate.Get())                 
        .AddAspNetIdentity<ApplicationUser>()  
        .AddConfigurationStore(builder =>  
            builder.UseSqlServer(connectionString, options =>  
                options.MigrationsAssembly(migrationsAssembly)))  
        .AddOperationalStore(builder =>  
            builder.UseSqlServer(connectionString, options =>  
                options.MigrationsAssembly(migrationsAssembly)))  
        .Services.AddTransient<IProfileService, ProfileService>();  
}

بعد از فراخوانی متد services.AddIdentityServer، fluent APIهای اضافی با پیکربندی‌های زیر فراخوانی می‌شوند:

مجوزهای مورد استفاده برای امضاء.

منابع identity و API که کاربران ممکن است دسترسی به آن‌ها را درخواست کنند.

کلاینت‌هایی که به درخواست توکن‌ها وصل خواهند شد.

ASP.NET Core Identity.

نکته: پیکربندی IdentityServer 4 به صورت داینامیک بارگیری می‌شود. APIهای IdentityServer 4 اجازه پیکربندی IdentityServer از یک لیست درون حافظه‌ای از پیکربندی اشیاء را می‌دهد. در برنامه مرجع eShopOnContainers، این مجموعه‌های درون حافظه‌ای در برنامه‌ها hard-coded هستند. با این حال، در سناریوها می‌توانند به صورت داینامیک از پیکربندی فایل یا از پایگاه داده بارگیری شوند.

پیکربندی منابع API

هنگام پیکربندی منابع API، متد AddInMemoryApiResources منتظر یک مجموعه IEnumerable<ApiResource> می‌ماند. کد زیر متد GetApis را نشان می‌دهد که این مجموعه را در برنامه مرجع eShopOnContainers ارائه می‌دهد:

public static IEnumerable<ApiResource> GetApis()  
{  
    return new List<ApiResource>  
    {  
        new ApiResource("orders", "Orders Service"),  
        new ApiResource("basket", "Basket Service")  
    };  
}

این متد مشخص می‌کند که IdentityServer باید دستورات APIها را حفظ کند. بنابراین، IdentityServer توکن‌ها را مدیریت می‌کند که هنگام فراخوانی این APIها ضروری خواهند بود.

پیکربندی منابع Identity

هنگام پیکربندی منابع Identity، متد AddInMemoryIdentityResources منتظر یک مجموعه IEnumerable<IdentityResource> است. منابع Identity شامل داده‌ها هستند مثل شناسه کاربر، نام یا آدرس ایمیل. هر یک از این منابع Identity یک نام منحصربه‌فرد دارد و انواع claimهای اختیاری را می‌توان به آن‌ها اختصاص داد که سپس در توکن identity کاربر قرار می‌گیرند. مثال زیر متد GetResources را نشان می‌دهد که این مجموعه را در برنامه مرجع eShopOnContainers ارائه می‌دهد:

public static IEnumerable<IdentityResource> GetResources()  
{  
    return new List<IdentityResource>  
    {  
        new IdentityResources.OpenId(),  
        new IdentityResources.Profile()  
    };  
}

نکته: کلاس IdentityResources از تمام زمینه‌های مختلف در مشخصات OpenID Connect (ایمیل، پروفایل، تلفن و آدرس) پشتیبانی می‌کند.

IdentityServer همچنین از منابع identity سفارشی مختلف پشتیبانی می‌کند.

پیکربندی کلاینت‌ها

کلاینت‌ها برنامه‌هایی هستند که می‌توانند توکن‌ها را از IdentityServer درخواست دهند. معمولا، برای هر کلاینت حداقل تنظیمات زیر باید تعریف شود:

یک شناسه منحصربه‌فرد کلاینت.

تعاملات مجاز با سرویس توکن. (با نوعی تصدیق شناخته می شود).

مکانی که identity و توکن‌های دسترسی به آن‌ها ارسال می‌شوند (با یک Url منتقل شده شناخته می‌شود).

لیستی از منابعی که کلاینت‌ها اجازه دسترسی به آن‌ها را دارند (به عنوان محدوده‌ها (scope) شناخته می‌شوند).

وقتی کلاینت‌ها پیکربندی می‌شوند، متد AddInMemoryClients منتظر یک مجموعه IEnumerable<Client> می‌ماند. مثال زیر برای برنامه موبایل eShopOnContainers در متد GetClients می‌باشد که این مجموعه را در برنامه مرجع eShopOnContainers ارائه می‌دهد:

public static IEnumerable<Client> GetClients(Dictionary<string,string> clientsUrl)
{
    return new List<Client>
    {
        ...
        new Client
        {
            ClientId = "xamarin",
            ClientName = "eShop Xamarin OpenId Client",
            AllowedGrantTypes = GrantTypes.Hybrid,
            ClientSecrets =
            {
                new Secret("secret".Sha256())
            },
            RedirectUris = { clientsUrl["Xamarin"] },
            RequireConsent = false,
            RequirePkce = true,
            PostLogoutRedirectUris = { $"{clientsUrl["Xamarin"]}/Account/Redirecting" },
            AllowedCorsOrigins = { "http://eshopxamarin" },
            AllowedScopes = new List<string>
            {
                IdentityServerConstants.StandardScopes.OpenId,
                IdentityServerConstants.StandardScopes.Profile,
                IdentityServerConstants.StandardScopes.OfflineAccess,
                "orders",
                "basket"
            },
            AllowOfflineAccess = true,
            AllowAccessTokensViaBrowser = true
        },
        ...
    };
}

این پیکربندی‌ها داده‌ها را برای خواص زیر تعیین می‌کنند:

ClientId: یک شناسه منحصربه‌فرد برای کلاینت.

ClientName: نام نمایش‌داده‌شده کلاینت که برای ورود و صفحه موافقت (consent) استفاده می‌شود.

AllowedGrantTypes: نحوه تعامل کلاینت با IdentityServer را مشخص می‌کند.

ClientSecrets: اعتبارنامه‌های محرمانه کلاینت را مشخص می‌کند که هنگام درخواست توکن‌ها استفاده می‌شوند.

RedirectUris: URLهای مجاز برای بازگشت توکن‌ها یا دستورهای مجوز را مشخص می‌کند.

RequireConsent: مشخص می‌کند که آیا صفحه موافقت نیاز است.

RequirePkce: مشخص می‌کند که آیا کلاینت‌هایی که از دستور مجوز استفاده می‌کنند باید کلید نشانه را ارسال کنند.

PostLogoutRedirectUris: URLهای مجاز برای انتقال پس از خروج از سیستم را مشخص می‌کند.

AllowedCorsOrigins: مبدأ کلاینت را مشخص می‌کند تا IdentityServer بتواند به فراخوانی‌های cross-origin از مبدأ اجازه دهد.

AllowedScopes: منابعی که کلاینت به آن دسترسی دارد را مشخص می‌کند. به طور پیش‌فرض، کلاینت به هیچ منبعی دسترسی ندارد.

AllowOfflineAccess: مشخص می‌کند که آیا کلاینت می‌تواند درخواست رفرش توکن را دهد.

پیکربندی جریان احراز هویت (Authentication Flow)

جریان احراز هویت بین کلاینت و IdentityServer می‌تواند با مشخص کردن انواع تصدیق‌ها در ویژگی Client.AllowedGrantTypes پیکربندی شود.

مشخصات OpenID Connect و OAuth 2.0 تعدادی از جریانات احراز هویت را تعریف می‌کنند، مثل:

Implicit: این جریان برای برنامه‌های مبنی بر مرورگر بهینه‌سازی شده است و باید برای فقط احراز هویت کاربر، یا احراز هویت و درخواست‌های توکن دسترسی استفاده شود. تمام توکن‌ها از طریق مرورگر ارسال می‌شوند، بنابراین ویژگی‌های پیشرفته مثل رفرش توکن مجاز نیستند.

Authorization code: این جریان توانایی بازیابی توکن‌ها در یک back channel را، در مقابل مرورگر front channel فراهم می‌کند، در حالی که از احراز هویت کلاینت هم پشتیبانی می‌کند.

Hybrid: این جریان انواع تصدیق authorization code و implicit است. توکن identity از طریق کانال مرورگر منتقل می‌شود و شامل پاسخ پروتکل امضاءشده همراه با ساخته‌های دیگر مانند authorization code می‌باشد. پس از اعتبارسنجی موفقیت‌آمیز پاسخ‌ها، back channel باید برای بازیابی توکن‌های رفرش‌شده و دسترسی استفاده شود.

نکته: از جریان احراز هویت hybrid استفاده کنید. Hybrid تعداد حملاتی که در کانال مرورگر اعمال می‌شود را کم می‌کند و این جریان برای برنامه‌های بومی که می‌خواهند توکن‌های دسترسی را بازیابی کنند توصیه می‌شود (و احتمالا توکن‌ها را رفرش کند).

اجرای احراز هویت

برای اینکه IdentityServer بتواند توکن‌ها را برای کاربر صادر کند، کاربر باید وارد IdentityServer شود. با این حال، IdentityServer رابط کاربری یا پایگاه داده‌ای را برای احراز هویت ارائه نمی‌دهد. بنابراین در برنامه مرجع eShopOnContainers، ASP.NET Core Identity برای این کار استفاده می‌شود.

برنامه‌های موبایل eShopOnContainers با IdentityServer توسط جریان احراز هویت hybrid اعتبارسنجی می‌شوند که در شکل زیر نشان داده شده‌ است.

درخواست ورود برای <base endpoint>:5105/connect/authorize ساخته شده است. پس از احراز هویت موفقیت‌آمیز، IdentityServer پاسخ احراز هویت حاوی کد مجوز و یک توکن شناسایی را بازمی‌گرداند. سپس کد مجوز به <base endpoint>:5105/connect/token ارسال می‌شود که به دسترسی، شناسایی و توکن‌های رفرش‌شده پاسخ می‌دهد.

برنامه موبایل eShopOnContainers از طریق ارسال درخواست به <base endpoint>:5105/connect/endsession، با پارامترهای اضافی، خارج می‌شود. پس از خروج، IdentityServer با ارسال پیام خروج از سیستم پاسخ می‌دهد و URI را به برنامه موبایل می‌فرستد. شکل زیر این فرآیند را نشان می‌دهد.

برنامه موبایل eShopOnContainers، با IdentityServer که توسط کلاس IdentityService اجرا می‌شود، که اینترفیس IIdentityService را پیاده‌سازی می‌کند، ارتباط برقرار می‌کند. این اینترفیس مشخص می‌کند که پیاده‌سازی کلاس باید متد GetTokenAsync، CreateAuthorizationRequest و CreateLogoutRequest را ارائه دهد.

ورود

هنگامی که کاربر دکمه LOGIN روی LoginView را می‌زند، SignInCommand در کلاس LoginViewModel اجرا می‌شود، که متد SignInAsync را اجرا می‌کند. مثال زیر این متد را نشان می‌دهد:

private async Task SignInAsync()  
{  
    ...  
    LoginUrl = _identityService.CreateAuthorizationRequest();  
    IsLogin = true;  
    ...  
}

این متد، متد CreateAuthorizationRequest در کلاس IdentityService را فراخوانی می‌کند، که در مثال زیر نشان داده شده است:

public string CreateAuthorizationRequest()
{
    // Create URI to authorization endpoint
    var authorizeRequest = new AuthorizeRequest(GlobalSetting.Instance.IdentityEndpoint);

    // Dictionary with values for the authorize request
    var dic = new Dictionary<string, string>();
    dic.Add("client_id", GlobalSetting.Instance.ClientId);
    dic.Add("client_secret", GlobalSetting.Instance.ClientSecret); 
    dic.Add("response_type", "code id_token");
    dic.Add("scope", "openid profile basket orders locations marketing offline_access");
    dic.Add("redirect_uri", GlobalSetting.Instance.IdentityCallback);
    dic.Add("nonce", Guid.NewGuid().ToString("N"));
    dic.Add("code_challenge", CreateCodeChallenge());
    dic.Add("code_challenge_method", "S256");

    // Add CSRF token to protect against cross-site request forgery attacks.
    var currentCSRFToken = Guid.NewGuid().ToString("N");
    dic.Add("state", currentCSRFToken);

    var authorizeUri = authorizeRequest.Create(dic); 
    return authorizeUri;
}

این متد URIای را برای endpoint مجوز IdentityServer، با پارامترهای مورد نیاز ایجاد می‌کند. endpoint  مجوز در connect/authorize/ روی پورت 5105 از endpoint نمایش داده شده به عنوان تتظیمات کاربر قرار دارد.

نکته: سطح حمله برنامه موبایل eShopOnContainers با پیاده‌سازی Proof Key (کلید نشانه) برای Code Exchange (PKCE) پروتکل OAuth کاهش می‌یابد. PKCE از کد مجوز محافظت می‌کند که توسط تأییدیه محرمانه‌ تولید شده اجرا می‌شود.

URI بازگشتی در ویژگی LoginUrl از کلاس LoginViewModel ذخیره می‌شود. هنگامی که ویژگی IsLogin، true می‌شود، WebView در LoginView قابل رؤیت می‌شود. داده‌های WebView ویژگی Source را به ویژگی LoginUrl در کلاس LoginViewModel متصل می‌کنند. بنابراین زمانی که ویژگی LoginUrl در endpoint مجوز IdentityServer تنظیم می‌شود، درخواست ورود برای IdentityServer ایجاد می‌شود. هنگامی که IdentityServer این درخواست را دریافت می‌کند و کاربر تأیید نشده است، WebView به صفحه ورود پیکربندی شده هدایت می‌شود، که در شکل زیر نشان داده شده است.

بعد از اینکه ورود کامل می‌شود، WebView به Url بازگشتی هدایت خواهد شد. این هدایت WebView باعث می‌شود متد NavigateAsync در کلاس LoginViewModel اجرا شود، که در مثال زیر نشان داده شده است:

private async Task NavigateAsync(string url)  
{  
    ...  
    var authResponse = new AuthorizeResponse(url);  
    if (!string.IsNullOrWhiteSpace(authResponse.Code))  
    {  
        var userToken = await _identityService.GetTokenAsync(authResponse.Code);  
        string accessToken = userToken.AccessToken;  

        if (!string.IsNullOrWhiteSpace(accessToken))  
        {  
            Settings.AuthAccessToken = accessToken;  
            Settings.AuthIdToken = authResponse.IdentityToken;  

            await NavigationService.NavigateToAsync<MainViewModel>();  
            await NavigationService.RemoveLastFromBackStackAsync();  
        }  
    }  
    ...  
}

این متد درخواست احراز هویت که شامل URI بازگشتی است را تجزیه می‌کند، و یک کد احراز هویت معتبر را ارائه می‌دهد، درخواستی برای endpoint توکن IdentityServer ایجاد کرده و کد مجوز، تأییدیه محرمانه PKCE و پارامترهای ضروری دیگر را پاس می‌دهد. endpoint توکن در connect/token/ روی پورت 5105 از endpoint نمایش داده شده به عنوان تتظیمات کاربر قرار دارد.

نکته: URIهای بازگشتی را اعتبارسنجی کنید. اگرچه برنامه موبایل eShopOnContainers، URI بازگشتی را اعتبارسنجی نمی‌کند، بهترین کار این است که URI بازگشتی که به مکان مشخصی فرستاده می‌شود را اعتبارسنجی کنید، تا از حملات open-redirect جلوگیری شود.

اگر endpoint توکن، یک کد مجوز معتبر و تأییدیه محرمانه PKCE را دریافت کند، با یک توکن دسترسی، توکن شناسایی و توکن رفرش‌شده پاسخ می‌دهد. توکن دسترسی (که اجازه دسترسی به درخواست‌های API را می‌دهد) و توکن شناسایی به عنوان تنظیمات برنامه ذخیره شده و هدایت صفحه انجام می‌شود. بنابراین نتیجه کلی در برنامه موبایل eShopOnContainers این است: درصورتی که کاربران بتوانند با موفقیت توسط IdentityServer احراز هویت شوند، به صفحه MainView هدایت می‌شوند، که TabbedPageای است که CatalogView را به عنوان تب انتخاب شده نمایش می‌دهد.

خروج

هنگامی که کاربر دکمه LOG OUT را در ProfileView فشار می‌دهد، LogoutCommand در کلاس ProfileViewModel اجرا می‌شود، که متد LogoutAsync را اجرا می‌کند. این متد صفحه را به صفحه LoginView هدایت کرده، نمونه‌ای از LogoutParameter را که به عنوان پارامتر تنظیم شده است را ارسال می‌کند.

هنگامی که یک view ایجاد شده و به آن هدایت می شود، متد InitializeAsync از view model مربوط به view اجرا می شود، و سپس متد Logout از کلاس LoginViewModel اجرا می‌شود، که در مثال زیر نشان داده شده است:

private void Logout()  
{  
    var authIdToken = Settings.AuthIdToken;  
    var logoutRequest = _identityService.CreateLogoutRequest(authIdToken);  

    if (!string.IsNullOrEmpty(logoutRequest))  
    {  
        // Logout  
        LoginUrl = logoutRequest;  
    }  
    ...  
}

این متد، متد CreateLogoutRequest در کلاس IdentityServer را فراخوانی کرده، توکن شناسایی که از تنظیمات برنامه گرفته است را به عنوان پارامتر ارسال می‌کند. مثال زیر متد CreateLogoutRequest را نشان می‌دهد:

public string CreateLogoutRequest(string token)  
{  
    ...  
    return string.Format("{0}?id_token_hint={1}&post_logout_redirect_uri={2}",   
        GlobalSetting.Instance.LogoutEndpoint,  
        token,  
        GlobalSetting.Instance.LogoutCallback);  
}

این متد URIای را برای endpoint پایان سشن IdentityServer، با پارامترهای ضروری ایجاد می‌کند. endpoint سشن پایان در connect/endsession/ روی پورت 5105 از endpoint نمایش داده شده به عنوان تتظیمات کاربر قرار دارد.

URI بازگشتی در ویژگی LoginUrl از کلاس LoginViewModel ذخیره می‌شود. هنگامی که ویژگی IsLogin، true می‌شود، WebView در LoginView قابل رؤیت می‌شود. داده‌های WebView ویژگی Source را به ویژگی LoginUrl در کلاس LoginViewModel متصل می‌کنند. بنابراین زمانی که ویژگی LoginUrl در endpoint سشن پایان  IdentityServer تنظیم می‌شود، درخواست خروج برای IdentityServer تنظیم می‌شود. وقتی IdentityServer این درخواست را دریافت می‌کند، درصورتیکه کاربر وارد سیستم باشد، خروج از سیستم رخ می‌دهد. احراز هویت با یک کوکی تحت کنترل میان‌افزار (middleware) کوکی احراز هویت از ASP.NET Core ردیابی می‌شود. بنابراین خروج از IdentityServer کوکی احراز هویت را حذف کرده و یک post logout redirect URI ارسال کرده و کلاینت را خارج می‌کند.

در برنامه موبایل، WebView به post logout redirect URI هدایت خواهد شد. این هدایت WebView باعث می‌شود متد NavigateAsync در کلاس LoginViewModel اجرا شود، که در مثال زیر نشان داده شده است:

private async Task NavigateAsync(string url)  
{  
    ...  
    Settings.AuthAccessToken = string.Empty;  
    Settings.AuthIdToken = string.Empty;  
    IsLogin = false;  
    LoginUrl = _identityService.CreateAuthorizationRequest();  
    ...  
}

این متد توکن شناسایی و توکن دسترسی را از تنظیمات برنامه پاک می‌کند و ویژگی IsLogin را با مقدار false تنظیم می‌کند، که باعث می‌شود WebView در صفحه LoginView دیده نشود. درنهایت، ویژگی LoginUrl با URIای از endpoint مجوز IdentityServer، با پارامترهای ضروری، در آماده‌سازی برای ورود بعدی کاربر، تنظیم می‌شود.

مجوز (Authorization)

پس از احراز هویت، ASP.NET Core web API غالبا نیاز به مجوز دسترس دارد، که اجازه دسترسی به برخی سرویس‌ها را به بعضی از کاربرانی که احراز هویت شده‌اند می‌دهد، اما نه به همه آن‌ها.

محدود کردن دسترسی برای یک مسیر ASP.NET Core MVC می‌تواند با استفاده از اتربیوت Authorize برای یک کنترلر یا اکشن انجام شود، که دسترسی به آن کنترلر یا اکشن را برای کاربران تأیید شده محدود می‌کند. در مثال زیر نشان داده شده است:

[Authorize]  
public class BasketController : Controller  
{  
    ...  
}

اگر کاربر غیرمجازی تلاش کند تا به کنترلر یا اکشن که دارای اتربیوت Authorize است، وارد شود، فریم‌ورک MVC، کد وضعیت HTTP (unauthorized) 401 را باز می‌گرداند.

نکته: می‌توان پارامترهایی را برای اتربیوت Authorize جهت محدودسازی API  برای کاربران خاص مشخص کرد.

IdentityServer می‌تواند گردش کار مجوز را متحد کند، به طوری که توکن‌های دسترسی مجوز را کنترل کنند. این رویکرد در شکل زیر نشان داده شده است.

برنامه موبایل eShopOnContainers با میکروسرویس identity ارتباط برقرار کرده و توکن دسترسی را به عنوان بخشی از فرآیند احراز هویت درخواست‌ می‌کند. سپس توکن دسترسی به APIهای نمایش داده شده که توسط میکروسرویس به عنوان بخشی از درخواست‌های دسترسی مرتب‌ شده‌اند، ارسال می‌شود. توکن‌های دسترسی حاوی اطلاعاتی در مورد کلاینت و کاربر است. سپس APIها از این اطلاعات برای اجازه دسترسی به داده‌های آن‌ها استفاده می‌کنند.

پیکربندی IdentityServer برای اجرای مجوز

برای اجرای مجوز با IdentityServer، میان‌افزار authorization آن باید به pipeline درخواست HTTP برنامه وب اضافه شود. میان‌افزار در متد ConfigureAuth در کلاس Startup برنامه وب افزوده می‌شود، که از متد Configure فراخوانی می‌شود، که در مثال زیر از برنامه مرجع eShopOnContainers نشان داده شده است:

protected virtual void ConfigureAuth(IApplicationBuilder app)  
{  
    var identityUrl = Configuration.GetValue<string>("IdentityUrl");  
    app.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions  
    {  
        Authority = identityUrl.ToString(),  
        ScopeName = "basket",  
        RequireHttpsMetadata = false  
    });  
}

این متد تضمین می کند که API فقط با یک توکن دسترسی معتبر قابل دسترسی است. میان‌افزار توکن وارد شده را اعتبارسنجی می‌کند تا مطمئن شود که از یک صادرکننده قابل اطمینان ارسال شده است، و معتبر بودن توکن برای استفاده اعتبارسنجی می‌شود. بنابراین مرورگر کنترلر را به وضعیت 401 میفرستد تا نشان دهد یک توکن دسترسی لازم است.

ایجاد درخواست‌های دسترسی به APIها

هنگام ایجاد درخواست‌ها به میکروسرویس‌ها، توکن دسترسی، که از IdentityServer طی فرآیند احراز هویت به دست می‌آید، باید در درخواست باشد. همان‌طور که در مثال زیر نشان داده شده است:

var authToken = Settings.AuthAccessToken;  
Order = await _ordersService.GetOrderAsync(Convert.ToInt32(order.OrderNumber), authToken);

توکن دسترسی به عنوان تنظیمات برنامه ذخیره می‌شود و از ذخیره‌سازی مخصوص پلت‌فرم بازیابی شده و در فراخوانی متد GetOrderAsync در کلاس OrderService وجود دارد.

به همین ترتیب، توکن دسترسی هنگام ارسال داده‌ها به API محفوظ در IdentityServer باید وجود داشته باشد، همانند مثال زیر:

var authToken = Settings.AuthAccessToken;  
await _basketService.UpdateBasketAsync(new CustomerBasket  
{  
    BuyerId = userInfo.UserId,   
    Items = BasketItems.ToList()  
}, authToken);

توکن دسترسی از ذخیره‌سازی مخصوص پلت‌فرم بازیابی شده و در فراخوانی متد UpdateBasketAsync در کلاس BasketService موجود است.

کلاس RequestProvider، در برنامه موبایل eShopOnContainers، از کلاس HttpClient برای درخواست به RESTful APIهای نمایش داده شده برنامه مرجع eShopOnContainers استفاده می‌کند. هنگام درخواست به APIهایی که نیاز به مجوز دارند، نیاز به درخواستی با توکن دسترسی معتبر است که توسط افزودن توکن دسترسی به هدرهای نمونه HttpClient به دست می‌آید، همانند مثال زیر:

httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);

ویژگی DefaultRequestHeaders از کلاس HttpClient، هدرهایی که با هر درخواست ارسال شده‌اند را نشان می‌دهد و توکن دسترسی به پیشوند هدر Authorization با رشته Bearer اضافه می‌شود. هنگامی که درخواست به RESTful API ارسال می‌شود، مقدار هدر Authorization استخراج و تأیید می‌شود تا اطمینان حاصل شود که از صادرکننده معتبری ارسال شده است و مورد استفاده قرار می‌گیرد که مشخص شود که آیا کاربر دارای مجوز فراخوانی APIای که دریافت شده است بوده است.

خلاصه‌گیری

روش‌های متفاوتی برای ادغام احراز هویت و مجوز در برنامه Xamarin.Forms که با برنامه وب ASP.NET MVC ارتباط برقرار می ‌کند وجود دارد. برنامه موبایل eShopOnContainers احراز هویت و مجوز را با میکروسرویس containerized identity اجرا می‌کند که از IdentityServer 4 استفاده می‌کند. IdentityServer یک فریم ورک OAuth 2.0 و OpenID Connect اپن سورس برای ASP.NET Core است که با ASP.NET Core Identity ادغام شده است تا توکن حامل احراز هویت را اجرا کند.

برنامه موبایل توکن‌های امنیتی را از IdentityServer، برای احراز هویت کاربر یا برای دسترسی به یک منبع درخواست می دهد. هنگام دسترسی به منبع، توکن دسترسی باید در درخواست APIهایی که نیاز به مجوز دارند وجود داشته باشد. میان‌افزار IdentityServer توکن‌های دسترسی واردشده را اعتبارسنجی می‌کند تا مطمئن شود که از یک صادرکننده قابل اعتماد صادر شده و برای استفاده با API ای که آن‌ها را دریافت کرده است معتبر می‌باشند. 

دوره های آموزشی زامارین 

دوره برنامه نویسی اندروید با سی شارپ ( Xamarin )

آموزش متریال دیزاین در زامارین

آموزش Xamarin Forms

دوره Xamarin Form پیشرفته

ایمان مدائنی

نویسنده 1299 مقاله در برنامه نویسان

کاربرانی که از نویسنده این مقاله تشکر کرده اند

در صورتی که در رابطه با این مقاله سوالی دارید، در تاپیک های انجمن مطرح کنید