diff --git a/src/MemberCenter.Api/Program.cs b/src/MemberCenter.Api/Program.cs index f7e4592..52c628c 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(); } }) @@ -140,6 +137,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(); @@ -166,3 +174,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); +}