Monday, January 23, 2017

Startup- Multi-Tenant Claim Based Identity for ASP.NET Core - Part 6 of 10



This part 5 of 10 part series which outlines my implementation of Multi-Tenant Claim Based Identity. For more details please see my index post.


As you know Startup is used for configure the request pipeline that handles all requests made to the application. I am using this Startup to configure the following:
  •  Configure Identity Framework
  • Dependency Injection of Identity classes and my classes
  • Automapper configuration (optional)

Here are the different sections of Startup. I have included only relevant code.

Startup Constructor
This is standard code. In this I am saving the connection string in a global variable so that I can later use it.
public Startup(IHostingEnvironment env)
{
    // Set up configuration sources.
    var builder = new ConfigurationBuilder()
        .SetBasePath(env.ContentRootPath)
        .AddJsonFile("appsettings.json")
        .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true);

    builder.AddEnvironmentVariables();
    this.Configuration = builder.Build();

    // saving the site connection string
    SiteSettings.ConnectionString = this.Configuration.GetConnectionString("MegaMine");
}

ConfigureServices
In this method I am configuring the following services
  • Configuring Entity Framework
  •  Configure ASP.NET Identity system
  • Configure MVC
  • Configure our Authorization filter
  • Dependency injection configuration for classes related to security
  • Automapper configuration for classes related to security

Here is the relevant code in this method
public void ConfigureServices(IServiceCollection services)
{
    // configuring Entity Framework
    services.AddEntityFrameworkSqlServer()
    .AddDbContext<SecurityDbContext>(options =>
    {
        options.UseSqlServer(SiteSettings.ConnectionString);
    });
   
    // Configuring ASP.NET Identity
    services.AddIdentity<ApplicationUser, ApplicationRole>()
    .AddEntityFrameworkStores<SecurityDbContext>()
    .AddDefaultTokenProviders();

    services.AddMvc()
    .AddMvcOptions(options =>
    {
        options.Filters.Add(new NTAuthorizeFilter()); // Adding our Authorization filter
    })
    .AddJsonOptions(options =>
    {
        options.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
        options.SerializerSettings.DateTimeZoneHandling = DateTimeZoneHandling.Local;
        options.SerializerSettings.DateFormatHandling = DateFormatHandling.IsoDateFormat;
    });

    // dependency injection
    services.AddScoped<SignInManager<ApplicationUser>, ApplicationSignInManager>();
    services.AddTransient<AccountDomain>();
    services.AddTransient<SecurityRepository>();

    // Automapper configurations
    Mapper.Initialize(x =>
    {
        x.AddProfile<SecurityMappingProfile>();
    });
}

Configure

In this Configure method, apart from standard request processing configuration, I added the following configurations:
  • Enable ASP.NET Identity
  • Enable my own middleware which stores User information in a custom request context

Here is the code for this configure method

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    // Middleware for storing context for the request
    app.UseContextMiddleware();

    // To enable deliverying static content
    app.UseStaticFiles();

    // Enabling ASP.NET Identity
    app.UseIdentity();

    // Custom middleware to store user details
    app.UseProfileMiddleware();

    // Enabling MVC
    app.UseMvc(routes =>
    {
        routes.MapRoute(
            name: "webapi",
            template: "api/{controller}/{action}/{id?}",
            defaults: new { controller = "Home", action = "Index" });

        routes.MapRoute(
            name: "error",
            template: "Error",
            defaults: new { controller = "Home", action = "Error" });

        routes.MapRoute(
            name: "default",
            template: "{controller}/{action}",
            defaults: new { controller = "Home", action = "Index" });
    });
}


With the above Configurations in the Startup class, the application is configure to utilize Multi-Tenant Claim Based Identity.

4 comments:

  1. Mr. Pattam, I read some posts on your blog noticed few on ASP MVC 5. I visited your blog on a search for ASP MVC 5 Identity 2.2 (and not for the ASP core, since it is still not production ready in many shops and perhaps guessing till another 2 yrs). Practically speaking, would you not agree more users in the ASP MVC 5 and very little help on multi-tenant. Particularly many issues around, setup and user information on creating ASP MVC 5 with ASP identity 2.2 multi tenant still challenge users.

    I would greatly like to see your approach in a blog post on the multi-tenant configuration and usage in EF with ASP identity 2.2 MVC 5. Maybe based around a simple use-case, like add manage employee & purchase requests, approvals at a manager, at a per tenant level. For e.g. how to set up tenant Id context @user login, dispose @ logout. While using/sharing the same DB across organizations, and filter (PO requests or employee entities) table records in users session scoped to only show the records that match his Organization Id (show records in PO table where user.tenantId => POtable.TentantIdCol == user.tenantId ).get to list... it could be anything.. but that gives you an idea on what would be helpful. If you search you will notice many questions around this, hoping to see an answer with post from your site on this.


    If you can also address, how are claims different from adding a property to the ASP Users table? I am guessing more of smaller shops like me, that are invested in ASP MVC 5 would be able to connect with you.

    Regards.
    Tristan

    ReplyDelete
  2. Tristan, thanks for your comment.

    Regarding ASP.NET Core, I am developing in core from the last 2 years (from betas). It is very stable and I strong recommend to start using it immediately. It will take time to migrate from MVC 5 to core. So the sooner you migrate the better.

    I have written a base class which would automatically include the tentantid in all the queries. Please take a look at this code
    https://github.com/Nootus/MegaMine/blob/master/MegaMine/src/MegaMine.Core/Repositories/BaseRepository.cs

    I tried to make this module as a pluggable service, with few modifications you can use this directly with MVC5. Here is the code for this service
    https://github.com/Nootus/MegaMine/tree/master/MegaMine/src/Services/MegaMine.Services.Security

    I will complete the rest of the sections and it will give a overview of the other questions you posted. In the meanwhile feel free to send me an email at prasannapattam@hotmail.com.

    ReplyDelete
  3. I still think MVC 5, has a lot of stakes in the ground from various people like me who made the investment.

    Can you outline what steps are required to migrate from 5 to Core, I head BCL is not supported so it still has issues running on Linux etc.

    Is Razor no longer there?

    I see tag helpers, I wish there was a parser to migrate easily.

    ReplyDelete
    Replies
    1. I feel that Angular will eventually take over the presentation layer. So the Views in MVC will slowly looses it's importance.

      Delete