ASP.NET Core Identity: Token-Based Authentication

asp.net core identity api

ASP.NET Core Identity kullanıcılar üzerinde Authentication ve Authorization işlemlerini yürüten bir üyelik sistemidir. Cookie-Based Authentication ve Token-Based Authentication kimlik doğrulama işlemlerini gerçekleştirebiliriz. Bu yazıda Token-Based Authentication implementasyonu yapılacaktır.

Token Based Authentication via JWT

Görselde istemciler, korunan servisler ve token dağıtmakla yükümlü bir Authorization Server görülmektedir. İstemcilerden biri üyelik gerektirmekte ve diğeri gerektirmemektedir. Üyeliğe tabi istemciler bu servislerle iletişime geçmek istediklerinde authorization server üzerinden kullanıcı credential bilgileriyle geçerli birer access token ve refresh token almaktadır. İstemciler bu servislerle access token üzerinen haberleşerek token ömrü dolduğunda refresh token ile yeni bir token almaktadırlar. Servisler koruma altına alınarak dış dünyaya kapalı hale getirildiğinden üyeliğe tabi olmayan istemci bu servislere erişemeyecektir. Bu işlem için istemciler auth server üzerinden ClientId ve ClientSecret bilgileriyle valid bir token almalıdırlar.

Authorization Server

Öncelikle webapi türünden bir proje oluşturarak authorization server‘ı yazmaya başlayalım. Identity API kullanılacağından Entity Framework Core implementasyonu Microsoft.AspNetCore.Identity.EntityFrameworkCore ve Bearer Token kullanarak servislerimizle haberleşeceğimiz için Microsoft.AspNetCore.Authentication.JwtBearer ve verilerimizi SQL Server üzerinde depolayacağımız için Microsoft.EntityFrameworkCore.SqlServer NuGet paketlerini kuralım.

Kullanıcı, refresh token ve DbContext sınıflarımızı oluşturalım.

DbContext sınıfımızı servis olarak ekleyelim. Identity mekanizmasınıda ekleyerek yapılandıralım ve Entity Framework Core kullanacağımızı belirtelim.

Gerekli yapılandırmaları yaptığımıza göre yetkilendirme aşamasına geçebiliriz. Token ayarlarını appsettings.json dosyasında tutacağız.

Token bilgilerini içeren issuer (iss), audiences (aud), security key ve token ömürleri (exp) belirtilmiştir. Bu bilgiler doğrultusunda yetkilendirme için gerekli servis ayarlamalarını yapalım.

Bir uygulamada birden fazla Authentication Handler kullanmak mümkündür. Çeşitli yetkilendirmeler için farklı isimlerde farklı cookie yapılandırılabilir. Uygulama aynı anda cookie ve token ile yetkilendirmeye tabi olabilir. Bu gibi durumlar için hangi Scheme‘in kullanılacağı belirtilmelidir. Authentication Scheme bilgileri AddAuthentication methodu içerisinde ayarlanmıştır. Scheme bilgisine Bearer değerine sahip AuthenticationScheme değeri atanmaktadır.

Bearer scheme kullanılarak Jwt-Bearer Authentication mekanizması AddJwtBearer methoduyla eklenmektedir. JwtBearerOptions ile Bearer Authentication Handler yapılandırılmaktadır. Token doğrulama işlemleri için TokenValidationParameters kullanılmaktadır.

  • ValidIssuer gelen jetonun kim tarafından oluşturulduğunu,
  • ValidateIssuer jeton doğrulanırken issuer alanının da doğrulanacağını,
  • ValidAudience gelen jetonun kim için oluşturulduğunu,
  • ValidateAudience jeton doğrulanırken audience alanının da doğrulanacağını,
  • IssuerSigningKey jeton imzasını doğrulayacak security key‘i,
  • ValidateIssuerSigningKey imzalı jetonun security key kullanılarak doğrulanacağını,
  • ValidateLifetime jeton doğrulanırken ömrünün de doğrulanacağını,
  • ClockSkew jeton ömrü dolduğunda zaman dilimi farklılıklarından dolayı belirtilen süre kadar daha geçerli olacağı belirtilmektedir.

Doğrulama işleminin nasıl yapılacağı ayarlanmıştır. Dikkat edilirse audience alanına issuer (yani kendisi) atanmıştır. Bu server ileride token dağıtırken aynı zamanda dışarıya açacağı ufak bir servisle token doğrulama işlemi de yapacağı için bu şekilde ayarlanmıştır.

Sonrasındaysa jetonun doğrulanması, yetkilendirmenin başarısız olması gibi olaylarda çalışması istenen iş parçacıkları belirtiliyor. Yetkilendirme mekanizması UseAuthentication methoduyla middleware olarak eklenmelidir.

Access Token

Artık kullanıcılar giriş yaparak geçerli bir access token ve refresh token edinebilirler.

