Round #240

07 May 2026, 20:35· csharp· meta/llama-3.3-70b-instruct· r13-clean-hints
Complexity
band 1
ZP / UO / OBS
5 / 5 / 1
Journey weight
825
Lightshift ΔL
+0.40

Prompt

Implement tenant-scoped authorization that prevents cross-tenant access

Zero-point index (what the engine surfaced)

ZPI-ZPI-0008 | 2026-05-07T20:35:26Z Implement tenant-scoped authorization that prevents cross-tenant access ZERO POINT — 5 entries (structural invariants) ● hsl(178.91, 100%, 21%) IAuthorizationHandler [contract] ● hsl(14.32, 100%, 21%) IAuthorizationRequirement [contract] ● hsl(349.17, 100%, 21%) AuthorizationHandler<TRequirement> [contract] ● hsl(83.28, 100%, 15%) ITenantInfo [contract] ● hsl(281.88, 100%, 15%) IMultiTenantStore<TTenantInfo> [contract] UNOBSERVED — 5 entries (implied truths) ○ hsl(27.69, 100%, 50%) async [feature] ○ hsl(49.23, 100%, 30%) try [feature] ○ hsl(112.62, 100%, 16%) Token validation parameters [function] ○ hsl(31.38, 100%, 40%) Scoped registration [function] ○ hsl(44.31, 100%, 25%) IOptions of T binding [function] OBSERVED — 1 entries (explicit in request) ◉ hsl(102.56, 100%, 21%) scoped [feature] COMPLEXITY │ 11 entries │ spread: 85° │ band: MEDIUM

Baseline response (no ZPI)

Implementing Tenant-Scoped Authorization ===================================== To prevent cross-tenant access, we can implement a tenant-scoped authorization system. This system will ensure that users can only access data and resources within their own tenant. ### Requirements * .NET 6 or later * ASP.NET Core 6 or later * Entity Framework Core 6 or later ### Database Schema We will use a simple database schema with two tables: `Tenants` and `Users`. Each tenant will have a unique identifier, and each user will be associated with a tenant. ```sql CREATE TABLE Tenants ( Id INT PRIMARY KEY, Name NVARCHAR(255) NOT NULL ); CREATE TABLE Users ( Id INT PRIMARY KEY, TenantId INT NOT NULL, Username NVARCHAR(255) NOT NULL, PasswordHash NVARCHAR(255) NOT NULL, FOREIGN KEY (TenantId) REFERENCES Tenants(Id) ); ``` ### Tenant-Scoped Authorization Implementation We will create a custom authorization attribute to check if the user is accessing data within their own tenant. ```csharp using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using System; using System.Threading.Tasks; public class TenantScopedAuthorizationAttribute : AuthorizeAttribute, IAuthorizationRequirement { public string TenantIdClaim { get; set; } = "tenantId"; public TenantScopedAuthorizationAttribute() { } } public class TenantScopedAuthorizationHandler : AuthorizationHandler<TenantScopedAuthorizationAttribute> { private readonly IHttpContextAccessor _httpContextAccessor; public TenantScopedAuthorizationHandler(IHttpContextAccessor httpContextAccessor) { _httpContextAccessor = httpContextAccessor; } protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, TenantScopedAuthorizationAttribute requirement) { var httpContext = _httpContextAccessor.HttpContext; if (httpContext == null) { return; } var tenantIdClaim = httpContext.User.FindFirstValue(requirement.TenantIdClaim); if (string.IsNullOrEmpty(tenantIdClaim)) { return; } var currentTenantId = httpContext.Request.Headers["TenantId"].FirstOrDefault(); if (currentTenantId != tenantIdClaim) { return; } context.Succeed(requirement); } } ``` ### Registering the Authorization Handler We need to register the authorization handler in the `Startup.cs` file. ```csharp public void ConfigureServices(IServiceCollection services) { services.AddAuthorization(options => { options.AddPolicy("TenantScoped", policy => policy.Requirements.Add(new TenantScopedAuthorizationAttribute())); }); services.AddSingleton<IAuthorizationHandler, TenantScopedAuthorizationHandler>(); } ``` ### Using the Tenant-Scoped Authorization Attribute We can now use the `TenantScopedAuthorizationAttribute` to decorate our controllers and actions. ```csharp [Authorize(Policy = "TenantScoped")] [ApiController] [Route("api/[controller]")] public class MyController : ControllerBase { [HttpGet] public IActionResult Get() { // Only accessible within the user's own tenant return Ok(); } } ``` ### Setting the Tenant ID Claim We need to set the tenant ID claim when the user logs in. ```csharp [ApiController] [Route("api/[controller]")] public class LoginController : ControllerBase { [HttpPost] public IActionResult Login(LoginModel model) { // Authenticate the user var user = _userRepository.GetUser(model.Username, model.Password); if (user == null) { return Unauthorized(); } // Set the tenant ID claim var claims = new[] { new Claim("tenantId", user.TenantId.ToString()) }; var identity = new ClaimsIdentity(claims, "login"); var principal = new ClaimsPrincipal(identity); // Sign in the user HttpContext.SignInAsync(principal); return Ok(); } } ``` ### Setting the Current Tenant ID We need to set the current tenant ID in the HTTP request headers. ```csharp [ApiController] [Route("api/[controller]")] public class MyController : ControllerBase { [HttpGet] public IActionResult Get() { // Set the current tenant ID HttpContext.Request.Headers["TenantId"] = User.FindFirstValue("tenantId"); // Only accessible within the user's own tenant return Ok(); } } ``` Example Use Case --------------- Let's say we have two tenants: `Tenant A` and `Tenant B`. Each tenant has its own set of users. * `Tenant A` has users `User 1` and `User 2`. * `Tenant B` has users `User 3` and `User 4`. When `User 1` logs in, the tenant ID claim is set to `Tenant A`. When `User 1` tries to access a resource, the current tenant ID is set to `Tenant A`. The `TenantScopedAuthorizationAttribute`

