you can multiple providers, but they each need a unique authentication scheme (name), that is you can not use the default for both. you will also need to define authentication polices because you are not using the default setup.
How to register two classes that inherit from IdentityUser with different DbContexts (.net 6 web api)
I want to use builder.Services.AddIdentity() as follows:
Both User and Employee classes inherit from IdentityUser. AppDbContext and EmployeeDbContext inherit from IdentityDbContext. As you might have guessed, Users and Employees are stored in different databases and they have different properties (thats why I didnt want to use the Role feature). I want to be able to use UserManager<User> and UserManager<Employee> so I cant handle logic of both types in the same method and make complex logic. But this here does not work. When I run the API project I get the error: "System.InvalidOperationException: 'Scheme already exists: Identity.Application'". Apparently an asp.net api doesnt support registering AddIdentity twice. Do you have a solution for my use case. I want to use AspNetCore.Identity for 2 different IdentityUser in 2 different databases in the same .net web api application.
builder.Services.AddIdentity<User, IdentityRole>()
.AddTokenProvider<DataProtectorTokenProvider<User>>("name1")
.AddEntityFrameworkStores<AppDbContext>()
.AddDefaultTokenProviders();
builder.Services.AddIdentity<Employee, IdentityRole>() .AddTokenProvider<DataProtectorTokenProvider<Employee>>("name2") .AddEntityFrameworkStores<EmployeeDbContext>()
.AddDefaultTokenProviders();
builder.Services.AddAuthentication(options => {
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; }).AddJwtBearer(options => { options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuerSigningKey = true, ValidateIssuer = true, ValidateAudience = true, ValidateLifetime = true, ClockSkew = TimeSpan.Zero, ValidIssuer = builder.Configuration["JwtSettings:Issuer"], builder.Configuration["JwtSettings:IssuerEmployees"] },
ValidAudience = builder.Configuration["JwtSettings:Audience"],
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["JwtSettings:Key"])) }; });
2 answers
Sort by: Most helpful
-
-
JasonPan - MSFT 5,981 Reputation points Microsoft Vendor
2023-10-30T06:31:11.92+00:00 Hi @ahmed mahjoub,
Thanks for your feedback, I learned that the search results provided by search engines such as Bing are not substantially helpful to you, but let me know more about your needs, the following updated answers, please check it out, if there are others who need help, you can let me know.
UPDATE
Here is my project structure.
Test Result
CustomSqlRoleProvider.cs
using AspCore7_Web_Identity.Areas.Identity.Data; using Microsoft.AspNetCore.Identity; using Microsoft.EntityFrameworkCore; namespace AspCore7_Web_Identity { public class CustomSqlRoleProvider : IRoleStore<IdentityRole> { private readonly MyDbContext _context; public CustomSqlRoleProvider(MyDbContext context) { _context = context; } public async Task<IdentityResult> CreateAsync(IdentityRole role, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); if (role == null) throw new ArgumentNullException(nameof(role)); _context.Roles.Add(role); await _context.SaveChangesAsync(cancellationToken); return IdentityResult.Success; } public async Task<IdentityResult> UpdateAsync(IdentityRole role, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); if (role == null) throw new ArgumentNullException(nameof(role)); _context.Roles.Update(role); await _context.SaveChangesAsync(cancellationToken); return IdentityResult.Success; } public async Task<IdentityResult> DeleteAsync(IdentityRole role, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); if (role == null) throw new ArgumentNullException(nameof(role)); _context.Roles.Remove(role); await _context.SaveChangesAsync(cancellationToken); return IdentityResult.Success; } public Task<string> GetRoleIdAsync(IdentityRole role, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); if (role == null) throw new ArgumentNullException(nameof(role)); return Task.FromResult(role.Id); } public Task<string?> GetRoleNameAsync(IdentityRole role, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); if (role == null) throw new ArgumentNullException(nameof(role)); return Task.FromResult(role.Name); } public Task SetRoleNameAsync(IdentityRole? role, string? roleName, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); if (role == null) throw new ArgumentNullException(nameof(role)); if (roleName == null) throw new ArgumentNullException(nameof(roleName)); role.Name = roleName; return Task.CompletedTask; } public Task<string?> GetNormalizedRoleNameAsync(IdentityRole role, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); if (role == null) throw new ArgumentNullException(nameof(role)); return Task.FromResult(role.NormalizedName); } public Task SetNormalizedRoleNameAsync(IdentityRole? role, string? normalizedName, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); if (role == null) throw new ArgumentNullException(nameof(role)); if (normalizedName == null) throw new ArgumentNullException(nameof(normalizedName)); role.NormalizedName = normalizedName; return Task.CompletedTask; } public async Task<IdentityRole?> FindByIdAsync(string roleId, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); if (roleId == null) throw new ArgumentNullException(nameof(roleId)); return await _context.Roles.FindAsync(new object[] { roleId }, cancellationToken); } public async Task<IdentityRole?> FindByNameAsync(string normalizedRoleName, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); if (normalizedRoleName == null) throw new ArgumentNullException(nameof(normalizedRoleName)); return await _context.Roles.FirstOrDefaultAsync(r => r.NormalizedName == normalizedRoleName, cancellationToken); } public async Task<IList<IdentityRole>> GetAllRolesAsync(CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); // Assuming _context.Roles is the roles DbSet var roles = await _context.Roles.ToListAsync(cancellationToken); return roles; } public void Dispose() { // Dispose of any unmanaged resources if necessary. _context?.Dispose(); } } }
MultiProviderRoleService.cs
using Microsoft.AspNetCore.Identity; namespace AspCore7_Web_Identity { public class MultiProviderRoleService { private readonly RoleManager<IdentityRole> _defaultRoleManager; private readonly CustomSqlRoleProvider _customSqlRoleProvider; public MultiProviderRoleService( RoleManager<IdentityRole> defaultRoleManager, CustomSqlRoleProvider customSqlRoleProvider) { _defaultRoleManager = defaultRoleManager; _customSqlRoleProvider = customSqlRoleProvider; } public async Task<IEnumerable<IdentityRole>> GetAllRolesAsync() { var defaultRoles = _defaultRoleManager.Roles.ToList(); var customRoles = await _customSqlRoleProvider.GetAllRolesAsync(CancellationToken.None); return defaultRoles.Concat(customRoles); } } }
Settings in Program.cs
var connectionString = builder.Configuration.GetConnectionString("SqlDataContextConnection") ?? throw new InvalidOperationException("Connection string 'SqlDataContextConnection' not found."); builder.Services.AddDbContext<SqlDataContext>( options => options.UseSqlServer(connectionString, sqlServerOptionsAction: sqlOptions => { sqlOptions.EnableRetryOnFailure(); }) ); builder.Services.AddIdentity<Users, IdentityRole>().AddTokenProvider<DataProtectorTokenProvider<Users>>("name1").AddEntityFrameworkStores<SqlDataContext>().AddDefaultTokenProviders(); builder.Services.AddDbContext<MyDbContext>(options => options.UseSqlServer(builder.Configuration.GetConnectionString("MyConnection"))); builder.Services.AddIdentityCore<Users>().AddRoles<IdentityRole>() // If you want roles for Employees. .AddTokenProvider<DataProtectorTokenProvider<Users>>("name2").AddEntityFrameworkStores<MyDbContext>(); builder.Services.AddTransient<CustomSqlRoleProvider>(); builder.Services.AddTransient<MultiProviderRoleService>();
Test Method
[HttpGet] public async Task<IActionResult> testrole() { var roles = await _roleService.GetAllRolesAsync(); return Ok(); }
If the answer is the right solution, please click "Accept Answer" and kindly upvote it. If you have extra questions about this answer, please click "Comment".
Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.
Best regards,
Jason