You added an authorisation policy to an ASP.NET Core endpoint and it is not being enforced. Where do you look?
The pipeline is an ordered list you assembled, and authorisation cannot decide anything before routing has selected an endpoint to decide about. Registered in the wrong order it either runs against nothing or never runs at all, and neither produces an error - the request simply succeeds.
What the interviewer is scoring
- Whether the candidate knows routing must precede authorisation and can say why
- That the pipeline is understood as ordered registration rather than configuration
- Does the candidate distinguish authentication from authorisation in the ordering
- Whether short-circuiting and the response unwinding back out are understood
- That a missing terminal call is recognised as a cause of a silently unenforced policy
Answer
The pipeline is a list, and you wrote the order
A request enters a chain of components assembled in Program.cs. Each one may
inspect the request, hand it to the next, inspect the response on the way back,
or answer immediately and stop. There is no priority system and no dependency
resolution: the order of your Use... calls is the order of execution.
That makes ordering a functional decision that looks like formatting, which is why this class of bug is common and why it produces no error. A policy that never runs is not an exception; it is a request that succeeded.
var app = builder.Build();
app.UseExceptionHandler("/error"); // outermost, so it can catch what follows
app.UseHttpsRedirection();
app.UseStaticFiles(); // before routing: no need to match a route
app.UseRouting(); // selects the endpoint
app.UseAuthentication(); // establishes who the caller is
app.UseAuthorization(); // decides whether they may reach that endpoint
app.MapControllers(); // terminal: runs the selected endpoint
app.Run();
Why routing has to come first
UseRouting is the component that matches the request against your route table
and attaches the selected endpoint to the HttpContext. Everything downstream
can then ask what was matched.
UseAuthorization works by reading that endpoint's metadata — the
[Authorize] attribute, the policy name, the required roles. Placed before
UseRouting, no endpoint has been selected yet, so there is no metadata to
read, and the middleware finds nothing to enforce and passes the request
through. Silently. The attribute is present, the policy is registered, and the
request succeeds.
Authentication sits between them because authorisation needs an identity to judge. Reversed, authorisation evaluates against an unauthenticated principal and rejects callers who are in fact signed in — the same class of bug producing the opposite symptom, and easier to notice precisely because someone complains.
The order is therefore forced by what each step needs, not chosen: match the endpoint, establish who is asking, decide whether they may.
The other way it silently does not run
The second cause of an unenforced policy is a middleware earlier in the chain
that never calls next.
app.Use(async (ctx, next) =>
{
if (ctx.Request.Path.StartsWithSegments("/health"))
{
ctx.Response.StatusCode = 200;
return; // deliberate short-circuit; nothing below runs
}
await next(ctx); // omit this and the whole pipeline stops here
});
Short-circuiting is a feature — it is how static files and health checks avoid
the cost of everything downstream — and it is indistinguishable from the bug
where someone forgot the await next(ctx) on a path. The tell is that the
endpoint returns something plausible without your handler ever being hit, so a
breakpoint in the controller is the fastest diagnosis.
Requests go down, responses come back up
The mental model that makes custom middleware behave is that each component
wraps the rest. Code before await next runs on the way in, code after it runs
on the way out.
app.Use(async (ctx, next) =>
{
var started = Stopwatch.GetTimestamp();
await next(ctx); // everything downstream
var elapsed = Stopwatch.GetElapsedTime(started); // now the response exists
logger.LogInformation("{Path} {Status} {Ms}ms",
ctx.Request.Path, ctx.Response.StatusCode, elapsed.TotalMilliseconds);
});
This is also why exception handling goes at the very top: it can only catch what happens inside the part of the chain it wraps, and anything registered above it is outside its reach.
One constraint follows from the same picture and catches people out. Headers are
sent with the first byte of the body, so once a downstream component has begun
writing, the response has already started and setting a header on the way out
throws. A middleware that wants to add a header must do it before next, or
register a callback on OnStarting.
What to check, in order
When a policy is not being enforced, the sequence that finds it quickly:
Confirm UseRouting precedes UseAuthentication, which precedes
UseAuthorization. Confirm a terminal call such as MapControllers exists and
is after all three. Put a breakpoint or a log line in the action itself to
establish whether the endpoint is reached at all, which separates "policy
ignored" from "request never got there". Then check for a custom middleware
registered above authorisation that returns without calling next for this
path.
Four checks, and the first one is right most of the time.
The pipeline has no notion of priority. Authorisation cannot judge an endpoint that routing has not yet selected, and nothing will tell you — the request just works.
Likely follow-ups
- What does UseRouting actually do that UseEndpoints then depends on?
- Your custom middleware never calls next. What happens to everything after it?
- Where would you put exception handling, and why there?
- How would you write a middleware that measures duration correctly?
Related questions
- A client disconnects but your server keeps working on their request. How does cancellation actually propagate in .NET?mediumAlso on aspnet-core4 min
- A request fails but your Express error-handling middleware never runs. How do you work out why?mediumAlso on middleware3 min
- Why does calling .Result or .Wait() on an async method deadlock, and how do you fix it properly?hardAlso on aspnet-core5 min
- Walk me through what happens between a customer tapping a card and the merchant getting paid.hardAlso on authorisation6 min
- Walk me through the middleware chain of an Express service. What has to come before what, and why?mediumAlso on middleware4 min
- Customers want per-project roles rather than one global role. How do you model that, and where in the request does the check happen?hardAlso on authorisation6 min
- Why would you stream a large file rather than read it into memory, and what is backpressure?mediumAlso on pipeline4 min
- When do you take the customer's money, and how much fraud are you willing to accept?hardAlso on authorisation6 min