Skip to main content

Pipeline Middleware Reference — ASP.NET Core

Data: 2026-07-09 Escopo: UseRouting, UseAuthentication, UseAuthorization, UseEndpoints, AddSecondaryAuthentication


1. Visão Geral

O pipeline de middleware do ASP.NET Core é uma cadeia de delegates executados em sequência. Cada middleware decide se passa o request para o próximo (_next) ou termina o pipeline (short-circuit).

Ordem Obrigatória (documentação Microsoft)

UseRouting → UseCors → UseAuthentication → UseAuthorization → UseEndpoints

Fonte: Microsoft Learn — Middleware order — "CORS Middleware, Authentication Middleware, and Authorization Middleware must appear in the order shown."

https://learn.microsoft.com/en-us/aspnet/core/fundamentals/middleware/#middleware-order

Terminal Middleware

Middleware que não invoca _next é chamado de terminal middleware — previne que middlewares registrados depois dele executem.

Fonte: Microsoft Learn — Middleware — "When a middleware short-circuits, it's called a terminal middleware because it prevents further middleware from processing the request."

https://learn.microsoft.com/en-us/aspnet/core/fundamentals/middleware/

UseEndpoints(MapControllers) é terminal quando uma rota casa com um controller.


2. UseRouting

Responsabilidade

Seleciona o endpoint que melhor casa com o request (URL path, HTTP method, constraints). Popula metadata no HttpContext.

Funcionamento

  1. Examina todos os endpoints registrados (controllers, health checks, etc.)
  2. Aplica URL matching, constraints, MatcherPolicy instances
  3. Seleciona o melhor match via EndpointSelector
  4. Define HttpContext.GetEndpoint() → disponível para middlewares subsequentes
  5. Popula HttpRequest.RouteValues com parâmetros da rota

Fonte: Microsoft Learn — Routing — "UseRouting adds route matching to the middleware pipeline. This middleware looks at the set of endpoints defined in the app, and selects the best match based on the request."

https://learn.microsoft.com/en-us/aspnet/core/fundamentals/routing#routing-basics

Restrições

  • Metadata do endpoint ([Authorize], [AllowAnonymous], etc.) só está disponível depois de UseRouting
  • Antes de UseRouting: HttpContext.GetEndpoint() = null
  • Depois de UseRouting e antes de UseEndpoints: endpoint disponível para inspeção por UseCors, UseAuthentication, UseAuthorization

Fonte: Microsoft Learn — Routing — "The endpoint is always null before UseRouting is called. If a match is found, the endpoint is non-null between UseRouting and UseEndpoints."

https://learn.microsoft.com/en-us/aspnet/core/fundamentals/routing#endpoints


3. UseAuthentication

Responsabilidade

Autentica o usuário usando o esquema de autenticação registrado (JWT Bearer, cookies, etc.). Popula HttpContext.User com um ClaimsPrincipal em caso de sucesso.

Funcionamento (código fonte)

// AuthenticationMiddleware.cs — dotnet/aspnetcore (MIT License)
// https://github.com/dotnet/aspnetcore/blob/main/src/Security/Authentication/Core/src/AuthenticationMiddleware.cs

public async Task Invoke(HttpContext context)
{
// 1. IAuthenticationRequestHandler schemes — handle if applicable
foreach (var scheme in await Schemes.GetRequestHandlerSchemesAsync())
{
var handler = await handlers.GetHandlerAsync(context, scheme.Name)
as IAuthenticationRequestHandler;
if (handler != null && await handler.HandleRequestAsync())
return; // ← único caso de short-circuit
}

// 2. Default authenticate scheme → AuthenticateAsync
var defaultAuthenticate = await Schemes.GetDefaultAuthenticateSchemeAsync();
if (defaultAuthenticate != null)
{
var result = await context.AuthenticateAsync(defaultAuthenticate.Name);
if (result?.Principal != null)
context.User = result.Principal; // ← popula User

if (result?.Succeeded ?? false)
{
// Armazena AuthenticateResult no HttpContext.Features
var authFeatures = new AuthenticationFeatures(result);
context.Features.Set<IHttpAuthenticationFeature>(authFeatures);
context.Features.Set<IAuthenticateResultFeature>(authFeatures);
}
}

// 3. SEMPRE chama o próximo middleware
await _next(context); // ← NUNCA bloqueia por falha de auth
}

