ASP.NET Core'da JWT ile Kimlik Doğrulama
JWT token uretimi, dogrulamasi ve refresh token deseni ile ASP.NET Core API guvenligi.
Siparişleri listeleyen bir API düşünün. Mobil uygulama bu API’yi çağırırken bir erişim belirteci gönderiyor. API önce belirteci kimin düzenlediğini, hangi API için verildiğini ve hâlâ geçerli olup olmadığını kontrol ediyor. Ardından belirtecin siparişleri okumaya izin verip vermediğine bakıyor. ASP.NET Core’da bu iki adımı JWT Bearer kimlik doğrulaması ve bir yetkilendirme politikasıyla ayırabiliriz.
API’nin güveneceği belirteci tanımlayın
Üretimde erişim belirtecini OAuth 2.0 uyumlu bir yetkilendirme sunucusu düzenlesin. Kullanıcı oturumu gerekiyorsa istemci, sağlayıcının OpenID Connect akışını kullanabilir. API’nin görevi kendisine sunulan erişim belirtecini doğrulamaktır. Kullanıcı adı ve parola alan bir uç noktada kendi token üretim akışınızı kurmayın. OIDC’nin istemciye oturum hakkında bilgi vermek için döndürdüğü ID token’ı da API çağrısında kullanmayın.
Aşağıdaki örnek, HTTPS üzerinden OIDC keşif bilgilerini ve açık imza anahtarlarını yayımlayan tek bir sağlayıcı varsayıyor. Sağlayıcıda sipariş API’si için ayrı bir hedef kitle ve orders.read izni tanımlayın. Authority ve Audience değerlerini sağlayıcıdaki gerçek kayıtla eşleştirin. Hedef kitle bazen bir URL, bazen de sağlayıcının verdiği başka bir tanımlayıcıdır. Sağlayıcı erişim belirteci türünü ayrı bir typ başlığıyla tanımlıyorsa o değeri de sağlayıcının profiline göre doğrulayın.
JWT Bearer doğrulamasını ve kapsam politikasını ekleyin
.NET 10 kullanan bir ASP.NET Core projesine Microsoft.AspNetCore.Authentication.JwtBearer paketinin güncel 10.0.x sürümünü ekleyin. Program.cs’deki ayar imzayı, düzenleyeni (iss), hedef kitleyi (aud) ve belirtecin geçerlilik süresini denetler. API, imzayı doğrulamak için yetkilendirme sunucusunun açık anahtarlarından yararlanır. Örnekteki JSON’u appsettings.json dosyanıza ekleyip yer tutucuları değiştirin.
{
"Jwt": {
"Authority": "https://auth.example.com",
"Audience": "https://api.example.com/orders"
}
}
using Microsoft.AspNetCore.Authentication.JwtBearer;
var builder = WebApplication.CreateBuilder(args);
var authority = builder.Configuration["Jwt:Authority"]
?? throw new InvalidOperationException("Jwt:Authority ayarı eksik.");
var audience = builder.Configuration["Jwt:Audience"]
?? throw new InvalidOperationException("Jwt:Audience ayarı eksik.");
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.Authority = authority;
options.Audience = audience;
options.RequireHttpsMetadata = true;
options.MapInboundClaims = false;
options.TokenValidationParameters.ValidateIssuer = true;
options.TokenValidationParameters.ValidateAudience = true;
options.TokenValidationParameters.ValidateLifetime = true;
options.TokenValidationParameters.ValidateIssuerSigningKey = true;
});
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("OrdersRead", policy => policy
.RequireAuthenticatedUser()
.RequireAssertion(context => context.User.FindAll("scope")
.SelectMany(claim => claim.Value.Split(' ', StringSplitOptions.RemoveEmptyEntries))
.Contains("orders.read", StringComparer.Ordinal)));
});
var app = builder.Build();
app.UseAuthentication();
app.UseAuthorization();
app.MapGet("/orders", () => Results.Ok(new[] { new { Id = 42 } }))
.RequireAuthorization("OrdersRead");
app.Run();
Politika, scope alanındaki boşlukla ayrılmış değerleri tek tek karşılaştırır. Böylece orders.read.all, orders.read yerine geçmez. Sağlayıcınız kapsamları scp gibi başka bir alanda yayımlıyorsa politikayı o sağlayıcının sözleşmesine göre uyarlayın. Kapsam kontrolü uç noktaya erişim verir. Bir kullanıcının yalnızca kendi siparişlerini görmesi gerekiyorsa kayıt sahipliğini ayrıca denetleyin.
401 ve 403 yanıtlarını ayrı ayrı sınayın
Yerelde güvenilen bir geliştirme sertifikasıyla API’yi HTTPS üzerinde çalıştırın. Aşağıdaki isteklerde ACCESS_TOKEN değişkenine sağlayıcıdan test için aldığınız erişim belirtecini koyun. Sunucudan yanıtı görmek için curl komutları başlıkları da gösterir.
dotnet run --no-launch-profile -- --urls https://localhost:7041
# Ayrı bir terminalde
curl -i https://localhost:7041/orders
curl -i -H "Authorization: Bearer ${ACCESS_TOKEN}" https://localhost:7041/orders
İlk istekte 401 ve WWW-Authenticate başlığında Bearer bekleyin. Süresi dolmuş, imzası değiştirilmiş veya farklı bir API için düzenlenmiş belirteçle de sonuç 401 olmalı. Doğru hedef kitleye verilmiş, geçerli fakat orders.read izni bulunmayan bir belirteç 403 dönmeli. Geçerli ve gerekli izni taşıyan belirteçle 200 ve örnek sipariş listesini görmelisiniz. Yanlış hedef kitleyi sınamak için sağlayıcıdan başka bir API için belirteç alın. Böylece hedef kitle kontrolünü imza kontrolünden ayrı denemiş olursunuz.
RequireHttpsMetadata, sağlayıcının keşif bilgilerinin HTTPS üzerinden alınmasını zorunlu kılar. API isteklerinin HTTPS kullanması için üretimde TLS’i API’de veya giriş katmanında ayrıca yapılandırın. Bearer belirtecini ele geçiren kişi geçerlilik süresi boyunca onu kullanabileceğinden istemciler belirteci Authorization başlığında göndersin. Belirteçleri URL’ye, uygulama günlüklerine veya hata yanıtlarına yazmayın.