Monday, July 11, 2016

Middleware - Multi-Tenant Claim Based Identity for ASP.NET Core - Part 4 of 10


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.