Middleware check the annotation from controller method - asp.net-core

Hello i want to check the annotation from a controller method in a middleware class.
My config:
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, BackendDbContext context)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
app.UseStaticFiles();
app.UseMiddleware<AuthMiddleware>();
app.UseMvc();
BackendDbInitializer.Init(context);
}
My Controller:
Route("api/[controller]")]
public class UserController : Controller
{
private readonly BackendDbContext _context;
public UserController(BackendDbContext context)
{
_context = context;
}
// GET api/values
[HttpGet]
[NoAuth]
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
}
My Middleware:
public class AuthMiddleware
{
private readonly RequestDelegate _next;
public AuthMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context)
{
//Here i want to check if the called method in UserController have a annotation...
await _next.Invoke(context);
}
}
In the AuthMiddleware i want to check if the called method have a specific annotation.

I don't know if this question is outdated, I came across the same question today, I'm using Casbin.Net package to implement RBAC for my asp dotnet core project, I need to implement an auth middleware which can recognize controllers with a [Authorize] annotation, so only these controllers need to check permission, the other controllers not, below are my code
rbac with asp dotnet core using casbin
You can ignore the dependency inject and casbin parts, in AuthzMiddleware.cs, I use context.User.Claims.Count() > 0 to check if the current request has passed a Authentication middleware.

Related

how to set the output type List<string> in a middleware in .NET Core 2.1?

