Looking for some assistance with OIDC Multitenant partially working #1161
Replies: 5 comments 5 replies
|
I’ll take a look. Just as a reality check can you confirm the issue doesn’t occur in a non multitenant app? |
|
Separating them out actually seems to break everying, so no dice there. |
|
Hi, this comment is misleading:
This is not needed--try removing the custom callback paths per tenant. The per-tenant auth logic will set and retrieve the tenant by including it in the state parameter passed to the OIDC server and interpret it within the Let me know if that helps. |
|
I managed to get this fixed, there were a couple of contributing factors, i think the only one that concerned finbuckle was token refresh requests were routed to oidc-signin which was throwing the exception regarding the post method. |
|
Excellent! Happy coding! |
Uh oh!
There was an error while loading. Please reload this page.
Really excited to get this working so i can use finbuckle in some other projects, but I ran into an issue with oidc logins which i'm sure is a misconfiguration on my end somewhere. I have okta working, but when i added entra, i get the following error:
fail: Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectHandler[17]
Exception occurred while processing message.
Microsoft.IdentityModel.Tokens.SecurityTokenSignatureKeyNotFoundException: IDX10503: Signature validation failed. The token's kid is: 'XXXXX', but did not match any keys in TokenValidationParameters or Configuration. Keys tried: 'Microsoft.IdentityModel.Tokens.X509SecurityKey, KeyId: 'XXXXX', InternalId: 'XXXXX'. , KeyId: wh06sEkzLHJ5sNNaUyRY2_6O8K0
Microsoft.IdentityModel.Tokens.RsaSecurityKey, KeyId: 'XXXXX', InternalId: 'XXXX'. , KeyId: XXXXX
Microsoft.IdentityModel.Tokens.X509SecurityKey, KeyId: 'XXXXX', InternalId: 'k2MRQ8fu3BbJrTPLnDOyWDq1m60'. , KeyId: XXXX
Microsoft.IdentityModel.Tokens.RsaSecurityKey, KeyId: 'XXXXX', InternalId: 'TG0sMUhLFy3r0RWvSkBRnQJFiohQtABeVm5f51XyNRE'. , KeyId: XXXXX
Microsoft.IdentityModel.Tokens.X509SecurityKey, KeyId: 'XXXXX, InternalId: 'XXXXX'. , KeyId: XXXXX
Microsoft.IdentityModel.Tokens.RsaSecurityKey, KeyId: 'XXXXX', InternalId: 'XXXXX'. , KeyId: XXXXX
Microsoft.IdentityModel.Tokens.X509SecurityKey, KeyId: 'XXXXX', InternalId: 'XXXXX. , KeyId: XXXXX
Microsoft.IdentityModel.Tokens.RsaSecurityKey, KeyId: 'XXXXX, InternalId: 'XXXXX'. , KeyId: XXXXX
Microsoft.IdentityModel.Tokens.X509SecurityKey, KeyId: 'IXXXXX', InternalId: 'XXXXX. , KeyId: IAwWqyVsYi4K2fULSN3n6aezXmU
Microsoft.IdentityModel.Tokens.RsaSecurityKey, KeyId: 'XXXXX', InternalId: 'XXXXX'. , KeyId: XXXXX
'. Number of keys in TokenValidationParameters: '0'.
Number of keys in Configuration: '10'.
Here's my configuration with code I needed to add to get the redirection urls to be generated using https instead of http while running in a docker container. If that https issue can be dealt with more elegantly please let me know. Other than that
using System.Diagnostics;
using Finbuckle.MultiTenant.AspNetCore.Extensions;
using Finbuckle.MultiTenant.EntityFrameworkCore.Extensions;
using Finbuckle.MultiTenant.Extensions;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.AspNetCore.HttpLogging;
using Microsoft.AspNetCore.HttpOverrides;
using Microsoft.EntityFrameworkCore;
using virtuescript.sso;
var builder = WebApplication.CreateBuilder(args);
builder.Logging.ClearProviders();
builder.Logging.AddConsole();
// Adds request/response logging to console for troubleshooting auth redirects.
builder.Services.AddHttpLogging(options =>
{
options.LoggingFields = HttpLoggingFields.RequestMethod |
HttpLoggingFields.RequestPath |
HttpLoggingFields.ResponseStatusCode |
HttpLoggingFields.RequestHeaders;
options.RequestHeaders.Add("X-Forwarded-Proto");
options.RequestHeaders.Add("X-Forwarded-Host");
});
builder.WebHost.ConfigureKestrel(options =>
{
options.ListenAnyIP(5101);
if (Debugger.IsAttached)
{
options.ListenLocalhost(5100, listenOptions =>
{
listenOptions.UseHttps(); // HTTPS
});
}
});
// ── MVC ──────────────────────────────────────────────────────────────────────
builder.Services.AddControllersWithViews();
builder.Services.Configure(options =>
{
options.ForwardedHeaders =
ForwardedHeaders.XForwardedFor |
ForwardedHeaders.XForwardedProto |
ForwardedHeaders.XForwardedHost;
});
// ── EF Core: Tenant Store DB ──────────────────────────────────────────────────
// This database holds the registry of all tenants and their Okta OIDC settings.
builder.Services.AddDbContext(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("Default")));
// ── Authentication ────────────────────────────────────────────────────────────
// Register Cookie + OpenIdConnect base schemes.
// WithPerTenantAuthentication() will override OIDC options per-tenant at runtime.
builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie()
.AddOpenIdConnect(OpenIdConnectDefaults.AuthenticationScheme, options =>
{
// Fallback/default OIDC options. Per-tenant values from AppTenantInfo
// (OpenIdConnectAuthority, OpenIdConnectClientId, OpenIdConnectClientSecret)
// override these automatically via WithPerTenantAuthentication().
options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.ResponseType = "code";
options.SaveTokens = true;
options.GetClaimsFromUserInfoEndpoint = true;
options.Scope.Add("openid");
options.Scope.Add("profile");
options.Scope.Add("email");
// ── Finbuckle MultiTenant ─────────────────────────────────────────────────────
builder.Services.AddMultiTenant()
// Resolve the tenant from the {tenant} route parameter.
// e.g., /acme/home → tenant identifier = "acme"
.WithRouteStrategy()
// Load tenant configurations from the EF Core store (TenantStoreDbContext).
.WithEFCoreStore<TenantStoreDbContext, AppTenantInfo>()
// Wire up per-tenant OIDC using convention-mapped AppTenantInfo properties:
// OpenIdConnectAuthority, OpenIdConnectClientId, OpenIdConnectClientSecret,
// ChallengeScheme, CookieLoginPath, CookieLogoutPath, CookieAccessDeniedPath
.WithPerTenantAuthentication();
// ── Per-Tenant OIDC Callback Path ─────────────────────────────────────────────
// The route strategy uses /{tenant}/... so the OIDC callback path must include
// the tenant identifier. This lets Finbuckle resolve the tenant during the Okta
// redirect, set the auth cookie, and return the user to the right tenant path.
// Register https://localhost:5100/{tenant}/signin-oidc as the redirect URI in Okta.
builder.Services.ConfigurePerTenant<OpenIdConnectOptions, AppTenantInfo>(
OpenIdConnectDefaults.AuthenticationScheme, (options, tenantInfo) =>
{
options.CallbackPath = $"/{tenantInfo.Identifier}/home/oidcsignin";
options.SignedOutCallbackPath = $"/{tenantInfo.Identifier}/home/oidcsignout";
builder.Services.AddSingleton();
builder.Services.Configure(builder.Configuration.GetSection("RemoteApiConfig"));
builder.Services.AddScoped();
builder.Services.AddScoped();
// ── Build ─────────────────────────────────────────────────────────────────────
var app = builder.Build();
app.Logger.LogInformation("Starting SSO host. Environment: {EnvironmentName}", app.Environment.EnvironmentName);
// Apply EF migrations and seed tenants on startup (development only).
if (app.Environment.IsDevelopment())
{
using var scope = app.Services.CreateScope();
var db = scope.ServiceProvider.GetRequiredService();
db.Database.Migrate();
}
// ── Middleware pipeline ───────────────────────────────────────────────────────
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
app.UseForwardedHeaders();
app.UseHttpLogging();
app.UseHttpsRedirection();
// UseRouting must come before UseMultiTenant when using the route strategy.
app.UseRouting();
// Resolve the tenant from the route before authentication middleware runs.
app.UseMultiTenant();
app.UseAuthentication();
app.UseAuthorization();
app.MapStaticAssets();
// The {tenant} segment is the first path component and is used by
// Finbuckle's route strategy to identify the current tenant.
// Example: /acme/home/index → tenant=acme, controller=Home, action=Index
app.MapControllerRoute(
name: "default",
pattern: "{tenant}/{controller=Home}/{action=Index}/{id?}")
.WithStaticAssets();
app.Run();
All reactions