C# Dependency Injection - Authentication - c#

I am trying to figure out .net core dependency injection. My project is currently a web api, with some custom authentication. I've added my authentication like so (in Startups.cs under "ConfigureServices":
services.AddAuthentication(Authentication.Hmac.HmacDefaults.AuthenticationScheme).AddHmac(options =>
{
options.AuthName = "myname";
options.CipherStrength = HmacCipherStrength.Hmac256;
options.EnableNonce = true;
options.RequestTimeLimit = 5;
options.PrivateKey = "myprivatekey";
});
My question is this: How do you access IMemoryCache within the authentication service? I've tried just created a new MemoryCache and passing it in, but that doesn't work. The main goal is for checking Nonce values (see if they are in the cache, if yes auth fails, if no, add to cache auth passes).
Again, this is .NET Core 2 (Web API).
UPDATES:
Here is the basis of the HmacHandler class (the part that ACTUALLY does the auth):
public class HmacHandler : AuthenticationHandler<HmacOptions>
{
private static string Signature;
private static string Nonce;
private static Encoding Encoder { get { return Encoding.UTF8; } set { } }
IMemoryCache MemoryCache { get; set; }
public HmacHandler(IOptionsMonitor<HmacOptions> options, ILoggerFactory logger, UrlEncoder encoder, IDataProtectionProvider dataProtection, ISystemClock clock)
: base(options, logger, encoder, clock)
{
}
protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
{...}
}
Then there is the "Options" class.
public class HmacOptions : AuthenticationSchemeOptions
{...}
It can't have a constructor that takes parameters. I need to USE the IMemoryCache in the HmacHandler class. I tried adding IMemoryCache to it (in the constructor, etc). That did NOT work.

You would need to set IMemoryCache MemoryCache { get; set; } to public if you would like to use outside of the class via dependency injection.
public IMemoryCache MemoryCache { get; set; }

So the answer ended up being a combination of things here. So here is what I did:
Add the "public" to the IMemoryCache in the HmacHandler
Added the IMemoryCache to the constructor of HmacHandler
Changed the get/set of the cache from "TryGetValue/CreateEntry" to pure "Get/Set".

private IMemoryCache memoryCache { get; set; }
public HmacAuthenticationHandler(IOptionsMonitor<HmacAuthenticationOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock, IMemoryCache memCache)
: base(options, logger, encoder, clock)
{
memoryCache = memCache;
}
And then in HandleAuthenticateAsync use Get and Set of memoryCache.

public void ConfigureServices(IServiceCollection services)
{
services.AddMemoryCache();
services.AddAuthentication(Authentication.Hmac.HmacDefaults.AuthenticationScheme).AddHmac(options =>
{
options.AuthName = "myname";
options.CipherStrength = HmacCipherStrength.Hmac256;
options.EnableNonce = true;
options.RequestTimeLimit = 5;
options.PrivateKey = "myprivatekey";
// do your stuff with Test class here
});
}
public class Test {
private IMemoryCache _cache;
public Test(IMemoryCache cache) {
_cache = cache;
}
}

Related

How to access WebRootPath anywhere in the project?

I'd like to access IWebHostEnvironment.WebRootPath anywhere in the asp.net core mvc application. For instance, some random class deep in the class hierarchy. Is there a static class or some other method to do so?
I am aware that I can inject IWebHostEnvironment or that I can cache the value on the startup of the application. My question is strictly about accessing it without these methods.
I am aware that I can inject IWebHostEnvironment or that I can cache the value on the startup of the application. My question is strictly about accessing it without these methods.
No, you cannot. There's no static built in here with access to this information. You can create your own though.
You can achieve this y doing the following
In your Shared project or common project which is reference by the Web project add the below interface
public interface IApplicationContext
{
public string BaseUrl { get; }
}
Then, in the web project add below code
public sealed class ApplicationContext : IApplicationContext
{
private readonly IWebHostEnvironment _webHostEnvironment;
private readonly IHttpContextAccessor _httpContextAccessor;
public ApplicationContext(IWebHostEnvironment webHostEnvironment, IHttpContextAccessor httpContextAccessor)
{
_webHostEnvironment = webHostEnvironment;
_httpContextAccessor = httpContextAccessor;
}
public string BaseUrl
{
get
{
var baseUrl = _webHostEnvironment.IsDevelopment() ? AppConstants.BaseUrl.FELocalHostBaseUrl :
_httpContextAccessor.HttpContext?.Request.BaseUrl();
return baseUrl!;
}
}
}
Then, in you need to configure the dependency injection in your Startup.cs or any where that you configure DI as below
services.AddHttpContextAccessor();
services.AddScoped<IApplicationContext, ApplicationContext>();
Then you can inject the IApplicationContext in any service class constructor and access the baseUrl like below
public sealed class SecurityService
{
private readonly IApplicationContext _applicationContext;
public SecurityService(IApplicationContext applicationContext)
{
_applicationContext = applicationContext;
}
public async Task<ResponseResult> SendResetPasswordEmail(ForgotPasswordModel forgotPasswordModel, CancellationToken cancellationToken)
{
var baseUrl = _applicationContext.BaseUrl;
return new ResponseResult();
}
}

