IdentityServer4 Yazı Serisi #4 – Cleam Bazlı Yetkilendirme

Merhaba,

IdentityServer4 Yazı Serisinin üçüncü makalesi olan Client Credentials başlıklı makalede IdentityServer4’ün pratiksel temelleri eşliğinde işlevsel mekanizmasına değinmiş ve bir yandan da sadece client’ları yetkilendirme merkezli(Client Credentials) temel bir uygulama geliştirmiştik. Bu içeriğimizde ise yetkilendirmeyi bir adım daha ilerletecek ve claim merkezli yetkilendirme gerçekleştireceğiz. Tabi konumuz bir bütün olan yazı serisinin küçük bir parçası olacağından dolayı bir önceki makalede geliştirdiğimiz uygulama üzerinden devam niteliğinde anlatım sergileyeceğiz.

Claim Nedir?

Öncelikle claim bazlı yetkilendirmenin ne olduğuna değinerek başlayalım. Bunun için claim’i tanımlamamız gerekmektedir. Asp.NET Core Identity – Yazı Dizisini okuyanlar bilir ki, Claim Bazlı Kimlik Doğrulama başlıklı makalemizde claim’in ne olduğunu detaylarıyla açıklamış bulunmaktayız. Hatta ilgili makaleden konuya dair alıntı yaparsak eğer;

kullanıcı hakkında key – value şeklinde hususi bilgiler tutan ve bunları bizlere yaptığımız talepler neticesinde getiren …

şeklinde tanımlama yapılmaktadır.

İşte buradaki claim’i IdentityServer4 için kullanıyorsak eğer altı çizili olan kullanıcı yerine client(istemci) kelimesini koymamız yeterli olacak ve şöyle bir anlam karşımıza çıkacaktır.

Claim; IdentityServer4 mimarisinda client hakkında key – value şeklinde hususi bilgiler tutan bir yapılanmadır. Yapılan taleplerde, client’tan gelen claim’ler Auth Server tarafından elde edilmekte ve işlevsel açıdan değerlendirilmektedir. Buradaki işlev genellikle client’ı yetkilendirmek, yetki alanlarını belirlemek üzere şekillenecektir.

IdentityServer4’te Claim Nasıl Tanımlanır?

Bu sorunun cevabını esasında bir önceki makalemizde(bknz: Client Credentials) vermiş bulunmaktayız. İlgili makaleye göz atarsanız eğer Auth Server uygulaması için yapılan konfigürasyon(Config.cs) tanımlamasında client’lara yerleştirilen ‘AllowedScopes’ alanı eşliğindeki değerler, oluşturulacak JWT’ye ‘scopes’ key’i karşılığında payload olarak eklenmektedirler. Böylece client’a ait token değeri zaten, içerisinde claim değerleri eklenmiş vaziyette elde edilmektedir.

Bu içeriğimizde, client’ı bu scope değerlerine(claim) göre yetkilendirmeyi amaç edinmekteyiz. Bunun için direkt olarak API’lar da politikalar oluşturmamız yeterli ve yerinde olacaktır.

API(lar)’da Claim Bazlı Politika(lar) Oluşturma?

API uygulamasının ‘Startup.cs’ dosyasındaki ‘ConfigureServices’ metodunda aşağıdaki gibi politikalar belirleyebilirsiniz.

    public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
                    .AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options =>
                    {
                        options.Authority = "https://localhost:1000";
                        options.Audience = "Garanti";
                    });

            services.AddAuthorization(_ =>
            {
                _.AddPolicy("ReadGaranti", policy => policy.RequireClaim("scope", "Garanti.Read"));
                _.AddPolicy("WriteGaranti", policy => policy.RequireClaim("scope", "Garanti.Write"));
                _.AddPolicy("ReadWriteGaranti", policy => policy.RequireClaim("scope", "Garanti.Write","Garanti.Read"));
                _.AddPolicy("AllGaranti", policy => policy.RequireClaim("scope", "Garanti.Admin"));
                _.AddPolicy("ReadHalkBank", policy => policy.RequireClaim("scope", "HalkBank.Read"));
                _.AddPolicy("WriteHalkBank", policy => policy.RequireClaim("scope", "HalkBank.Write"));
                _.AddPolicy("ReadWriteHalkBank", policy => policy.RequireClaim("scope", "HalkBank.Write", "HalkBank.Read"));
                _.AddPolicy("AllHalkBank", policy => policy.RequireClaim("scope", "HalkBank.Admin"));
            });
        }
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            .
            .
            .
            app.UseAuthentication();
            app.UseAuthorization();
            .
            .
            .
        }
    }

Artık uygulamada belli başlı işlemleri tarif eden politikalar mevcuttur. Yukarıdaki politikalara göz atarsanız eğer gelen istekteki token değeri içerisinde ‘scope’ alanındaki değerin karşılığını farklı değerler ile şart koşan birden fazla politika inşa edilmiştir. Eğer ki bir politika ‘RequireClaim’ ile zorunluluğunu ifade ediyorsa burada claim bazlı bir politika söz konusudur. Tabi ki de burada çok daha farklı türlerde politikalarda inşa edilebilir. Lakin bizler konuyu fazla dağıtmamak ve içerik olarak IdentityServer4’te ki authorization mantığını en sade haliyle aktarabilmek için bu ufak politikalar eşliğinde yolumuza devam edelim.

Action’ları Politikalarla Yetkilendirme

Şimdi hangi action’ın hangi politikayı benimseyeceğini bildirmemiz gerekmektedir. Bunun için aşağıdaki gibi ‘Authorize’ attribute’u ile çalışılması yeterlidir;

    [Route("api/[controller]/[action]")]
    [ApiController]
    [Authorize]
    public class GarantiBankController : ControllerBase
    {
        [HttpGet("{musteriId}")]
        [Authorize(Policy = "ReadGaranti")]
        public double Bakiye(int musteriId)
        {
            //....
            return 1000;
        }
        [HttpGet("{musteriId}/{tutar}")]
        [Authorize(Policy = "AllGaranti")]
        public double YatirimYap(int musteriId, double tutar)
        {
            return tutar * 0.5;
        }
        .
        .
        .
    }

Burada ‘Authorize’ attribute’u, ilgili action’a erişim yetkisini sınırlandırırken biryandan da ‘Policy’ property’sine bildirilen politika ismi ile ilgili politikanın şartlarının doğrulanmasını da zorunlu hale getirmektedir. Evet, ilgili action’lar belirtilen politikalardaki şartı sağlayan client’lar tarafından tetiklenecektir. Böylece sadece token’ın var olması yeterli olmayacak bir yandan da taşıdığı claim değerleri içerisinde buradaki ‘scope’ gerekliliğini sağlayacak olan değeri de taşıyor olması gerekecektir.

Test

Auth Server dahil tüm API’ları ayağa kaldırarak, client olarak Postman eşliğinde aşağıdaki gibi bir test gerçekleştirelim;

Client JWT olmaksızın istek gönderdiğinde IdentityServer4 Yazı Serisi #4 - Cleam Bazlı Yetkilendirme

IdentityServer4 Yazı Serisi #4 - Cleam Bazlı Yetkilendirme

‘Garanti.Read’ ve ‘Garanti.Write’ yetkilerine(scope) sahip olan ‘GarantiBankasi’ client’ına ait bir JWT elde etme IdentityServer4 Yazı Serisi #4 - Cleam Bazlı Yetkilendirme
JWT ile istek gönderme IdentityServer4 Yazı Serisi #4 - Cleam Bazlı Yetkilendirme
Görüldüğü üzere sadece ‘Garanti.Read’ yetkisini gerektiren ‘ReadGaranti’ politikasını uygulayan ‘Bakiye’ action’ı token ile yapılan talebi doğruladı ve çalıştı.
IdentityServer4 Yazı Serisi #4 - Cleam Bazlı Yetkilendirme
Halbuki ‘Garanti.Admin’ yetkisini gerektiren ‘AllGaranti’ isimli politikayı uygulayan ‘YatirimYap’ action’ı, request içerisindeki token’da ilgili yetkiye karşılık gelen bir scope olmadığı için isteği onaylamadı ve 403 hata kodu döndürdü. 403 hata kodu 401’e nazaran; token’ın var lakin yetkin/scope yok/yetersiz anlamına gelmektedir.

Netice olarak claim bazlı yetkilendirme, claim bazlı politika tasarlamamızı ve ilgili controller ya da action’ların bu politikalar ile davranışlarını şekillendirmemizi gerektiren ve böylece token’dan ziyade, token’ı var olan client’lar arasında yetki ayrımını gerçekleştirmemizi sağlayan bir yapılanmadır. Tasarımsal açıdan sizlerin de taktir edeceği gibi kurumsal yapılanmalarda ihtiyaçlara binaen oldukça efektif çözümler getirmemizi sağlayan olmazsa olmaz tekniklerimizden birisidir diyebiliriz…

İ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...

1 Cevap

  1. 21 Ekim 2020

    […] IdentityServer4 Yazı Serisi #4 – Cleam Bazlı Yetkilendirme […]

Bir cevap yazın

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

*