Fonte: https://github.com/dotnet/aspnetcore/blob/main/src/Security/Authentication/Core/src/AuthenticationMiddleware.cs

Comportamento com JWT Bearer (Coezzion)

┌─ JwtBearerHandler.HandleAuthenticateAsync() ─────────────┐
│ 1. OnMessageReceived: carrega IssuerSigningKey do AWS │
│ Secrets Manager (lazy, só no primeiro request) │
│ 2. Lê header Authorization: Bearer {token} │
│ 3. Valida assinatura (HMAC-SHA256) │
│ 4. Valida expiração (ValidateLifetime = true) │
│ 5. NÃO valida Issuer/Audience (ValidateIssuer = false, │
│ ValidateAudience = false) │
│ 6. Cria ClaimsPrincipal com claims: │
│ ├── sub → ClaimTypes.NameIdentifier │
│ ├── schema → "schema" │
│ ├── profile → "profile" │
│ └── unique_name │
└──────────────────────────────────────────────────────────┘

AuthenticationConfig.cs (Coezzion):

  • ValidateIssuer = false
  • ValidateAudience = false
  • ValidateLifetime = true
  • ValidateIssuerSigningKey = true (HMAC-SHA256 com secret do AWS Secrets Manager)

Restrições e Side Effects

AspectoDetalhe
Nunca rejeita requestSempre chama _next(context) — sucesso, falha, ou sem token
Sem tokenAuthenticateResult.NoResult()HttpContext.User = anonymous
Token inválido/expiradoAuthenticateResult.Fail(...)HttpContext.User = anonymous
Token válidoAuthenticateResult.SuccessHttpContext.User = ClaimsPrincipal populado
OnMessageReceived lazyIssuerSigningKey = null até o primeiro request disparar o evento
IAuthenticationRequestHandlerÚnico caso de short-circuit (ex: remote auth redirect)

Fonte: Microsoft Learn — Authentication — "Authentication doesn't short-circuit unauthenticated requests. Although Authentication Middleware authenticates requests, authorization (and rejection) occurs only after MVC selects a specific controller and action."

https://learn.microsoft.com/en-us/aspnet/core/security/authentication/


4. UseAuthorization

Responsabilidade

Autoriza o acesso ao endpoint baseado em políticas ([Authorize], [AllowAnonymous], políticas customizadas). Único middleware que pode rejeitar requests com 401/403.

Funcionamento (código fonte)

// AuthorizationMiddleware.cs — dotnet/aspnetcore (MIT License)
// https://github.com/dotnet/aspnetcore/blob/main/src/Security/Authorization/Policy/src/AuthorizationMiddleware.cs

public async Task Invoke(HttpContext context)
{
var endpoint = context.GetEndpoint();

// Tenta cached policy, senão combina metadata do endpoint
var authorizeData = endpoint?.Metadata.GetOrderedMetadata<IAuthorizeData>()
?? Array.Empty<IAuthorizeData>();

policy = await AuthorizationPolicy.CombineAsync(_policyProvider,
authorizeData, policies);

// ⚠️ SE policy == null → endpoint NÃO tem [Authorize] → skip total
if (policy == null)
{
await _next(context); // ← request prossegue sem verificação
return;
}

// ⚠️ LAZY AUTH: AuthenticateAsync dispara JWT validation mesmo
// sem UseAuthentication ter executado antes
var authenticateResult = await policyEvaluator.AuthenticateAsync(policy, context);

// ⚠️ [AllowAnonymous]: roda auth (popula User) mas NÃO bloqueia
if (endpoint?.Metadata.GetMetadata<IAllowAnonymous>() != null)
{
await _next(context);
return;
}

// Authorize: verifica políticas contra o User autenticado
var authorizeResult = await policyEvaluator.AuthorizeAsync(
policy, authenticateResult!, context, resource);

// Result handler: Challenge (401) ou Forbid (403)
await authorizationMiddlewareResultHandler.HandleAsync(
_next, context, policy, authorizeResult);
}