I have this middleware class when I want to show a List<string> in the output:
namespace WebAspNetCore2_1
{
public class LearningMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger<LearningMiddleware> _logger_log;
private readonly List<string> logger;
public LearningMiddleware(RequestDelegate next,ILogger<LearningMiddleware> logger_log)
{
_next = next;
_logger_log = logger_log;
List<string> _logger = new List<string>
{
("EUR/USD"),
("1.0500")
};
logger = _logger;
}
public Task Invoke(HttpContext httpContext)
{
_logger_log.Log(Microsoft.Extensions .Logging.LogLevel.Information,"information of logger",logger[0]);
return _next(httpContext);
}
}
}
I have debugged my code but seen to be correct, my List<> is filled, I don't know why the compiler is throwing this exception:
InvalidOperationException: Could not create an instance of type Microsoft.Extensions.Logging.ILogger`1[[System.String, System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]]'. Model bound complex types must not be abstract or value types and must have a parameterless constructor. Alternatively, give the 'logger' parameter a non-null default value.
i thought was the order declaration in StartUp, but not
public void ConfigureServices(IServiceCollection services)
{
services.Configure<CookiePolicyOptions>(options =>
{
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
// app.UseLearningMiddleware();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseCookiePolicy();
app.UseMiddleware<LearningMiddleware>();
app.UseMvc();
}
link in video for detail evidence: https://youtu.be/2FoLvhLweYo
I tested your code in my side but it worked well... I created a new asp.net core 2.1 MVC project and create a middleware. In StartUp.cs, I put app.UseMiddleware<MyMiddleware>(); just before app.UseMvc(routes =>
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace WebApplication2
{
// You may need to install the Microsoft.AspNetCore.Http.Abstractions package into your project
public class MyMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger<MyMiddleware> _logger_log;
private readonly List<string> logger;
public MyMiddleware(RequestDelegate next, ILogger<MyMiddleware> logger_log)
{
_next = next;
_logger_log = logger_log;
List<string> _logger = new List<string>
{
("EUR/USD"),
("1.0500")
};
logger = _logger;
}
public Task Invoke(HttpContext httpContext)
{
_logger_log.Log(Microsoft.Extensions.Logging.LogLevel.Information, "information of logger", logger[0]);
return _next(httpContext);
}
}
// Extension method used to add the middleware to the HTTP request pipeline.
public static class MyMiddlewareExtensions
{
public static IApplicationBuilder UseMyMiddleware(this IApplicationBuilder builder)
{
return builder.UseMiddleware<MyMiddleware>();
}
}
}

.NET Core (.NET 5) HandleChallengeAsync in custom authentication

I'm trying to write a custom authentication handler in a .NET 5 web app. The problem I'm unable to solve is how to handle redirection in HandleChallengeAsync. I see there's a RedirectUri property in the AuthenticationProperties parameter, but I don't know how and where to set it up, and once the value is set, do I need to issue a command to redirect to login URL or will it happen when calling base HandleChallengeAsync method?
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication("TeamAuth")
.AddScheme<AuthenticationSchemeOptions, NewTeamAuthHandler>("TeamAuth", null);
// ...
}
handler code
public class NewTeamAuthHandler : AuthenticationHandler<AuthenticationSchemeOptions>
{
public NewTeamAuthHandler(
IOptionsMonitor<AuthenticationSchemeOptions> options,
ILoggerFactory logger,
UrlEncoder encoder,
ISystemClock clock)
: base(options, logger, encoder, clock)
{
}
protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
{
return AuthenticateResult.Fail("authentication faiure");
}
protected override async Task HandleChallengeAsync(AuthenticationProperties properties)
{
// probable location of the redirection command
await base.HandleChallengeAsync(properties);
}
protected override async Task HandleForbiddenAsync(AuthenticationProperties properties)
{
throw new NotImplementedException();
}
}

Validating AntiForgery token globally in ASP.NET Core

I want to validate AntiForgery token in ASP.NET Core application. I know i can individually do that by adding [AutoValidateAntiforgeryToken] or [ValidateAntiforgeryToken] attributes on Action methods as suggested in SO post here
I'm looking for global approach to validate token for all POST methods. So i created a middleware to do so. However i could not find suitable method to validate the token. Like in classic asp.net there is AntiForgery.Validate().
What's the equivalent method in ASP.NET Core
public class ValidateAntiForgeryTokenMiddleware
{
private readonly RequestDelegate _next;
public ValidateAntiForgeryTokenMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext httpContext)
{
if (httpContext.Request.Method.ToUpper() == "POST")
{
// where does this mehod exists?
// i could not find it in Microsoft.AspNetCore.Antiforgery namespace
AntiForgery.Validate();
}
await _next(httpContext);
}
}
public static class ValidateAntiForgeryTokenMiddlewareExtensions
{
public static IApplicationBuilder UseValidateAntiForgeryToken(this IApplicationBuilder builder)
{
return builder.UseMiddleware<ValidateAntiForgeryTokenMiddleware>();
}
}
I have to Inject Antiforgery as service
public class ValidateAntiForgeryTokenMiddleware
{
private readonly RequestDelegate _next;
private readonly IAntiforgery _antiforgery;
public ValidateAntiForgeryTokenMiddleware(RequestDelegate next, IAntiforgery antiforgery)
{
_next = next;
_antiforgery = antiforgery;
}
public async Task Invoke(HttpContext httpContext)
{
if (httpContext.Request.Method.ToUpper() == "POST")
{
await _antiforgery.ValidateRequestAsync(httpContext);
}
await _next(httpContext);
}
}
add Antiforgery as service in startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddAntiforgery();
}
Use my middlware
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, IApplicationLifetime appLifetime)
{
app.UseValidateAntiForgeryToken();
}

Getting SignalR IConnectionManager GetHubContext working in Startup.cs in aspnet core

I can't seem to the following code working. All I'm doing is in ConfigureServies calling _serviceProvider.GetService<IConnectionManager>(); and saving it in a static field and trying to use it later to get access to a IConnectionManager and subsequently call GetHubContext<MyHub> so I can broadcast messages to all connected clients.
_connectionManager.GetHubContext<MyHub>().Clients.All.doSomethingOnClients();
Just as a test, the same line of code inside a webapi controller action method works fine! (with IConnectionManager injected via constructor). That makes me believe my signalr set up is just fine, just how I got things in the startup class is wrong somewhere. GetHashCode on the IConnectionManager in startup and my controller gives different hash codes. I just need to hook things up on the ApplicationLifetime OnStartUp ...
Can you help me understand where things are going wrong please?
public class Startup
{
public static IServiceProvider _serviceProvider;
public static IConnectionManager _connectionManager;
private readonly IHostingEnvironment _hostingEnv;
public IConfigurationRoot Configuration { get; }
public Startup (IHostingEnvironment env)
{
// ...
}
public void ConfigureServices (IServiceCollection services)
{
// ....
services.AddSignalR(options => {
options.Hubs.EnableDetailedErrors = true;
});
services.AddMvc();
// ...
_serviceProvider = services.BuildServiceProvider();
_connectionManager = _serviceProvider.GetService<IConnectionManager>();
}
public void Configure (IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, IApplicationLifetime applicationLifetime)
{
// ...
applicationLifetime.ApplicationStarted.Register(OnStartUp);
// ...
app.UseMvc(routes => {
routes.MapRoute("default", "{controller=Home}/{action=Index}/{id?}");
});
app.UseSignalR();
// ...
}
public void OnStartUp ()
{
var x = _serviceProvider.GetService<MySingletonObject>();
// MySingletonObject has a VersionUpdated event handler
x.VersionUpdated += OnUpdate;
}
private void OnUpdate (object sender, EventArgs e)
{
// I get here everytime my singleton gets updated fine!
// but the following does not work
_connectionManager.GetHubContext<MyHub>().Clients.All.doSomethingOnClients();
}
}
I am using "Microsoft.AspNetCore.SignalR.Server/0.2.0-alpha1-22362".
1st thing is to realize this version of SignalR isn't shipping, it's just alpha. The problem you're having is because you're building 2 service providers and they're not talking to each other. You call BuildServiceProvider() instead of injecting the IConnectionManager into your Configure method. You can also clean up a lot of the service locator pattern by injecting dependencies directly into configure and then using them in the callbacks.
public class Startup
{
public IConfigurationRoot Configuration { get; }
public Startup (IHostingEnvironment env)
{
// ...
}
public void ConfigureServices (IServiceCollection services)
{
// ....
services.AddSignalR(options => {
options.Hubs.EnableDetailedErrors = true;
});
services.AddMvc();
// ...
}
public void Configure (IApplicationBuilder app,
IHostingEnvironment env,
ILoggerFactory loggerFactory,
IApplicationLifetime applicationLifetime,
MySingletonObject obj,
IHubContext<MyHub> context)
{
// ...
applicationLifetime.ApplicationStarted.Register(() => OnStartUp(obj, context));
// ...
app.UseMvc(routes => {
routes.MapRoute("default", "{controller=Home}/{action=Index}/{id?}");
});
app.UseSignalR();
// ...
}
public void OnStartUp(MySingletonObject obj, IHubContext<MyHub> context)
{
// MySingletonObject has a VersionUpdated event handler
obj.VersionUpdated += (sender, e) =>
{
context.Clients.All.doSomethingOnClients();
};
}
}
Even cleaner would be a another service that would compose everything so you don't end up with so many arguments in your startup method.

add claims to windows identity

I am trying to assign roles as claims for Windows Authentication for Asp.net Core Webapi project. Below is my transform by adding a role claim current identity.
public class ClaimsTransformer : IClaimsTransformer
{
public Task<ClaimsPrincipal> TransformAsync(ClaimsTransformationContext context)
{
//add new claim
var ci = (ClaimsIdentity) context.Principal.Identity;
var c = new Claim(ClaimTypes.Role, "admin");
ci.AddClaim(c);
return Task.FromResult(context.Principal);
}
}
And this middleware is added to Startup.Configure:
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(LogLevel.Debug);
loggerFactory.AddDebug();
app.UseClaimsTransformation(o => new ClaimsTransformer().TransformAsync(o));
app.UseStaticFiles();
app.UseMvc();
}
However role admin is not authorized in this method (403-Forbidden).
[Route("api/[controller]")]
public class ValuesController : Controller
{
// GET api/values/5
[HttpGet("{id}")]
[Authorize(Roles = "admin")]
public string Get(int id)
{
return "value";
}
}
It is working properly if [Authorize] is used. Any missing?
Unfortunately User.IsInRole method doesn't work with ClaimsTransformer(if you add role with ClaimsTransformer, IsInRole will be false) so you can't use [Authorize(Roles = "")] with ClaimsTransformer. In this case you can use Claims Based Authorization to handle authotorization.
So add below code to ConfigureServices and use Authorize attribute:
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddAuthorization(options =>
{
options.AddPolicy("admin", policy => policy.RequireClaim(ClaimTypes.Role, "admin"));
});
//...
}
[Route("api/[controller]")]
public class ValuesController : Controller
{
// GET api/values/5
[HttpGet("{id}")]
[Authorize(Policy = "admin")]
public string Get(int id)
{
return "value";
}
}