I've got a scoped service, that needs to instantiate with user specific variables.
builder.Services.AddScoped<IUserService, UserService>();
UserService currently has a logger as it's constructor.
I'm currently doing the following through a factory, in a hacky way:
public class UserServiceFactory
{
private readonly ServiceProvider _sp;
private readonly DbContext _db;
public UserServiceFactory(ServiceProvider sp, DbContext db) { _sp = sp; _db = db; }
public async Task<IUserService> GetUserServiceForUserAsync(Guid userId)
{
var (apiKey, apiSecret) = await _db.FetchApiKeyAndSecretAsync(userId);
var userService = _sp.GetRequiredService<IUserService>();
userService.InitUser(apiKey, apiSecret);
return userService;
}
}
I'm running into the following problems:
I can't use builder.Services.AddScoped<IUserService, UserService>(); with string parameters, because as soon as it attempts to register in DI, it can't resolve the string parameters in the constructor, even though the only place I'm going to be initializing it will be in the factory, and I'll be providing said string parameters.
If I don't use builder.Services.AddScoped<IUserService, UserService>();, I'd need to use Activator.CreateInstance<UserService>(...), which ties a concrete implementation to this class which is not ideal. In addition, I can't track said UserService for disposal when the scope gets disposed.
It was suggested that I register a Func<> to return a user service. If I do this, I don't believe it will be a scoped service, and thus not be disposed of properly when the scope is destroyed.
The implementation of UserService is essentially an an HTTP Client, that will make requests with an apiKey and apiSecret of the IdentityUser. I'd like it to exist for the duration of the scope (In the case of asp.net core, the request, or in the case of being called from a Quartz job, the duration of the job), and then dispose afterwards.
UserService contains about 20 various methods, such as FetchAccountAsync, BuyItemAsync(itemId, quantity), SellItemAsync(itemId), which should make requests using the initialized httpclient. I'd like to avoid trying to initialize the apiKey/apiSecret in each method, because this will add a level of synchronization that I don't feel is needed. HttpClient is by default multithreaded, so my methods are fairly pain free:
Task BuyItemAsync(string itemId, int quantity)
{
var res = await _httpClient.GetAsync($"{_baseUrl}/buy?itemId={itemId}&qty={quantity}");
res.EnsureSuccessStatusCode();
}
How can I initialize my UserService with these apiKeys, apiSecrets, to be used in a scoped manner?
NOTE: Some of these details I've added based on your comments. Some of these may appear a little contrived because I don't know your full logic, context, or needs.
Design
I suggest the following
Remove the factory.
Go ahead and use builder.Services.AddScoped<IUserService, UserService>();.
Change the constructor of UserService to accept an ISecurityContext that will provide the API key and secret. This context will also be registered with AddScoped.
Have the UserService use the ISecurityContext at runtime, and remove any properties/parameters for API key and secret (if you had them).
Have the SecurityService use an IUserProvider at runtime, and remove any properties/parameters for user ID (if you had them).
This means no runtime data needs to be injected, no hacky method to expose, no factory, and no injecting the service provider.
At startup register the interfaces. It is important that they be scoped. They are going to share the lifetime, which will be short.
...
builder.Services.AddScoped<IUserService, UserService>();
builder.Services.AddScoped<ISecurityContext, SecurityContext>();
builder.Services.AddScoped<IUserProvider, UserProvider>();
Then implement the classes and a Result that can return all the contextual data.
public class Result
{
public Result(string apiKey, string apiSecret, Guid userId)
{
ApiKey = apiKey;
ApiSecret = apiSecret;
UserId = userId;
}
public string ApiKey { get; }
public string ApiSecret { get; }
public Guid UserId { get; }
}
public interface IUserProvider
{
Guid GetUserId();
}
public class UserProvider : IUserProvider
{
public async Task<Guid> GetUserId() => IdentityUser.GetUserId());
}
public interface ISecurityContext
{
Task<Result> GetApiKeyAndSecretAsync();
}
public class SecurityContext : ISecurityContext
{
private readonly DbContext _db;
private readonly IUserProvider userProvider;
// safe because this SecurityContext will be scoped!
private Result _result;
public SecurityContext(DbContext db, IUserProvider userProvider)
{
_db = db;
_userProvider = userProvider;
}
public async Task<Result> GetApiKeyAndSecretAsync()
{
if (_result != null) return _result;
var userId = _userProvider.GetUsedId();
var (apiKey, apiSecret) = await _db.FetchApiKeyAndSecretAsync(userId);
return _result = new Result(apiKey, apiSecret, userId);
}
}
public interface IUserService
{
Task DoWhatever();
}
public class UserService : IUserService
{
private readonly ISecurityContext _context;
public UserService(ISecurityContext context) => _context = context;
public async Task DoWhatever()
{
// use the security context
var result = await _context.GetApiKeyAndSecretAsync();
// use the result; e.g. pass the key/secret/user ID
// on to an HttpClient, RestClient, etc.
...
}
...
}
Usage
Using an IUserService means injecting that into your Quartz.NET job, a message handler, a web controller... wherever. In each case you may realize that one single implementation of any of these interfaces is not enough. That's OK. There are ways in dependency injection to fix that (e.g. named resolutions of multiple different concrete implementations), but I leave that to you.
Here's an example usage for a web controller.
public class MyController
{
private readonly IUserService _userService;
public MyController(IUserService userService, ...)
{
_userService = userService;
...
}
[HttpGet]
public async Task<IActionResult> GetStuff(...)
{
// gets the key and secret first time
await _userService.DoWhatever();
// uses cached versions of key, secret, guid across
// calls of _userService methods within scope
var someResult = await _userService.GetSomethingElse();
...
}
Commentary
This design has a few advantages
Security details are encapsulated behind an abstraction and not mixed into the UserService
The whole thing is more testable because the security details can be mocked when testing the UserService.
Key and secret are cached once within the scope and can be reused across methods in UserService that are invoked while in the same scope.
As #NKosi said in the comments, mixing runtime data at construction time is an anti-pattern. The link they referenced, Dependency Injection Code Smell: Injecting runtime data into components, is a good read and goes into more depth.
As you add more runtime data, you can expand the properties in Result and logic in SecurityContext or you can inject more context-like objects into UserService returning their own result-like instances.
There is a placeholder pattern that I have found useful here.
STARTUP CODE
Define dependencies in your application startup code, something like the following. Note that .NET does not allow you to run async processing in the factory method for IUserService:
app.UseMiddleware<DependencySetupMiddleware>();
services.AddSingleton(new MyDatabase());
services.AddScoped<UserServiceHolder>();
services.AddScoped<IUserService>(ctx =>
{
return ctx.GetRequiredService<UserServiceHolder>().UserService;
});
The holder class just looks like this:
public class UserServiceHolder {
public IUserService UserService { get; set; }
}
MIDDLEWARE CODE
The async processing can be done in a small middleware class. For the HTTP case you would do it like this, assuming that you get the User Id after authentication. Note that dependencies cannot be added to the .NET container at runtime, but you can update the holder object:
public class DependencySetupMiddleware
public DependencySetupMiddleware(RequestDelegate next) {
}
public async Task Invoke(HttpContext context, MyDatabase db) {
var userId = context.User.Claims.First(c => c.Type == "UserId")
var (apiKey, apiSecret) = await db.FetchApiKeyAndSecretAsync(userId);
var userService = new UserService(apiKey, apiSecret)
context.GetRequiredService<UserServiceHolder>().UserService = userService;
await next();
}
}
For Quartz you would have a similar middleware class - a Job Factory, which reads the job's user ID rather than using claims or the HTTP context.
BUSINESS LOGIC
With this code in place you can inject an IUserService into your business logic and forget about the holder class:
class MyController {
public MyController(IUserService userService) {
}
}
I think you might already have an answer here, but let me give you a working example. Here's my assumption:
I want to have an instance of a class that has all the things about the user available.
Here's the approach I used for PopForums.
Step 1: You're using some kind of built-in ASP.NET authentication, probably cookies or something external. I won't cover that here, because there are many ways to do it, but look at HttpContext.SignInAsync() for more. The important part is to use a name or identifier that will be put into the token it reads back in the next step.
Step 2: Use middleware to get your user and make it stick. You'll start with a ClaimsIdentity when you use HttpContext.AuthenticateAsync(schemeName). For example:
public async Task InvokeAsync(HttpContext context, IUserService userService)
{
var authResult = await context.AuthenticateAsync(schemeNameUsedFromSignIn);
var identity = authResult?.Principal?.Identity as ClaimsIdentity;
if (identity != null)
{
var user = userService.GetUserByName(identity.Name);
if (user != null)
{
// add claims to the identity if you want
// then stash your user object in the Items collection, which lasts the duration of the request
context.Items["TheUser"] = user;
context.User = new ClaimsPrincipal(identity);
}
}
await _next.Invoke(context);
Step 3: Enable getting the user anywhere you want by pulling it out of the context of the request, but isolate it to an interface so there are no hard dependencies. Example:
public interface IUserRetrievalShim
{
User GetUser();
}
public class UserRetrievalShim : IUserRetrievalShim
{
private readonly IHttpContextAccessor _httpContextAccessor;
public UserRetrievalShim(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
public User GetUser()
{
var user = _httpContextAccessor.HttpContext?.Items["TheUser"] as User;
return user;
}
}
What I like about this approach is that any classes up and down the dependency chain can be mocked out and unit tested without all of the HttpContext references. IHttpContextAccessor does a great job isolating it, and if it's not available, you'll get a null. And in this case, you're getting your user object, not the one tied to ASP.NET. You can still check HttpContext.User != null if you want, but this similarly will be null if there's no authenticated user. I only do the above with claims because maybe other app areas may want it.
Step 4: In your controllers, service classes or anything in between, inject IUserRetrievalShim and call its GetUser() method to get the user.
The bottom line here is that dependency injection is not the place to make the user stuff contextual. DI is purely setup and configuration, not run-time context. Use your UserService where ever you want, and combined with this shim, you can pass its ID or whatever to those service methods. You should not expect the service to be contextual out of the box by way of injection.
With that said, your User objects (not to be confused with HttpContext.User) can be composed of whatever you want, so long as you're OK with whatever the cost is to fetch that information and hydrate the object.
my controller method consumes a JWT token which was enabled in ConfigureServices method in Startup.cs
.AddJwtBearer(options => { // some code }; });
The CreateUser() action in UserController consumes this token
[HttpPost, Authorize("JWT")]
public SaveResponse CreateUser(IUnitOfWork uow, UserRequest request) {
return new UserRepository().Create(uow, request);
}
The problem is as follows: A few methods deeper upon creating a new user, there's a method HasPermission() that checks logged in user's Administration permissions. However, in this particular case using JWT, there's no logged in user. The presence of valid JWT suffices. I am going to modify this HasPermission() in a way, that it also accepts JWT.
At CreateUser method level, the JWT is present inside HttpRequest's 'Authorization' header.
The question is - How can I deliver this JWT token to like a 8th method in a chain of methods executed by UserRepository().Create(uow, request) ? Is there a way to pull this off without modifying parameters of these methods?
thank you
If you use DI to instantiate service dependecies you can register IHttpContextAccessor via services.AddHttpContextAccessor() and use it to get information about request:
public SomeService(IHttpContextAccessor contextAccessor)
{
_contextAccessor = contextAccessor;
}
public void SomeServiceMethod()
{
var auth = _contextAccessor.HttpContext.Request.Headers[HeaderNames.Authorization].ToString(); // possibly will need to remove scheme from the header
}
This particular case using JWT, there's no logged in user. The presence of valid JWT suffices.
Assuming you have the auth middleware enabled, if the request is able to reach CreateUser action, then [Authorize] attribute makes sure that the token is valid. So you don't need to do another validation.
Second, you shouldn't flow the token down to the repository. Keep HTTP and data retrieval concerns separate.
The solution to not "passing a parameter down 8 level" is to use dependency injection throughout your application and let it keep track of dependencies.
To access the current user inside your repo, create an interface that exposes the user:
interface IPrincipalAccessor {
ClaimsPrincipal? Principal { get; }
}
then implement this with IHttpContextAccessor
private class HttpPrincipalAccessor : IPrincipalAccessor
{
private IHttpContextAccessor _httpContextAccessor;
public HttpPrincipalAccessor(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
public ClaimsPrincipal? Principal => _httpContextAccessor?.HttpContext?.User;
}
You need to enable IHttpAccessor and register this class in DI:
services.AddHttpContextAccessor();
services.AddScoped<IPrincipalAccessor, HttpPrincipalAccessor>();
Now you can inject this interface in your repo and use the user claims. The repo isn't aware, and doesn't care where the user comes from, it just needs to know the current user.
class MyRepo
{
private IPrincipalAccessor _principalAccessor;
public MyRepo(IPrincipalAccessor principalAccessor)
{
_principalAccessor = principalAccessor;
}
Task Create(/* some parameters */)
{
var user = _principalAccessor.Principal;
if (user.HasClaim("eyes", "2"))
{
// user has two eyes
}
// ...
}
}
But the problem with your code is that you're not using dependency injection, so you need to inject your repo, instead of newing it up.
I have a Service and virtually every method requires the current user to execute. Before I started using autofac I basically created a public property like this:
private IOrderProvider _orderProvider;
public IOrderProvider OrderProvider => _orderProvider ?? (_orderProvider = new OrderProvider((ClaimsIdentity)User.Identity));
Because this was a public property on the controller, it would have access to the User. Now using autofac I register my Service in the StartupConfig. Naturally I don't have access to the User there.
Is there a way to inject the current user into the OrderProvider constructor, or another way to get at it?
The user principal can be accessed through the HttpContext.Current.User. How ever tightly coupling code to HttpContext is frowned upon as it is not very unit test friendly. so create a service that exposes what you want.
using System.Security.Principal;
public interface IPrincipalProvider
{
IPrincipal User { get; }
}
An implementation in production can look like this.
using System.Security.Principal;
public class DefaultPrincipalProvider : IPrincipalProvider
{
public IPrincipal User
{
get
{
return HttpContext.Current.User;
}
}
}
With that done update the dependent class to use the abstraction
public class OrderProvider : IOrderProvider
{
private readonly ClaimsIdentity identity;
public OrderProvider(IPrincipalProvider provider) {
identity = (ClaimsIdentity)provider.User.Identity;
}
}
and then register the provider on startup as usual
//...other code removed for brevity
builder.RegisterType<DefaultPrincipalProvider>().As<IPrincipalProvider>();
builder.RegisterType<OrderProvider>().As<IOrderProvider>();
//...other code removed for brevity
The service should get everything injected
public class Service {
private readonly IOrderProvider _orderProvider;
public Service(IOrderProvider orderProvider) {
_orderProvider = orderProvider;
}
public IOrderProvider OrderProvider => _orderProvider;
}
I have a custom authorization attribute which verifies token inside header and sets user-principal.
//when token is validated
var userIdentityBase = new UserIdentityBase(token); <-- Inherited GenericIdentity
IPrincipal principal = new GenericPrincipal(userIdentityBase, null);
actionContext.Request.GetRequestContext().Principal = principal;
HttpContext.Current.User = principal;
Thread.CurrentPrincipal = principal;
This works fine when I check for Identity inside controller
But when i inject IPrincipal to my service classes, it does not work anymore. IsAuthenticated is false.
Unity setup code:
container.RegisterType<IPrincipal>(new InjectionFactory(u => HttpContext.Current.User));
When injected, it does not work (both screenshots are taken withing same request):
Any suggestions?
I've solved this by creating own interface and implementation, which will extract user identity at runtime. Works like a charm for my case
Interface
public interface IIdentityProvider
{
IPrincipal GetPrincipal();
}
Implementation:
public class IdentityProvider : IIdentityProvider
{
public IPrincipal GetPrincipal()
{
return HttpContext.Current.User;
}
}
unity registration:
container.RegisterType<IIdentityProvider, IdentityProvider>();
And usage:
IIdentityProvider _identityProvider;
public BookingRecordService(IIdentityProvider identityProvider)
{
_identityProvider = identityProvider;
var isAuth = _identityProvider.GetPrincipal().Identity.IsAuthenticated;
}
RequestContext.Principal.Identity.IsAuthenticated
I'm working on an application which has two projects:
Core - houses the data access layer using repository pattern and domain-driven design
UI - using ASP.Net MVC. Currently, I am able to get the current logged in user's info(id, name, etc..) inside the UI controller via the User property like this:
using Microsoft.AspNet.Identity;
public class ExamController : Controller
{
IExaminationRepository _repository;
public ExamController()
{
_repository = RepositoryFactory.Get<IExaminationRepository>();
}
[HttpPost]
[Authorize(Roles = "Examiner")]
public ActionResult Create(ExamViewModel viewModel)
{
try
{
ExaminationDomain domain = Mapper.Map<ExamViewModel, ExaminationDomain>(viewModel);
//TODO: Move this to the repository
domain.AuthorId = User.Identity.GetUserId();
_repository.Add(domain);
return RedirectToAction("Index");
}
catch
{
return View();
}
}
}
I would like to move the line: domain.AuthorId = User.Identity.GetUserId(); to my repository concrete implementation like this:
using Microsoft.AspNet.Identity;
using System.Security.Principal;
internal class ExaminationRepository
{
public DBEntities context;
public IPrincipal User;
public ExaminationRepository(DBEntities context)
{
this.context = context;
//I'd like to instantiate User property here:
this.User = "not sure what to instantiate with";
}
public void Add(ExaminationDomain domain)
{
Examination newExam = Mapper.Map<ExaminationDomain, Examination>(domain);
newExam.AuthorId = User.Identity.GetUserId();
newExam.CreatedBy = User.Identity.Name;
newExam.CreatedDate = DateTime.Now;
context.Examinations.Add(newExam);
context.SaveChanges();
}
But I am not sure what to instantiate the User property to in the constructor. I've read some suggestions to use WindowsIdentity.GetCurrent().User instead of creating a user property but this doesn't contain the user id, only user name.
Any other suggestions on getting user info?
I'd really appreciate some help on this..
Thanks,
I would decouple your repository from the httpcontext with a custom manager. For example I have a interface called IAUthenticationManager
public interface IAUthenticationManager
{
string CurrentUserId();
bool HasCurrentUserRole(string roleName),
}
Easy to test and fully decoupled.
This won't work easily since the repository can be used in many different contexts, even such contexts where user is not set. If you create a concrete dependency in your constructor, your repository will no longer be an independent data provider.
For example, referencing
HttpContext.Current.User.Identity
directly would create a dependency to a web context and the repository would be unusable in non-web contexts.
The best you could do is just to let the repository client provide this:
public void Add(ExaminationDomain domain, IPrincipal principal)
{
Examination newExam = Mapper.Map<ExaminationDomain, Examination>(domain);
newExam.AuthorId = principal.Identity.GetUserId();
newExam.CreatedBy = principal.Identity.Name;
newExam.CreatedDate = DateTime.Now;
context.Examinations.Add(newExam);
context.SaveChanges();
}
or (which could be possible)
public ExaminationRepository(DBEntities context, IPrincipal user)
{
this.context = context;
this.user = user;
}
The latter case could still be correctly resolved by an IoC container if you tell the container how to resolve the dependency.
In a web context, you could set the container to resolve it to HttpContext.Current.User.
In a non-web context, you could set the container to resolve it to WindowsIdentity.GetCurrent().User.
use HttpContext class witch is a singleton class:
first add a using to Microsoft.AspNet.Identity;
and then you can do some thing like this:
HttpContext.Current.User.Identity.GetUserId();
since GetUserId is an extension method you have a reference to Microsoft.AspNet.Identity
but if you need to access user information in several places of you app I suggest to have a wrapper class with properties that you need and instantiate when user logs in then store it in session variable this way you have two benefits:
1- you don't need to query db to get username, email etc.. on each user info usage across the app.
2- you don't need assembly that your repository lives to aspnet identity.
You need to instantiate this.User with identity information of current thread:
this.User = System.Threading.Thread.CurrentPrincipal;
var currentIdentity = (System.Security.Claims.ClaimsIdentity)User.Identity;
var userId = currentIdentity.Claims
.Where(p => p.Type.EndsWith("nameidentifier")).Single().Value;
Note that the type of CurrentPrincipal.Identity is an IIdentity. You can cast it to System.Security.Claims.ClaimsIdentity, which contains a property named Claims. This property contains all your claims, including userid and 3rd party token (e.g. Facebook token).
To retrieve UserId, find a claims with Type == "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier"