Logged user based connection string in .NET Core

396 Views Asked by At

I have an application that uses identity database to store users and customers.

Each customer has also a separate database with its data and its connection string is stored in Customer table in the identity database.

AspNetUsers has a field to tell which customer the user belongs to (also identity db).

I want to assign connection string to the user when he logs in and make it available in the application for the duration of the session.

I currently have customer model:

public partial class `Customer`
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int NoLicenses { get; set; }
    public bool? Enabled { get; set; }
    public string CustomerConnectionString { get; set; }
}

and user model:

public class ApplicationUser : IdentityUser
{
    public string CustomerId { get; set; }

    public bool? IsEnabled { get; set; }

    // there ideally I'd have a connstring property
}

The models map db table fields.

I'm using .NET Core 1.1 and EF Core.

1

There are 1 best solutions below

0
Nan Yu On

With the defalut ASP.NET Identity template , you can :

  1. extend the ApplicationUser class in Models folder :

    public class ApplicationUser : IdentityUser
    {
        public string CustomerId { get; set; }
    
        public bool? IsEnabled { get; set; }
    
        //add your custom claims
        public string CustomerConnectionString { get; set; }
    }
    
  2. Add your custom model to ApplicationDbContext in Data folder :

    public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
    {
        public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
            : base(options)
        {
        }
    
        protected override void OnModelCreating(ModelBuilder builder)
        {
            base.OnModelCreating(builder);
            // Customize the ASP.NET Identity model and override the defaults if needed.
            // For example, you can rename the ASP.NET Identity table names and more.
            // Add your customizations after calling base.OnModelCreating(builder);
    
    
        }
    
        public DbSet<Customer> Customers { get; set; }
    }
    
  3. Sync your database : Add-Migration xxxx , then run the Update-Database command in Package Manager Console . Now you have the Customer table and have CustomerConnectionString column in AspNetUsers table.

  4. Create you own implementation of IUserClaimsPrincipalFactory by inheriting the default one to generate a ClaimsPrincipal from your user :

    public class AppClaimsPrincipalFactory : UserClaimsPrincipalFactory<ApplicationUser, IdentityRole>
    {
        public AppClaimsPrincipalFactory(
          UserManager<ApplicationUser> userManager
          , RoleManager<IdentityRole> roleManager
          , IOptions<IdentityOptions> optionsAccessor)
        : base(userManager, roleManager, optionsAccessor)
        { }
    
        public async override Task<ClaimsPrincipal> CreateAsync(ApplicationUser user)
        {
            var principal = await base.CreateAsync(user);
    
            if (!string.IsNullOrWhiteSpace(user.CustomerId))
            {
    
                ((ClaimsIdentity)principal.Identity).AddClaims(new[] {
                new Claim("customid", user.CustomerId)
                });
            }
    
            if (!string.IsNullOrWhiteSpace(user.CustomerConnectionString))
            {
    
                ((ClaimsIdentity)principal.Identity).AddClaims(new[] {
                new Claim("CustomerConnectionString", user.CustomerConnectionString)
                });
            }
            return principal;
        }
    }
    
  5. Register the custom factory you just created in your application startup class, after adding Identity service:

    // Add framework services.
    services.AddDbContext<ApplicationDbContext>(options =>
            options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
    
    services.AddIdentity<ApplicationUser, IdentityRole>()
            .AddEntityFrameworkStores<ApplicationDbContext>()
            .AddDefaultTokenProviders();
    
      services.AddScoped<Microsoft.AspNetCore.Identity.IUserClaimsPrincipalFactory<ApplicationUser>, AppClaimsPrincipalFactory>();
    
  6. Then you could access the claims like :

    var connectString = User.Claims.FirstOrDefault(c => c.Type == "CustomerConnectionString").Value;
    
  7. Modify the creating/editing user view/controller , add the customer dropdownlist on view , get the custom id in Register function in AccountController , query the connectingString of custom from db , and save in ApplicationUser object :

     var user = new ApplicationUser { UserName = model.Email, Email = model.Email };
     var result = await _userManager.CreateAsync(user, model.Password);