Authentication answers "who is this?"; authorization answers "are they allowed to do this?" — always in that order in the pipeline (see the middleware section above).
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = "myapp",
ValidAudience = "myapp-clients",
IssuerSigningKey = new SymmetricSecurityKey(signingKeyBytes),
};
});
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("RequireAdmin", policy => policy.RequireRole("Admin"));
});
[Authorize] // requires any authenticated user
[HttpGet("me")]
public IActionResult GetCurrentUser() => Ok(User.Identity?.Name);
[Authorize(Policy = "RequireAdmin")] // requires the specific policy defined above
[HttpDelete("{id}")]
public async Task<IActionResult> Delete(int id) { /* ... */ return NoContent(); }
[AllowAnonymous] // explicitly opts out of a controller-level [Authorize]
[HttpGet("public-info")]
public IActionResult PublicInfo() => Ok("anyone can see this");
See OAuth and JWT Explained for how the token itself is issued and validated; this section covers only how ASP.NET Core wires that validation into the pipeline. A [Authorize] attribute can be applied at the controller level (protecting every action) with [AllowAnonymous] on specific actions that should be exempt — the more common pattern than sprinkling [Authorize] on every individual action in a mostly-protected controller.