.net core middleware and authorization - c#

I have a custom middleware that is configured like this
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseWhen(context => GetCondition(context)
, appBuilder =>
{
appBuilder.UseAuthentication(); // this does not do the trick
appBuilder.UseAuthorization();// this does not do the trick
appBuilder.UseMiddleware<MyCustomMiddleware>();
});
}
I want my custom middleware to execute only when the user is authorized to do so.
If I am using controller, I can provide Authorize attribute on controller which forces the user to login or 401 if they are not logged-in.
[Authorize(AuthenticationSchemes = "Bearer")]
public class WeatherForecastController : ControllerBase
How can I do the similar authorization in middleware. One way to do is to check in the invoke method if user is authenticated but that does not seems right to me
public async Task Invoke(HttpContext context)
{
if (context.User.Identity.IsAuthenticated)
{
// my code goes here
}
}

Related

.NET Core Endpoint + Global CORS

I've found this in official documentation -
We recommend against combining policies. Use the [EnableCors]
attribute or middleware, not both in the same app.
My scenario is quite simple - I want to enable CORS globally but disable it only for one specific controller endpoint (endpoint is used on frontend widget which can be embedded on any site so I can't have CORS on that endpoint).
I don't understand why they are recommending against combining both approaches - not only that they don't recommend but it just doesn't work.
This is the setup of CORS:
services.AddCors(opts =>
{
opts.AddPolicy(nameof(MyCorsPolicy), new MyCorsPolicy());
});
And this is registration in Configure method of startup
public void Configure(
IApplicationBuilder app,
IWebHostEnvironment env)
{
app.UseRouting();
app.UseCors(nameof(MyCorsPolicy));
app.UseHsts();
app.UseExceptionHandler(env);
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints => endpoints.MapControllers());
}
And now in my XY controller method I have [DisableCors] attribute which just doesn't work.
Any help would be appreciated. Thank you.
After hundreds of tests and internal .NET Core debugging, only way I could implement this is by using global CORS:
services.AddCors(opts =>
{
opts.AddPolicy(nameof(MyCorsPolicy), new MyCorsPolicy());
});
Then I'd create another policy
public class AllowAnyCorsPolicy : CorsPolicy
{
public AllowAnyCorsPolicy()
{
Origins.Clear();
IsOriginAllowed = origin => true;
Headers.Clear();
Headers.Add("*");
Methods.Clear();
Methods.Add("*");
SupportsCredentials = true;
}
}
And apply that policy to specific controller method e.g.
[EnableCors(nameof(AllowAnyCorsPolicy))]
[HttpPost("/user/add")]
[AllowAnonymous]
public async Task<IActionResult> AddUser(UserRequestModel requestModel)
{
// ...
}
If I used [DisableCors] or even used default policy registration and then added pure [EnableCors] attribute to controller method, it just wouldn't work. Pretty weird way of their implementation because I think this can be simplified a lot, and I have no idea how this might behave in future, so we might even consider writing our own full CORS middleware.
Way 1. Because a default policy hasn't been configured, app.UseCors() alone doesn't enable CORS. Use RequireCors to enable all controllers.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCors();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers()
.RequireCors(MyCorsPolicy);//Enable Cors with endpoint routing
// /xy/getvalues2 and Razor Pages not allow cross-origin requests because no default policy was specified.
endpoints.MapGet("/xy/getvalues2",
context => context.Response.WriteAsync("xy/getvalues2")); //do XY Controller Action logic
endpoints.MapRazorPages();
});
}
Way 2. The [DisableCors] attribute does not disable CORS that has been enabled by endpoint routing. Uses [EnableCors("MyCorsPolicy")] to enable the "MyCorsPolicy" CORS policy for each controller. Disables CORS for the GetValues2 method.
[EnableCors("MyCorsPolicy")]
[Route("api/[controller]")]
[ApiController]
public class XYController : ControllerBase
{
// GET api/values
[HttpGet]
public IActionResult Get() =>
ControllerContext.MyDisplayRouteInfo();
// GET: api/values/GetValues2
[DisableCors]
[HttpGet("{action}")]
public IActionResult GetValues2() =>
ControllerContext.MyDisplayRouteInfo();
}

Rewrite Route in middleware after Endpoint got matched

I am currently writing a rate-limiter with the help of a middleware, this is based on an per-user basis. When the rate-limit exceeds for a user, I want to rewrite the route to my 429 page which tells the use that he exceeded the rate-limit.
This is an example of what my current middle-ware looks like:
public class RateLimitMiddleware : IMiddleware
{
public Task InvokeAsync(HttpContext context, RequestDelegate next)
{
if(RateLimitExceeded(context.User))
{
context.Request.Path = "/Home/Ratelimit";
}
return next.Invoke(context);
}
public bool RateLimitExceeded(ClaimsPrincipal user)
{
//...
}
}
Now to my Configure method:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
// ...
app.UseCookiePolicy();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseRateLimit(); // This adds the RateLimitMiddleware class to the middlewares.
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
Now to the actual problem, you can't rewrite the route in any middleware after the UseRouting. Since it is the actual method deciding which Endpoint to pick. Therefore, is it possible to rewrite the route afterwards?
However, please do note, that I don't want to redirect the user to the rate-limit view. Additionally, I can't just move the middleware up in the chain, since I need the HttpContext.User to be set.

