.NET'te Authentication ve Authorization: JWT ve Identity

14 dakika okuma9 Şubat 2026Güncellendi: 9 Mar 2026
.NET authenticationJWT .NETASP.NET Core IdentityOAuth2 .NET.NET authorizationBearer token C#.NET securityClaims-based auth

# .NET'te Authentication ve Authorization

Güvenlik, her API'nin temel gereksinimidir. Authentication (kimlik dogrulama) ile authorization (yetkilendirme) arasindaki farki net bir sekilde ayirmak, saglam bir guvenlik mimarisinin ilk adimidir. ASP.NET Core, her iki alan icin de olgun bir middleware altyapisi ve policy tabanli kontroller sunar. Bu yazida JWT yapilandirmasi, Identity kurulumu, OAuth2/OIDC entegrasyonu ve production sistemlerde kullandigim guvenlik pratiklerini detayli olarak paylasiyorum.

Authentication Yaklasimlari

JWT Bearer Authentication

JWT (JSON Web Token), API authentication icin en yaygin tercih edilen yontemdir. Sunucu, kullanicinin claim'lerini iceren imzali bir token uretir ve sonraki her istek bu token'i `Authorization` header'inda tasir. Sunucu tarafinda oturum durumu tutulmaz, sticky session gereksinimi ortadan kalkar.

`Program.cs` icinde tipik bir JWT yapilandirmasi:

csharp
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[class="code-string">"Jwt:Issuer"],
        ValidAudience = builder.Configuration[class="code-string">"Jwt:Audience"],
        IssuerSigningKey = new SymmetricSecurityKey(
            Encoding.UTF8.GetBytes(builder.Configuration[class="code-string">"Jwt:Key"]!)),
        ClockSkew = TimeSpan.FromSeconds(class="code-number">30)
    };

    options.Events = new JwtBearerEvents
    {
        OnAuthenticationFailed = context =>
        {
            if (context.Exception is SecurityTokenExpiredException)
            {
                context.Response.Headers.Append(class="code-string">"X-Token-Expired", class="code-string">"true");
            }
            return Task.CompletedTask;
        }
    };
});

`ClockSkew` degerini her zaman dusuk tutarim. Varsayilan 5 dakikalik tolerans cogu sistem icin gereksiz derecede genistir ve suresi dolmus token'larin kabul edildigi bir pencere olusturur.

Token Uretimi

Token uretimi basit gorunur ama detaylar onemlidir. Tipik olarak kullandigim servis yapisi:

csharp
public class TokenService : ITokenService
{
    private readonly IConfiguration _config;

    public TokenService(IConfiguration config)
    {
        _config = config;
    }