Dependency injection in authentication options

I'm trying to inject a service into a ValidationHandler that inherits from JwtSecurityTokenHandler which validates the Jwt's signature. Unfortunately, to use the handler, I have to use object initialization with new in ConfigureServices, which means I can't use the injected services that comes with adding the service to the dependency container.
public class DynamicKeyJwtValidationHandler : JwtSecurityTokenHandler
{
private readonly IMemoryCache _cache;
public DynamicKeyJwtValidationHandler(IMemoryCache cache)
{
_cache = cache;
}
}
services.AddTransient<DynamicKeyJwtValidationHandler>();
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(opts =>
{
opts.SecurityTokenValidators.Clear();
opts.SecurityTokenValidators.Add(new DynamicKeyJwtValidationHandler(???));
});
So what can I do to still be able to use the IMemoryCache?
You can create an implementation of IConfigureNamedOptions<JwtBearerOptions>:
public class JwtOptionsConfigurer : IConfigureNamedOptions<JwtBearerOptions>
{
private readonly DynamicKeyJwtValidationHandler _tokenValidator;
public JwtOptionsConfigurer(DynamicKeyJwtValidationHandler tokenValidator)
{
_tokenValidator = tokenValidator;
}
public void Configure(string name, JwtBearerOptions options)
{
options.SecurityTokenValidators.Clear();
options.SecurityTokenValidators.Add(_tokenValidator);
}
public void Configure(JwtBearerOptions options)
{
Configure(JwtBearerDefaults.AuthenticationScheme, options);
}
}
And then add it like so:
services.AddSingleton<IConfigureOptions<JwtBearerOptions>, JwtOptionsConfigurer>();
services
.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer();
We still need to call .AddJwtBearer() because that does some necessary registrations, etc.
Side note (in case it's useful to anyone): the authentication middleware creates a new JwtBearerOptions every time it is needed, so the configuration code above will be run multiple times.

dotnet core 3.0 WebApi, applicationPart and authorization

We have a modular application, which means that our api controllers get loaded during startup. We load the controllers into the applicationPart like this:
services.AddMvc()
.SetCompatibilityVersion(CompatibilityVersion.Version_3_0)
.ConfigureApplicationPartManager(applicationPartManager =>
{
foreach (var module in _modules)
{
var apiControllerAssemblies = module.GetApiControllerAssemblies();
foreach (var apiControllerAssembly in apiControllerAssemblies)
applicationPartManager.ApplicationParts.Add(new AssemblyPart(apiControllerAssembly));
}
});
We want to protect our apis with Basic authentication. I've created a middleware like this:
public class BasicAuthenticationHandler : AuthenticationHandler<AuthenticationSchemeOptions>
{
public BasicAuthenticationHandler(IOptionsMonitor<AuthenticationSchemeOptions> options,
ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock)
{
}
protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
{
if (!Request.Headers.ContainsKey("Authorization"))
return AuthenticateResult.Fail("Missing Authorization
Header");
//More to come
}
}
The middleware is registered in startup.cs like this:
services.AddAuthentication("Basic")
.AddScheme<AuthenticationSchemeOptions, BasicAuthenticationHandler>("Basic", null);
Accessing localhost: will always trigger the HandleAuthenticateAsync method. However when I try to access the localhost:/user/users endpoint the method never hit the breakpoint and will always result in a HTTP 401 Unauthorized. The controller itself is marked with the Authorize attribute.
Any ideas where it goes wrong? Any hints to where I should start looking for a solution?
Thanks!
Not sure if this helps, but when I had to implement Authentication this is what I did.
a. Declare a class extending AuthenticationSchemeOptions
public class CustomAuthOptions: AuthenticationSchemeOptions
{
}
b. Declare a class implementing the AuthenticationHandler<TOptions>
internal class CustomAuthHandler : AuthenticationHandler<CustomAuthOptions>
{
IHttpContextAccessor _httpContextAccessor;
IUser _user;
public CustomAuthHandler(IOptionsMonitor<CustomAuthOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock,
IHttpContextAccessor httpContextAccessor, IUser user) : base(options, logger, encoder, clock)
{
_httpContextAccessor = httpContextAccessor;
_user = user;
}
protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
{
//logic to authenticate
}
protected override Task HandleChallengeAsync(AuthenticationProperties properties)
{
//more code
}
}
c. Add an extension method to the AuthenticationBuilder class
public static AuthenticationBuilder AddCustomAuth(this AuthenticationBuilder builder,
Action<CustomAuthOptions> config)
{
return builder.AddScheme<CustomAuthOptions, CustomAuthHandler>("CheckInDB", "CheckInDB", config);
}
d. Finally in the Startup.cs
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = "CheckInDB";
options.DefaultChallengeScheme = "CheckInDB";
}).AddCustomAuth(c => { });
This may be more than what is needed, but when I was in the same boat, a couple of months ago, I spent a good few days piecing all of this together.

