How ASP.NET Core Handles Authentication and Authorization: A Deep Dive into the Security Middleware Pipeline
ASP.NET Core separates authentication (identifying the user) from authorization (controlling access) into two distinct middleware components that run sequentially in the request pipeline, using dependency-injected services to validate credentials and enforce policies before reaching your application code.
In the dotnet/aspnetcore repository, ASP.NET Core implements security as a layered middleware architecture where authentication and authorization operate as distinct but coordinated concerns. Understanding how ASP.NET Core handles authentication and authorization requires examining the internal implementations that intercept requests, validate security tokens, and enforce access policies based on endpoint metadata. The framework achieves this through two primary middleware classes that populate HttpContext.User and evaluate authorization policies against endpoint requirements.
The Authentication Pipeline
The AuthenticationMiddleware class located in src/Security/Authentication/Core/src/AuthenticationMiddleware.cs serves as the entry point for identity validation. This middleware creates the infrastructure for authenticating requests and establishes the security principal that downstream components consume.
Creating the Authentication Feature
When a request enters the pipeline, the middleware immediately establishes tracking infrastructure by setting an IAuthenticationFeature on the HTTP context. According to the source code, this feature stores the original request path for later use during challenge or redirect operations. The middleware initializes this feature using context.Features.Set<IAuthenticationFeature>(new AuthenticationFeature ...), ensuring that authentication handlers can access the original URL even if the request gets modified during processing.
Handling Request Handlers
Before performing default authentication, the middleware iterates through registered IAuthenticationRequestHandler implementations using await Schemes.GetRequestHandlerSchemesAsync(). These specialized handlers, such as those for OpenID Connect or Ws-Federation, receive an opportunity to process the request directly. If a request handler handles the authentication challenge (for example, processing an OAuth callback), the pipeline short-circuits immediately, preventing unnecessary processing of the remaining middleware chain.
Default Authentication and User Population
If no request handler processes the request, the middleware obtains the default authentication scheme via IAuthenticationSchemeProvider and executes await context.AuthenticateAsync(defaultAuthenticate.Name). Upon successful authentication, the middleware populates HttpContext.User with the resulting principal using context.User = result.Principal. Additionally, it registers an IHttpAuthenticationFeature containing the authentication result, enabling downstream middleware like authorization to access the authenticated identity and authentication properties without re-validating credentials.
The Authorization Pipeline
Following authentication, the AuthorizationMiddleware in src/Security/Authorization/Policy/src/AuthorizationMiddleware.cs determines whether the authenticated user possesses sufficient permissions to access the requested resource. This middleware operates on endpoint metadata and policy-based authorization requirements.
Policy Resolution and Caching
The middleware first retrieves the endpoint using context.GetEndpoint() and inspects its metadata for IAuthorizeData attributes or custom policy configurations. It constructs the effective authorization policy by calling AuthorizationPolicy.CombineAsync, which merges requirements from the endpoint metadata with the default policy provider configuration. For performance optimization, the middleware optionally caches the computed policy using _policyCache!.Store(endpoint!, policy), eliminating redundant policy construction for repeat requests to the same endpoint.
Policy Evaluation and Enforcement
The middleware utilizes IPolicyEvaluator to perform two critical operations. First, it calls await policyEvaluator.AuthenticateAsync(policy, context) to re-run authentication respecting the policy's specific authentication schemes, updating the IHttpAuthenticationFeature with the latest results. Subsequently, it executes await policyEvaluator.AuthorizeAsync(policy, ...) to validate the principal against the policy's requirements. If authorization fails, the middleware invokes IAuthorizationMiddlewareResultHandler to generate appropriate 401 or 403 responses, or proceeds to the next middleware if the policy succeeds.
Middleware Interaction Flow
The sequential registration of these middleware components creates a coordinated security boundary:
app.UseAuthentication()insertsAuthenticationMiddlewareinto the pipeline, which validates credentials and establishesHttpContext.User.- The authentication middleware runs request handlers and executes the default authentication scheme, storing results in HTTP features.
app.UseAuthorization()insertsAuthorizationMiddleware, which evaluates endpoint-specific policies against the authenticated principal.- The authorization middleware retrieves endpoint metadata, combines policies, and uses
IPolicyEvaluatorto authenticate and authorize the request. - If authorization fails,
IAuthorizationMiddlewareResultHandlerproduces the challenge or forbidden response; otherwise, the request proceeds to application endpoints.
Both middlewares rely on dependency-injected services including IAuthenticationSchemeProvider for discovering configured schemes, IAuthenticationHandlerProvider for resolving specific handlers, IPolicyEvaluator for central authorization logic, and IAuthorizationPolicyProvider for supplying default and custom policies.
Extensibility and Custom Implementations
The architecture supports extensive customization through handler interfaces. Custom authentication handlers implement IAuthenticationHandler or IAuthenticationRequestHandler and register via services.AddAuthentication().AddScheme<...>(). Custom authorization policies require implementing IAuthorizationRequirement and a corresponding IAuthorizationHandler, then attaching policies to endpoints using the [Authorize] attribute or explicit endpoint metadata configuration.
Implementation Example
The following example demonstrates configuring both middleware components with cookie and JWT authentication, plus a custom authorization policy:
var builder = WebApplication.CreateBuilder(args);
// Register authentication schemes
builder.Services.AddAuthentication(options =>
{
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddCookie()
.AddJwtBearer(options =>
{
options.Authority = "https://login.example.com/";
options.Audience = "api1";
});
// Register authorization policies
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("AdminOnly", policy =>
policy.RequireRole("Administrator"));
});
var app = builder.Build();
// Insert middleware in correct order
app.UseAuthentication(); // Runs AuthenticationMiddleware
app.UseAuthorization(); // Runs AuthorizationMiddleware
app.MapGet("/admin", [Authorize("AdminOnly")] (HttpContext ctx) =>
{
return Results.Ok($"Hello {ctx.User.Identity?.Name}");
});
app.Run();
The UseAuthentication call wires the AuthenticationMiddleware that populates HttpContext.User, while UseAuthorization inserts the AuthorizationMiddleware that evaluates the "AdminOnly" policy against the endpoint metadata before executing the request delegate.
Summary
- AuthenticationMiddleware in
src/Security/Authentication/Core/src/AuthenticationMiddleware.csestablishes identity by running request handlers and authenticating the default scheme, storing results inHttpContext.UserandIHttpAuthenticationFeature. - AuthorizationMiddleware in
src/Security/Authorization/Policy/src/AuthorizationMiddleware.csevaluates policies usingIPolicyEvaluatorto check the authenticated principal against endpoint requirements. - Middleware Ordering matters:
UseAuthentication()must precedeUseAuthorization()to ensure the user principal exists before policy evaluation. - Policy Caching improves performance by storing computed authorization policies per endpoint.
- Extensibility comes through
IAuthenticationHandlerfor custom schemes andIAuthorizationHandlerfor custom authorization requirements.
Frequently Asked Questions
What is the difference between UseAuthentication and UseAuthorization in ASP.NET Core?
UseAuthentication() registers the AuthenticationMiddleware that validates security credentials and populates HttpContext.User with the authenticated principal. UseAuthorization() registers the AuthorizationMiddleware that evaluates authorization policies against that principal based on endpoint metadata. Authentication must run before authorization in the pipeline because authorization decisions depend on the identity established during authentication.
How does the AuthorizationMiddleware determine which policy to apply?
The middleware retrieves the current endpoint using context.GetEndpoint() and inspects its metadata for IAuthorizeData implementations, such as the [Authorize] attribute. It calls AuthorizationPolicy.CombineAsync to merge requirements from the endpoint metadata with the default policy, optionally caching the result for subsequent requests. The computed policy specifies which authentication schemes to use and which requirements the user must satisfy.
Can authentication handlers short-circuit the request pipeline?
Yes. The AuthenticationMiddleware iterates through registered IAuthenticationRequestHandler implementations before performing default authentication. If a handler processes the request (for example, handling an OAuth callback or WS-Federation sign-in), it can short-circuit the pipeline by completing the response, preventing subsequent middleware from executing. This behavior is defined in src/Security/Authentication/Core/src/AuthenticationMiddleware.cs.
How do custom authentication schemes integrate with the pipeline?
Custom schemes implement IAuthenticationHandler or IAuthenticationRequestHandler and register through services.AddAuthentication().AddScheme<TOptions, THandler>(). The AuthenticationMiddleware resolves these handlers using IAuthenticationSchemeProvider and IAuthenticationHandlerProvider, allowing custom logic for credential validation and challenge generation to execute within the standard authentication flow.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →