From 693c6d262a4f0e5743923534d60263b4be95667f Mon Sep 17 00:00:00 2001 From: Warren Chen Date: Thu, 2 Apr 2026 02:43:50 +0900 Subject: [PATCH] Fix OIDC issuer handling behind internal HTTP proxies --- src/MemberCenter.Api/Program.cs | 48 ++++++++++++++++++++++++++------- 1 file changed, 39 insertions(+), 9 deletions(-) diff --git a/src/MemberCenter.Api/Program.cs b/src/MemberCenter.Api/Program.cs index b20a96b..80d28d8 100644 --- a/src/MemberCenter.Api/Program.cs +++ b/src/MemberCenter.Api/Program.cs @@ -14,6 +14,9 @@ EnvLoader.LoadDotEnvIfDevelopment(); var builder = WebApplication.CreateBuilder(args); var pathBase = NormalizePathBase(builder.Configuration["PathBase"]); +var issuer = builder.Configuration["Auth:Issuer"]; +var issuerUri = ParseAbsoluteUriOrThrow(issuer, "Auth:Issuer"); +var allowInsecureHttp = builder.Configuration.GetValue("Auth:AllowInsecureHttp", false); builder.Services.AddDbContext(options => { @@ -58,14 +61,8 @@ builder.Services.AddOpenIddict() WithPathBase(pathBase, "/auth/login"), WithPathBase(pathBase, "/auth/refresh")); options.SetLogoutEndpointUris(WithPathBase(pathBase, "/auth/logout")); - var issuer = builder.Configuration["Auth:Issuer"]; - if (!string.IsNullOrWhiteSpace(issuer)) + if (issuerUri is not null) { - if (!Uri.TryCreate(issuer, UriKind.Absolute, out var issuerUri)) - { - throw new InvalidOperationException("Auth:Issuer must be an absolute URI."); - } - options.SetIssuer(issuerUri); } @@ -98,9 +95,9 @@ builder.Services.AddOpenIddict() .EnableLogoutEndpointPassthrough() .EnableStatusCodePagesIntegration(); - if (builder.Environment.IsDevelopment()) + if (builder.Environment.IsDevelopment() || allowInsecureHttp) { - // TEST/LOCAL ONLY: allow HTTP for local Docker integration testing. + // Allows OIDC/OAuth endpoints to operate behind non-HTTPS internal networks/proxies. aspNetCore.DisableTransportSecurityRequirement(); } }) @@ -139,6 +136,17 @@ if (!string.IsNullOrWhiteSpace(pathBase)) app.UsePathBase(pathBase); } +app.Use(async (context, next) => +{ + if (issuerUri is not null && IsOpenIddictRequest(context.Request.Path)) + { + context.Request.Scheme = issuerUri.Scheme; + context.Request.Host = HostString.FromUriComponent(issuerUri); + } + + await next(); +}); + app.UseRouting(); app.UseAuthentication(); app.UseAuthorization(); @@ -165,3 +173,25 @@ static string WithPathBase(string? pathBase, string relativePath) ? normalizedRelativePath : $"{pathBase}{normalizedRelativePath}"; } + +static Uri? ParseAbsoluteUriOrThrow(string? uri, string configKey) +{ + if (string.IsNullOrWhiteSpace(uri)) + { + return null; + } + + if (!Uri.TryCreate(uri, UriKind.Absolute, out var parsed)) + { + throw new InvalidOperationException($"{configKey} must be an absolute URI."); + } + + return parsed; +} + +static bool IsOpenIddictRequest(PathString path) +{ + return path.StartsWithSegments("/.well-known", StringComparison.OrdinalIgnoreCase) + || path.StartsWithSegments("/oauth", StringComparison.OrdinalIgnoreCase) + || path.StartsWithSegments("/auth", StringComparison.OrdinalIgnoreCase); +}