Claims Transformation în .NET
Doru Bulubasa
19 March 2026

Cum construiești permisiuni dinamice și multi-tenant în ASP.NET Core

Seria: Security by Design în .NET: De la JWT la Certificate-based Authentication

În articolele anterioare am discutat despre:

  • JWT

  • Refresh Tokens

  • Authorization (roles vs policies)

Dar apare o problemă reală în aplicații:

JWT-ul conține informații limitate și statice.

Ce faci când ai nevoie de:

  • permisiuni dinamice din DB

  • logică multi-tenant

  • roluri care se schimbă fără re-login

Aici intervine Claims Transformation.


🧠 Ce este Claims Transformation

Claims Transformation îți permite să:

modifici sau să adaugi claims după autentificare, la fiecare request.

ASP.NET Core oferă interfața:

IClaimsTransformation

Este executată după validarea token-ului.


⚙️ 1️⃣ Implementare IClaimsTransformation

Exemplu simplu:

public class CustomClaimsTransformation : IClaimsTransformation
{
    public Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal)
    {
        var identity = (ClaimsIdentity)principal.Identity;

        if (!identity.HasClaim(c => c.Type == "custom"))
        {
            identity.AddClaim(new Claim("custom", "true"));
        }

        return Task.FromResult(principal);
    }
}

Înregistrare:

builder.Services.AddScoped<IClaimsTransformation, CustomClaimsTransformation>();


🧩 2️⃣ Enriching Claims din baza de date

Cel mai util scenariu:

adaugi claims din DB la fiecare request.

Exemplu:

public class DbClaimsTransformation : IClaimsTransformation
{
    private readonly IUserRepository _repo;

    public DbClaimsTransformation(IUserRepository repo)
    {
        _repo = repo;
    }

    public async Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal)
    {
        var identity = (ClaimsIdentity)principal.Identity;

        var userId = identity.FindFirst("sub")?.Value;

        var user = await _repo.GetUserWithPermissions(userId);

        foreach (var permission in user.Permissions)
        {
            identity.AddClaim(new Claim("permission", permission));
        }

        return principal;
    }
}


🏢 3️⃣ Multi-tenant logic

În aplicațiile reale:

un user poate aparține mai multor companii.

JWT-ul conține:

{
  "sub": "user123"
}

Dar la runtime ai nevoie de:

  • CompanyId curent

  • Rol în companie

  • Permisiuni specifice companiei

Claims Transformation poate face asta:

identity.AddClaim(new Claim("companyId", selectedCompanyId));
identity.AddClaim(new Claim("role", "Manager"));


🔄 4️⃣ Dynamic Permissions

Role-based nu este suficient în aplicații complexe.

În schimb:

permission = "invoice.read"
permission = "invoice.create"
permission = "invoice.delete"

Adăugate ca claims:

identity.AddClaim(new Claim("permission", "invoice.read"));

Apoi folosești policy:

options.AddPolicy("CanReadInvoice",
    policy => policy.RequireClaim("permission", "invoice.read"));


⚠️ Probleme și capcane


❌ Executare la fiecare request

Claims Transformation rulează la fiecare request.

Dacă faci query în DB:

→ impact de performanță


✅ Soluții

  • cache (MemoryCache / Redis)

  • claim versioning

  • limitare date


❌ Duplicate claims

Dacă nu verifici:

→ vei adăuga aceleași claims de mai multe ori


❌ Logică prea complexă

Nu transforma ClaimsTransformation într-un „mini service layer”.


🧱 Arhitectură recomandată

JWT conține:

sub (userId)
email

Claims Transformation adaugă:

permissions
companyId
roles

Authorization folosește:

policies + handlers


🔥 Best Practices

✔ JWT minimal (doar identificare)

✔ Claims enrichment din DB

✔ Cache pentru performanță

✔ Dynamic permissions

✔ Multi-tenant aware

✔ Policy-based authorization


🎯 Concluzie

Claims Transformation este cheia pentru:

✔ permisiuni dinamice

✔ aplicații multi-tenant

✔ logică flexibilă de autorizare

Este diferența dintre:

un sistem simplu de roluri
și
un sistem enterprise de permisiuni