Fonte: https://github.com/dotnet/aspnetcore/blob/main/src/Security/Authorization/Policy/src/AuthorizationMiddleware.cs

Fluxo Detalhado

┌─ AuthorizationMiddleware.Invoke() ─────────────────────────┐
│ │
│ 1. context.GetEndpoint() → extrai metadata │
│ │
│ 2. endpoint.Metadata.GetOrderedMetadata<IAuthorizeData>() │
│ ├── Sem [Authorize] → authorizeData vazio │
│ │ └── policy = null → await _next(context) ✅ SKIP │
│ │ │
│ └── Com [Authorize] → combine policy │
│ │
│ 3. policy != null → PolicyEvaluator.AuthenticateAsync() │
│ └── LAZY AUTH: dispara JwtBearerHandler mesmo se │
│ UseAuthentication NÃO executou antes │
│ │
│ 4. IAllowAnonymous? → skip auth check, prossegue │
│ │
│ 5. PolicyEvaluator.AuthorizeAsync(policy, result, ctx) │
│ ├── Sucesso → _next(context) │
│ ├── Falha (não autenticado) → Challenge() → 401 │
│ └── Falha (autenticado, sem permissão) → Forbid() → 403 │
│ │
└────────────────────────────────────────────────────────────┘

Lazy Authentication

Mecanismo que permite que UseAuthorization funcione mesmo sem UseAuthentication ter executado antes:

UseAuthorization encontra [Authorize]
→ PolicyEvaluator.AuthenticateAsync("Bearer")
→ IAuthenticationService.AuthenticateAsync(context, "Bearer")
→ JwtBearerHandler.HandleAuthenticateAsync()
→ Valida token → HttpContext.User populado

Este é o mecanismo que mascara o bug de ordering do Coezzion: endpoints com [Authorize] funcionam porque o AuthorizationMiddleware dispara a autenticação lazy. Endpoints sem [Authorize]policy == null → skip total → HttpContext.User permanece vazio.

FallbackPolicy

Configuração opcional que aplica uma política de autorização a todos os endpoints, incluindo os que não têm [Authorize]:

// Exemplo da documentação Microsoft — NÃO configurado no Coezzion
services.AddAuthorization(options =>
{
options.FallbackPolicy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
});

Fonte: Microsoft Learn — Secure data — "Setting the fallback authorization policy to require users to be authenticated protects newly added Razor Pages and controllers. Having authorization required by default is more secure than relying on new controllers and Razor Pages to include the [Authorize] attribute."

https://learn.microsoft.com/en-us/aspnet/core/security/authorization/secure-data#require-authenticated-users

No Coezzion: FallbackPolicy NÃO é configurada. Logo, endpoints sem [Authorize] não são bloqueados. Apenas endpoints com [Authorize] (ou com FallbackPolicy configurada, o que não é o caso) são verificados.

[AllowAnonymous]

Endpoint metadata que instrui o AuthorizationMiddleware a pular a verificação de autorização, mesmo quando há [Authorize] no controller ou FallbackPolicy configurada. O middleware ainda executa AuthenticateAsync() (popula HttpContext.User) mas não rejeita requests não autenticados.

Fonte: Microsoft Learn — Simple authorization — "Use the [AllowAnonymous] attribute to allow access by non-authenticated users to individual actions."

https://learn.microsoft.com/en-us/aspnet/core/security/authorization/simple

Restrições e Side Effects

AspectoDetalhe
Endpoint sem [Authorize]policy == null → skip total → request prossegue
Endpoint com [Authorize]Lazy auth → valida token → 401 se inválido
Endpoint com [AllowAnonymous]Popula User (se token) mas NÃO bloqueia
Sem FallbackPolicyApenas endpoints com [Authorize] são verificados
Com FallbackPolicyTodos endpoints exigem auth (exceto [AllowAnonymous])
Duplicata de UseAuthorizationSegunda chamada é no-op: AuthorizationMiddlewareInvokedWithEndpointKey no HttpContext.Items previne dupla execução

5. UseEndpoints

Responsabilidade

Executa o delegate associado ao endpoint selecionado por UseRouting. É o ponto final do pipeline para requests que casam com uma rota.

Funcionamento

app.UseEndpoints(endpoints =>
{
endpoints.MapHealthChecks("/check"); // Health check endpoint
endpoints.MapControllers(); // Controller actions
});
  • MapControllers(): executa o controller e action selecionados por UseRouting
  • MapHealthChecks(): executa health checks
  • MapRazorPages(): executa Razor Pages
  • MapHub<T>(): SignalR hubs

Fonte: Microsoft Learn — Routing — "UseEndpoints adds endpoint execution to the middleware pipeline. It runs the delegate associated with the selected endpoint."

https://learn.microsoft.com/en-us/aspnet/core/fundamentals/routing#routing-basics

Terminal Middleware

UseEndpoints é terminal quando encontra um match:

Fonte: Microsoft Learn — Routing — "The UseEndpoints middleware is terminal when a match is found. The middleware after UseEndpoints execute only when no match is found."

https://learn.microsoft.com/en-us/aspnet/core/fundamentals/routing#endpoints

Request casa com /api/payment/profile-fraud
→ UseEndpoints(MapControllers) executa ProfileFraudController.Get()
→ Response enviada
→ NÃO chama _next
→ Middleware registrado DEPOIS de UseEndpoints NUNCA executa ❌

Restrições

AspectoDetalhe
Terminal com matchNão chama _next → middleware depois dele nunca executa
Sem matchChama _next → middleware depois dele executa (ex: 404 handler)
Ordem no pipelineDeve ser o último middleware (depois de authN, authZ)
Registro de endpointsFeito em ConfigureServices ou no WebApplication builder

6. AddSecondaryAuthentication (Coezzion)

Responsabilidade

Registra um segundo esquema JWT (PaymentsScheme) para autenticação de requests de pagamento, com issuer/audience específicos.

Código

// coezzion-service-checkout/src/Checkout.API/Configuration/AuthenticationExtensions.cs
public static IServiceCollection AddSecondaryAuthentication(
this IServiceCollection services, IConfiguration configuration)
{
services.AddAuthentication()
.AddJwtBearer("PaymentsScheme", options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true, // ← diferente do esquema default
ValidateAudience = true, // ← diferente do esquema default
ValidateLifetime = true,
ValidAudience = configuration["TokenConfigurations:PaymentsTokenAudience"],
ValidIssuer = configuration["TokenConfigurations:PaymentsTokenIssuer"],
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(
Encoding.UTF8.GetBytes(configuration["TokenConfigurations:PaymentsTokenSecretKey"]))
};
});
return services;
}

Diferenças entre esquemas

ParâmetroDefault (Bearer)PaymentsScheme
ValidateIssuerfalsetrue
ValidateAudiencefalsetrue
ValidateLifetimetruetrue
IssuerSigningKeyAWS Secrets Manager (lazy)appsettings.json
Uso[Authorize(JwtBearer)][Authorize(AuthenticationSchemes = "PaymentsScheme")]

Funcionamento no Pipeline

