Asp.NET Core 3.1 ile Token Bazlı Kimlik Doğrulaması ve Refresh Token Kullanımı(JWT)
Merhaba,
Günümüz web uygulamalarında veri güvenliğini sağlayabilmek için kullanıcı doğrulama yöntemleri arasından en etkili ve çağdaş olanlarından kabul edebileceğimiz Token Bazlı Kimlik Doğrulama yöntemi ile sistemsel ve kullanıcı açısından en az maliyette en yüksek verimi elde edebilmekte ve güvenli bir şekilde gerekli authorization işlemlerini gerçekleştirebilmekteyiz.
Bu makalemizde Asp.NET Core uygulamalarında Token Bazlı Kimlik Doğrulama yapılanmasını a’dan z’ye inceleyeceğiz. Esasında daha önceden bu konu üzerine kâh Asp.NET mimarisi için olsun kâh Node.JS için olsun birçok makale karalamış bulunmaktayım. İlgili makaleleri aşağıda listelersek eğer;
- Asp.NET MVC – Web Api Token Authentication
- Angular İle Token Authentication Uygulanan Web API Tetikleme
- Ajax İle Token Authentication Uygulanan Web API Tetikleme
- JSON Web Token(JWT) Nedir?
- Node.js – JWT İle Token Oluşturma
- Node.js – JWT Token Kullanımı
- Asp.NET Core/Angular 7 – Web Api Token Authentication Kullanımı
Görüldüğü üzere konuya dair bloğumuzda önceden birçok kaynak mevcuttur. Peki hoca, madem konuya dair önceden karaladıkların var. Ne diye tekrar aynı konuda içerik oluşturuyorsun? sorunuzu duyar gibiyim. Biliyorsunuz ki son zamanlarda Asp.NET Core Identity üzerine detaylı yazı dizisi kaleme almaktayım. İşte bu yazı dizisinde authentication yöntemlerinden biri olarak yer alacak ve konuyu sıfırdan ele alıp ve adım adım kodlama standartlarını belirleyecek bir içerik ortaya koyma niyetindeyim. Ayriyetten diğer makalelerde bulamayacağınız terminolojilerle birlikte farklı tanımlamalar ve yöntemlerde ele alınmış olacaktır.
İşte bu niyetle yeni bir minvalde yol alacağız… O halde haydi gelin başlayalım.
Başlarken
Makalemize başlarken bir adet Asp.NET Core Web API projesinin hali hazırda bulunmasıyla birlikte gerekli authentication kontrolünde bizlere eşlik edecek olan aşağıdaki “Users” tablosunu tasarlamamız gerekmektedir.
public class User { public int Id { get; set; } public string Name { get; set; } public string Surname { get; set; } public string Email { get; set; } public string Password { get; set; } public string RefreshToken { get; set; } public DateTime? RefreshTokenEndDate { get; set; } }
Users tablosunu Code First yahut Database First yaklaşımlarından herhangi biriyle tasarlayıp, oluşturabilirsiniz.
Oluşturulan “Users” tablosuna tekrar göz atarsak eğer standart kolonların dışında “RefreshToken” ve “RefreshTokenEndDate” kolonları mevcuttur. Bu kolonların ne amaçla oluşturulduğuna değinebilmek için öncelikle üretilecek token yapılanmasıyla ilintili “Access Token” ve “Refresh Token” terimlerini açıklamamız gerekmektedir.
- Access Token
OAuth 2.0 protokolüne göre RFC 7519 standartında olan ve authorization neticesinde belirli bir expire süresine bağlı bir şekilde üretilip kullanıcıya sunulan güvenli bir anahtar değeridir. Anlayacağınız token’ın ta kendisidir 🙂 - Refresh Token
Access token’ın expire süresi sona ermeye yaklaştığında veya sona erdiğinde yeni bir access token üretebilmek için kullanılan token değeridir. Kullanıcıya verilen access token değeri yanında refresh token değeri verilerek, access token süresi dolduğu taktirde bu refresh token ile yeni token talebinde bulunabilecektir. Böylece kullanıcı token süresi dolduğu taktirde oturumdan düşürülmeden yeni token elde edebilecek ve yoluna devam edecektir.
Dikkat!!! Her zaman Refresh Token Expiration Access Token Expiration’dan fazla olmalıdır!
İşte “Users” tablosundaki “RefreshToken” kullanıcı için üretilmiş olan refresh token değerini tutacak olan kolondur. “RefreshTokenEndDate” kolonu ise üretilen refresh token değerinin işlev/kullanım süresini belirleyecek olan zaman bilgisini tutan alandır.
Token Servisinin Uygulamaya Eklenmesi
Asp.NET Core uygulamasında token ile authentication işlemlerini gerçekleştirebilmek için JWT Token servisini uygulamaya eklememiz gerekmektedir. Bunun için “Startup.cs” dosyasındaki “ConfigureServices” metodu içerisinde “AddAuthentication” middleware’i aracılığıyla bir şema oluşturarak işe başlamalıyız. İlgili metodu aşağıdaki görselde olduğu gibi çağırarak ikinci overloadına göz atarsak eğer;
görüldüğü üzere bir default scheme değeri istemektedir.
Peki bizden istenen bu default scheme değeri nedir?
Bir authentication işlemini farklı pozisyonlar için farklı şekilde tanımlamak isteyebiliriz.
Örneğin; ‘Bayi Authentication‘ – ‘Kullanıcı Authentication‘ gibi…
İşte bu mantıkta bir isimlendirmeyle farklı authentication şeması tanımlanabilir. Bizler ister benzer şekilde opsiyonel değerler tanımlayarak şema belirleyebilir yahut default şema ayarı vererek buradaki ihtiyaca daha spesifik çözüm getirebiliriz. Eğer ki tercihimiz default şema ayarı ise bunun için “Microsoft.AspNetCore.Authentication.JwtBearer” kütüphanesindeki “JwtBearerDefaults” sınıfını kullanmamız gerekecektir.
Velhasıl, JWT Token servisini uygulamaya entegre edersek;
public class Startup { public Startup(IConfiguration configuration) { Configuration = configuration; } public IConfiguration Configuration { get; } public void ConfigureServices(IServiceCollection services) { services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(option => { option.TokenValidationParameters = new TokenValidationParameters { ValidateAudience = true, ValidateIssuer = true, ValidateLifetime = true, ValidateIssuerSigningKey = true, ValidIssuer = Configuration["Token:Issuer"], ValidAudience = Configuration["Token:Audience"], IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Token:SecurityKey"])), ClockSkew = TimeSpan.Zero }; }); . . . services.AddControllers(); } }
şeklinde çalışma yapmamız yeterli olacaktır. Burada dikkat ederseniz “Audience”, “Issuer”, “LifeTime”, “SigningKey” ve “ClockSkew” kavramları mevcuttur. Bu kavramların ne olduğuna dair bir açıklama yapmamız gerekirse eğer;
- Audience
Oluşturulacak token değerini kimlerin/hangi originlerin/sitelerin kullanacağını belirlediğimiz alandır. Örneğin; “www.bilmemne.com” - Issuer
Oluşturulacak token değerini kimin dağıttığını ifade edeceğimiz alandır. Örneğin; “www.myapi.com” - LifeTime
Oluşturulan token değerinin süresini kontrol edecek olan doğrulamadır. - SigningKey
Üretilecek token değerinin uygulamamıza ait bir değer olduğunu ifade eden security key verisinin doğrulamasıdır. - ClockSkew
Üretilecek token değerinin expire süresinin belirtildiği değer kadar uzatılmasını sağlayan özelliktir. Örneğin; kullanılabilirlik süresi 5 dakika olarak ayarlanan token değerinin ClockSkew değerine 3 dakika verilirse eğer ilgili token 5 + 3 = 8 dakika kullanılabilir olacaktır. Bunun nedeni, aralarında zaman farkı olan farklı lokasyonlardaki sunucularda yayın yapan bir uygulama üzerinde elde edilen ortak token değerinin saati ileride olan sunucuda geçerliliğini daha erken yitirmemesi için ClockSkew propertysi sayesinde aradaki fark kadar zamanı tokena eklememiz gerekmektedir. Böylece kullanım süresi uzatılmış ve tüm sunucularda token değeri adil kullanılabilir hale getirilmiş olunacaktır.
Şimdi yönümüzü tekrar yukarıdaki kod bloğuna çevirerek 15 ile 22. satır arasındaki yapılanları inceleyelim;
- 15. satır; ‘ValidateAudience’ ile token üzerinde Audience doğrulamasını aktifleştirdik.
- 16. satır; ‘ValidateIssuer’ ile token üzerinde Issuer doğrulamasını aktifleştirdik.
- 17. satır; ‘ValidateLifetime’ ile token değerinin kullanım süresi doğrulamasını aktifleştirdik.
- 18. satır; ‘ValidateIssuerSigningKey’ ile token değerinin bu uygulamaya ait olup olmadığını anlamamızı sağlayan Security Key doğrulamasını aktifleştirdik.
- 19. satır; ‘ValidIssuer’ ile uygulamadaki tokenın Issuer değerini belirledik.
- 20. satır; ‘ValidAudience’ ile uygulamadaki tokenın Audience değerini belirledik.
- 21. satır; ‘IssuerSigningKey’ ile Security Key doğrulaması için SymmetricSecurityKey nesnesi aracılığıyla mevcut keyi belirtiyoruz.
- 22. satır; ‘ClockSkew’ ile TimeSpan.Zero değeri ile token süresinin üzerine ekstra bir zaman eklemeksizin sıfır değerini belirtiyoruz.
Ayriyetten yukarıdaki kod bloğunda kullanılan değerler için Configuration propertysi kullanılmaktadır. Yani bu ilgili verileri “appsettings.json” dosyasından okuyoruz anlamına gelmektedir. Haliyle örnek uygulamamızda verileri ilgili dosyada aşağıdaki gibi tuttuğumuzu ifade etmekte fayda var;
{ "Logging": { "LogLevel": { "Default": "Information", "Microsoft": "Warning", "Microsoft.Hosting.Lifetime": "Information" } }, "AllowedHosts": "*", "Token": { "Issuer": "www.myapi.com", "Audience": "www.bilmemne.com", "SecurityKey": "doldur be meyhaneci, boş kalmasın kadehim ..." } }
Token Handler Sınıfını Oluşturma
JWT Token servisini uygulamaya entegre ettikten sonra sıra Token değerini oluşturmaktan sorumlu olan Token Handler sınıfını inşa etmeye geldi.
İlk olarak üretilecek token ve refresh token değerlerini taşıyacak olan “Token” modelimizi tasarlıyoruz.
public class Token { public string AccessToken { get; set; } public DateTime Expiration { get; set; } public string RefreshToken { get; set; } }
Ardından Token Handler sınıfımızı inşa edebiliriz;
public class TokenHandler { public IConfiguration Configuration { get; set; } public TokenHandler(IConfiguration configuration) { Configuration = configuration; } //Token üretecek metot. public TokenAuthentication.Models.Token CreateAccessToken(User user) { Models.Token tokenInstance = new Models.Token(); //Security Key'in simetriğini alıyoruz. SymmetricSecurityKey securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Token:SecurityKey"])); //Şifrelenmiş kimliği oluşturuyoruz. SigningCredentials signingCredentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256); //Oluşturulacak token ayarlarını veriyoruz. tokenInstance.Expiration = DateTime.Now.AddMinutes(5); JwtSecurityToken securityToken = new JwtSecurityToken( issuer: Configuration["Token:Issuer"], audience: Configuration["Token:Audience"], expires: tokenInstance.Expiration,//Token süresini 5 dk olarak belirliyorum notBefore: DateTime.Now,//Token üretildikten ne kadar süre sonra devreye girsin ayarlıyouz. signingCredentials: signingCredentials ); //Token oluşturucu sınıfında bir örnek alıyoruz. JwtSecurityTokenHandler tokenHandler = new JwtSecurityTokenHandler(); //Token üretiyoruz. tokenInstance.AccessToken = tokenHandler.WriteToken(securityToken); //Refresh Token üretiyoruz. tokenInstance.RefreshToken = CreateRefreshToken(); return tokenInstance; } //Refresh Token üretecek metot. public string CreateRefreshToken() { byte[] number = new byte[32]; using (RandomNumberGenerator random = RandomNumberGenerator.Create()) { random.GetBytes(number); return Convert.ToBase64String(number); } } }
Yukarıdaki kod bloğunu incelerseniz eğer access token ile birlikte refresh token değerlerini üretecek bir sınıf tasarlamış bulunmaktayız. Burada “CreateRefreshToken” metodu içerisinde refresh token değeri için “RandomNumberGenerator” nesnesinin kullanıldığına dikkatinizi çekerim. Alternatif olarak burada isterseniz Guid tipinde bir değer dönebilir yahut farklı bir Token değeri oluşturup refresh token olarak kullanabilirsiniz.
Controller’ların Oluşturulması
İlk olarak kullanıcılardan gelecek olan login istekleri için “UserLogin” view model’ını oluşturalım.
public class UserLogin { public string Email { get; set; } public string Password { get; set; } }
Ardından kullanıcılar tarafından yapılacak olan kayıt yahut login isteklerini karşılayacak olan “Login(Controller).cs” dosyasını oluşturalım.
[ApiController] [Route("api/[controller]")] public class LoginController : ControllerBase { readonly TokenExampleContext _context; readonly IConfiguration _configuration; public LoginController(TokenExampleContext content, IConfiguration configuration) { _context = content; _configuration = configuration; } [HttpPost("[action]")] public async Task<bool> Create([FromForm]User user) { _context.Users.Add(user); await _context.SaveChangesAsync(); return true; } [HttpPost("action")] public async Task<TokenAuthentication.Models.Token> Login([FromForm]UserLogin userLogin) { User user = await _context.Users.FirstOrDefaultAsync(x => x.Email == userLogin.Email && x.Password == userLogin.Password); if (user != null) { //Token üretiliyor. TokenHandler tokenHandler = new TokenHandler(_configuration); TokenAuthentication.Models.Token token = tokenHandler.CreateAccessToken(user); //Refresh token Users tablosuna işleniyor. user.RefreshToken = token.RefreshToken; user.RefreshTokenEndDate = token.Expiration.AddMinutes(3); await _context.SaveChangesAsync(); return token; } return null; } }
Authorization işlemlerini test edebilmek için kullanıcı isteklerini karşılayacak “Test(Controller).cs” dosyasını oluşturalım.
[Authorize] [ApiController] [Route("api/[controller]")] public class TestController : ControllerBase { public string Index() { return "Yetkilendirme başarılı..."; } }
Yukarıdaki “Test(Controller).cs” sınıfını incelerseniz eğer “Authorize” attribute’u ile işaretlenerek yetkisiz erişim engellenir bir hale getirilmiştir.
Test Edelim
Sıra oluşturduğumuz yapılanmayı test etmeye gelmiştir. Tüm yapılanmayı Postman vasıtasıyla test edeceğiz.
İlk olarak kullanıcı oluşturalım.
Ardından “Test(Controller)” controller’ına bir istekte bulunalım.
Görüldüğü üzere ilgili istek neticesinde yetkisiz erişim yapmaya çalışıldığına dair “401 Unauthorized” durum kodunu döndürmektedir.
O halde token talebinde bulunalım.
Görüldüğü üzere token talebi neticesinde bizlere access token, expiration ve refresh token verilerini barındıran bir JSON verisi döndürülmüştür. Artık yapmamız gereken ilgili access token değerini kullanıp bir önceki “Test(Controller)” sınıfına istekte bulunmaktır.
Yukarıdaki ekran görüntüsünü incelerseniz eğer “Headers” kategorisinde “Authorization” keyine karşılık verilen “Bearer {token}” değeri ile yapılan istek neticesinde token değerimizin başarılı bir şekilde doğrulandığını görmekteyiz.
Unutmayın!!! Token bazlı kimlik doğrulama senaryosunda sistemin çalışması için “Startup.cs” dosyasındaki “Configure” metodu içerisinde ‘UseAuthorization’ middleware’ından önce ‘UseAuthentication’ middleware’inin çağrılması gerekmektedir. Aksi taktirde token ile yapılan istekler yine -401 Unauthorized- durum kodu olarak geri dönecektir.
Refresh Token Kullanımı
Üretilen token değerinin süresi bitmeden yahut bittiğinde kullanıcıyı yeniden login işlemleriyle meşgul etmeden gerekli yetkilendirmeyi tekrar sağlayabilmek için refresh token mekanizmasını kullanacağız. Bu mekanizma temelde aşağıdaki gibi bir stratejiden ibarettir.
Yukarıdaki görüntüde olduğu gibi client öncelikle gerekli authorization için Authorization Server’a bir istekte bulunuyor ve bu istek neticesinde “Access Token” ile birlikte “Refresh Token” değerlerini elde ediyor. Süreçte Resource Server’a yapılan tüm istekler access token aracılığıyla gerçekleştiriliyor… Taa ki access token’ın süresi bitip geçersiz oluncaya kadar… İşte bu durumda kullanıcıyı tekrardan login sayfasına yönlendirerek kullanıcı adı ve şifre kontrolü yapmak yerine kullanıcıya önceden gönderilmiş olan refresh token değeri ile Authorization Server’a bir istekte daha bulunuluyor ve tekrar yeni bir access token üretilerek hiç vakit kaybetmeksizin kullanıcıya bu yeni token değeri ile kaldığı yerden devam etme olanağı tanınıyor. Tabi bu arada refresh token kullanıldıktan sonra geçerliliğini yitirerek üretilen access token değerine eşlik edebilecek yenisi üretilmiş oluyor.
Bu mantıkta refresh token değerini kullanarak yeni bir access token üretebilmek için gerekli istekleri karşılayacak endpointi aşağıdaki gibi tasarlayabiliriz.
[ApiController] [Route("api/[controller]")] public class LoginController : ControllerBase { readonly TokenExampleContext _context; readonly IConfiguration _configuration; public LoginController(TokenExampleContext content, IConfiguration configuration) { _context = content; _configuration = configuration; } . . . [HttpGet("[action]")] public async Task<TokenAuthentication.Models.Token> RefreshTokenLogin([FromForm] string refreshToken) { User user = await _context.Users.FirstOrDefaultAsync(x => x.RefreshToken == refreshToken); if (user != null && user?.RefreshTokenEndDate > DateTime.Now) { TokenHandler tokenHandler = new TokenHandler(_configuration); TokenAuthentication.Models.Token token = tokenHandler.CreateAccessToken(user); user.RefreshToken = token.RefreshToken; user.RefreshTokenEndDate = token.Expiration.AddMinutes(3); await _context.SaveChangesAsync(); return token; } return null; } }
Görüldüğü üzere refresh token değeri ile yapılan istek neticesinde yeni bir access token değeri elde etmekte ve bu değeri aşağıdaki görselde olduğu gibi devam faaliyetlerimizde kullanabilmekteyiz.
Evet… Görüldüğü üzere Refresh Token ile birlikte Token Bazlı Yetkilendirme işlemi bu çabadan ibarettir diyebiliriz 🙂 Sabredipte son noktasına kadar okuduğunuz için teşekkür ederim.
İlgilenenlerin faydalanması dileğiyle…
Sonraki yazılarımda görüşmek üzere…
İyi çalışmalar…
Not : Örnek projeyi indirmek için buraya tıklayınız.
Merhabalar hocam
Jwt te header da token göndermemiz ile bir güvenlik sorunu olmuyormu ?.
Yani biri profiler dan bakım bizim api endpoint ve token degerimizi bildigi zaman api izinsiz kullanmış olmazmı. Bu konu hakkında nasıl bir çalışma ile jwt daha güvenli yapabiliriz ?.
Merhaba Recep
Sordugun sorunun cevabını bende merak ediyorum. Cevabını bulabilirsen banada haber verirsin..
caglargul52@outlook.com
Yanlış bilmiyorsam Startup dosyasında CORS ayarlarını yaparak sana istek gönderebilecek domainleri filtreleyebiliyorsun.
Merhabalar, Refresh Token Login yaparken Get methodunda body göndermişsiniz fakat Get işleminde body alınmıyor, Post olucaktı sanırım.
Merhaba,
‘refreshToken’ parametresi [FromForm] attribute’u ile işaretlenmiştir. Sanırım gözünüzden kaçmış 🙂
Hocam bu token bazlı kimlik doğrulama yaparken İdentity de olan AppUser :IdentityUser modelini kullanarak yapmaya çalışıyorum yapamadım bi türlü acaba yapılamıormu yapılıorsada bildiğiniz bi kaynak var
Sevgili okurların dikkatine;
İlgili makalede aşağıdaki middlewareleri eklemeyi unutmuşum. Bilginize 🙂
Sevgiler.
Hocam refresh token çürüme süresi olamsı doğrum mu ? refresh token da çürürse tekrar login ekranına dönmesi gerekmez mi uygulamanın ?
Evet, refresh token’da son buluyorsa artık kullanıcıyı login’e yönlendirip tekrar giriş yapmasını sağlamak gerekmektedir.
Merhaba benim anlamadığım bir yer var. Token süresi dolduğunda otomatik olarak RefreshToken kullanılarak yeni Token alınması gerekiyor. Bunu nerede yaptınız. RefreshTokenLogin var ama bu otomatik değil. Yada JWT bunu kendisimi yapıyor.
Merhaba,
İçerikte refresh token’ın mantığı ve teknik tasarımı ele alınmıştır lakin access token’ın süresi dolduğu vakit refresh token ile talepte bulunarak token’ı yenileme senaryosu artık kullanılan UI’da yapılan çalışmadaki stratejiye bırakılmıştır. Misal; jQuery ile yaptığınız istek neticesinde 401 hata koduyla error alırsanız, refresh token kullanılarak ilgili adrese istekte bulunabilir ve geçerli süre içerisindeyseniz access token’ı böylece yenileyebilirsiniz.
Kolay gelsin.
Merhaba Recep,
Genelde Login için gerekli kullanıcı adı ve şifre de açık bir şekilde istenmez. Belli bir algoritma ve key’e göre hashlenip eklenmesi istenir. Client hashleyerek gönderir ve karşı taraf bu hashi çözüp token verir.
Konu sadece token ile ilgili olduğundan olabildiğince basit tutulup sadece bu konu üzerine odaklanıldığını düşünüyorum.
merhaba,
mesela bir haftalık bir access token oluşturduk, bir hafta geçti ve süresi doldu. mobil uygulamada da hala kullanıcı giriş yapılmış şekilde karşılanmalı. refresh token süresi ise access token’dan yalnızca 3 dakika fazla. access token bittiği andan itibaren kullanıcı sunucuya herhangi bir istek atmazsa 3 dakika sonra refresh token’ın da süresi bitecek demek oluyor. bu durumda nasıl kullanıcı girişi yapılmış halde kalabilir? (refresh token süresi +3 dakikadan fazla da olabilir yine de o süre içinde kullanıcının istek atmama ihtimali var)
Merhaba,
Ben ne şekilde bir ihtiyaca binaen probleminiz olduğunu ne yazık ki tam olarak anlayabilmiş değilim. Access token süresi bittiğinde kullanıcıyı yormaksızın token değerini yenileyebilmek ve oturumdan düşürmemek için refresh token yapılanması kullanılmaktadır. Ee doğal olarak verilen süreler çerçevesinde kullanıcı tarafında herhangi bir hayat belirtisi yoksa ilgili tokenların etkinliğini yitirmesi en doğalı olacaktır. Refresh token süresinden daha fazla kullanıcıyı oturumda tutmayı istemek birnevi refresh token yapılanmasını gölgeleme zarureti doğuracağından dolayı, sualinize anladığım kadarıyla önerim, refresh token kullanmaksızın 60 günlük, 6 aylık vs. gibi süresi uzun bir access token üretmeniz ve sadece onu kullanmanızdır.
Sevgiler.
cevabınız için teşekkür ederim, aslında şunu söylemek istiyorum
eğer ürün bir mobil uygulama ise ve bir kullanıcı haftada bir uygulamaya bakıyorsa (tüm kullanıcılar bu şekilde olmak zorunda değil bazısı daha sık bakıyordur) sunucuya o kullanıcıdan haftada bir istek geliyor
token 3 günlük olduğunu varsayalım 3 gün sonra token expire oldu
3gün+3 dakika sonra refresh token da expire oldu
kullanıcı bir hafta sonra girdiğinde tekrar oturum açması mı gerekiyor?
bu bir mobil uygulama için pek doğru bir senaryo olarak gelmedi bana refresh token’ın bu kadar kısa süreli olmasını pek anlayamadım belki örneğin bir banka uygulaması için doğru olabilir
dediğiniz gibi uzun süre kullanıcıyı oturumda tutmak isteyen bir uygulamanın herhangi bir koşulda refresh token’a ihtiyacı olmaz olarak anlayabilir miyim?
Tekrar merhaba,
Esasında JWT ile refresh token’ı mobil veya PC senaryosu diye ayırmak yanlış bir yorum olacaktır. JWT ile refresh token kullanıcıya kolaylık sağlaması için uygulanan mantıksal bir manevradır, algoritmadır. Kullanılacağı yer vardır, kullanılsa dahi pek yakışmayacağı yerler de vardır. Lakin bir kullanıcının herhangi bir platformdan 1 hafta sonra girdiğinde tekrar oturum açması sizin için problemse bunu JWT ile refresh token’dan bağımsız bir şekilde ele alıp ona göre geliştirme yapmanız sizce de daha doğru olmayacak mıdır?
Nasıl ki, 10 tane 3’ün toplamı direkt olarak 3 + 3 + 3 + … + 3 şeklinde değilde 3 x 10 olarak hesaplamak işin doğasındansa benzer mantıkla refresh token’ında access token’dan biraz uzun olması bu stratejinin/mantığın/algoritmanın doğasındandır. Ve nasıl ki, ilk örneği anlayabilmek için toplamayı ve toplamanın nedenini anlamak gerekiyorsa, aynı şekilde ikinci vurgudaki refresh token için refresh token’ı ve nedenini anlamak gerekmektedir.
Sözgelimi, dillendirdiğiniz senaryoda refresh token’ın mantığına aykırı bir problem görmemekteyim. Sanırım siz, çözüm getiremediğiniz noktalar çerçevesinde refresh token’ın kısalığına takılmışsınız 🙂 Bu durumda platformu ayırt edebilir bir kod geliştirerek ona göre access token ve refresh token üretebilmeniz yahut refresh token’ı istediğiniz kadar uzatma hüvviyetine sahip olmanız sizin için yeterli değil mi?
Uzun lafın kısası demek istediğim ‘kuralları siz koyun, yazılım aracınız olsun‘ 😉
Çok elzem olmasada yinede basit bir öneride bulunmamız gerekirse;
Sevgiler.
teşekkür ederim
selamlarımla
30 dakika accesstoken süresi olsun ,
3 dk da refreshtoken süresi olsun. kullanıcı 30 ve 33. dakikalar arasında tekrar uygulamayı kullanmaya başladığında yeni accesstoken oluşsun ve bir 30 dk daha login yapmadan işlemlerini yapabilsin anlamına geliyor. Eğer kullanıcı 33 dakikadan daha fazla uygulamayı kullanmamışsa da artık yeniden giriş yapmak zorunda.
Gencay’ın dediği gibi senaryoya göre de bu süreler değişir.
Hocam Merhaba
JwtTokeni urettikten sonra WebUI kısmında nasıl kullanabiliriz ?
Yani sessiona hangi adımda eklememiz gerekiyor ?
Örnek bir config gösterebilir misiniz ?
Merhaba,
Makale içeriğinde referans edilen Asp.NET Core/Angular 7 – Web Api Token Authentication Kullanımı başlıklı makaleyi okumanızı tavsiye ederim. Örnek bir UI + JWT çalışması yapılmaktadır.
Kolay gelsin.
Hocam Selamlar
Jwt web token WebUI tarafında sessiona nasıl kaydedebiliriz. Bende bir takım hatalar oluşuyor runtime kısmında.
Şöyle ki:
WebUI tarafında gelen token’i Login actionunda sessiona setobjeyle atıyorum. Sonra startupda şu komuta
sessiiondan okuyorum.
Şimdi sormak istediğim tam olarak şunlar:
1- Tokeni sessionda tutmalı mıyım ?
2- app.use komutu her http isteğine sorgulanıyor. tokenin süresi bitse bile sessionda token duruyor. Bundan dolayı site bir hayli yavaşlıyor ve en sonunda beni sistemden logoff ediyor. app.use komutunu doğru mu kullanıyorum ?
3- Web site canlıda https üzerinden çıkıyor. Httponly gibi bir durum https istekleri içinde geçerli midir ?
Merhaba,
Cevaplar aşağıdadır;
1 – Token değerine ihtiyacınız yoksa silebilirsiniz.
2 – Kanımca doğru bir middleware seçilimi yapmışsınız. Kendinizde yazıp değerlendirebilirsiniz.
3 – Evet, geçerlidir.
Kolay gelsin.
Merhaba hocam asp.net core projemde apiyi güvenli hale getirmek için jwt yapıcaktım ancak oauth 2.0 varmış sanırım onu da kullanabiliyoruz .Müsait bir zamanınızda o konuyuda anlatırsanız çok sevinirim. İyi çalışmalar hocam
Merhaba,
Çoktan anlattım 🙂
Kolay gelsin…
Ayriyetten şu yazı serisine de göz atmanı tavsiye ederim…
Merhaba hocam,
.Net Core Api projem var. 2 sorum olacak.
1-Multiple login’i nasıl engelleyebiliriz?
2-localhost’ta çalışırken token alıyorum, debug’ı durdurup bir daha çalıştırdığımda aynı token geçerli oluyor. Bunu nasıl engelleyebilirim?
Merhaba,
1- Multiple’dan kastınız bir token ile birden fazla client’tan giriş yapmayı soruyorsanız eğer bunun için özel bir alan tanımlamasıyla oturumu check edebilirsiniz.
2- Token yapısı gereği expire değeri kadar yaşam sürecine devam edecektir.
Aynı user ile iki farklı client’tan giriş yapılmasını engellemek istiyorum. Bir kullanıcı hesabıyla token aldım diyelim. Farklı client’tan aynı user ile başka bir token aldığımda ilk token iptal olmasını nasıl sağlayabilirim. Cevap için teşekkürler hocam.
Tekrar merhaba,
Kullanıcıya dair üretilen token değerlerini Redis ya da SQL tarafında tutabilir ve iptal olmasını istediklerinizi BlackList mantığında bir tabloya atabilirsiniz. Gelen request’te token bilgisini blacklist üzerinden check edersiniz, eğer varsa request’i geri yönlendirirsiniz.
Sevgiler.
Çalışmalarınız için çok teşekkürler.
1- Oluşturulan tokenlar’ı, cookie içerisinde standart bir şekilde saklamanızın güvenlik açısından bir sakıncası var mı?
2- Cookie yönetiminde bilmemiz gereken başka bir durum var mı?
3- User.Identity.Name gibi erişimler için ek kullanıcı bilgilerini bu yapılar içinde saklayabiliyormuyuz? Yani cookie’de token’ı saklayıp, yeniden geldiğinde süresi bitmediği zamanda, token sorgusu yapıp session ile mi tutulmalı, yoksa token içeriği bu verileri saklayacak bir yapı sağlıyor mu?
Merhaba,
1- Yoktur.
2- İhtiyaca göre bilinmesi gerekenler bir nebze olsun detaylarda olabilir amma velakin JWT için daha ötesine gerek bulunmadığı kanaatindeyim.
3- Anlayamadım 🙂 Sorunuzu biraz daha açar mısınız?
Kolay gelsin.
Çalışmalarınız için çok teşekkürler.
Bir web api ve bir mvc client olan bir projede, Jwt ile authentication ve authorization yapmak istiyorum. Api tarafını oluşturdum ancak consume ederken cookie bazlı yetkilendirme kullanıyorum.
Sorularım biraz bunun üzerinden olacak.
1. Bu şekilde yaklaşım doğru mudur? Veya bu konuda tavsiyeleriniz nelerdir?
2. Permissionları tutmak için claimler güvenli olacak mıdır?
3. Hem api hem clientta permission kontrolü için ne yapmak lazım?
4. Son olarak da 2FA, bu yapıda mümkün olur mu?
Merhaba,
Öncelikle nezaketiniz için ben teşekkür ederim.
Suallere gelirsek eğer;
1- API’lar ile çalışırken JWT, MVC ile çalışırken Cookie tavsiye edilir. Yaklaşımınız yanlış değildir, ihtiyaca nazaran doğrudur.
2- Evet, permissionları tutmak için claim güvenli olacaktır.
3- Her iki taraftada Authorize attribute’u aynı işlevi görmektedir. Bu makalede anlatılan yöntemi her ikisinde de kullanabilirsiniz.
4- Evet, mümkündür.
Sevgiler.
Merhaba içerik için çok teşekkürler. Benim bir sorum olacak Token Authentication aynı zamanda Bearer Authentication mı oluyor? Http Authenticaiton Metodları içerisinde basic,bearer,digest ve oauth geçiyor ondan sordum.Teşekkürler.
Mehraba,
Evet, token authentication bearer token olarak geçmektedir. Misal, basic auth ise username ve password temelli doğrulamadır vs.
Kolay gelsin.
Audience kısmı için birşey sormak istiyorum. Örneğin ben Angular ile bir istek atacak isem angularda kullandığım hostu Audience kısmına mı yazacağım. Birde linkini bıraktığım yazıda:
token-authentication kulanımını anlatmışsınız bu yazıdakiyle farkı tam olarak nedir? Son olarak CORS ayarlarını yaparken jwt için dikkat etmemiz gereken birşey var mıdır? Teşekkürler.
https://www.gencayyildiz.com/blog/asp-net-core-angular-7-web-api-token-authentication-kullanimi/
Merhaba,
İki yazıda mahiyet olarak birebir aynıdır. Sadece bu yazı, diğer referans ettiğiniz yazıya nazaran konuyu daha geniş ele almakta ve refresh token gibi ekstra teknikleri kapsamaktadır.
‘Audience’ kısmına genellikle bu JWT’yi kullanacak olan host yazılır. Yani evet, Angular uygulamasının adresini yazmanız yeterli olacaktır.
CORS konusuna dair Asp.NET Core Uygulamalarında CORS(Cross-Origin-Resource-Sharing) Politikası Ayarlama başlıklı makalede tüm detayları izah etmiş bulunmaktayım. İncelemenizi tavsiye ederim.
Kolay gelsin.
En güveli kod olmuş hocam ” doldur be meyhaneci boş kalmasın kadehim” :))))
🙂
Gençay bey merhaba,
Peki oluşturduğumuz token ile backend de örneğin bir post atıyoruz token içinde ıd mevcut, bu ıd ye nasıl ulaşıyoruz yani bu postu oluşturan kullanıcının ıd sini çekmem lazım buda token da gömülü buna nasıl ulaşabiliriz ilgili yerlerde ?
Bunun için
NameClaimType
‘ı kullanabilirsiniz. Gerekli konfigürasyon için ‘Startup.cs’ dosyasında aşağıdakine benzer çalışma yapmanız gerekmektedir.Burada
NameClaimType
property’si ilgili token’da ki herhangi bir claim’e karşılık gelebilir. Verilen claim değerine controller’lar dan aşağıdaki gibi erişebilirsiniz.Tabi burada ilgili kod her ne kadar Name yani isim karşılında gözükse de siz ihtiyaç doğrultusunda ‘NameClaimType’a id vererek bu kod üzerinden id’yi elde edebilirsiniz.
Kolay gelsin…
Sevgiler…
Yanıtınız için teşekkür ederim, verdiğiniz bilgi doğrultusunda ıd yi yine yakalayamadım null geldi fakat biraz daha araştırdığımda şöyle bir yöntem buldum:
controller da bu şekilde token da ki ıd yi yakalıyorum startupa da claimtype eklemeden, ne kadar kullanışlı veya daha kısa bir yöntem varmı bilmiyorum paylaşmak istedim.
Öncelikle dönüşünüz için teşekkür ederim.
Gösterdiğiniz yöntemde
ClaimTypes.Name
‘e karşılık claim’in değerini elde edebilirsiniz. Name’e karşılık id değeri mi geliyor?Ayrıca gösterdiğim yöntemde neden olmadığına dair bakabiliriz. İsterseniz bana token’da ki claimleri gönderin. Ayrıca bahsettiğim konfigürasyon kodlarınızı paylaşın. İnceleyip, geri dönebilirim.
sizin yönteminiz ile yaptığımda startupım:
servis katmanımda login fonksiyonumda oluşturduğum token claimler:
fakat dediğiniz yöntemle controllerımda ıd yi çekemiyorum null çekiyor.
Bir önceki mesajta ilgili satırda vurguladığım gibi
NameClaimType
Claim’in key’i ile birebir aynı olmalıdır. Siz claim’e ‘ClaimTypes.Name’i koymuşsunuz. Halbuki ‘NameClaimType’dan ‘Id’yi bekliyorsunuz. İkisinden biri değişmeli… Misal olarak sizin ‘Startup.cs’ dosyasında aşağıdaki değişikliği yapmanızUser.Identity.Name
‘a ilgili değerin gelmesini sağlayacaktır.Hazır bu kadar değinmişken hatanıza değinmeden geçemeyeceğim. ‘Name’e karşılık id değerini tutmak pekte mantıklı değildir 🙂 Name’de gerçek kullanıcı adını tutup, controller’da elde ettikten sonra ufak bir sorgulamayla id’sini elde etmeniz daha makuldur. Ya da ‘id’ isminde bir claim üretip onunla çalışmak daha da mantıklı olacaktır kanaatindeyim.
Sevgiler…
tamam şimdi anladım ve çalıştı 🙂 evet id yi deneme amaçlı name e set ediyorum dediğiniz gibi bir get işlemi ile de idyi alabilirim fakat bir de şu var, ben katmanlı mimari kullanıyorum yani servis katmanımda User.Identity.Name’e erişemiyorum sizce bunu controllerda yakalayıp örneğin admin bir ürün ekle işlemi yapıyor bunun içerisinde oluşturan kullanıcı id si var bu ilk aşamada null geliyor bende bunu controllerda bu şekilde online olan kullanıcıyı tokendan çekip id sini orada set edip sonra mı servis katmanıma yollayayım sizce uygun bir yöntem mi ? kusura bakmayın çok uzattım lakin temelden oturuyor şuan mantık 🙂
Mimariye göre uygundur tabi…
Kolay gelsin…
Hocam postman olmadan test edebilmenin bir yolu var mı?
ya da ben direk mvc prjesi üzerinden geliştirsem sayfası ile test edilebilinir değil mi?
Ben işin içinden çıkamadım vallahi billahi. Hocamız keşke videolu anlatım yapsa bunla ilgili. Günlerdir boğuşuyorum.
Şimdi hocam Token sınıfımız var. Bu veritabanında bir tablo olacak öyle değil mi? AccessToken bilgisi ve onla ilişkili RefreshToken bilgisi de bu tabloda mı yer alacak? User tablomuzda da bir adet RefreshToken var. Bu RefreshToken ile Token tablosundaki RefreshToken aynı veriyi mi tutuyor. Valla kafam karıştı.
Ellerine sağlık Gencay Bey. Harika bir makale olmuş.
Teşekkür ederim. Yazınız benim tarafımdan oldukça anlaşılır idi.
Gerçekten güzel anlatım. Teşekkürler. Saygılar
bilgi için teşekkürler, şöyle bir ihityaç hasıl oldu, sadece 1 browserda kullanıcı aktif olması gerekiyor, aynı anda aynı hesap ile farklı bağlantı sağlanmasını engellemek istesek nasıl bir yol izlemek gerek acaba.
gencay kardeşimizin gözünden kaçmış olabilir.
Refreshtoken süresi ,accesstoken dan daha uzun olmamalı.
Çok basit anlatımla ;
Kullanıcı login oldu -> 25 günlük accesstoken verildi , 30 günlük refreshtoken verildi.
25 günlük süre doldu, Refreshtoken ile yeni accesstoken alman gerekiyor. Fakat oda ne method içinde şöyle bir sorgu var.
if (user != null && user?.RefreshTokenEndDate > DateTime.Now)
Yani daha 5 günün var , öyleyse ben sana accesstoken veremem.O halde kullanıcı ne yapacak ? Tabi ki 5 gün boşta bekleyecek yada 2 sürecin yerini değiştirin ve her şey daha iyi hale gelsin.
Aslında gencay kardeşimizin neden böyle yaptığını anladım , refreshtoken süresinden niye daha fazla süreyle accesstoken verip boşuna belleğimi yorayım diyor ama yöntem yanlış. Ayrıca konuların güzel tebrik ederim.
Tam anlayamadım ama yine de teşekkür ederim 🙂