Pass identity user to services - c#

We are developing a web application in which the user can register orders, customers, etc. and later review them. We have services that are used by MVC controllers in order to interface with the web UI.
Now we face the problem of multiple users: each service should be provided the currently authorised user Id, so all operations (CRUD and bussiness logic) will only be allowed for that user id. How is it supposed to be passed?
I am thinking about having a parameter passed to my IDataService (base class for services), which is instantiated by the WhateverController, which in turn has access to the User.Identity.GetUserId() method, BUT as I am using an IoC container (Ninject) I don't know how to do that. I guess that IDataService needs a reference to a IUserInfoProvider, so it can call IUserInfoProvider.GetUserId(). Then I can inject somehow an implementation based on Identity and having the current web context information, pretty much in the same way that the Controller must be instantiated.
Question is: how to get that data?
A simpler solution, of course, would be to do it by hand in each Controller constructor, but there should be a more automatic and elegant way to solve this.
EDIT: After some more reasearch, thanks to the answer of Cuong Le, the question I had to ask was, in fact, "how to inject the UserManager from the current context?".
However, in order to decouple my services layer from MVC, I created an IUserInfoProvider, which provides access to the authenticated user data. The implementation based in Identity and the UserManager lies in the Web UI (MVC) project, so it has a IPrincipal as suggested by Cuong Le, and an ApplicationUserManager, all injected using Ninject.
The following interface abstract the user information from Identity and the UserManager.
public interface IUserInfoProvider<T>
{
string GetUserId();
T GetUserData();
}
Here is the implementation in the MVC project using Identity and UserManager.
public class IdentityUserInfoProvider : IUserInfoProvider<DatosEmpresa>
{
private readonly ApplicationUserManager _userManager;
private readonly IPrincipal _user;
public IdentityUserInfoProvider(ApplicationUserManager userManager, IPrincipal user)
{
_userManager = userManager;
_user = user;
}
public string GetUserId()
{
return _user.Identity.GetUserId();
}
public DatosEmpresa GetUserData()
{
return _userManager.FindById(_user.Identity.GetUserId()).DatosEmpresa;
}
}
And the Ninject configuration bit
kernel.Bind<IUserInfoProvider<DatosEmpresa>>().To<IdentityUserInfoProvider>();
kernel.Bind<IPrincipal>()
.ToMethod(ctx => HttpContext.Current.User)
.InRequestScope();
kernel.Bind<ApplicationUserManager>()
.ToMethod(ctx => HttpContext.Current.GetOwinContext().GetUserManager<ApplicationUserManager>())
.InRequestScope();
Then I can use an IUserInfoProvider inside any service object and it gets the correct user.

The simple solution is you can put IPrincipal into NInject Container:
kernel.Bind<IPrincipal>().ToMethod(context => HttpContext.Current.User);
So in your ServiceBase you can inject IPrincipal via either property or contructor, like this:
class ServiceBase
{
[Inject]
public IPrincipal User { get; set; }
}
Now you can get information from this property.

Related

EF Core reusable DbContext

I'm trying to create a reusable base for future web applications made with asp net core.
I created a library that contains a BaseDbContext that inherit from IdentityDbContext:
public class BaseDbContext : IdentityDbContext<ApplicationUser>
{
public BaseDbContext(DbContextOptions options) : base(options)
{
}
}
Inside this library there are some services for login and creation of Users.
Everytime that I will be creating a new WebApplication I will reference the library and I will create a new DbContext like this:
public class ProjectDbContext : BaseDbContext
{
//some generics DBSET
public ProjectDbContext (DbContextOptions<ProjectDbContext> options) : base(options)
{
}
}
And in the startup:
services.AddDbContext<ProjectDbContext>(options =>
{
options.UseSqlServer(connection);
});
Since the service for the login and creation of users require a reference of BaseDbContext, I created a IDbContextFactory inside the base project that will be implemented by the main project like this:
public class ProjectContextFactory : IDbContextFactory
{
private readonly ProjectDbContext _projectDbContext;
public ProjectDbContextFactory(ProjectDbContext remDbContext)
{
_remDbContext = remDbContext;
}
public BaseDbContext GetBaseDbContext()
{
return _projectDbContext;
}
}
This factory will be used inside the base project to get a reference to the BaseDbContext.
Is this a good thing to do? Can this create some kind of problems?
In general, no, this is not a good thing to do.
that will contains the entities that will be used for all web applications
If there's entities that are common to all projects, then those should be factored out completely. In other words, you'd have one project (your base project) with a context like UserContext, which will have your User and Credential entities, and then every other project would have its own separate context that deals with just what it needs. If the other application(s) need to access users, they'd either do so via an instance of UserContext or, better, through a service, such as an API.
That said, it sounds like you're rolling your own auth, which you should emphatically not do. Use Identity. And, if you need to share that between applications, you need a centralized auth provider, such as Identity Server.

