.NET Core / Web API71 min total · 19 parts
Building REST APIs with ASP.NET Core: Routing, Middleware, and Dependency Injection
Part 12 of 19 · ~1 min
Authentication and Authorization Middleware
Authentication settles who's actually making the request. Authorization settles what that identity is allowed to do once it's known — and the middleware chapter's ordering rule applies here without exception, because the second question is meaningless until the first one has an answer.
Every Ridgeline student and staff member already has a campus-wide login — Bench doesn't run its own password system, it trusts tokens issued by the university's identity provider, CampusKey:
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = "https://id.campuskey.example",
ValidAudience = "bench-api",
IssuerSigningKey = campusKeySigningKey,
};
});
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("RequireStaff", policy => policy.RequireClaim("role", "MakerspaceStaff"));
});
[Authorize] // any signed-in member
[HttpPost]
public async Task<ActionResult<Reservation>> Create(int toolId, CreateReservationRequest request) { /* ... */ }
[Authorize(Policy = "RequireStaff")] // staff only — overrides a reservation someone else made
[HttpPost("{id:guid}/override")]
public async Task<IActionResult> Override(int toolId, Guid id) { /* ... */ return NoContent(); }
[AllowAnonymous] // browsing tools doesn't require signing in
[HttpGet("/api/tools")]
public async Task<ActionResult<IEnumerable<Tool>>> ListTools() => Ok(await _tools.GetAllAsync());
OAuth and JWT Explained covers how CampusKey actually mints and signs that token in the first place; this chapter's only concern is what ASP.NET Core does with it once it arrives — plugging validation into the pipeline, nothing about issuance. Bench settled on [Authorize] sitting at the top of ReservationsController, covering the whole class by default, with [AllowAnonymous] called out on the handful of actions that need to opt back out. Most of what's in there genuinely does require a signed-in member, so that's the sane default — and naming the exceptions explicitly is easier to audit than the reverse would be.