.NET SDK
İstemci SDK'sı ve ASP.NET Core entegrasyonu.
ErtisAuth iki .NET kütüphanesiyle gelir:
| Paket | Ne yapar |
|---|---|
ErtisAuth.Sdk | ErtisAuth REST API'si için tipli bir istemci: giriş, token'lar, kullanıcılar, roller, uygulamalar, hook'lar… |
ErtisAuth.Sdk.AspNetCore | Kendi ASP.NET Core API'lerinizi attribute'larla, ErtisAuth token'ları ve yetkileriyle korur. ErtisAuth.Sdk'yı içerir. |
Endpoint'lerini ErtisAuth kullanıcılarının ve uygulamalarının çağırdığı bir ASP.NET Core API'si için ErtisAuth.Sdk.AspNetCore'u kullanın. ErtisAuth'u bir worker'dan, bir console uygulamasından ya da başka bir .NET programından çağırmak için yalnızca ErtisAuth.Sdk yeterlidir. İkisi de .NET 10 hedefler.
Hızlı başlangıç#
Bir ASP.NET Core API'sini dört adımda koruyun.
1. Paketi ekleyin:
dotnet add package ErtisAuth.Sdk.AspNetCore2. Ayarları appsettings.json dosyasına ekleyin:
{
"ErtisAuth": {
"BaseUrl": "https://auth.example.com",
"MembershipId": "66f1c0d2a4b5c6d7e8f90123"
}
}3. SDK'yı Program.cs içinde kaydedin:
using ErtisAuth.Sdk.AspNetCore.Extensions;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddErtisAuth();
var app = builder.Build();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.Run();4. Bir controller'ı koruyun:
using ErtisAuth.Core.Models.Roles;
using ErtisAuth.Extensions.Authorization.Attributes;
using Microsoft.AspNetCore.Mvc;
[ApiController]
[Route("orders")]
[Authorized]
[RbacResource("orders")]
public class OrdersController : ControllerBase
{
// Requires "orders.read"
[HttpGet]
[RbacAction(Rbac.CrudActions.Read)]
public IActionResult List() => this.Ok();
}GET /orders endpoint'ini bir ErtisAuth kullanıcısının access token'ıyla (Authorization: Bearer <access_token>) ya da bir uygulamanın Basic token'ıyla çağırın:
| İstek | Yanıt |
|---|---|
| Token olmadan ya da geçersiz, süresi dolmuş veya iptal edilmiş bir token'la | 401 |
Rolü orders.read izni vermeyen bir kullanıcı ya da uygulama | 403 AccessDenied |
Rolü orders.read izni veren bir kullanıcı ya da uygulama | 200 |
Yapılandırma#
| Ayar | Zorunlu | Açıklama |
|---|---|---|
BaseUrl | evet | ErtisAuth'un, varsa yol öneki dahil, mutlak http ya da https URL'si. |
MembershipId | evet | Uygulamanızın ait olduğu membership. |
BasicTokenCacheTTL | hayır | Doğrulanmış bir Basic token'ın API'niz tarafından önbellekte tutulacağı saniye (ErtisAuth.Sdk.AspNetCore). 0 ya da verilmezse önbellek kapalıdır. |
Ayarlar nereden okunur#
AddErtisAuth(), uygulamanızın yapılandırmasındaki (builder.Configuration) ErtisAuth bölümünü okur. Uygulamanın tüm yapılandırma kaynakları geçerlidir: appsettings.json ve appsettings.{Environment}.json, ortam değişkenleri, user secrets, komut satırı argümanları, Azure Key Vault vb. Örneğin Kubernetes'te:
env:
- name: ErtisAuth__BaseUrl
value: https://auth.example.com
- name: ErtisAuth__MembershipId
value: 66f1c0d2a4b5c6d7e8f90123Ayarlar uygulama başlarken doğrulanır. Geçersiz ayarlar, tüm sorunları listeleyen bir mesajla uygulamayı durdurur.
Ayarları vermenin diğer yolları:
| Çağrı | Okunan |
|---|---|
AddErtisAuth() | ErtisAuth bölümü |
AddErtisAuth("Identity") | Yapılandırmanın başka bir bölümü |
AddErtisAuth(builder.Configuration.GetSection("Identity")) | Verilen bölüm, hemen doğrulanır |
AddErtisAuth(options => { … }) | Yalnızca verilen değerler; hiçbir yapılandırma okunmaz |
builder.Services.AddErtisAuth(options =>
{
options.BaseUrl = "https://auth.example.com";
options.MembershipId = "66f1c0d2a4b5c6d7e8f90123";
});AddErtisAuth SDK'yı bir kez kaydeder: sonraki çağrılar, ayarları ne olursa olsun yok sayılır.
WebApplication.CreateBuilder ya da Host.CreateApplicationBuilder kullanmayan) uygulama yapılandırması yoktur. Bu durumda AddErtisAuth(), çıktı klasöründeki appsettings.json ve appsettings.{ASPNETCORE_ENVIRONMENT}.json dosyalarını ve ortam değişkenlerini okur.Endpoint'leri koruma#
Hangi endpoint'ler korunur#
Controller'ı [Authorized] ile işaretleyin: controller'ın her action'ı geçerli bir token ve rbac attribute'larının tanımladığı yetkiyi gerektirir. Tek bir action için istisna yapmak isterseniz action'ı işaretleyin:
[Unauthorized]: action herkese açıktır.[SelfAuthorized]: action geçerli bir token gerektirir, ama yetkiyi kendisi kontrol eder.
En spesifik attribute geçerlidir: action'daki attribute controller'dakini ezer.
| Controller | Action | Endpoint |
|---|---|---|
[Authorized] | token ve yetki gerektirir | |
[Authorized] | [Unauthorized] | herkese açıktır |
[Authorized] | [SelfAuthorized] | token gerektirir; yetkiyi action kontrol eder |
[Unauthorized] | [SelfAuthorized] | token gerektirir; yetkiyi action kontrol eder |
[SelfAuthorized] | token gerektirir; yetkiyi action kontrol eder | |
| hiç korunmaz |
[Authorized] yalnızca controller'a konabilir; action'larda tekrarlamaya gerek yoktur.
[Authorized] ya da [SelfAuthorized] yoksa endpoint herkese açıktır ve rbac attribute'ları hiçbir işe yaramaz. SDK bunu derleme sırasında bildirir (ERTISAUTH610, bkz. Derleme zamanı kontrolleri).Attribute'lar#
Attribute'lar ErtisAuth.Extensions.Authorization.Attributes namespace'indedir.
| Attribute | Nerede | Anlamı |
|---|---|---|
[Authorized] | controller | Her action'da geçerli bir token ve rbac attribute'larının tanımladığı yetki gerekir. |
[SelfAuthorized] | controller ya da action | Geçerli bir token gerekir; yetkiyi action'ın kendisi kontrol eder. |
[Unauthorized] | controller ya da action | Herkese açık: token gerekmez. |
[RbacResource("orders")] | controller ya da action | Yetkinin resource bölümü. |
[RbacAction("approve")] ya da [RbacAction(Rbac.CrudActions.Read)] | action | action bölümü. |
[RbacObject("{id}")] | action | object bölümü; genellikle route'taki id. |
[RbacSubject("…")] | action | subject bölümü. |
Kontrol edilen yetki#
Rbac attribute'ları bir yetki ifadesi oluşturur: subject.resource.action.object. ErtisAuth bu ifadeyi çağıranın rolüne, kendi yetkilerine ve token'ının scope'larına göre kontrol eder.
| Bölüm | Kaynağı | Attribute verilmezse |
|---|---|---|
| subject | [RbacSubject] | Çağıranın id'si; neredeyse her zaman istediğiniz de budur |
| resource | Action'ın, yoksa controller'ın [RbacResource]'u | ⚠️ Route şablonunun son parçası, ör. orders/{id} için {id} |
| action | [RbacAction] | ⚠️ *: yalnızca kaynak üzerindeki her işleme izin veren yetkiler geçer |
| object | [RbacObject] | * |
[RbacResource] ve [RbacAction]'ı her zaman verin. Bir action'daki [RbacResource], controller'dakini ezer; örneğin [RbacResource("users")] bir controller'ın tek bir action'ındaki [RbacResource("otp")].
using ErtisAuth.Core.Models.Roles;
using ErtisAuth.Extensions.Authorization.Attributes;
using ErtisAuth.Sdk.AspNetCore.Extensions;
using Microsoft.AspNetCore.Mvc;
[ApiController]
[Route("orders")]
[Authorized]
[RbacResource("orders")]
public class OrdersController : ControllerBase
{
// Requires "orders.read"
[HttpGet]
[RbacAction(Rbac.CrudActions.Read)]
public IActionResult List() { … }
// Requires "orders.read.{id}" (granted by "orders.read" too)
[HttpGet("{id}")]
[RbacAction(Rbac.CrudActions.Read)]
[RbacObject("{id}")]
public IActionResult Get(string id) { … }
// A custom action: requires "orders.approve.{id}"
[HttpPost("{id}/approve")]
[RbacAction("approve")]
[RbacObject("{id}")]
public IActionResult Approve(string id)
{
var utilizer = this.GetUtilizer();
// utilizer.Id, utilizer.Username, utilizer.Role, utilizer.Type (User or Application)…
…
}
// Another resource: requires "invoices.read.{id}"
[HttpGet("{id}/invoice")]
[RbacResource("invoices")]
[RbacAction(Rbac.CrudActions.Read)]
[RbacObject("{id}")]
public IActionResult Invoice(string id) { … }
// Public
[HttpGet("statuses")]
[Unauthorized]
public IActionResult Statuses() { … }
}Yetkiyi kendisi kontrol eden endpoint'ler#
Yetki, attribute'ların erişemediği bir veriye bağlıysa (örneğin istenen kaydın bir alanına) action'ı [SelfAuthorized] ile işaretleyin. SDK token'ın kimliğini doğrular; action ise yetkiyi çağıranın token'ıyla IRoleService.CheckPermissionAsync üzerinden kontrol eder:
using ErtisAuth.Core.Models.Identity;
using ErtisAuth.Extensions.Authorization.Attributes;
using ErtisAuth.Sdk.AspNetCore.Extensions;
using ErtisAuth.Sdk.Services.Interfaces;
using Microsoft.AspNetCore.Mvc;
[ApiController]
[Route("documents")]
public class DocumentsController(IDocumentStore documents, IRoleService roleService) : ControllerBase
{
// Requires "documents-{category}.read.{id}", where the category is a field of the document
[HttpGet("{id}")]
[SelfAuthorized]
public async Task<IActionResult> Get(string id, CancellationToken cancellationToken)
{
var document = await documents.GetAsync(id, cancellationToken);
if (document == null)
{
return this.NotFound();
}
var utilizer = this.GetUtilizer()!.Value;
TokenBase token = utilizer.TokenType == SupportedTokenTypes.Basic ? new BasicToken(utilizer.Token!) : BearerToken.CreateTemp(utilizer.Token!);
if (!await roleService.CheckPermissionAsync($"documents-{document.Category}.read.{id}", token, cancellationToken))
{
return this.StatusCode(StatusCodes.Status403Forbidden);
}
return this.Ok(document);
}
}CheckPermissionAsync, attribute'lar gibi rolü, çağıranın kendi yetkilerini ve token'ının scope'larını uygular. ErtisAuth'a ulaşılamazsa exception fırlatır.
Minimal API'ler#
Minimal API endpoint'leri aynı attribute'ları metadata olarak alır:
app.MapGet("/orders", () => Results.Ok())
.WithMetadata(new AuthorizedAttribute(), new RbacResourceAttribute("orders"), new RbacActionAttribute(Rbac.CrudActions.Read));
app.MapGet("/profile", () => Results.Ok())
.WithMetadata(new SelfAuthorizedAttribute());Derleme zamanı kontrolleri minimal API endpoint'lerini kapsamaz, çünkü attribute'ları ancak çalışma zamanında bilinir.
Bir istekte ne olur#
Bearer token için SDK:
- token'ın kimliğini doğrulamak için ErtisAuth'un
/whoamiendpoint'ini çağırır, - attribute'lardan oluşturulan ifadeyle
/roles/check-permissionendpoint'ini çağırır; bu endpoint rolü, kullanıcının kendi yetkilerini ve token'ın scope'larını uygular.
Basic token için uygulamayı token'ın kendisiyle ErtisAuth'tan okur (bu, token'ın kimliğini doğrular) ve yetkiyi aynı şekilde kontrol eder. BasicTokenCacheTTL ile doğrulanmış bir Basic token o kadar saniye önbellekte tutulur; bu çağrıları azaltır ama bir secret yenilemesinin etkisini en fazla o süre kadar geciktirir.
| API'nizin yanıtı | Ne zaman |
|---|---|
ErtisAuth hata gövdesiyle 401 | Token yok, desteklenmeyen token tipi ya da geçersiz, süresi dolmuş veya iptal edilmiş bir token |
403 AccessDenied | Token geçerli ama yetki eksik |
503 AuthenticationServiceUnavailable | ErtisAuth'a ulaşılamadı ya da bir hatayla yanıt verdi. İstemciler kullanıcıyı çıkış yaptırmak yerine yeniden denemelidir. |
Placeholder'lar#
Rbac attribute değerleri, her istekte çözülen placeholder'lar içerebilir:
| Placeholder | Kaynak |
|---|---|
{id} ya da {route::id} | Bir route değeri |
{query::id} | Bir query string parametresi |
{header::X-Tenant} | Bir istek header'ı |
{env::REGION} | Bir ortam değişkeni |
[HttpGet("reports")]
[RbacAction(Rbac.CrudActions.Read)]
[RbacObject("{query::reportId}")]
public IActionResult Report([FromQuery] string reportId) { … }Kontrol ile action'ın aynı şeye bakmasını sağlayan kurallar:
- çözülemeyen bir
query,headerya daenvplaceholder'ı isteği reddeder; değeri eksik bir route placeholder'ı ise yazıldığı gibi kalır; - çözülen değer
*ise ya da nokta (%2E), boşluk veya kontrol karakteri içeriyorsa istek reddedilir; [RbacSubject], istemcinin kontrol ettiği kaynakları (query,header) kullanamaz.
Derleme zamanı kontrolleri#
ErtisAuth.Sdk.AspNetCore, hataları derleme sırasında bildiren Roslyn analyzer'ları içerir:
| Tanılama | Önem | Sorun |
|---|---|---|
ERTISAUTH601 | uyarı | Placeholder, action'ın bir route parametresi değil |
ERTISAUTH602 | hata | Placeholder bilinmeyen bir kaynak belirtiyor (yalnızca route, query, header ve env vardır) |
ERTISAUTH603 | hata | [RbacSubject] istemcinin kontrol ettiği bir kaynaktan okuyor |
ERTISAUTH610 | uyarı | Action'ın rbac attribute'ları var, ama ne action'da ne controller'ında [Authorized] ya da [SelfAuthorized] var: endpoint herkese açık |
ERTISAUTH611 | uyarı | [Authorized], [SelfAuthorized] ve [Unauthorized] aynı seviyede çelişiyor (base sınıflarıyla birlikte controller ya da action) |
ERTISAUTH612 | bilgi | [SelfAuthorized] ya da [Unauthorized] bir action'ın rbac attribute'ları kontrol edilmiyor |
ERTISAUTH613 | uyarı | Kimlik doğrulanmayan bir action GetUtilizer() çağırıyor; orada her zaman null döner |
Çağıran#
Bir action içinde this.GetUtilizer(), ErtisAuth'un kimliğini doğruladığı çağıranı döner: Id, Username, Role, Permissions, Forbidden, MembershipId, Type, Token ve TokenType özellikleriyle bir kullanıcı ya da uygulama. Kimlik doğrulanmayan endpoint'lerde ([Unauthorized] ya da ErtisAuth attribute'u olmayan) null döner; böyle bir action'daki çağrıyı ERTISAUTH613 bildirir.
this.GetUnverifiedUtilizer() bu endpoint'lerde de çalışır: orada isteğin token'ını doğrulamadan okur (Bearer token'larda imza, süre ve iptal kontrolü, Basic token'larda secret kontrolü yapılmaz).
GetUnverifiedUtilizer()'ı yetki kararlarında ya da veriye erişimde asla kullanmayın; [Authorized] ya da [SelfAuthorized] bir endpoint'te GetUtilizer() kullanın.ErtisAuth API'sini çağırma#
İstemciyi tek başına (ASP.NET Core entegrasyonu olmadan) ErtisAuth.Sdk ile kaydedin:
using ErtisAuth.Sdk.Extensions;
builder.Services.AddErtisAuth();Aynı ayarları kullanır. Ardından ihtiyacınız olan servisleri inject edin:
| Servis | Metotlar |
|---|---|
IAuthenticationService | GetTokenAsync, RefreshTokenAsync, VerifyTokenAsync, RevokeTokenAsync, MeAsync, WhoAmIAsync |
IUserService | GetAsync, QueryAsync, CreateAsync, UpdateAsync, DeleteAsync, BulkDeleteAsync, GetActiveTokensAsync, GetRevokedTokensAsync |
IPasswordService | ChangePasswordAsync, ResetPasswordAsync, SetPasswordAsync |
IRoleService | CRUD, CheckPermissionAsync, CheckPermissionByRoleAsync |
IApplicationService, IWebhookService, IMailHookService | CRUD |
IMembershipService | GetMembershipAsync, GetMembershipsAsync, QueryMembershipsAsync, CreateMembershipAsync, UpdateMembershipAsync, DeleteMembershipAsync |
IActiveTokensService, IRevokedTokensService | GetAsync, QueryAsync |
IAuthenticationService, ASP.NET Core'un Microsoft.AspNetCore.Authentication.IAuthenticationService arayüzüyle aynı adı taşır; iki namespace birden import edilmişse tam adı ya da bir alias kullanın.
Token'lar#
Kaynak servisleri, çağrıda kullanılacak token'ı parametre olarak alır:
- kullanıcı adına yapılan çağrılar için giriş yapmış kullanıcının bir
BearerToken'ı:BearerToken.CreateTemp(accessToken)ham bir access token'ı sarmalar; - backend'inizin çağrıları için uygulamanızın bir
BasicToken'ı:new BasicToken($"{applicationId}:{applicationSecret}").
Uygulama secret'ını sunucuda tutun: bir secret store'da ya da bir ortam değişkeninde; asla bir tarayıcıda ya da mobil uygulamada değil.
Yanıtları işleme#
Her metot, HTTP hatalarında exception fırlatmak yerine bir IResponseResult (ya da IResponseResult<T>) döner:
| Özellik | İçerik |
|---|---|
IsSuccess | ErtisAuth'un başarılı bir durum koduyla yanıt verip vermediği |
Data | Başarı durumunda sonuç |
StatusCode | ErtisAuth yanıtının HTTP durum kodu; yanıt hiç gelmediyse (ör. bir ağ hatası) null |
Json | Yanıtın gövdesi: hata durumunda ErtisAuth'un hata JSON'u |
Exception | Yanıt hiç gelmediğinde exception |
response.IsServiceUnavailable() (namespace ErtisAuth.Sdk.Extensions), ErtisAuth'un erişilemez olmasını (yanıt yok ya da 5xx) bir reddetmeden (ör. 401, 403) ayırır. İkisini farklı ele alın: erişilemezlik durumunda yeniden denenmeli, reddetmede denenmemelidir.
ErtisAuth'un hatalarını istemciye ileten, backend'inizdeki bir giriş endpoint'i:
using System.Net;
using ErtisAuth.Extensions.Authorization.Attributes;
using ErtisAuth.Sdk.Extensions;
using Microsoft.AspNetCore.Mvc;
using IErtisAuthAuthenticationService = ErtisAuth.Sdk.Services.Interfaces.IAuthenticationService;
[ApiController]
[Route("account")]
public class AccountController(IErtisAuthAuthenticationService authenticationService) : ControllerBase
{
[HttpPost("sign-in")]
[Unauthorized]
public async Task<IActionResult> SignIn(SignInRequest request, CancellationToken cancellationToken)
{
var response = await authenticationService.GetTokenAsync(
request.Username,
request.Password,
ipAddress: this.HttpContext.Connection.RemoteIpAddress?.ToString(),
userAgent: this.Request.Headers.UserAgent,
cancellationToken: cancellationToken);
if (response.IsSuccess)
{
return this.Ok(response.Data);
}
if (response.IsServiceUnavailable())
{
return this.StatusCode((int)HttpStatusCode.ServiceUnavailable);
}
// e.g. 401 InvalidCredentials, as ErtisAuth answered it
return new ContentResult { StatusCode = (int?)response.StatusCode, Content = response.Json, ContentType = "application/json" };
}
}Sık yapılan işler#
Access token'ın süresi dolmadan token çiftini yenileme:
var response = await authenticationService.RefreshTokenAsync(refreshToken, cancellationToken);
if (response.IsSuccess)
{
var tokens = response.Data!; // tokens.AccessToken, tokens.RefreshToken…
}Çıkış yapma (logoutFromAllDevices: true ile tüm cihazlarda):
await authenticationService.RevokeTokenAsync(accessToken, logoutFromAllDevices: false, cancellationToken);Uygulamanın token'ıyla kullanıcı oluşturma. Kullanıcı tipinin özel alanları, UserWithPassword'dan türeyen bir sınıfın özellikleridir:
public class Employee : UserWithPassword
{
[JsonPropertyName("department")]
public string? Department { get; set; }
}
var response = await userService.CreateAsync(new Employee
{
MembershipId = membershipId, // the MembershipId of the SDK settings
Username = "ada",
EmailAddress = "ada@example.com",
FirstName = "Ada",
LastName = "Lovelace",
Password = password,
Role = "support",
UserType = "employee",
Department = "Engineering"
}, appToken, cancellationToken);Parola sıfırlama (bkz. Hesap Kurtarma): sıfırlamayı sıfırlama sayfanızın URL'siyle başlatın, ardından linkteki token'la yeni parolayı belirleyin:
await passwordService.ResetPasswordAsync(emailAddress, "https://app.example.com/reset-password", appToken, cancellationToken);
// On the reset page, with the token of the link
await passwordService.SetPasswordAsync(emailAddress, newPassword, resetToken, appToken, cancellationToken);Kullanıcıları sorgulama:
var users = await userService.QueryAsync(appToken, """{ "where": { "is_active": false } }""", limit: 50, sorting: null);GetAsync ve QueryAsync'in yalnızca sıralama parametreleriyle ayrılan iki overload'ı vardır (sorting, ya da orderBy ve sortDirection). Sıralama yapmıyorsanız birini adıyla verin (sorting: null); aksi halde çağrı belirsiz olur ve derlenmez (CS0121).ASP.NET Core olmayan uygulamalar#
Bir worker ya da console uygulaması ErtisAuth.Sdk'yı .NET generic host ile kullanır; bu ona aynı yapılandırmayı sağlar:
using ErtisAuth.Sdk.Extensions;
var builder = Host.CreateApplicationBuilder(args);
builder.Services.AddErtisAuth();
builder.Services.AddHostedService<InactiveUsersReport>();
builder.Build().Run();using ErtisAuth.Core.Models.Identity;
using ErtisAuth.Sdk.Services.Interfaces;
public class InactiveUsersReport(IUserService userService, IConfiguration configuration, ILogger<InactiveUsersReport> logger) : BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
var appToken = new BasicToken($"{configuration["Report:ApplicationId"]}:{configuration["Report:ApplicationSecret"]}");
var response = await userService.QueryAsync(appToken, """{ "where": { "is_active": false } }""", withCount: true, sorting: null, cancellationToken: stoppingToken);
if (response.IsSuccess)
{
logger.LogInformation("{Count} inactive users", response.Data!.Count);
}
}
}Özel authentication handler#
AddErtisAuth<THandler>(), SDK'nın handler'ı yerine kendi authentication handler'ınızı kaydeder ve AddErtisAuth() ile aynı argümanları alır. ErtisAuth kontrollerini koruyup çevresine kendi davranışınızı eklemek için handler'ı ErtisAuthAuthenticationHandler'dan türetin:
using System.Text.Encodings.Web;
using ErtisAuth.Core.Models.Identity;
using ErtisAuth.Sdk.AspNetCore.Middleware;
using Microsoft.AspNetCore.Authentication;
using Microsoft.Extensions.Options;
public class AuditedAuthenticationHandler(
IAuthorizationHandler<BasicToken> basicAuthorizationHandler,
IAuthorizationHandler<BearerToken> bearerAuthorizationHandler,
IOptionsMonitor<AuthenticationSchemeOptions> options,
ILoggerFactory logger,
UrlEncoder encoder)
: ErtisAuthAuthenticationHandler(basicAuthorizationHandler, bearerAuthorizationHandler, options, logger, encoder)
{
protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
{
var result = await base.HandleAuthenticateAsync();
if (result.Failure != null)
{
this.Logger.LogWarning("Request to {Path} was not authenticated: {Reason}", this.Request.Path, result.Failure.Message);
}
return result;
}
}builder.Services.AddErtisAuth<AuditedAuthenticationHandler>();API'nizi test etme#
API'nizin entegrasyon testleri (örneğin WebApplicationFactory ile) çalışan bir ErtisAuth'a ihtiyaç duymamalı. Program.cs'i değiştirmeden handler'ı, bir test çağıranını giriş yaptıran bir handler'la değiştirin. AddErtisAuth yalnızca bir kez kaydettiği için test handler'ını ErtisAuthAuthenticationHandler olarak kaydedin:
using System.Security.Claims;
using System.Text.Encodings.Web;
using ErtisAuth.Core.Models.Identity;
using ErtisAuth.Extensions.Authorization.Extensions;
using ErtisAuth.Sdk.AspNetCore.Middleware;
using Microsoft.AspNetCore.Authentication;
using Microsoft.Extensions.Options;
public class TestAuthenticationHandler(
IAuthorizationHandler<BasicToken> basicAuthorizationHandler,
IAuthorizationHandler<BearerToken> bearerAuthorizationHandler,
IOptionsMonitor<AuthenticationSchemeOptions> options,
ILoggerFactory logger,
UrlEncoder encoder)
: ErtisAuthAuthenticationHandler(basicAuthorizationHandler, bearerAuthorizationHandler, options, logger, encoder)
{
protected override Task<AuthenticateResult> HandleAuthenticateAsync()
{
var utilizer = new Utilizer
{
Id = "test-user",
Username = "test",
Role = "admin",
Type = Utilizer.UtilizerType.User,
MembershipId = "test-membership",
Token = "test-token",
TokenType = SupportedTokenTypes.Bearer
};
var principal = new ClaimsPrincipal(utilizer.ToClaimsIdentity());
return Task.FromResult(AuthenticateResult.Success(new AuthenticationTicket(principal, this.Scheme.Name)));
}
}using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.AspNetCore.TestHost;
public class OrdersApiTests(WebApplicationFactory<Program> factory) : IClassFixture<WebApplicationFactory<Program>>
{
private HttpClient CreateClient() => factory
.WithWebHostBuilder(builder => builder.ConfigureTestServices(services =>
services.AddTransient<ErtisAuthAuthenticationHandler, TestAuthenticationHandler>()))
.CreateClient();
}Böylece her istek test çağıranı olarak doğrulanır ve hiçbir yetki kontrol edilmez. Token ve TokenType verilmelidir: GetUtilizer() bunları gerektirir.
Kontrol listesi#
- Korunacak endpoint'leri olan her controller'da
[Authorized]var ya da action'larında[SelfAuthorized]var. HerERTISAUTH610uyarısını düzeltin. - Korunan her action'da (ya da controller'ında)
[RbacResource]ve action'da[RbacAction]var. - Yetki kararlarında
GetUtilizer()kullanılıyor, aslaGetUnverifiedUtilizer()değil. - İstemciler
503 AuthenticationServiceUnavailablealdığında kullanıcıyı çıkış yaptırmak yerine yeniden deniyor. - Uygulama secret'ı yalnızca sunucuda.
BasicTokenCacheTTLile, yenilenmiş bir secret API'nizde en fazla o kadar saniye çalışmaya devam eder.- Ayarlar kodda değil, uygulamanın yapılandırmasında (
appsettings.json, ortam değişkenleri…).
Dokümantasyonda bir hata mı buldunuz? Issue açın