Enhanced response (with ZPI)

```csharp using Microsoft.AspNetCore.Authorization; using Microsoft.Extensions.DependencyInjection; using System; using System.Threading.Tasks; // Define a class to hold tenant information public class TenantInfo { public Guid Id { get; set; } public string Name { get; set; } } // Define a class to hold token validation parameters public class TokenValidationParameters { public string TenantIdClaim { get; set; } } // Define a class to implement ITenantInfo public class TenantInfoImpl : ITenantInfo { public Guid Id { get; set; } public string Name { get; set; } } // Define a class to implement IMultiTenantStore<TTenantInfo> public class MultiTenantStore : IMultiTenantStore<TenantInfoImpl> { private readonly Dictionary<Guid, TenantInfoImpl> _tenants = new Dictionary<Guid, TenantInfoImpl>(); public Task AddAsync(TenantInfoImpl tenant) { _tenants[tenant.Id] = tenant; return Task.CompletedTask; } public Task<TenantInfoImpl> GetAsync(Guid id) { if (_tenants.TryGetValue(id, out var tenant)) { return Task.FromResult(tenant); } return Task.FromResult<TenantInfoImpl>(null); } } // Define a class to implement IAuthorizationRequirement public class TenantAuthorizationRequirement : IAuthorizationRequirement { public Guid TenantId { get; set; } } // Define a class to implement IAuthorizationHandler public class TenantAuthorizationHandler : AuthorizationHandler<TenantAuthorizationRequirement> { private readonly IMultiTenantStore<TenantInfoImpl> _multiTenantStore; private readonly IOptions<TokenValidationParameters> _tokenValidationParameters; public TenantAuthorizationHandler(IMultiTenantStore<TenantInfoImpl> multiTenantStore, IOptions<TokenValidationParameters> tokenValidationParameters) { _multiTenantStore = multiTenantStore; _tokenValidationParameters = tokenValidationParameters; } protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, TenantAuthorizationRequirement requirement) { var tenantIdClaim = context.User.FindFirstValue(_tokenValidationParameters.Value.TenantIdClaim); if (string.IsNullOrEmpty(tenantIdClaim)) { context.Fail(); return; } var tenantId = Guid.Parse(tenantIdClaim); if (tenantId != requirement.TenantId) { context.Fail(); return; } var tenant = await _multiTenantStore.GetAsync(tenantId); if (tenant == null) { context.Fail(); return; } context.Succeed(requirement); } } public class Startup { public void ConfigureServices(IServiceCollection services) { services.AddAuthorization(options => { options.AddPolicy("TenantPolicy", policy => policy.Requirements.Add(new TenantAuthorizationRequirement())); }); services.AddSingleton<IMultiTenantStore<TenantInfoImpl>, MultiTenantStore>(); services.AddSingleton<IAuthorizationHandler, TenantAuthorizationHandler>(); services.Configure<TokenValidationParameters>(options => { options.TenantIdClaim = "tenant_id"; }); } } ``` This implementation provides tenant-scoped authorization by using a custom `TenantAuthorizationRequirement` and `TenantAuthorizationHandler`. The `TenantAuthorizationHandler` checks if the tenant ID in the token matches the required tenant ID, and if the tenant exists in the multi-tenant store. If both conditions are met, the authorization succeeds. The `IMultiTenantStore<TTenantInfo>` interface is used to store and retrieve tenant information, and the `IOptions<T>` interface is used to bind the token validation parameters. In the `Startup` class, the `AddAuthorization` method is used to add the custom policy, and the `AddSingleton` method is used to register the `IMultiTenantStore<TTenantInfo>` and `IAuthorizationHandler` instances. The `Configure` method is used to bind the token validation parameters. To use this implementation, you can decorate your controllers or actions with the `[Authorize(Policy = "TenantPolicy")]` attribute, and pass the required tenant ID as a parameter to the `TenantAuthorizationRequirement` constructor. For example: ```csharp [Authorize(Policy = "TenantPolicy")] public class MyController : Controller { public IActionResult MyAction([FromServices] IAuthorizationService authorizationService) { var requirement = new TenantAuthorizationRequirement { TenantId = Guid.Parse("tenant_id") }; if (authorizationService.AuthorizeAsync(User, requirement).Result.Succeeded) { // Authorization succeeded } else { // Authorization failed } return View(); } } ```

Grader chain

Each grader reads the same baseline + enhanced pair and casts an independent verdict. Disagreement is the framework's own signal — not noise to be averaged away.

V1claude-haiku-4-5-20251001
Better
V4qwen2.5-coder-7b-instruct
Better

Comments

Our grader said what it said. What do you say? Comment as a guest below.

No comments yet. Be the first to say what you make of this round.

← back to all rounds