I'm creating ASP.NET Core 3.1 app, using SPA for front end. So I decided to create custom Authentication & Authorization. So I created custom attributes to give out and verify JWTs.
Lets say it looks like this:
[AttributeUsage(AttributeTargets.Method)]
public class AuthLoginAttribute : Attribute, IAuthorizationFilter
{
public async void OnAuthorization(AuthorizationFilterContext filterContext)
{
//Checking Headers..
using (var EF = new DatabaseContext)
{
user = EF.User.Where(p => (p.Email == username)).FirstOrDefault();
}
filterContext.HttpContext.Response.Headers.Add(
"AccessToken",
AccessToken.CreateAccessToken(user));
}
}
Everything was Okay, but my DatabaseContext, looked like this:
public class DatabaseContext : DbContext
{
public DbSet<User> User { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseMySQL("ConnectionString");
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
//....
}
}
I wanted to take Connection string from Appsettings.json and maybe use Dependency injection. I Changed Startup.cs to look like this:
//...
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
services.AddDbContext<DatabaseContext>(
options => options.UseMySQL(Configuration["ConnectionStrings:ConnectionString"]));
services.Add(new ServiceDescriptor(
typeof(HMACSHA256_Algo), new HMACSHA256_Algo(Configuration)));
services.AddSpaStaticFiles(configuration =>
{
configuration.RootPath = "ClientApp/build";
});
}
//...
Changed Database Context class to this:
public class DatabaseContext : DbContext
{
public DatabaseContext(DbContextOptions<DatabaseContext> options) : base(options) { }
public DbSet<User> User { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
///..
}
}
In Controllers I injected DB context and everything works. It looks like this:
[ApiController]
[Route("API")]
public class APIController : ControllerBase
{
private DatabaseContext EF;
public WeatherForecastController(DatabaseContext ef)
{
EF = ef;
}
[HttpGet]
[Route("/API/GetSomething")]
public async Task<IEnumerable<Something>> GetSomething()
{
using(EF){
//.. this works
}
}
}
But my custom Attribute doesn't work no more. I can't declare new Database context, because it needs DatabaseContextOptions<DatabaseContext> object to declare, so how do I inject DBContext to Attribute as I did to Controller?
This doesn't work:
public class AuthLoginAttribute : Attribute, IAuthorizationFilter
{
private DatabaseContext EF;
public AuthLoginAttribute(DatabaseContext ef)
{
EF = ef;
}
public async void OnAuthorization(AuthorizationFilterContext filterContext)
{
using(EF){
}
}
}
this works with controller, but with attribute complains about there not being constructor with 0 arguments.
What you can do is utilize the RequestServices:
[AttributeUsage(AttributeTargets.Method)]
public class AuthLoginAttribute : Attribute, IAuthorizationFilter
{
public void OnAuthorization(AuthorizationFilterContext context)
{
var dbContext = context.HttpContext
.RequestServices
.GetService(typeof(DatabaseContext)) as DatabaseContext;
// your code
}
}
If you allow me to add two comments to your code:
async void because in the event of an exception you will be very confused what is going on.using(EF) { .. }. You will dispose it early and this will lead to bugs later in the request. The DI container is managing the lifetime for you, trust it.If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With