Derinlemesine yazılım eğitimleri için kanalımı takip edebilirsiniz...

IdentityServer4 Yazı Serisi #11 – Merkezi Üyelik Sistemi Temelleri

Merhaba,

Bu içeriğimizde yazı serimiz boyunca ara ara geliştirdiğimiz örnek uygulama üzerinden Merkezi Üyelik Sistemi’ni örneklendireceğiz. Geliştirdiğimiz uygulamanın bu makaleye kadar olan en son halini elde edebilmek için buraya tıklayınız.

Şimdi Merkezi Üyelik Sistemi örneklendirmesi için adım adım aşağıdaki önergeleri takip edelim;

  • Adım 1
    İlk olarak Auth Server’da bir login sayfasına ihtiyacımız vardır. Lakin bu sayfayı bizler manuel tasarlamaktansa IdentityServer4 geliştiricilerinin cenneti garanti etmek pahasına bizlere sunmuş olduğu IdentityServer4.Quickstart.UI paketini kullanabiliriz.

    İlgili paketi kurabilmek için verilen adrese tıkladığınızda açılan sayfada bulunan ‘Adding the quickstart UI’ başlığı altındaki powershell kodunu kopyalayıp ‘AuthServer’ uygulamasının dizininde Powershell üzerinden çalıştırınız.
    IdentityServer4 Yazı Serisi #8 - Authorization Code Grant(Flow)

    IdentityServer4 Yazı Serisi #8 - Authorization Code Grant(Flow)

    Kod çalıştırıldığında uzaktan yükleme ve genişletme süreci…


    IdentityServer4 Yazı Serisi #8 - Authorization Code Grant(Flow)

    Yükleme tamamlandıktan sonra nihai olarak dosyaların oluşturulması…


    Paketi yükledikten sonra oluşturulan dosyalara şöyle bir göz atarsak eğer;
    IdentityServer4 Yazı Serisi #8 - Authorization Code Grant(Flow)
    Görüldüğü üzere kullanıcı işlemlerine dair aklınıza gelebilecek tüm çalışmalar controller olarak tasarlanmış bulunmaktadır. ‘IdentityServer4.Quickstart.UI’ paketi IdentityServer4 ile ilgili tüm işlemleri tek satır kod yazmaya gerek kalmaksızın bünyesinde barındırmaktadır. Bizlere tek düşen paketin sunmuş olduğu .cshtml dosyaları içerisindeki tasarımlarla oynamak, css vs. gibi dosyalarda düzeltmeler yapmaktır. Makale serimizin devamında bu paketin daha da derinlemesine kullanımını inceleyeceğimizden dolayı şimdilik pek üzerinde durmaksızın yola devam ediyoruz…
     
     
     
     
  • Adım 2
    ‘AuthServer’ projesinde kullanılan ‘IdentityServer4.Quickstart.UI’ paketinden dolayı MVC tabanlı bir çalışma yapılacaktır. Haliyle ‘Startup.cs’ dosyasında aşağıdaki servislerin eklenmesi ve middleware’lerin çağrılması gerekmektedir;

        public class Startup
        {
            public void ConfigureServices(IServiceCollection services)
            {
                .
                .
                .
                services.AddControllersWithViews(); //MVC için
            }
    
            public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
            {
                .
                .
                .
                app.UseRouting();
                app.UseStaticFiles(); //wwwroot'a erişim için
                app.UseAuthentication(); //kimlik doğrulama için
                app.UseAuthorization(); //yetkilendirme için
    
                app.UseIdentityServer();
    
                app.UseEndpoints(endpoints =>
                {
                    endpoints.MapDefaultControllerRoute(); //url rotası için
                });
                .
                .
                .
            }
        }
    
  • Adım 3
    Bu konfigürasyonlardan sonra ‘AuthServer’ uygulamasını ayağa kaldıralım ve test edelim.
    IdentityServer4 Yazı Serisi #8 - Authorization Code Grant(Flow) Görüldüğü üzere ‘AuthServer’ uygulaması direkt olarak bir UI ile bizi karşılamaktadır. Burada oturumumuzla ilgili bilgileri görebilmek için ‘Click here to see the claims for your current session.’ yazısındaki ‘here’ linkine tıklayalım.
     
     
     
     

    IdentityServer4 Yazı Serisi #8 - Authorization Code Grant(Flow)Görüldüğü üzere ilgili linke tıkladığımızda herhangi bir oturum açık olmadığı için bizi login sayfasına yönlendirmektedir. Tabi ki de login olabilmek için öncelikle bir kullanıcı tanımlamamız gerekmektedir. Bunu şimdilik bu makalede gerçekleştirmeyecek ve In-Memory’de test user’lar tutacağız. Nihayetinde bu makalede amacımız kullanıcı kaydı değildir.
     
     
     
     

  • Adım 4
    ‘AuthServer’da yetki verebilmek için kullanıcılara ihtiyacımız vardır. Konumuz gereği kullanıcı kayıt işlemleriyle uğraşmamak ve development aşamasında hızlıca kullanıcı ayağa kaldırabilmek için In-Memory’de test user’lar tanımlayacağız. Bunun için ‘AuthServer’ uygulamasında ki ‘Config.cs’ dosyasında aşağıdaki metodu tasarlamamız gerekmektedir.

            public static IEnumerable<TestUser> GetTestUsers()
            {
                return new List<TestUser> {
                    new TestUser {
                        SubjectId = "test-user1",
                        Username = "test-user1",
                        Password = "12345",
                        Claims = {
                            new Claim("name","test user1"),
                            new Claim("website","https://wwww.testuser1.com"),
                            new Claim("gender","1")
                        }
                    },
                    new TestUser {
                        SubjectId = "test-user2",
                        Username = "test-user2",
                        Password = "12345",
                        Claims = {
                            new Claim("name","test user2"),
                            new Claim("website","https://wwww.testuser2.com"),
                            new Claim("gender","0")
                        }
                    }
                };
            }
    

    Yukarıdaki kodu incelerseniz eğer test user’ları ‘IdentityServer4.Test’ namespace’i altındaki ‘TestUser’ nesnesi ile tanımlamaktayız. Tanımlanan test user’ların claim’lerine göz atarsanız eğer ‘name’, ‘website’, ‘gender’ gibi bilgiler tutulmaktadır. Bu bilgiler IdentityServer4 mekanizmasında bir user profili için önceden tanımlanmış default anahtarlar eşliğinde yazılmaktadır.

    Bir user profili için varsayılan olarak ayarlanmış tüm claim anahtarları şunlardır; name, family_name, given_name, middle_name, nickname, preferred_username, profile, picture, website, gender, birthdate, zoneinfo, locale, updated_at

  • Adım 5
    Test user’ları tanımladıktan sonra sırada ‘IdentityResource’ tanımlamaya gelmiştir. ‘IdentityResource’ü açıklamamız gerekirse eğer;
    Client’lar giriş yapan kullanıcıların farklı bilgilerini isteyebilmektedirler. Bu bilgileri alabilmeleri için ‘IdentityResource’ tanımlanmalıdır. Bu ‘IdentityResource’ler client’lara verilerek hangi bilgileri alabilecekleri bildirilebilmektedir. Örneğin; ‘client1’e email ile ilgili identity resource verilirse client tarafından ilgili kullanıcının sadece email bilgisi alınmış olacaktır. Benzer mantıkla ‘client2’ye email ve username ile ilgili identity resource verilirse bu seferde adı geçen kullanıcının ilgili bilgileri alınmış olacaktır.

    ‘IdentityResource’ tanımlayabilmek için tekrar ‘Config.cs’e gelerek aşağıdaki metot oluşturulmalıdır;

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

    Burada tanımlanan ‘IdentityResource’leri incelersek eğer;

    • OpenId : Üretilecek token içerisinde kesinlikle bir kullanıcı id/user id/subject id olmalıdır. OpenId kullanıcı id değerini ifade eder. Token’da ‘subid’ olarak tutulacaktır.
    • Profile : Kullanıcı profil bilgilerini ve biryandan da kullanıcı için var olan tüm claim’leri barındırır.

    Bu iki değerde kendi bünyelerinde birçok claim barındırabilmektedirler. Hatta ‘Profile’ ile ilgili birçok claim’i bir önceki adımda not almıştık.

  • Adım 6
    ‘Config.cs’ dosyasında tanımlanan user’lar ile birlikte ‘IdentityResource’leri ‘AuthServer’ uygulamasına eklememiz gerekmektedir. Bunun için ‘Startup.cs’de ‘AddIdentityServer’ metodu üzerinden ‘AddTestUsers’ ve ‘AddInMemoryIdentityResources’ metotları ile aşağıdaki gibi ekleme işlemini gerçekleştirebiliriz;

        public class Startup
        {
            public void ConfigureServices(IServiceCollection services)
            {
                .
                .
                .
                services.AddIdentityServer()
                    .AddInMemoryApiResources(Config.GetApiResources())
                    .AddInMemoryApiScopes(Config.GetApiScopes())
                    .AddInMemoryClients(Config.GetClients())
                    .AddTestUsers(Config.GetTestUsers().ToList())
                    .AddInMemoryIdentityResources(Config.GetIdentityResources())
                    .AddDeveloperSigningCredential();
                .
                .
                .
            }
    
  • Adım 7
    Şimdi bu işlemlerden sonra tekrar ‘AuthServer’ uygulamasını ayağa kaldıralım ve test edelim.
    IdentityServer4 Yazı Serisi #8 - Authorization Code Grant(Flow)
    Biraz önce tanımladığımız kullanıcı adı ve şifreyi girerek ‘Login’ butonuna tıklayalım.
     
     
     
     
     
     
     
     
     

    Evet… Görüldüğü üzere kullanıcı(test) olarak ‘AuthServer’da bir login işlemi gerçekleştirmiş olduk. Haliyle kullanıcı bilgileri ekranda listelenmektedir. Hatta tarayıcının Cookie değerlerine bakarsanız ‘AuthServer’ tarafından ‘idsrv’ anahtarı karşılığına bir değer yerleştirildiğini dahi göreceksiniz.

    Lakin burada dikkat edilmesi gereken bir husus söz konusudur. O da yukarıdaki görsele tekrar bakarsanız ‘Claims’ kısmında yazan ‘amr’ başlığıyla alakalıdır. Bu başlık ilgili authentication işleminin tipini yani grant tipini bizlere sunmaktadır. ‘pwd’ esasında password demektir ve bu, ileride pratiksel olarak göreceğimiz grant tiplerinden biri olan ‘Resource Owner Credentials Grant‘ı temsil etmektedir. Artık yetkilendirme için bir client uygulamasına ihtiyacımız olacak.

  • Adım 8
    Bu aşamadan sonra client tasarımına geçmemiz gerekmektedir. Burada tasarlamaktan kastımız tabi ki de görsel bir çalışma değildir. Mimarisel tasarım kastedilmektedir 🙂 Client’ı tasarladıktan sonra kullanıcıyı ‘AuthServer’a authentication code onayı için yönlendireceğiz. Burada gerekli UI’ı yine ‘IdentityServer4.Quickstart.UI’ kütüphanesi sağlayacaktır.

    Tasarlayacağımız client şimdilik bir web uygulaması(Asp.NET Core MVC) olacaktır. Şimdi ilgili projeyi oluşturalım(Projeye OnlineBankamatik adını veriyorum) ve temel konfigürasyonlarını yapalım. Ardından ilgili client’ta kimlik doğrulama ve cookie işlemleri için Microsoft.AspNetCore.Authentication.OpenIdConnect kütüphanesini yüklememiz gerekmektedir. Neden bu kütüphaneyi yüklüyoruz? diye sorduğunuzu duyar gibiyim… Biliyorsunuz ki, OpenID Connect kimlik doğrulama operasyonunu üstlenen bir katmandır. Dolayısıyla kimlik doğrulama yapılacağı noktada bu kütüphanenin yüklenmesi zaruridir.
    IdentityServer4 Yazı Serisi #8 - Authorization Code Grant(Flow)

    Bildirilen paketi yükledikten sonra ‘Startup.cs’ dosyasında aşağıdaki gibi Cookie ve OpenId konfigürasyonlarını yapmamız gerekmektedir.

    Access token client türüne göre token yahut cookie olarak elde edilecektir. Client’ın türü web uygulaması(MVC) ise cookie, server side(SPA, Mobile vs.) ise token olacaktır.

        public class Startup
        {
            public void ConfigureServices(IServiceCollection services)
            {
                .
                .
                .
                services.AddAuthentication(_ =>
                {
                    _.DefaultScheme = "OnlineBankamatikCookie";
                    _.DefaultChallengeScheme = "oidc";
                })
                .AddCookie("OnlineBankamatikCookie")
                .AddOpenIdConnect("oidc", _ =>
                {
                    _.SignInScheme = "OnlineBankamatikCookie";
                    _.Authority = "https://localhost:1000";
                    _.ClientId = "OnlineBankamatik";
                    _.ClientSecret = "onlinebankamatik";
                    _.ResponseType = "code id_token";
                });
                .
                .
                .
            }
            .
            .
            .
        }
    

    Yukarıda yapılan konfigürasyonu incelersek eğer; uygulamaya ‘AddAuthentication’ metodu ile şema isimleri herhangi bir değer olabilecek şekilde authentication servisi eklenmekte, ardından ‘AddCookie’ metoduyla cookie servisi ve yukarıda yüklediğimiz paket neticesinde gelen ‘AddOpenIdConnect’ metodu aracılığıyla da OpenIdConnect protokolü dahil edilmektedir. Her ne kadar authentication kısmında tanımlanan şemalar random bir isim olabilirsede, Cookie ve OpenIdConnect servislerinde kullanılan şemaların sırasıyla ‘DefaultScheme’ ve ‘DefaultChallengeScheme’ ile birebir aynı olmasına özen gösteriniz.

    OpenIdConnect metodu içerisinde değer atanan alanlara gelirsek eğer;

    • SignInScheme : SignIn işleminin yapacağı şemayı temsil eder. ‘AddAuthentication’ metodundaki ‘DefaultScheme’ ile aynı olmalıdır.
    • Authority : Yetkinin kimden alındığını tutar. ‘AuthServer’ın adresini belirtiyoruz.
    • ClientId : Bu client’ın ‘AuthServer’da ki Client_Id karşılığı.
    • ClientSecret : Bu client’ın ‘AuthServer’da ki Client_Secret karşılığı.
    • ResponseType : Oluşturulacak Authorization Code içerisinde bulunmasını istediğimiz dataları belirtiyoruz. ‘code’, üretilecek olan authorize kodu ifade ederken, ‘id_token’ ise access token’ın bize ait olan Auth Server’dan gelip gelmediğini test etmek için üretilen bir değerdir. Bu konuyu sonraki makalelerimizde salt başlık olarak değerlendireceğiz.
  • Adım 9
    Şimdi bir adım önce oluşturulan client’ın bilgilerine ‘AuthServer’da karşılık gelecek olan client’ı tasarlayalım. Bunun için ‘Config.cs’ dosyasında yazmış olduğumuz ‘GetClients’ metodu içerisinde aşağıdaki kodu geliştirelim;

            public static IEnumerable<Client> GetClients()
            {
                return new List<Client>
                {
                             .
                             .
                             .
                    new Client
                            {
                                ClientId = "OnlineBankamatik",
                                ClientName = "OnlineBankamatik",
                                ClientSecrets = { new Secret("onlinebankamatik".Sha256()) },
                                AllowedGrantTypes = GrantTypes.Hybrid,
                                AllowedScopes = { IdentityServerConstants.StandardScopes.OpenId, IdentityServerConstants.StandardScopes.Profile },
                                RedirectUris = { "https://localhost:4000/signin-oidc" },
                                RequirePkce = false
                            }
                };
            }
    

    Yukarıdaki client’a dair oluşturulan kodlara göz atarsak;

    ‘AllowedGrantTypes’ property’sine atanan ‘GrantTypes.Hybrid’ değeri ile client’ın ‘ResponseType’ında belirttiği ‘code’ ve ‘id_token’ değerlerini alabilecek bir grant type bildirilmektedir. Bir önceki adımda bildirdiğimiz gibi ‘code’ esasında ‘Authorization Code Grant’a karşılık gelmekte ve sadece authorization code’u temsil etmektedir. ‘code id_token’ gibi birden fazla ifadenin bir araya gelmiş hali ise Hybrid olarak isimlendirilmekte ve burada ‘id_token’ access token’ın bize ait Auth Server’dan geldiğini doğrulayacak kodu temsil etmektedir. Haliyle ‘GrantTypes.Hybrid’ diyerek bu client’ın authororization code ile birlikte access token’ı elde etmek istediği bildirilmektedir. Bir önceki adımda bahsedildiği gibi bu Response Type konusu yazı serimizin devamında salt başlık olarak detaylıca incelenecektir.

    Client’ın kullanıcı hakkında elde edebileceği bilgileri ise ‘AllowedScopes’ property’si üzerinden bildirilmektedir.

    ‘RedirectUris’ property’sine ise /signin-oidc adresi verilerek client’ın yetkilendirme neticesinde hangi sayfasına yönlendirileceği bildirilmektedir. Burada /signin-oidc adresinin nereden kaynaklandığını ve ne zaman oluşturulduğunu merak edebilirsiniz. Bu OpenIdConnect paketi tarafından ilgili client’ta oluşturulan hali hazırda bir adrestir. ‘AuthServer’, IdentityServer4 framework’ü sayesinde client’ta ki bu adrese dönüş yapacağını varsayılan olarak bilmekte ve üretilen token yahut cookie gibi değerleri bu adres üzerinden servis etmektedir.

    Ve son olarak ‘RequirePkce’ property’sini ‘false’ yaparak ileride göreceğimiz (Proof Key For Code) yapılanmasını devre dışı bırakıyoruz. Bu yapılanma client secret ve client id değerlerinin çalınmasına karşı alınmış bir güvenliksel stratejik önlemdir.

  • Adım 10
    Client’ta kullanıcı için ‘BankamatikController’ isminde farazi bir controller tasarlayalım ve aşağıdaki gibi ‘OdemeYap’ action’ını ‘Authorize’ ederek yetkisiz erişimi engelleyelim.

        public class BankamatikController : Controller
        {
            public IActionResult Index()
            {
                return View();
            }
            [Authorize]
            public IActionResult OdemeYap()
            {
                return View();
            }
        }
    
  • Adım 11
    Ve son olarak geliştirilen bu client’da authentication ve authorization middleware’lerini çağıralım.

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

İşte bu kadar… 🙂

Örnek Uygulama Testi

Şimdi geliştirdiğimiz bu örnek uygulamayı topyekün ayağa kaldırarak tam teferruatlı test edelim.
IdentityServer4 Yazı Serisi #8 - Authorization Code Grant(Flow)
Yukarıdaki ekran görüntüsünden de görüldüğü üzere testimiz başarıyla sonuçlanmış ve client authorization code’u cookie olarak almış bulunmaktadır. Dikkat ederseniz user, ‘OdemeYap’ action’ına erişmeye çalıştığı vakit ‘Authorize’ attribute’u sayesinde yetkisiz kullanıcı olduğu anlaşılmakta ve ‘AuthServer’ adresindeki ‘login’ sayfasına aşağıdaki adres üzerinden yönlendirilmektedir.(Ne yazık ki ekran görüntüsünü alırken kullanılan Opera tarayıcısı query string değerlerini görmemizi engellemektedir.)

https://localhost:1000/Account/Login?ReturnUrl=%2Fconnect%2Fauthorize%2Fcallback%3Fclient_id%3DOnlineBankamatik%26redirect_uri
%3Dhttps%253A%252F%252Flocalhost%253A4000%252Fsignin-oidc%26response_type%3Dcode%2520id_token%26scope%3Dopenid%2520profile%26response_mode%3Dform_post%26nonce%3D637396
630618971508.ZWEyNjYzNTAtMmYyNS00OGEyLThhOGEtMjY4ZDk1ZjlkMjU1NGM4OWQ0MmMtMGEz
OC00MzhlLWEyNTEtMWIxYjJmNmNiYzAx%26state%3DCfDJ8MUlvpM_ov1Il6SbMbgfypVc7I_
Cgt3Ld5pnDRb1gWJfV371PJnQKk_UcLTSqYcxHgXn4gsPZl3Y3GV-VaDCSYkT8a9poiM1nPvMRprJzoxy_4EhZvkZRQR7JEFIp7K5HLT7Q_lEj5Zsm4Ead02luApp9bE8BR_6YyhMchH7u7c2amvUEgH
sZ65l0-zQH6jqGg2BXaLKK-hoqSXYGFpCMRgveuVIgyomL1P9wM7nAJ2Al8T9PmXlvXvOLCBrPyHfuOca1-DZ9MDiNgFps9TO0YJ82r5qJrYhv81XUq2qFgXE-hC6Aiz0o
213O9R5JkuoIw%26x-client-SKU%3DID_NETSTANDARD2_0%26x-client-ver%3D5.5.0.0

Kullanıcı bilgilerini giren kullanıcı onaylandıktan sonra tekrar ‘AuthServer’ tarafından ilgili client’a ‘idsrv’ adı altında bir cookie ile yönlendirilmekte ve erişim sağlanmaktadır.

Evet… Böylece client ‘AuthServer’dan yetki(authorization code) almış bulunmaktadır. Bundan sonra bu authorization code’u kullanarak kullanıcıya dair claim’leri okuyabilir, access token, refresh token, id token vs. gibi bilgileri elde edebilir ve API’lara isteklerde bulunabiliriz.

Tabi ki de bu işlemleri sonraki makalelere bırakıyorum 🙂

İlgilenenlerin faydalanması dileğiyle…
Sonraki yazılarımda görüşmek üzere…
İyi çalışmalar…

Not : Örnek uygulamayı indirmek için buraya tıklayınız.

Bunlar da hoşunuza gidebilir...

2 Cevaplar

  1. mehmet dedi ki:

    client uygulamasında yönlendirme olmadan doğrudan yetkilendiriliyor. sebebi ne olabilir?

  1. 16 Kasım 2020

    […] Yazı Serisinin onbirinci makalesi olan Merkezi Üyelik Sistemi Temelleri başlıklı içeriğimizin 5. adımında Identity Resource üzerine odaklanmıştık ve […]

Bir cevap yazın

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir