MVC authorization inside MapWhen() is applied to all controllers
It's important to note that app.UseAuthorization
must appear between app.UseRouting()
and app.UseEndpoints(...)
;
So, it should look something like this:
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller}/{action=Index}/{id?}");
});
I'm giving myself credit for the full answer, having spent some time today on this and posting the code. Thanks to mwilson for giving me the nudge to try again.
Prior to my new approach, every time the code ran, this warning was shown...
Startup.cs(189,13): warning ASP0001: The call to UseAuthorization
should appear between app.UseRouting() and app.UseEndpoints(..)
for authorization to be correctly evaluated.
So the solution is not merely recognizing that warning and heeding it, but also to find a way to appease the compiler gods. (And you'll see below that I still haven't figured out how to do that, yet.)
Here's what I did figure out today. I placed the UseAuthentication
and UseAuthorization
calls in two different places. This works. Somewhat.
In this API project I now am able to run anonymous unsecured endpoints, secured endpoints, and, for bonus points, secured GraphQL endpoints as well.
FWIW, code below:
public void Configure(...)
{
...
...
app.UseStaticFiles();
app.UseRouting();
app.MapWhen(
context => (context.Request.Path
.StartsWithSegments(new PathString("/api"))
),
a =>
{
a.UseRouting();
a.UseAuthentication();
a.UseAuthorization();
a.UseEndpoints(endpoints =>
{
endpoints
.MapControllers()
.RequireAuthorization(...);
});
}
);
app.UseAuthentication();
app.UseAuthorization();
app.UseWebSockets();
app.UseGraphQLWebSockets<...>(...);
app.UseGraphQL<...>(...);
}
That works, but I still get the compiler warning. What's more, if I get too smart by half and try to use the following controller...
[Route("vapi/[controller]")]
//[AllowAnonymous]
public class VersionController : Controller
{ ...
along with this additional Startup.cs code...
app.MapWhen(
context => (context.Request.Path
.StartsWithSegments(new PathString("/vapi"))
),
a =>
{
a.UseRouting();
// a.UseAuthentication(); <-- look, Ma, no authentication!
// a.UseAuthorization(); <-- look, Ma, no authorization!
a.UseEndpoints(endpoints =>
{
endpoints
.MapControllers()
.RequireAuthorization(...);
});
}
);
I also still get the error noted in my OP. (It's all coming back to me, like an old nightmare...)
Endpoint ....Controllers.VersionController.Version (...) contains authorization
metadata, but a middleware was not found that supports authorization.
Configure your application startup by adding app.UseAuthorization() ...
So where I am leaving the matter is this: I still cannot do what I wanted to do, which was to secure only certain controller paths. What I can do (without adding that "vapi"-path-supporting code to Startup) is this:
[Route("api/[controller]")]
[AllowAnonymous]
public class VersionController : Controller
{ ...
So... I'll call this the answer, until someone posts a usable set of code that improves on it.
Move the below code higher up in your Startup class (above app.MapWhen
).
app.UseAuthentication();
app.UseAuthorization();