Jeton oluşturulurken kullanıcıların sahip olması istenen claim listesi oluşturuluyor.

Jeton imzalanırken kullanılacak bilgiler SigningCredentials sınıfı örneklenerek belirtilmektedir. Bu bilgiler jetonun nasıl imzalanacağı ve imzalanırken hangi şifreleme algoritmasının kullanılacağıdır. Jeton simetrik olarak şifreleneceğinden SymmetricSecurityKey sınıfıyla bir simetrik anahtar oluşturulmakta ve şifreleme algoritması Sha256 olarak belirtilmektedir.

Sonrasındaysa token oluşturulurken ihtiyaç duyulan bilgiler (issuer, audience, claims gibi) JwtSecurityToken sınıfı yapılandırıcısına verilerek örneklenmektedir. Böylece JwtSecurityTokenHandler sınıfı üzerinden bir access token oluşturulmaktadır.

Refresh token üretecek methodumuzu yazalım.

Yukarıda tekrarlanması neredeyse imkansız olan bir değer RandomNumberGenerator sınıfı üzerinden oluşturularak refresh token üretilmektedir.

Kullanıcıları giriş yaptıracağımız action methodumuzu yazalım.

Öncelikle kullanıcıyla ilgili bir takım kontroller yapılmaktadır. Sonrasındaysa kullanıcı üzerinden bir claim listesi GetClaims methoduyla oluşturulmaktadır. Bu claim listesi CreateAccessToken methoduna argüman olarak verilerek bir access token üretilmektedir. Sonraki satırlarda veritabanında öncesinde kullanıcıya bir refresh token üretilip üretilmediği kontrol edilmekte ve CreateRefreshToken methoduyla bir refresh token üretilerek kullanıcıya dönülmektedir.

Access token ömrü dolduğunda refresh token ile yeni token üretecek action methodunu yazalım.

Gönderilen refresh token veritabanından çekilerek ömrü kontrol edilmektedir. Sonrasındaysa oluşturulan claim listesi doğrultusunda yeni bir access token üretilerek kullanıcıya dönülmektedir.

Api Servisleri

Senaryomuza göre üç adet servisimiz olacaktır. Servislerimizi oluşturarak gerekli Microsoft.AspNetCore.Authentication.JwtBearer NuGet paketini kuralım.

Sonrasındaysa token ayarlarını appsettings.json dosyasında saklayalım.

Audience alanına her servis için sırasıyla discountapi, orderapi ve productapi gelecek şekilde ayarlayalım. Sonrasındaysa yetkilendirme için gerekli servis ayalarmalarını authorization server‘da olduğu gibi tüm servislere uygulayalım.

DiscountsController sınıfını aşağıdaki şekilde dolduralım.

OrdersController sınıfını aşağıdaki gibi dolduralım.

Son servisimizin ProductsController sınıfını da dolduralım.

Yukarıda tüm servisler koruma altına alınmıştır. Bu servislere erişmeye çalıştığımızda 401 durum kodunu alacağız. Servis kaynaklarına erişebilmek için öncelikle authorization server üzerinden geçerli credential bilgileriyle geçerli access token edinmemiz gerekmektedir. Sonrasındaysa göndereceğimiz isteklerin Authorization header başlığına Bearer token eklenmelidir.

Client Authentication

Üyeliğe tabi olmayan client uygulamalarının authorization server üzerinden ClientId ve ClientSecret bilgileriyle geçerli bir token almasını sağlayarak servislere erişmelerine imkan verelim. Öncelikle appsettings.json dosyasında istemcilerimizi tanımlayalım. Bu bilgiler istenirse veritabanında da saklanabilir.

Görüldüğü üzere client bilgileri ve hangi servislere istek yapabilecekleri Audiences alanında tanımlanmıştır.

Client uygulamalarının sahip olması istenen claim listesi oluşturuluyor. Buna göre jetonun kim için oluşturulduğu (sub), hedef kitleyle (aud) hangi servislere erişebileceği ve jeton için unique identifier (jti) tanımlanmıştır. Access token üretecek method yukarıda kullanılan methodla aynıdır. Claim listesini parametresi üzerinden almakta ve bu yüzden iki işlem için ortaklaşa kullanılmaktadır.

Yukarıda client uygulamalarının geçerli bir token alacağı action method yazılmıştır. İstemci bilgileri Options Pattern kullanılarak appsettings.json dosyasından okunmaktadır. Gelen bilgilerle eşleşen bir client mevcutsa access token üretilerek istemciye dönülmektedir.

Client uygulamalar elde ettiği bu token ile servislere artık erişim sağlayabilir.

Geliştirilen proje dosyalarına GitHub üzerinden ulaşabilirsiniz.

You may also like...

Bir cevap yazın

E-posta hesabınız yayımlanmayacak.