    public string GenerateAccessToken(ApplicationUser user, IList<string> roles)
    {
        var claims = new List<Claim>
        {
            new(JwtRegisteredClaimNames.Sub, user.Id),
            new(JwtRegisteredClaimNames.Email, user.Email!),
            new(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
            new(class="code-string">"username", user.UserName!)
        };

        claims.AddRange(roles.Select(role => new Claim(ClaimTypes.Role, role)));

        var key = new SymmetricSecurityKey(
            Encoding.UTF8.GetBytes(_config[class="code-string">"Jwt:Key"]!));
        var credentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);

        var token = new JwtSecurityToken(
            issuer: _config[class="code-string">"Jwt:Issuer"],
            audience: _config[class="code-string">"Jwt:Audience"],
            claims: claims,
            expires: DateTime.UtcNow.AddMinutes(class="code-number">15),
            signingCredentials: credentials);

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

    public RefreshToken GenerateRefreshToken()
    {
        return new RefreshToken
        {
            Token = Convert.ToBase64String(RandomNumberGenerator.GetBytes(class="code-number">64)),
            ExpiresAt = DateTime.UtcNow.AddDays(class="code-number">7),
            CreatedAt = DateTime.UtcNow
        };
    }
}

Production ortaminda guvence altina aldigim API'lerde access token suresini 15 dakika veya daha kisa tutarim ve bunlari sunucu tarafinda saklanan refresh token'larla eslestiririm. Bu yaklasim, bir token ele gecirildiginde hasar penceresini minimuma indirir.

ASP.NET Core Identity

Uygulamaniz kullanici yasam dongusu uzerinde tam kontrole sahipse -- kayit, sifre yonetimi, e-posta dogrulama, iki faktorlu kimlik dogrulama -- ASP.NET Core Identity dogru temeldir. Sifre hashleme, hesap kilitleme ve kullanici deposu soyutlamasi gibi agir isleri ustlenir.

csharp
builder.Services.AddIdentity<ApplicationUser, IdentityRole>(options =>
{
    class=class="code-string">"code-comment">// Sifre kurallari
    options.Password.RequireDigit = true;
    options.Password.RequiredLength = class="code-number">12;
    options.Password.RequireNonAlphanumeric = true;
    options.Password.RequireUppercase = true;
    options.Password.RequireLowercase = true;

    class=class="code-string">"code-comment">// Kilitleme
    options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(class="code-number">15);
    options.Lockout.MaxFailedAccessAttempts = class="code-number">5;
    options.Lockout.AllowedForNewUsers = true;

    class=class="code-string">"code-comment">// Kullanici
    options.User.RequireUniqueEmail = true;
    options.SignIn.RequireConfirmedEmail = true;
})
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();

Refresh Token Akisi

Duzgun bir refresh token akisi vazgecilmezdir. Istemci, suresi dolmus access token ile birlikte gecerli bir refresh token gonderir ve yeni bir cift alir:

csharp
[HttpPost(class="code-string">"refresh")]
[AllowAnonymous]
public async Task<IActionResult> Refresh([FromBody] TokenRefreshRequest request)
{
    var principal = GetPrincipalFromExpiredToken(request.AccessToken);
    if (principal is null)
        return Unauthorized(class="code-string">"Gecersiz access token.");

    var userId = principal.FindFirstValue(ClaimTypes.NameIdentifier);
    var user = await _userManager.FindByIdAsync(userId!);
    if (user is null)
        return Unauthorized();

    var storedToken = await _tokenRepository.GetRefreshTokenAsync(user.Id);

    if (storedToken is null ||
        storedToken.Token != request.RefreshToken ||
        storedToken.ExpiresAt <= DateTime.UtcNow ||
        storedToken.IsRevoked)
    {
        class=class="code-string">"code-comment">// Olasi token yeniden kullanim saldirisi -- tum aileyi iptal et
        if (storedToken?.IsRevoked == true)
        {
            await _tokenRepository.RevokeAllTokensForUserAsync(user.Id);
        }
        return Unauthorized(class="code-string">"Gecersiz veya suresi dolmus refresh token.");
    }

    class=class="code-string">"code-comment">// Rotasyon: eskiyi iptal et, yenisini uret
    storedToken.IsRevoked = true;
    var newRefreshToken = _tokenService.GenerateRefreshToken();
    newRefreshToken.UserId = user.Id;
    newRefreshToken.ReplacedByToken = newRefreshToken.Token;

    await _tokenRepository.SaveRefreshTokenAsync(newRefreshToken);

    var roles = await _userManager.GetRolesAsync(user);
    var newAccessToken = _tokenService.GenerateAccessToken(user, roles);

    return Ok(new TokenResponse
    {
        AccessToken = newAccessToken,
        RefreshToken = newRefreshToken.Token
    });
}

Buradaki kritik detay token ailesi takibidir. Iptal edilmis bir refresh token tekrar kullanilirsa, tum zinciri iptal ederim. Bu, saldirganin meşru istemcinin zaten rotate ettigi bir token'i ele gecirdigi replay saldirilarini tespit eder.

Authorization Stratejisi

Policy Tabanli Authorization

Kod tabanina dagilmis `[Authorize(Roles = "Admin")]` attribute'leri bakim kabusu haline gelir. Policy tabanli authorization mantigi merkezilestir ve test edilebilir kilar:

csharp
builder.Services.AddAuthorization(options =>
{
    options.AddPolicy(class="code-string">"CanManageProducts", policy =>
        policy.RequireRole(class="code-string">"Admin", class="code-string">"ProductManager"));

    options.AddPolicy(class="code-string">"CanViewReports", policy =>
        policy.RequireClaim(class="code-string">"permission", class="code-string">"reports:read"));

    options.AddPolicy(class="code-string">"PremiumUser", policy =>
        policy.Requirements.Add(new PremiumSubscriptionRequirement()));

    class=class="code-string">"code-comment">// Varsayilan olarak reddet: tum endpoint'ler authentication gerektirir
    options.FallbackPolicy = new AuthorizationPolicyBuilder()
        .RequireAuthenticatedUser()
        .Build();
});

Karmasik is kurallari icin `IAuthorizationHandler` uygulayin:

csharp
public class PremiumSubscriptionHandler
    : AuthorizationHandler<PremiumSubscriptionRequirement>
{
    private readonly ISubscriptionService _subscriptionService;

    public PremiumSubscriptionHandler(ISubscriptionService subscriptionService)
    {
        _subscriptionService = subscriptionService;
    }

    protected override async Task HandleRequirementAsync(
        AuthorizationHandlerContext context,
        PremiumSubscriptionRequirement requirement)
    {
        var userId = context.User.FindFirstValue(ClaimTypes.NameIdentifier);
        if (userId is null) return;

        var isActive = await _subscriptionService
            .HasActiveSubscriptionAsync(userId);

        if (isActive)
        {
            context.Succeed(requirement);
        }
    }
}

Ardindan endpoint'lere temiz bir sekilde uygulayin:

csharp
app.MapGet(class="code-string">"/api/reports", GetReports)
    .RequireAuthorization(class="code-string">"CanViewReports");

app.MapDelete(class="code-string">"/api/products/{id}", DeleteProduct)
    .RequireAuthorization(class="code-string">"CanManageProducts");

Bu yaklasim controller'lari yalin tutar ve authorization mantigini bagımsız olarak test edilebilir kilar.

OAuth2/OIDC Entegrasyonu

Kurumsal SSO veya sosyal giris icin, OpenID Connect uzerinden harici bir identity provider ile entegrasyon standart yoldur. Azure AD veya Keycloak gibi bir provider ile pratik kurulum:

csharp
builder.Services.AddAuthentication(options =>
{
    options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddCookie()
.AddOpenIdConnect(options =>
{
    options.Authority = builder.Configuration[class="code-string">"Oidc:Authority"];
    options.ClientId = builder.Configuration[class="code-string">"Oidc:ClientId"];
    options.ClientSecret = builder.Configuration[class="code-string">"Oidc:ClientSecret"];
    options.ResponseType = class="code-string">"code";
    options.SaveTokens = true;
    options.GetClaimsFromUserInfoEndpoint = true;

    options.Scope.Add(class="code-string">"openid");
    options.Scope.Add(class="code-string">"profile");
    options.Scope.Add(class="code-string">"email");

    options.TokenValidationParameters = new TokenValidationParameters
    {
        NameClaimType = class="code-string">"name",
        RoleClaimType = class="code-string">"role"
    };

    options.Events = new OpenIdConnectEvents
    {
        OnTokenValidated = async context =>
        {
            class=class="code-string">"code-comment">// Harici kullaniciyi yerel veritabanina senkronize et
            var userService = context.HttpContext.RequestServices
                .GetRequiredService<IUserSyncService>();
            await userService.SyncExternalUserAsync(context.Principal!);
        }
    };
});

Tarayici olmayan API'den API'ye senaryolarda client credentials akisini kullanin:

csharp
public class MachineToMachineTokenService
{
    private readonly HttpClient _httpClient;
    private readonly IConfiguration _config;

    public async Task<string> GetAccessTokenAsync()
    {
        var disco = await _httpClient
            .GetDiscoveryDocumentAsync(_config[class="code-string">"Oidc:Authority"]);

        var tokenResponse = await _httpClient.RequestClientCredentialsTokenAsync(
            new ClientCredentialsTokenRequest
            {
                Address = disco.TokenEndpoint,
                ClientId = _config[class="code-string">"Oidc:ServiceClientId"]!,
                ClientSecret = _config[class="code-string">"Oidc:ServiceClientSecret"]!,
                Scope = class="code-string">"api.read api.write"
            });

        if (tokenResponse.IsError)
            throw new InvalidOperationException(
                $class="code-string">"Token istegi basarisiz: {tokenResponse.Error}");

        return tokenResponse.AccessToken!;
    }
}

Production ortaminda guvence altina aldigim API'lerde, ilk giriste harici identity claim'lerini her zaman yerel bir kullanici kaydina senkronize ederim. Bu, uygulamaya denetim izleri icin yerel bir referans saglar ve identity provider'a tekrar tekrar sorgu yapmayi onler.

Guvenlik Saglamlastirma Kontrol Listesi

Her production dagitimi oncesinde gozden gecirdigim kontroller:

  • **Her yerde HTTPS** -- `app.UseHsts()` ve JWT yapilandirmasinda `RequireHttpsMetadata = true` ile zorunlu kilin
  • **Token depolama** -- access token'lar yalnizca bellekte (asla localStorage'da degil); refresh token'lar httpOnly, Secure, SameSite=Strict cookie'lerde
  • **Anahtar yonetimi** -- imzalama anahtarlari Azure Key Vault, AWS Secrets Manager veya HashiCorp Vault'ta; asla appsettings.json'da degil
  • **Token omru** -- access token'lar en fazla 15 dakika; refresh token'lar her kullanimda rotasyonla 7 gun
  • **CORS yapilandirmasi** -- acik izin verilen origin'ler, credentials ile birlikte asla `AllowAnyOrigin()` degil
  • **Auth endpoint'lerinde hiz sinirlamasi** -- login, register ve token refresh endpoint'lerinde agresif hiz siniri
  • **Denetim kaydi** -- her giris, basarisiz deneme, token yenileme ve yetki yukseltmeyi korelasyon ID'leriyle kaydedin
  • **Sifre politikasi** -- en az 12 karakter, HaveIBeenPwned API ile sizdirilmis sifre kontrolu
  • **Hesap kilitleme** -- 5 basarisiz denemeden sonra kilitle, 15 dakika bekleme suresi
  • **Guvenlik basliklari** -- `X-Content-Type-Options`, `X-Frame-Options`, `Strict-Transport-Security`, `Content-Security-Policy`
  • csharp
    class=class="code-string">"code-comment">// Auth endpointclass="code-string">'lerinde hiz sinirlamasi
    builder.Services.AddRateLimiter(options =>
    {
        options.AddFixedWindowLimiter(class="code-string">"AuthEndpoints", limiter =>
        {
            limiter.PermitLimit = class="code-number">10;
            limiter.Window = TimeSpan.FromMinutes(class="code-number">1);
            limiter.QueueLimit = class="code-number">0;
        });
    });
    
    class=class="code-string">"code-comment">// Auth route'larina uygula
    app.MapPost(class="code-string">"/api/auth/login", Login)
        .RequireRateLimiting(class="code-string">"AuthEndpoints");
    
    app.MapPost(class="code-string">"/api/auth/refresh", Refresh)
        .RequireRateLimiting(class="code-string">"AuthEndpoints");

    Yaygin Auth Hatalari

    Yillar icinde projelerde ayni hatalarin tekrarlandigini gordum. Gercek hasara neden olanlar:

    Token'lari localStorage'da saklamak

    En yaygin hata budur. Herhangi bir XSS acigi, saldırgana token'a tam erisim verir. Access token'lar bellekte (bir JavaScript degiskeni, kalici depolama degil) yasamalidir. Refresh token'lar JavaScript'in okuyamayacagi httpOnly cookie'lerde bulunmalidir.

    Refresh token rotasyonu yapmamak

    Bir refresh token 30 gun gecerli ve hic rotate edilmiyorsa, tek bir sizinti saldırgana bir aylik oturum verir. Her kullanimda rotate edin ve token ailesini takip edin, boylece eski bir token'in yeniden kullanimi tum zinciri iptal eder.

    Yalnizca imzayi dogrulamak

    Gecerli bir imza, gecerli bir token anlamina gelmez. Her zaman `iss`, `aud`, `exp` ve `nbf` claim'lerini dogrulayin. Imzayi kontrol eden ama audience'i gormezden gelen sistemler gordum -- bir servis icin uretilen token'lar baska bir serviste calismaya devam ediyordu.

    Hassas veri iceren siskin token'lar

    JWT'ler sifrelenmis degil, encode edilmistir. Token'lara e-posta adresleri, telefon numaralari veya sistem mimarisini aciga cikaran dahili ID'ler koymayin. Claim'leri minimal tutun -- bir kullanici ID'si ve rol listesi genellikle yeterlidir. Daha fazla veriye ihtiyaciniz varsa sunucu tarafinda arama yapin.

    Servisler arasinda paylasilan simetrik anahtarlar

    Birden fazla serviste ayni HMAC anahtarini kullanmak, herhangi bir servisin digerleri icin token uretebilecegi anlamina gelir. Mikroservis mimarisinde asimetrik anahtarlar (RSA veya ECDSA) kullanin. Auth servisi private key'i tutar, tuketici servisler yalnizca public key'e ihtiyac duyar.

    csharp
    class=class="code-string">"code-comment">// Tuketici servisler icin asimetrik anahtar dogrulamasi
    options.TokenValidationParameters = new TokenValidationParameters
    {
        ValidateIssuerSigningKey = true,
        IssuerSigningKey = new RsaSecurityKey(rsaPublicKey),
        ValidateIssuer = true,
        ValidIssuer = class="code-string">"https:class="code-commentclass="code-string">">//auth.mycompany.com",
        ValidateAudience = true,
        ValidAudience = class="code-string">"https:class="code-commentclass="code-string">">//api.mycompany.com"
    };

    Token iptal stratejisinin olmamasi

    JWT'ler tasarim geregi stateless'tir, bu da ek altyapi olmadan onlari iptal edemeyeceginiz anlamina gelir. Production sistemlerde, middleware'in her istekte kontrol ettigi bir iptal listesi (Redis'te kisa omurlu cache) tutarim. Ek yuk, sagladigi guvenlige kiyasla minimaldir.

    Auth olaylarini kaydetmemek

    "Kim, nereden, ne zaman giris yapti ve neye eristi?" sorularini cevaplayamiyorsaniz production icin hazir degilsiniz. Her authentication ve authorization karari bir denetim olayi uretmelidir.

    Sonuc

    Dogru authentication stratejisi, API guvenliginin temelidir. .NET'te etkili bir guvenlik mimarisi, standart tabanli authentication'i acik ve policy odakli authorization ile birlestirir. Framework saglam yapi taslari sunar, ancak guvenli bir sistem ile savunmasiz bir sistem arasindaki fark detaylarda gizlidir -- token omru, rotasyon stratejileri, depolama kararlari ve tutarli denetim kaydi.

    Guvenlik mimarinizi gozden gecirebilirim.

    İlgili Makaleler

    Flutter Projeniz mi Var?

    iOS, Android ve web için yüksek performanslı Flutter uygulamaları geliştiriyorum.

    İletişime Geç