Dependency Injection into Entity Class

Using Asp.Net Core we can make use of Dependency Injection in controllers/repositories.
However, I wish do do some logging in my Entity Class.
class Person
{
private ILogger<Person> _logger;
private List<Pets> pets;
public Person(ILogger<Person> logger)
{
_logger = logger;
}
public bool HasCat()
{
_logger.LogTrace("Checking to see if person has a cat.");
// logic to determine cat ownership
hasCat = true;
return hasCat;
}
}
When the Person class is instantiated by EntityFramework it does not attempt to inject any dependencies.
Can I force this? Am i going about it in completely the wrong way?
Ultimatley I just want to be able to use logging consistently throughout the application.
Thanks,
It is possible but I don't recommend it because I agree with commenters that logging belongs in your services and controllers.
EF Core 2.1 allows injecting the DbContext into a private constructor that EF will invoke. See the official docs.
First you need to expose a LoggerFactory property in your DbContext class.
public class MyDbContext : DbContext
{
public MyDbContext(DbContextOptions<MyDbContext> options, ILoggerFactory loggerFactory = null)
{
LoggerFactory = loggerFactory;
}
public ILoggerFactory LoggerFactory { get; }
}
Then you can inject the DbContext into a private constructor in your entity class.
public class Person
{
private readonly ILogger _logger;
public Person() { } // normal public constructor
private Person(MyDbContext db) // private constructor that EF will invoke
{
_logger = db.LoggerFactory?.CreateLogger<Person>();
}
public bool HasCat()
{
_logger?.LogTrace("Check has cat");
return true;
}
}

How to implement a UserFactory using IHttpContextAccessor

I used to have a UserFactory (before vNext) that used HttpContext.Current but since that is now gone I am having trouble recreating it.
I want it to be a static class that sets and gets the current user to access user data throughout the application.
I know I must use the DI system but not sure how.
Code so far:
public class CurrentContext : IHttpContextAccessor
{
private IHttpContextAccessor ctx;
public HttpContext HttpContext
{
get
{
return ctx.HttpContext;
}
set
{
ctx.HttpContext = value;
}
}
}
services.AddTransient<IHttpContextAccessor, CurrentContext>();
public class UserFactory
{
private IHttpContextAccessor _context;
public UserFactory(IHttpContextAccessor Context)
{
_context = Context;
}
public void Add(string s) => _context.HttpContext.Session.SetString(s, s);
public string Get(string s) => _context.HttpContext.Session.GetString(s);
}
How can I get a UserFactory instance anywhere in my app with the current context?
I suggest you make the UserFactory class non-static and register it as scoped:
services.AddScoped<UserFactory>();
This will create one instance per web request. You can inject this into every other class and let the UserFactory take a dependency on IHttpContextAccessor to get the current HttpContext.
This adheres to the dependency inversion philosophy Microsoft is trying to implement in ASP.NET 5. Static classes do not really fit into this and should be avoided as much as possible.
Example
UserFactory class:
public class UserFactory
{
private readonly IHttpContextAccessor _httpContextAccessor;
public UserFactory(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
// other code...
}
ConfigureServices() in Startup class:
public void ConfigureServices(IServiceCollection services)
{
// ...
services.AddScoped<UserFactory>();
// ...
}
Now you can inject an instance of UserFactory in a controller for example:
public class SomeController : Controller
{
private readonly UserFactory _userFactory;
public SomeController(UserFactory userFactory)
{
_userFactory = userFactory;
}
// ...
}
Now when UserFactory is begin resolved, IHttpContextFactory inside UserFactory will also be resolved.

Categories