Interview question
Authorize a loaded resource by ownership
Loads a resource once, evaluates owner-or-capability authorization, and avoids trusting route ownership claims.
TL;DR
Loads a resource once, evaluates owner-or-capability authorization, and avoids trusting route ownership claims.
Resource authorization, ownership, capability overrides, enumeration resistance, and handler boundaries.
Practice the problem like a real interview: restate, reason, implement, and test.
A user may read their own invoice. Support staff with invoices.read.any may read any invoice. Everyone else receives the endpoint's deliberate not-found response.
Next in API Implementation Labs: Secure Upload
Changing the route id to another customer's invoice never bypasses the resource authorization handler.
I load the invoice using only its id, then pass the actual entity to IAuthorizationService. The handler compares trusted identity data to the entity and supports one explicit override capability.
public sealed record ReadInvoiceRequirement : IAuthorizationRequirement;
public sealed class ReadInvoiceHandler
: AuthorizationHandler<ReadInvoiceRequirement, Invoice>
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
ReadInvoiceRequirement requirement, Invoice invoice)
{
var userId = context.User.TryGetUserId();
var canReadAny = context.User.HasClaim("capability", "invoices.read.any");
if (userId == invoice.OwnerId || canReadAny)
context.Succeed(requirement);
return Task.CompletedTask;
}
}
app.MapGet("/invoices/{id:guid}", async Task<IResult> (Guid id,
AppDbContext db, IAuthorizationService authorization,
ClaimsPrincipal user, CancellationToken ct) =>
{
var invoice = await db.Invoices.AsNoTracking().SingleOrDefaultAsync(x => x.Id == id, ct);
if (invoice is null) return Results.NotFound();
var allowed = await authorization.AuthorizeAsync(user, invoice,
new ReadInvoiceRequirement());
return allowed.Succeeded
? Results.Ok(InvoiceResponse.From(invoice))
: Results.NotFound();
}).RequireAuthorization();
Returning 404 for both absent and unauthorized ids reduces resource enumeration in this contract. Other products may choose 403 for transparency, but the choice should be consistent and logged safely.
Share the question with a concise Core-answer excerpt and invite other developers to add their perspective.
Help improve the interview library.
Say thanks with a standalone, one-time $5 contribution. No account required.
Aporeon is shaped by Aleksandar Tomovski, a software developer with experience on both sides of technical interviews. Content is reviewed for accuracy, natural spoken delivery, useful depth, and honest trade-offs.