Duende Identity Server Base Path Strategy
Andrew,
I have read several of the issues regarding Duende Identity Server 6 and Finbuckle. In most of the issues, you recommend using the base path strategy (Use of claim strategy and Identity types #613). I have tried to set this up, but I keep getting a 404 error when navigating to the login page with the tenant identifier.
Here is how things are setup in with Identity server.
public static WebApplication ConfigureServices(this WebApplicationBuilder builder)
{
builder.Services.AddRazorPagesServices();
builder.Services.AddIdentityServices(builder.Configuration);
builder.Services.AddMultiTenantServices(builder.Configuration); //Configure Finbuckle components
builder.Services.AddMessagingServices(builder.Configuration);
return builder.Build();
}
IdentityServer configuration below. For now I am using in memory clients, scopes and resources for development purposes
static IServiceCollection AddIdentityServices(this IServiceCollection services, IConfiguration configuration)
{
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlite(configuration.GetConnectionString("IdentityConnection")));
services.AddIdentity<ApplicationUser, IdentityRole>(options =>
{
options.User.RequireUniqueEmail = true;
})
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
services
.AddIdentityServer(options =>
{
options.Events.RaiseErrorEvents = true;
options.Events.RaiseInformationEvents = true;
options.Events.RaiseFailureEvents = true;
options.Events.RaiseSuccessEvents = true;
// see https://docs.duendesoftware.com/identityserver/v6/fundamentals/resources/
options.EmitStaticAudienceClaim = true;
})
.AddInMemoryClients(Config.Clients)
.AddInMemoryApiScopes(Config.ApiScopes)
.AddInMemoryIdentityResources(Config.IdentityResources)
.AddAspNetIdentity<ApplicationUser>()
.AddProfileService<CustomProfileService>();
return services;
}
EFCoreStore below
static IServiceCollection AddMultiTenantServices(this IServiceCollection services,
IConfiguration configuration)
{
services.AddDbContext<TenantDbContext>(options =>
options.UseSqlite(configuration.GetConnectionString("TenantConnection")))
.AddMultiTenant<MyTenant>()
.WithEFCoreStore<TenantDbContext, MyTenant>()
.WithBasePathStrategy(options =>
{
options.RebaseAspNetCorePathBase = true;
});
return services;
}
Below is how the pipline is set up
public static WebApplication ConfigurePipeline(this WebApplication app)
{
app.UseSerilogRequestLogging();
if (app.Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseStaticFiles();
app.UseRouting();
app.UseIdentityServer();
app.UseMultiTenant();
app.UseAuthorization();
app.MapRazorPages();
return app;
}
Below are my two EF DB Contexts. One for Identity and the other for the EFCore Store
Identity...
public class ApplicationDbContext : MultiTenantIdentityDbContext<ApplicationUser>
{
public ApplicationDbContext(ITenantInfo tenantInfo) : base(tenantInfo)
{
}
public ApplicationDbContext(ITenantInfo tenantInfo,
DbContextOptions<ApplicationDbContext> options) : base(tenantInfo, options)
{
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
base.OnConfiguring(optionsBuilder);
}
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);
builder.ApplyConfiguration(new ApplicationUserConfiguration());
}
}
Store..
public class TenantDbContext : EFCoreStoreDbContext<MyTenant>
{
public TenantDbContext(DbContextOptions<TenantDbContext> options) : base(options)
{
}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.ApplyConfiguration(new TenantConfiguration());
}
}
When I navigate to https://localhost:5001/Goforebroke/Account/Login, I get a 404 error.
My serilog log shows that Finbuckle discovers the tenant, but the URL is not found.
[10:46:09 Debug] Finbuckle.MultiTenant.Stores.EFCoreStore
TryGetByIdentifierAsync: Tenant found with identifier "Goforebroke"
[10:46:09 Information] Serilog.AspNetCore.RequestLoggingMiddleware
HTTP GET /Account/Login responded 404 in 184.8214 ms
Have set things up in the correct order, or am I missing something my setup?
After a little more research, reading , and looking at your sample with Identity Server 4,I adjusted the order where I add "UseMultiTenant()". I have things working now
Hi there, glad you got it working and sorry for the slow reply. Nice to hear from you!
@goforebroke did you get this rolling? i am removing base path strategy while i'm updating identity server to use razor pages instead of controllers.
its like you have to get the acr tenant then add it to a temporary claim almost immediately so that it tracks through the rest of the flow? i am not always able to get the acr values tenant because that query string with returnUrl doesn't always have it.
@natelaff sorry I have taken so long to get back to you. I have been super busy with my regular job outside of this personal project.
I have two Identity server clients, both razor applications. One client uses a static strategy (default tenant) and the other uses a base path strategy . The second client has an area where a user "sets" their tenant. My identity server is also configured to use a base path strategy. I don't pass the tenant acr value through from the clients to Identity Server. The tenant is always passed in the URL as part of the authorization/authentication request. In addition, I use a custom RedirectUriValidator in Identity server to ensure that the registered "RedirectUris" for the identity clients validate. I probably have not got far enough into my project to encounter real problems. Is there something in my setup that you would like to see?