From 89c829aa3a58bbb502a16e7be97d3370f339e7b6 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 5 Apr 2026 11:58:55 +0000 Subject: [PATCH 1/3] Fix login/signup in Docker deployments behind reverse proxy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three issues prevented auth from working at app.simplemodule.dev: 1. No ForwardedHeaders middleware — the app behind a reverse proxy saw Request.Scheme as "http" and Request.Host as "localhost:8080" instead of the real values, breaking cookie security, redirects, and email confirmation links. 2. OpenIddict client redirect URIs were seeded with https://localhost:5001 because OpenIddict:BaseUrl was never configured in docker-compose.yml. Added APP_BASE_URL env var support (defaults to http://localhost:8080). 3. The seed service skipped updating an existing OAuth client, so changing BaseUrl after initial seeding had no effect. Now it updates redirect URIs on every startup to match the current configuration. --- docker-compose.yml | 3 ++ .../SimpleModuleHostExtensions.cs | 11 ++++++++ .../Services/OpenIddictSeedService.cs | 28 ++++++++++++------- 3 files changed, 32 insertions(+), 10 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 8bb6df1c..d8dafec3 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -9,6 +9,9 @@ services: ASPNETCORE_ENVIRONMENT: Development Database__DefaultConnection: "Host=postgres;Port=5432;Database=simplemodule;Username=simplemodule;Password=${POSTGRES_PASSWORD:-simplemodule}" Database__Provider: PostgreSQL + # Set to your public URL so OpenIddict registers correct redirect URIs. + # Examples: https://app.simplemodule.dev, http://localhost:8080 + OpenIddict__BaseUrl: ${APP_BASE_URL:-http://localhost:8080} depends_on: postgres: condition: service_healthy diff --git a/framework/SimpleModule.Hosting/SimpleModuleHostExtensions.cs b/framework/SimpleModule.Hosting/SimpleModuleHostExtensions.cs index 56aeb393..618de2fe 100644 --- a/framework/SimpleModule.Hosting/SimpleModuleHostExtensions.cs +++ b/framework/SimpleModule.Hosting/SimpleModuleHostExtensions.cs @@ -1,6 +1,7 @@ using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Diagnostics.HealthChecks; using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.HttpOverrides; using Microsoft.AspNetCore.StaticFiles; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Diagnostics; @@ -48,6 +49,15 @@ public static WebApplicationBuilder AddSimpleModuleInfrastructure( BridgeAspireConnectionString(builder.Configuration); options.DatabaseProvider = ValidateDatabaseConfiguration(builder.Configuration); + builder.Services.Configure(fhOptions => + { + fhOptions.ForwardedHeaders = + ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto; + // Allow any proxy in containerized/cloud environments + fhOptions.KnownIPNetworks.Clear(); + fhOptions.KnownProxies.Clear(); + }); + builder.Services.AddProblemDetails(); builder.Services.AddExceptionHandler(); @@ -130,6 +140,7 @@ public static async Task UseSimpleModuleInfrastructure(this WebApplication app) } app.UseExceptionHandler(); + app.UseForwardedHeaders(); var options = app.Services.GetRequiredService(); if (options.EnableSwagger && app.Environment.IsDevelopment()) diff --git a/modules/OpenIddict/src/SimpleModule.OpenIddict/Services/OpenIddictSeedService.cs b/modules/OpenIddict/src/SimpleModule.OpenIddict/Services/OpenIddictSeedService.cs index 77334626..8e7073a0 100644 --- a/modules/OpenIddict/src/SimpleModule.OpenIddict/Services/OpenIddictSeedService.cs +++ b/modules/OpenIddict/src/SimpleModule.OpenIddict/Services/OpenIddictSeedService.cs @@ -38,16 +38,6 @@ CancellationToken cancellationToken { var manager = scope.ServiceProvider.GetRequiredService(); - if ( - await manager.FindByClientIdAsync(ClientConstants.ClientId, cancellationToken) - is not null - ) - { - return; - } - - LogSeedingClient(logger); - var baseUrl = configuration[ConfigKeys.OpenIddictBaseUrl] ?? ClientConstants.DefaultBaseUrl; var descriptor = new OpenIddictApplicationDescriptor @@ -97,6 +87,18 @@ is not null } } + var existing = await manager.FindByClientIdAsync( + ClientConstants.ClientId, + cancellationToken + ); + if (existing is not null) + { + LogUpdatingClient(logger); + await manager.UpdateAsync(existing, descriptor, cancellationToken); + return; + } + + LogSeedingClient(logger); await manager.CreateAsync(descriptor, cancellationToken); } @@ -106,6 +108,12 @@ is not null )] private static partial void LogSeedingClient(ILogger logger); + [LoggerMessage( + Level = LogLevel.Information, + Message = "Updating OpenIddict client application redirect URIs..." + )] + private static partial void LogUpdatingClient(ILogger logger); + [LoggerMessage( Level = LogLevel.Warning, Message = "OpenIddict seeding skipped due to error: {ErrorMessage}" From 69b896a6f955084446dbfd808633ed82f802c0d0 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 5 Apr 2026 12:07:56 +0000 Subject: [PATCH 2/3] Add OpenIddict base URL to production appsettings Sets the OAuth redirect URI base to https://app.simplemodule.dev so login and signup work correctly in production without requiring an environment variable override. --- template/SimpleModule.Host/appsettings.Production.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/template/SimpleModule.Host/appsettings.Production.json b/template/SimpleModule.Host/appsettings.Production.json index bacc57d2..96aa3626 100644 --- a/template/SimpleModule.Host/appsettings.Production.json +++ b/template/SimpleModule.Host/appsettings.Production.json @@ -3,6 +3,9 @@ "DefaultConnection": "Data Source=/app/data/app.db", "Provider": "Sqlite" }, + "OpenIddict": { + "BaseUrl": "https://app.simplemodule.dev" + }, "Storage": { "Local": { "BasePath": "/app/storage" From d17229633ee9659b551e684ea3acd052105f2d25 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 5 Apr 2026 12:14:05 +0000 Subject: [PATCH 3/3] Fix middleware ordering and misleading log message Move UseForwardedHeaders before UseExceptionHandler so all downstream middleware sees the correct scheme/host/IP. Fix log message that said "redirect URIs" when the full client descriptor is updated. --- framework/SimpleModule.Hosting/SimpleModuleHostExtensions.cs | 2 +- .../SimpleModule.OpenIddict/Services/OpenIddictSeedService.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/framework/SimpleModule.Hosting/SimpleModuleHostExtensions.cs b/framework/SimpleModule.Hosting/SimpleModuleHostExtensions.cs index 618de2fe..b6eaa933 100644 --- a/framework/SimpleModule.Hosting/SimpleModuleHostExtensions.cs +++ b/framework/SimpleModule.Hosting/SimpleModuleHostExtensions.cs @@ -139,8 +139,8 @@ public static async Task UseSimpleModuleInfrastructure(this WebApplication app) } } - app.UseExceptionHandler(); app.UseForwardedHeaders(); + app.UseExceptionHandler(); var options = app.Services.GetRequiredService(); if (options.EnableSwagger && app.Environment.IsDevelopment()) diff --git a/modules/OpenIddict/src/SimpleModule.OpenIddict/Services/OpenIddictSeedService.cs b/modules/OpenIddict/src/SimpleModule.OpenIddict/Services/OpenIddictSeedService.cs index 8e7073a0..b8b3eba2 100644 --- a/modules/OpenIddict/src/SimpleModule.OpenIddict/Services/OpenIddictSeedService.cs +++ b/modules/OpenIddict/src/SimpleModule.OpenIddict/Services/OpenIddictSeedService.cs @@ -110,7 +110,7 @@ CancellationToken cancellationToken [LoggerMessage( Level = LogLevel.Information, - Message = "Updating OpenIddict client application redirect URIs..." + Message = "Updating OpenIddict client application..." )] private static partial void LogUpdatingClient(ILogger logger);