Terminal middleware (api key check) not working with endpoint routing

I am implementing an api where on one specific POST route there must be an API key check.
The SDK is dotnet core 3.1.
When I run the API using Postman, I get the following error:
Microsoft.AspNetCore.Routing.Matching.AmbiguousMatchException: The request matched multiple endpoints
I looks like the route attribute on the api controller, together with endpoint route registration are stacked...
How can this be fixed ?
public void Configure(IApplicationBuilder app, IHostEnvironment env)
{
app.UseSerilogRequestLogging();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseSwagger(Configuration);
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapApiKeyAuthentication();
endpoints.MapControllers();
});
}
(extension class)
public static IEndpointRouteBuilder MapApiKeyAuthentication(this IEndpointRouteBuilder endpoints)
{
endpoints.MapPost("/v1/xyz", endpoints.CreateApplicationBuilder()
.UseMiddleware<ApiKeyMiddleware>()
.Build())
.WithDisplayName("ApiKey");
return endpoints;
}

Get controller name in AuthenticationHandler in asp.net core 2.2 web api

I have a basic custom AuthenticationHandler like in this post https://jasonwatmore.com/post/2018/09/08/aspnet-core-21-basic-authentication-tutorial-with-example-api
In the Task<AuthenticateResult> HandleAuthenticateAsync() override I need to to get the controller name.
Injecting IActionContextAccessor in the startup doesn't work:
services.AddSingleton<IActionContextAccessor, ActionContextAccessor>();
The ActionContext property is null (not set yet I think)
HttpContext is available, so should i just try to parse the Context.Request.Path ?
Thanks in advance !
As I explained in the comment that you won't get route data before the Router middleware is invoked.
For your tutorial code,I find out a solution to create a router and make it runs before the Authentication middleware. In startup.cs
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
app.UseRouter(routeBuilder => {
var template = "{controller=Users}/{action=GetAll}/{id?}";
routeBuilder.MapMiddlewareRoute(template, appBuilder => {
appBuilder.UseAuthentication();
appBuilder.UseMvc();
});
});
//add more middlewares if you want other routes
app.UseAuthentication();
app.UseMvc();
}
Get controller name in AuthenticationaHandler (using Microsoft.AspNetCore.Routing reference):
protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
{
var name = Context.GetRouteValue("controller");
//...
}

Policy based Authorization not working in asp.net core

I'm trying to get Policy based Authorization working in .net core 2.1 based on this article: https://learn.microsoft.com/en-us/aspnet/core/security/authorization/policies?view=aspnetcore-2.1
However I cannot seem to get it to fire.
In the below example, I've commented out the context.suceeed line, so I would have thought my api call to my usercontroller would fail.
However my API call is being accepted.
What am I doing wrong?
This is my startup.cs
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IAuthorizationHandler, VerifyAuthCookieHandler>();
services.AddAuthorization(options =>
{
options.AddPolicy("VerifyAuthCookie", policy =>
policy.Requirements.Add(new VerifyAuthCookieRequirement()));
});
services.AddMvcCore().AddJsonFormatters();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseMvc();
}
}
Here is my handler
public class VerifyAuthCookieHandler : AuthorizationHandler<VerifyAuthCookieRequirement>
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
VerifyAuthCookieRequirement requirement)
{
//context.Succeed(requirement);
return Task.CompletedTask;
}
}
And here is my requirement:
public class VerifyAuthCookieRequirement : IAuthorizationRequirement
{
public VerifyAuthCookieRequirement()
{
}
}
And finally, my controller:
[Route("api/[controller]")]
[Authorize(Policy = "VerifyAuthCookie")]
public class UserController : Controller
{
}
If I add code in HandleRequirementAsync and set a breakpoint then it's not being hit when I debug, so my Handler doesn't appear to be called at all.
You should call app.UseAuthentication(); before the app.UseMvc(); in the Configure method of the Startup class. This will add the ASP.NET Core authentication middleware to the request pipeline.
Since you are using services.AddMvcCore() we'll need to configure the authorization services manually, something services.AddMvc() does for you automatically. We should add .AddAuthorization() to the IMvcCoreBuilder.
This will add the default Authentication- and Authorization services and a PolicyEvaluator.
If you're interested in the exact services that will be registered in your DI container you should follow this link.
I had similar issue, I fix it by :
services.AddMvcCore().AddAuthorization().AddJsonFormatters();

Categories

Resources