ConfigureServices:
services.AddAuthenticationConfiguration()
→ AddAuthentication("Bearer").AddJwtBearer(...) // esquema DEFAULT

services.AddSecondaryAuthentication(Configuration)
→ AddAuthentication().AddJwtBearer("PaymentsScheme") // esquema adicional

Pipeline:
UseAuthentication() → tenta autenticar com esquema DEFAULT ("Bearer")
→ JwtBearerHandler (default) valida token do header Authorization

UseAuthorization() → verifica metadata do endpoint
→ [Authorize] sem schemes → usa default ("Bearer")
→ [Authorize(AuthenticationSchemes = "PaymentsScheme")] → usa PaymentsScheme

7. Restrições de Ordenação

Ordem Documentada pela Microsoft

1. Exception/Error Handling (UseExceptionHandler, UseDeveloperExceptionPage)
2. HTTPS Redirection (UseHttpsRedirection)
3. Static Files (UseStaticFiles)
4. UseRouting() ← endpoint selection
5. UseCors() ← must be after UseRouting, before UseEndpoints
6. UseAuthentication() ← must be after UseRouting, before UseEndpoints
7. UseAuthorization() ← must be after UseAuthentication
8. UseEndpoints() ← TERMINAL — must be LAST

Fonte: Microsoft Learn — Middleware order — multiple versions document this exact sequence.

https://learn.microsoft.com/en-us/aspnet/core/fundamentals/middleware/#middleware-order

Regras Fundamentais

RegraMotivo
UseRouting antes de UseAuthN/UseAuthZMetadata do endpoint ([Authorize]) só existe depois do routing
UseAuthN antes de UseAuthZUseAuthZ precisa do HttpContext.User populado
UseEndpoints por últimoÉ terminal — middleware depois dele nunca executa para requests que casam com rota
UseCors antes de UseEndpointsCORS headers precisam ser aplicados antes da resposta

Fonte: Microsoft Learn — Authentication — "When using endpoint routing, the call to UseAuthentication must go: After UseRouting, so that route information is available for authentication decisions. Before UseEndpoints, so that users are authenticated before accessing the endpoints."

https://learn.microsoft.com/en-us/aspnet/core/security/authentication/


8. Análise do Bug de Ordenação no Coezzion

Pipeline Real (Bug)

// Startup.Configure() — todos os 4 serviços do pentest
app.UseApiConfiguration(env); // ← contém UseRouting, UseAuthZ, UseEndpoints
app.UseSwaggerConfiguration(env);
app.UseAuthenticationConfiguration(); // ← contém UseAuthN, UseAuthZ — NUNCA EXECUTA

// ApiConfig.UseApiConfiguration():
app.UseRouting(); // ✅
app.UseCors("Total"); // ✅
app.UseAuthorization(); // ✅ (mas HttpContext.User VAZIO)
app.UseMiddleware<SerilLogMiddleware>(); // ✅
app.UseEndpoints(endpoints => // ✅ ← TERMINAL
{
endpoints.MapControllers(); // match → executa → não chama _next
});
// ──── NADA abaixo executa ────
// app.UseAuthentication(); // ❌ NUNCA EXECUTA
// app.UseAuthorization(); // ❌ NUNCA EXECUTA (duplicado)

Como o Sistema "Funciona" Apesar do Bug

O UseAuthorization() em ApiConfig:79 encontra [Authorize] na metadata do endpoint e dispara lazy auth:

UseAuthorization (ApiConfig L79)
→ vê [Authorize(JwtBearer)] na metadata
→ PolicyEvaluator.AuthenticateAsync("Bearer")
→ JwtBearerHandler.HandleAuthenticateAsync()
→ Valida token → HttpContext.User = { schema, sub, profile }

Isso mascara o bug para endpoints com [Authorize]. Mas endpoints sem [Authorize]:

UseAuthorization (ApiConfig L79)
→ NÃO vê [Authorize] → policy = null → skip total
→ HttpContext.User permanece VAZIO

