Search code examples
c#jwtasp.net-core-2.2

How to create DBContext/Tenant Factory per user in Dot Net Core 2.2, JWT


I have two DBContext in my web API app. 1st one is to have all my clients with their connestionstring and 2nd one is real app DB.

Login controler use MyClientContext & other all controllers use MyContext

My startup.cs looks

public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2)

            services.AddDbContext<MyContext>(options =>
                    options.UseNpgsql(Configuration.GetConnectionString("MyContext")));

        services.AddDbContext<MyClientContext>(options =>
                    options.UseNpgsql(Configuration.GetConnectionString("MyClientContext")));
        }

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            using (var serviceScope = app.ApplicationServices.GetService<IServiceScopeFactory>().CreateScope())
            {
                var context = serviceScope.ServiceProvider.GetRequiredService<MyContext>();
                context.Database.Migrate(); // Code First Migrations for App DB

        var context2 = serviceScope.ServiceProvider.GetRequiredService<MyClientContext>();
                context2.Database.Migrate(); // Code First Migrations for Clients DB
            }

            app.UseCors("AllowSpecificOrigin");
            app.UseAuthentication();
            app.UseHttpsRedirection();
            app.UseMvc();
        }

On Login success, I issue JWT Token that looks

private string GenerateJSONWebToken(UserAuth userInfo)
        {
            var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["Jwt:Key"]));
            var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);

            var claims = new[] {
                new Claim(JwtRegisteredClaimNames.Sub, userInfo.UserName),
                new Claim(JwtRegisteredClaimNames.Email, userInfo.Email),
                new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString())
            };
            var token = new JwtSecurityToken(_config["Jwt:Issuer"], _config["Jwt:Issuer"], claims,
              expires: DateTime.Now.AddHours(24), signingCredentials: credentials);

            return new JwtSecurityTokenHandler().WriteToken(token);
        }

Here, I have assigned ConnectionString for real DB at startup file. I want to assign this, when user logs in. How do I achieve this?


Solution

  • I have done below changes in my code and its working as expected now. It may useful to some one else.

    services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
    
                services.AddDbContext<MyContext>((serviceProvider, options) =>
                {
                    var httpContext = serviceProvider.GetService<IHttpContextAccessor>().HttpContext;
                    var connection = GetConnection(httpContext);
                    options.UseNpgsql(connection);
                });
    
    private string GetConnection(HttpContext httpContext)
            {
                var UserName = httpContext?.User?.FindFirst(JwtRegisteredClaimNames.Jti)?.Value;
                 // Here extract ConnectionString from "MyClientContext"'s DB
            }
    

    And for Code First DB Migration, I moved Database.Migrate() to its Class

    public MyContext (DbContextOptions<MyContext> options)
                : base(options)
            {
                Database.Migrate();
            }
    

    This works fine for me.