IHttpContextAccessor contains empty User.Identity, when used outside of the controller

I am writing an app ASP.Net Core (2.2) MVC. I need to filter some the data inside the DbContext by value of certain claims of the Logged in user. I inject IHttpContextAccessor, but when I try to access HttpContext.User.Identity - all properties are null and all claims are empty.
This is how I am trying to achieve that
I wire up IHttpContextAccessor. I use a standard method like that:
public void ConfigureServices(IServiceCollection services){
services.AddHttpContextAccessor();
...
}
Then I build a custom Provider to extract claims from the User:
public class GetClaimsFromUser : IGetClaimsProvider
{
public string UserId {get; private set;}
public GetClaimsFromUser(IHttpContextAccessor accessor)
{
UserId = accessor.HttpContext?.User.Claims.SingleOrDefault(x => x.Type == ClaimTypes.Name)?.Value;
}
}
Then I also inject it inside ConfigureServices method:
public void ConfigureServices(IServiceCollection services){
...
services.AddScoped<IGetClaimsProvider, GetClaimsFromUser>();
...
}
Afterwards I injected it inside the ApplicationDbContext and try to set the private _userId field inside the constructor:
public class ExpenseManagerDbContext: IdentityDbContext<ApplicationUser>
{
private string _userId;
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options, IGetClaimsProvider claimsProvider) : base(options)
{
_userId = claimsProvider.UserId;
...
}
...
}
And exactly here it is empty. When I access the HttpContext inside the controller, the User.Identity is not empty and everything is fine. However, when I need to access it outside the controller, it is empty.
Thanks for any help!!!
The full code can be found here:
https://github.com/dudelis/expense-manager/blob/master/ExpenseManager.DataAccess/Concrete/EntityFramework/ExpenseManagerDbContext.cs?
You are attempting to access the user in ExpenseManagerDbContext which is the application’s IdentityDbContext. As such, it itself is a dependency of the authentication system and will get resolved when the framework performs the authentication.
So the flow is somewhat like this:
Request comes in.
Authentication middleware runs to authenticate the user.
UserManager resolves ExpenseManagerDbContext.
ExpenseManagerDbContext resolves IGetClaimsProvider.
GetClaimsProvider resolves the HttpContext and attempts to access the user’s claims.
Authentication middleware performs the authentication and sets HttpContext.User with the result.
If you look at steps 5 and 6, you will see that the HttpContext is accessed before the authentication middleware is able to actually authenticate the user and update the user object on the context. And since the authentication middleware always runs at the beginning of a request, this will always be the case.
I would recommend you to rethink your ExpenseManagerDbContext since it probably shouldn’t depend on the currently signed-in user. It should be independent of that. If you have logic there that depends on the user id, then it should probably be a separate service.
Solved!
The problem was in the sharing of the same DbContext for IdentityDbContext and ApplicationDataDbContext.
In my controller I had the following code:
[Authorize]
public class AccountController : Controller
{
[HttpGet]
public IActionResult Index()
{
var accounts = _accountService.GetAll();
var models = _mapper.Map<List<AccountDto>>(accounts);
return View(models);
}
}
And when I tried to call the controller from the browser, the app initialized DbContext first time due to [Authorize] attribute. And this was done without any HttpContext. So when the application made a call to the DbContext in '_accountService.GetAll()', the DbContext was already instantiated and the Constructor method was not called, therefore, all my private fields remained empty!
So I created a second DbContext class only for authentication/authorization purposes.
public class ApplicationDbAuthContext : IdentityDbContext
{
public ApplicationDbAuthContext(DbContextOptions<ApplicationDbAuthContext> options) : base(options)
{
}
}
Due to this, during the request inside the controller the correct DbContext was instantiated when I made a call and it contained the HttpContext.
I will update my code in the repo to show the changes.
Meanwhile, thanks for all the answers.

How to pass another parameter to extended userStore class using Identity in dotnet core

I'm implementing a simple Multi-Tenant app and for that, I have extended the UserStore class this way:
public class ApplicationUserStore : UserStore<OmniUser>
{
public string TenantId { get; set; }
public ApplicationUserStore(OmniDbContext context, IdentityErrorDescriber describer = null)
: base(context, describer) { }
// overrided methods
}
To configure dependency injection to use the extended UserStore I added this code to the Startup ConficureServices method:
services.AddIdentity<OmniUser, IdentityRole>()
.AddEntityFrameworkStores<OmniDbContext>()
.AddDefaultTokenProviders()
.AddUserStore<ApplicationUserStore>(); // Extended UserStore
I can see the service is correctly instantiated and that the overrided methods are working well on ApplicationUserStore.
The problem is I need to pass the TenantId to my UserStore. I know that I can create another constructor that accepts an additional variable but then I don't know how to instantiate it using DI.
I could create a shortcut and define it manually through the UserManager class but it seems I don't have access to the UserStore from the UserManager instance.
How can I do this in an elegant way? I'm trying to avoid to instantiate both my custom UserStore and UserManager manually.
Thank you.

Factory Pattern with Identity 2.0 and Entity Framework

We are developing a Full Stack Authorised WebApi with Entity Framework and Identity 2.0.
Its based on a git repo here
WebApi Full Stack Entity Framework Repository
In our service layer we pass across the a custom interface from our context i.e.
private readonly Func<IGPFocusDataContext> _contextFactory;
public PatientService(Func<IGPFocusDataContext> contextFactory)
{
this._contextFactory = contextFactory;
}
I've created a similar UserService, its at this point I want to inject the UserManager and RoleManager interface in a similar way.
Can anyone recommend the best way of doing this?
Looking at the repo you can add the instances/implementations of UserManger and RoleManager to the Unity container in UnityConfig.cs something like:
container.RegisterType<UserManager, UserManager>();
Or
container.RegisterType<IUserManager, UserManager>();
And then in your UserService
private readonly IUserManger _userManger;
private readonly IRoleManger _roleManger;
public UserService(IUserManger userManger, IRoleManager roleManger)
{
this._userManager = userManger;
this._roleManger = roleManger;
}
When you add the service in your controller similar to the PatientService in PatientsController, although I am not familiair with Unity, I expect Unity ties it all together.

Combine WindowsAuthentication with rules stored in DB

I have an ASP.NET MVC5 application that uses WindowsAuhhentication to authenticate the user. Now I need to add a security layer to this application and would like to base this on the standard MVC security model and use the AuthorizeAttribute. This relies on User.IsInRole, but currently this will return the groups that the user belongs to. I do not want to have to store roles as groups in the AD, instead I would like to just have the roles for each user stored in my DB.
So the question is, how do I override the IsInRole method in the WindowsPrincipal, or can I create a CustomPricipal that does what I want?
I have found lots of information on similar topics but most of them seem to reference MVC4 and from what I can gather the entire security model has changed between MVC4 and MVC5. So what is the best way of doing that now?
All help and pointers much appreciated.
Cheers Mike
P.S. And if anyone has any idea how to best incorporate EF, IOC and caching into this then that would be great.
I have found MVC5 Asp.Identity to be really developer friendly compared to the old membership provider... which might be the reason there is a lack of documentation at this time. It is actually intuitive.
If your authentication rules reside in a database EntityFramework will convert stored procedures into Complex Types. After you do that you could create an 'AuthenticationService' service layer and use DI to inject the complex types into Asp.Identity as needed.
To customize Asp.Net Identity all you have to do is add properties to IdentityModels.cs and AccountViewModels.cs, by default Asp.Identity uses the ApplicationDbContext which you have to do absolutely nothing to configure.
In addition, you can access the users information in a similar manner to User.IsInRole.
OK, this is what I have done. I would really like peoples feedback as to best practice and improvements I could make.
I created a new Principal derived from WindowsPrincipal and with an overridden IsInRole Method.
public class QSDPrincipal : WindowsPrincipal
{
private readonly IUserService userService;
public QSDPrincipal(WindowsIdentity ntIdentity,
IUserService userService) :
base(ntIdentity)
{
this.userService = userService;
}
public override bool IsInRole(string role)
{
return userService.CurrentUserIsInRole(role);
}
}
This uses DI to populate the userService object that lives in my BL layer. So I had to configure the container to build this properly.
container.RegisterType<WindowsIdentity>(new HierarchicalLifetimeManager(),
new InjectionFactory(x => (WindowsIdentity)HttpContext.Current.User.Identity));
container.RegisterType<IPrincipal, QSDPrincipal>(new HierarchicalLifetimeManager());
The I use the DependencyResolved to create my new Principal in the PostAuthenticateRequest event.
protected void Application_PostAuthenticateRequest(object sender, EventArgs e)
{
var newUser = (IPrincipal)DependencyResolver.Current.GetService(typeof(IPrincipal));
HttpContext.Current.User = newUser;
}
Then in the UserService itself I implement a method and implement some simple caching so it only makes one DB query per request.
public bool CurrentUserIsInRole(string role)
{
return CurrentUserRoles.Contains(role);
}
private IEnumerable<string> currentUserRoles;
public IEnumerable<string> CurrentUserRoles
{
get
{
if (currentUserRoles == null)
{
var user = GetCurrentUser();
currentUserRoles = new List<string>
{
user.Role.Name
};
}
return currentUserRoles;
}
}
And that is it and it all seems to work.
Thoughts and improvements much appreciated.
Cheers Mike

Categories

Resources