This part 4 of 10 part series which outlines my
implementation of Multi-Tenant Claim Based Identity. For more details please
see my index post.
With ASP.NET Core, Microsoft introduced application pipeline
to handle requests and responses. This is roughly similar to HttpModules, but this
Middleware behavior significantly differs from HttpModules. HttpModules are
event driven, whereas Middleware is a pipeline, so we clearly know the order of
execution.
In my Multi-Tenant Claim Based Identity, I am using this
Middleware to store the user profile details such as UserId, UserName,
CompanyId etc in the current context so that this info can be readily available
throughout the application. With ASP.NET Core we use async/await and
HttpContext is the not the right way to store this context information, hence I
am storing this data into the CallContext. For more details see my other blog
post on this ASP.NET
Core Async Context Data.
Here is my Middleware code:
public class ProfileMiddleware
{
private RequestDelegate next;
public ProfileMiddleware(RequestDelegate next)
{
this.next = next;
}
public async Task Invoke(HttpContext context, SignInManager<ApplicationUser> signInManager, UserManager<ApplicationUser> userManager)
{
if (context.User.Identity.IsAuthenticated)
{
SetNTContext(context);
}
//automatically
logging in in the dev mode
else if (SiteSettings.IsEnvironment(SecurityConstants.DevEnvironment))
{
ApplicationUser user = await userManager.FindByNameAsync(SecuritySettings.NootusProfileUserName);
await signInManager.SignInAsync(user, false);
}
await next(context);
}
public void SetNTContext(HttpContext context)
{
var claims = context.User.Claims;
string companyId = context.Request.Headers[SecurityConstants.HeaderCompanyId];
companyId = companyId ??
context.User.Claims.Where(c => c.Type == NTClaimTypes.CompanyId).Select(c => c.Value).FirstOrDefault();
NTContextModel model = new NTContextModel()
{
UserId = claims.First(c =>
c.Type == ClaimTypes.NameIdentifier).Value,
UserName =
context.User.Identity.Name,
FirstName = claims.First(c =>
c.Type == NTClaimTypes.FirstName).Value,
LastName = claims.First(c =>
c.Type == NTClaimTypes.LastName).Value,
CompanyId = Convert.ToInt32(companyId
?? "0")
};
//setting
the Group CompanyId
model.GroupCompanyId = PageService.CompanyClaims[model.CompanyId]?.ParentCompanyId
?? model.CompanyId;
NTContext.Context = model;
}
}
The below are the salient points in the above code:
- For Authenticated users I am storing the user info in the context. As outlined in my other blog ASP.NET Core Async Context Data, I am using my internal class NTContext to store the context.
- During Development, I am hardcoding the user and doing a login so that there is no need to login
- The TenantID, in my case CompanyId, is passed through the Http Headers. I am retrieving this CompanyId from the HttpHeaders and storing this in the Context
- Similarly I am retrieving User details from the Claims and storing them in the Context as well.
As you see I am using this Middleware to retrieve and store
user profile details in the context so that this data is available throughout
the application.