Consequência Explorada no Pentest

GET /api/payment/profile-fraud
Authorization: Bearer eyJ...schema:"arezzo"... ← JWT válido com tenant "arezzo"
api-company-target: schutz ← header malicioso

Pipeline:
UseAuthorization → sem [Authorize] no endpoint → lazy auth NÃO dispara
UseEndpoints → controller executa
→ GetSchemaName():
→ http.User.FindFirst("schema") = null (User VAZIO!)
→ cai no header api-company-target → "schutz" ❌

Resultado: servidor conecta ao schema do ATACANTE (schutz), não do JWT (arezzo)

Análise de Impacto: Fix vs Regressão

Pipeline corrigido:
UseAuthN → JwtBearerHandler SEMPRE executa → User populado (se token)
UseAuthZ → verifica endpoint metadata
UseEndpoints → executa controller

Endpoints sem [Authorize]:
- Sem FallbackPolicy → policy = null → prossegue SEM bloqueio
- User populado (se token presente) → claim "schema" disponível ✅
- User vazio (se sem token) → mesmo comportamento de antes

Endpoints com [Authorize]:
- Funcionamento idêntico (lazy auth ou auth normal produzem mesmo resultado)

9. Referências

Documentação Microsoft Learn

TópicoURL
ASP.NET Core Middlewarehttps://learn.microsoft.com/en-us/aspnet/core/fundamentals/middleware/
Middleware order (seção)https://learn.microsoft.com/en-us/aspnet/core/fundamentals/middleware/#middleware-order
Authentication overviewhttps://learn.microsoft.com/en-us/aspnet/core/security/authentication/
Authorization introductionhttps://learn.microsoft.com/en-us/aspnet/core/security/authorization/introduction
Simple authorization ([Authorize], [AllowAnonymous])https://learn.microsoft.com/en-us/aspnet/core/security/authorization/simple
FallbackPolicy / Require authenticated usershttps://learn.microsoft.com/en-us/aspnet/core/security/authorization/secure-data#require-authenticated-users
Routing in ASP.NET Corehttps://learn.microsoft.com/en-us/aspnet/core/fundamentals/routing

Código Fonte (dotnet/aspnetcore — MIT License)

ArquivoURL
AuthenticationMiddleware.cshttps://github.com/dotnet/aspnetcore/blob/main/src/Security/Authentication/Core/src/AuthenticationMiddleware.cs
AuthorizationMiddleware.cshttps://github.com/dotnet/aspnetcore/blob/main/src/Security/Authorization/Policy/src/AuthorizationMiddleware.cs

Código Fonte Coezzion

ArquivoDescrição
Startup.csOrdem de chamada dos middlewares (4 serviços)
ApiConfig.csUseRouting, UseCors, UseAuthorization, UseEndpoints
AuthenticationConfig.cs (nuget)AddAuthentication(JwtBearer), UseAuthentication, UseAuthorization
AuthenticationExtensions.csAddSecondaryAuthenticationPaymentsScheme

Apêndice: Glossário

TermoDefinição
Terminal middlewareMiddleware que não chama _next — interrompe o pipeline
Lazy authenticationUseAuthorization dispara AuthenticateAsync sob demanda quando encontra [Authorize], mesmo sem UseAuthentication ter executado
FallbackPolicyPolítica aplicada a TODOS endpoints (exceto [AllowAnonymous]) quando configurada — não configurada no Coezzion
DefaultPolicyPolítica usada pelo [Authorize] sem parâmetros — exige usuário autenticado (DenyAnonymousAuthorizationRequirement)
IAuthenticationRequestHandlerHandler que pode short-circuitar UseAuthentication (ex: redirect OAuth)
IPolicyEvaluatorServiço que orquestra AuthenticateAsync + AuthorizeAsync dentro do AuthorizationMiddleware