Fix: incompatibility with reverse proxy forward auth providers

Signed-off-by: solidDoWant <fred.heinecke@yahoo.com>
This commit is contained in:
solidDoWant 2025-10-03 07:27:32 +00:00
parent ff6a69701f
commit 32e5aee9bf
No known key found for this signature in database
GPG key ID: 8FB1B42C043D666B
4 changed files with 63 additions and 2 deletions

View file

@ -27,6 +27,7 @@ function addContentType(ajaxOptions) {
export default function createAjaxRequest(originalAjaxOptions) {
const requestXHR = new window.XMLHttpRequest();
requestXHR.withCredentials = true; // Needed for CORS requests with cookies, which some reverse proxies with forward auth require
let aborted = false;
let complete = false;

View file

@ -4,6 +4,7 @@ public class ServerOptions
{
public string UrlBase { get; set; }
public string BindAddress { get; set; }
public string AllowedCORSOrigins { get; set; } // TODO
public int? Port { get; set; }
public bool? EnableSsl { get; set; }
public int? SslPort { get; set; }

View file

@ -31,6 +31,7 @@ public interface IConfigFileProvider : IHandleAsync<ApplicationStartedEvent>,
void EnsureDefaultConfigFile();
string BindAddress { get; }
string AllowedCORSOrigins { get; }
int Port { get; }
int SslPort { get; }
bool EnableSsl { get; }
@ -174,6 +175,8 @@ public string BindAddress
}
}
public string AllowedCORSOrigins => _serverOptions.AllowedCORSOrigins ?? GetValue("AllowedCORSOrigins", "*");
public int Port => _serverOptions.Port ?? GetValueInt("Port", 7878);
public int SslPort => _serverOptions.SslPort ?? GetValueInt("SslPort", 9898);

View file

@ -4,6 +4,7 @@
using DryIoc;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Cors.Infrastructure;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.HttpOverrides;
@ -11,6 +12,7 @@
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.OpenApi.Models;
using NLog.Extensions.Logging;
using NzbDrone.Common.EnvironmentInfo;
@ -70,15 +72,18 @@ public void ConfigureServices(IServiceCollection services)
services.AddCors(options =>
{
// Origin policy will be added after configuration is complete, because it depends on config file values
options.AddPolicy(VersionedApiControllerAttribute.API_CORS_POLICY,
builder =>
builder.AllowAnyOrigin()
builder
.AllowCredentials()
.AllowAnyMethod()
.AllowAnyHeader());
options.AddPolicy("AllowGet",
builder =>
builder.AllowAnyOrigin()
builder
.AllowCredentials()
.WithMethods("GET", "OPTIONS")
.AllowAnyHeader());
});
@ -194,6 +199,8 @@ public void ConfigureServices(IServiceCollection services)
services.AddAppAuthentication();
services.ConfigureOptions<CORSOriginConfigurator>();
services.PostConfigure<ApiBehaviorOptions>(options =>
{
var builtInFactory = options.InvalidModelStateResponseFactory;
@ -327,3 +334,52 @@ private void EnsureSingleInstance(bool isService, IStartupContext startupContext
}
}
}
public class CORSOriginConfigurator : IPostConfigureOptions<CorsOptions>
{
private readonly IConfigFileProvider _configFileProvider;
private readonly NLog.Logger _logger;
public CORSOriginConfigurator(IConfigFileProvider configFileProvider, NLog.Logger logger)
{
_configFileProvider = configFileProvider;
_logger = logger;
}
public void PostConfigure(string name, CorsOptions options) => PostConfigure(options);
public void PostConfigure(CorsOptions options)
{
_logger.Info("Configuring CORS. Allowed origins: {0}", _configFileProvider.AllowedCORSOrigins);
options.GetPolicy(VersionedApiControllerAttribute.API_CORS_POLICY).IsOriginAllowed = CorsOriginCheck;
options.GetPolicy("AllowGet").IsOriginAllowed = CorsOriginCheck;
}
protected bool CorsOriginCheck(string requestOrigin)
{
var allowedOrigin = _configFileProvider.AllowedCORSOrigins;
if (allowedOrigin.Equals(CorsConstants.AnyOrigin, StringComparison.Ordinal))
{
return true;
}
if (string.IsNullOrWhiteSpace(requestOrigin))
{
return false;
}
var allowedOriginAsUri = new Uri(allowedOrigin);
Uri requestOriginAsUri;
try
{
requestOriginAsUri = new Uri(requestOrigin);
}
catch (UriFormatException)
{
return false;
}
// Compare the scheme and authority (host + port) of the two URIs, according to RFC 6454
return Uri.Compare(allowedOriginAsUri, requestOriginAsUri, UriComponents.SchemeAndServer, UriFormat.Unescaped, StringComparison.OrdinalIgnoreCase) == 0;
}
}