Back to posts
ASP.NET Core'da Kimlik Doğrulama ve Yetkilendirme Pratikleri

ASP.NET Core'da Kimlik Doğrulama ve Yetkilendirme Pratikleri

buraxta / April 8, 2025

ASP.NET Core'da Kimlik Doğrulama ve Yetkilendirme Pratikleri

Neden Bu Kadar Önemli?

Basit söyleyeyim: Kimlik doğrulama "sen kimsin?" sorusunu cevaplarken, yetkilendirme "ne yapabilirsin?" sorusuna cevap verir. Bu ikisini doğru yapmazsanız, veri sızıntıları ve yetkisiz erişimler kaçınılmazdır.

Gelin birlikte ASP.NET Core'da bu iki kritik güvenlik bileşenini nasıl uygulayacağımızı adım adım görelim.

JWT ile Kimlik Doğrulama

JWT (JSON Web Token) günümüzün en popüler kimlik doğrulama yöntemlerinden biri. Neden mi? Çünkü durumsuz (stateless) bir yapıya sahip ve ölçeklendirme işlemini kolaylaştırıyor.

Hemen bir örnek verelim:

İlk olarak, appsettings.json dosyanıza şu ayarları ekleyin:

{
  "AuthConfiguration": {
    "Key": "guclu_ve_karmasik_bir_anahtar_kullanin",
    "Issuer": "DevTips",
    "Audience": "DevTips"
  }
}

Ardından Program.cs dosyanızda JWT ayarlarını yapılandırın:

builder.Services.AddAuthentication(options =>
{
    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
    options.TokenValidationParameters = new TokenValidationParameters
    {
        ValidateIssuer = true,
        ValidateAudience = true,
        ValidateLifetime = true,
        ValidateIssuerSigningKey = true,
        ValidIssuer = builder.Configuration["AuthConfiguration:Issuer"],
        ValidAudience = builder.Configuration["AuthConfiguration:Audience"],
        IssuerSigningKey = new SymmetricSecurityKey(
            Encoding.UTF8.GetBytes(builder.Configuration["AuthConfiguration:Key"]!))
    };
});

builder.Services.AddAuthorization();

Son olarak middleware'leri kaydedin:

var app = builder.Build();

// Diğer ayarlar...

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

Token Güvenliği İçin Altın Kurallar

  1. Kısa Ömürlü Tokenlar: JWT tokenlarınızı 15-30 dakika arası bir süreyle sınırlayın ve uzun süreli oturumlar için refresh token kullanın.
  2. Güçlü İmza: En az HMAC-SHA256 algoritması ve güçlü bir anahtar kullanın.
  3. Sıkı Doğrulama: Issuer, audience, lifetime ve signing key parametrelerini mutlaka kontrol edin.

Kullanıcı Kimlik Doğrulama

İşte ASP.NET Core Identity ile kullanıcı girişi yapan bir endpoint örneği:

public void AddRoutes(IEndpointRouteBuilder app)
{
    app.MapPost("/api/users/login", Handle);
}

private static async Task<IResult> Handle(
    [FromBody] LoginUserRequest request,
    IOptions<AuthConfiguration> authOptions,
    UserManager<User> userManager,
    SignInManager<User> signInManager,
    CancellationToken cancellationToken)
{
    var user = await userManager.FindByEmailAsync(request.Email);
    if (user is null)
    {
        return Results.NotFound("Kullanıcı bulunamadı");
    }

    var result = await signInManager.CheckPasswordSignInAsync(user, request.Password, false);
    if (!result.Succeeded)
    {
        return Results.Unauthorized();
    }

    var token = GenerateJwtToken(user, authOptions.Value);
    return Results.Ok(new { Token = token });
}

JWT token oluşturmak için yardımcı metodumuz:

private static string GenerateJwtToken(User user, AuthConfiguration authConfiguration)
{
    var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(authConfiguration.Key));
    var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);

    var claims = new[]
    {
        new Claim(JwtRegisteredClaimNames.Sub, user.Email!),
        new Claim("userid", user.Id)
    };

    var token = new JwtSecurityToken(
        issuer: authConfiguration.Issuer,
        audience: authConfiguration.Audience,
        claims: claims,
        expires: DateTime.Now.AddMinutes(30),
        signingCredentials: credentials
    );

    return new JwtSecurityTokenHandler().WriteToken(token);
}

Minimal API'lerde endpoint'leri güvenli hale getirmek için RequireAuthorization() metodunu kullanabilirsiniz:

app.MapPost("/api/books", Handle)
   .RequireAuthorization();

Controller'larda ise [Authorize] attribute'ünü kullanın.

Rol Tabanlı Yetkilendirme (RBAC)

RBAC yetkilendirmeyi basitleştirir: Kullanıcıları roller altında gruplayıp bu rollere erişim hakları tanımlarsınız.

Örneğin Admin ve Yazar rolleri oluşturalım:

var adminRole = new Role { Name = "Admin" };
var authorRole = new Role { Name = "Author" };

await roleManager.CreateAsync(adminRole);
await roleManager.CreateAsync(authorRole);

Rolleri politikalar olarak tanımlayalım:

builder.Services.AddAuthorization(options =>
{
    options.AddPolicy("Admin", policy =>
    {
        policy.RequireRole("Admin");
    });

    options.AddPolicy("Author", policy =>
    {
        policy.RequireRole("Author");
    });

    options.AddPolicy("BookEditor", policy =>
    {
        // Hem Admin hem de Author rolündekilerin kitapları düzenlemesine izin ver
        policy.RequireRole("Admin", "Author");
    });
});

Endpointlerde bu politikaları kullanabiliriz:

app.MapPost("/api/books", Handle)
    .RequireAuthorization("BookEditor");

app.MapPost("/api/users", Handle)
    .RequireAuthorization("Admin");

İddia Tabanlı Yetkilendirme (Claims-Based)

Daha esnek bir yaklaşım olarak, claim'lere dayalı yetkilendirme kullanabiliriz. Rolleri sabit kodlamak yerine, her endpoint için belirli claim'ler isteyebiliriz:

await roleManager.AddClaimAsync(adminRole, new Claim("users:create", "true"));
await roleManager.AddClaimAsync(adminRole, new Claim("users:update", "true"));
await roleManager.AddClaimAsync(adminRole, new Claim("users:delete", "true"));

await roleManager.AddClaimAsync(adminRole, new Claim("books:create", "true"));
await roleManager.AddClaimAsync(adminRole, new Claim("books:update", "true"));
await roleManager.AddClaimAsync(adminRole, new Claim("books:delete", "true"));

await roleManager.AddClaimAsync(authorRole, new Claim("books:create", "true"));
await roleManager.AddClaimAsync(authorRole, new Claim("books:update", "true"));
await roleManager.AddClaimAsync(authorRole, new Claim("books:delete", "true"));

JWT token oluştururken kullanıcının claim'lerini ekleyelim:

var roleClaims = await userManager.GetClaimsAsync(user);

List<Claim> claims = [
    new(JwtRegisteredClaimNames.Sub, user.Email!),
    new("userid", user.Id),
    new("role", userRole)
];

foreach (var roleClaim in roleClaims)
{
    claims.Add(new Claim(roleClaim.Type, roleClaim.Value));
}

var token = new JwtSecurityToken(
    issuer: authConfiguration.Issuer,
    audience: authConfiguration.Audience,
    claims: claims,
    expires: DateTime.Now.AddMinutes(30),
    signingCredentials: credentials
);

Ve claim politikalarını kaydedelim:

builder.Services.AddAuthorization(options =>
{
    options.AddPolicy("books:create", policy => policy.RequireClaim("books:create"));
    options.AddPolicy("books:update", policy => policy.RequireClaim("books:update"));
    options.AddPolicy("books:delete", policy => policy.RequireClaim("books:delete"));

    options.AddPolicy("users:create", policy => policy.RequireClaim("users:create", "true"));
    options.AddPolicy("users:update", policy => policy.RequireClaim("users:update"));
    options.AddPolicy("users:delete", policy => policy.RequireClaim("users:delete"));
});

Öznitelik Tabanlı Yetkilendirme (ABAC)

Bazen erişim kararlarının, hem kullanıcının hem de kaynağın özniteliklerini dikkate alması gerekir. Örneğin, bir yazarın sadece kendi kitaplarını güncelleyebilmesine izin vermek gibi.

Önce özel bir IAuthorizationRequirement tanımlayalım:

public class BookAuthorRequirement : IAuthorizationRequirement
{
}

Ardından kontrol yapan AuthorizationHandler sınıfımızı:

public class BookAuthorHandler : AuthorizationHandler<BookAuthorRequirement, Author>
{
    protected override Task HandleRequirementAsync(
        AuthorizationHandlerContext context,
        BookAuthorRequirement requirement,
        Author resource)
    {
        var userId = context.User.FindFirst("userid")?.Value;
        if (userId is not null && userId == resource.UserId)
        {
            context.Succeed(requirement);
        }

        return Task.CompletedTask;
    }
}

DI container'a kaydedelim:

builder.Services.AddScoped<IAuthorizationHandler, BookAuthorHandler>();

API endpoint'inde kullanımı:

app.MapPut("/api/books/{id}", Handle)
    .RequireAuthorization("books:update");

private static async Task<IResult> Handle(
    [FromRoute] Guid id,
    [FromBody] UpdateBookRequest request,
    IBookRepository repository,
    IAuthorizationService authService,
    ClaimsPrincipal user,
    CancellationToken cancellationToken)
{
    var book = await repository.GetByIdAsync(id, cancellationToken);
    if (book is null)
    {
        return Results.NotFound($"Kitap bulunamadı, id: {id}");
    }

    var requirement = new BookAuthorRequirement();

    var authResult = await authService.AuthorizeAsync(user, book.Author, requirement);
    if (!authResult.Succeeded)
    {
        return Results.Forbid();
    }

    book.Title = request.Title;
    book.Year = request.Year;

    await repository.UpdateAsync(book, cancellationToken);

    return Results.NoContent();
}

Hangi Yöntemi Ne Zaman Kullanmalı?

  • Rol Tabanlı (RBAC): Basit ve merkezi izin yönetimi için Admin, Editör gibi önceden tanımlanmış rollere dayalı erişim kontrolü gerektiğinde.
  • İddia Tabanlı (Claims-Based): Daha ince granüler kontrol gerektiğinde, "books:create" veya "users:delete" gibi belirli izinlere dayalı erişim. Claim'leri roller altında gruplayabilir ve her rol için izin setini dinamik olarak güncelleyebilirsiniz.
  • Öznitelik Tabanlı (ABAC): Erişim kararları kullanıcının, kaynağın veya ortamın dinamik özniteliklerine bağlı olduğunda, örneğin kullanıcıların yalnızca kendi verilerini düzenlemesine izin vermek veya bağlama dayalı izinleri yönetmek için.

Güvenlik konusu her zaman önemli ve bu yazıda ASP.NET Core'da kimlik doğrulama ve yetkilendirmenin temel prensiplerini gördük. Unutmayın, yetki kontrolü katmanlarını ne kadar iyi tasarlarsanız, uygulamanız o kadar güvenli olur.

Sorularınız varsa yorumlarda beklerim!