diff --git a/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs b/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs
index 0fd599cfcd..b2f861542a 100644
--- a/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs
+++ b/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs
@@ -135,13 +135,17 @@ namespace Jellyfin.Server.Extensions
///
/// The service collection.
/// The base url for the API.
+ /// The configured cors hosts.
/// The MVC builder.
- public static IMvcBuilder AddJellyfinApi(this IServiceCollection serviceCollection, string baseUrl)
+ public static IMvcBuilder AddJellyfinApi(
+ this IServiceCollection serviceCollection,
+ string baseUrl,
+ string[] corsHosts)
{
return serviceCollection
.AddCors(options =>
{
- options.AddPolicy(ServerCorsPolicy.DefaultPolicyName, ServerCorsPolicy.DefaultPolicy);
+ options.AddPolicy(ServerCorsPolicy.DefaultPolicyName, new ServerCorsPolicy(corsHosts).Policy);
})
.Configure(options =>
{
diff --git a/Jellyfin.Server/Middleware/DynamicCorsMiddleware.cs b/Jellyfin.Server/Middleware/DynamicCorsMiddleware.cs
new file mode 100644
index 0000000000..4fad898a73
--- /dev/null
+++ b/Jellyfin.Server/Middleware/DynamicCorsMiddleware.cs
@@ -0,0 +1,68 @@
+using System;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Cors.Infrastructure;
+using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.Logging;
+using Microsoft.Net.Http.Headers;
+
+namespace Jellyfin.Server.Middleware
+{
+ ///
+ /// Dynamic cors middleware.
+ ///
+ public class DynamicCorsMiddleware
+ {
+ private readonly RequestDelegate _next;
+ private readonly ILogger _logger;
+ private readonly CorsMiddleware _corsMiddleware;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// Next request delegate.
+ /// Instance of the interface.
+ /// Instance of the interface.
+ /// The cors policy name.
+ public DynamicCorsMiddleware(
+ RequestDelegate next,
+ ICorsService corsService,
+ ILoggerFactory loggerFactory,
+ string policyName)
+ {
+ _corsMiddleware = new CorsMiddleware(next, corsService, loggerFactory, policyName);
+ _next = next;
+ _logger = loggerFactory.CreateLogger();
+ }
+
+ ///
+ /// Invoke request.
+ ///
+ /// Request context.
+ /// Instance of the interface.
+ /// Task.
+ ///
+ public async Task Invoke(HttpContext context, ICorsPolicyProvider corsPolicyProvider)
+ {
+ // Only execute if is preflight request.
+ if (string.Equals(context.Request.Method, CorsConstants.PreflightHttpMethod, StringComparison.OrdinalIgnoreCase))
+ {
+ // Invoke original cors middleware.
+ await _corsMiddleware.Invoke(context, corsPolicyProvider).ConfigureAwait(false);
+ if (context.Response.Headers.TryGetValue(HeaderNames.AccessControlAllowOrigin, out var headerValue)
+ && string.Equals(headerValue, "*", StringComparison.Ordinal))
+ {
+ context.Response.Headers[HeaderNames.AccessControlAllowOrigin] = context.Request.Host.Value;
+ _logger.LogDebug("Overwriting CORS response header: {HeaderName}: {HeaderValue}", HeaderNames.AccessControlAllowOrigin, context.Request.Host.Value);
+
+ if (!context.Response.Headers.ContainsKey(HeaderNames.AccessControlAllowCredentials))
+ {
+ context.Response.Headers[HeaderNames.AccessControlAllowCredentials] = "true";
+ }
+ }
+ }
+
+ // Call the next delegate/middleware in the pipeline
+ await this._next(context).ConfigureAwait(false);
+ }
+ }
+}
diff --git a/Jellyfin.Server/Models/ServerCorsPolicy.cs b/Jellyfin.Server/Models/ServerCorsPolicy.cs
index ae010c042e..3a45db3b44 100644
--- a/Jellyfin.Server/Models/ServerCorsPolicy.cs
+++ b/Jellyfin.Server/Models/ServerCorsPolicy.cs
@@ -1,30 +1,47 @@
-using Microsoft.AspNetCore.Cors.Infrastructure;
+using System;
+using Microsoft.AspNetCore.Cors.Infrastructure;
namespace Jellyfin.Server.Models
{
///
/// Server Cors Policy.
///
- public static class ServerCorsPolicy
+ public class ServerCorsPolicy
{
///
/// Default policy name.
///
- public const string DefaultPolicyName = "DefaultCorsPolicy";
+ public const string DefaultPolicyName = nameof(ServerCorsPolicy);
///
- /// Default Policy. Allow Everything.
+ /// Initializes a new instance of the class.
///
- public static readonly CorsPolicy DefaultPolicy = new CorsPolicy
+ /// The configured cors hosts.
+ public ServerCorsPolicy(string[] corsHosts)
{
- // Allow any origin
- Origins = { "*" },
+ var builder = new CorsPolicyBuilder()
+ .AllowAnyMethod()
+ .AllowAnyHeader();
- // Allow any method
- Methods = { "*" },
+ // No hosts configured or only default configured.
+ if (corsHosts.Length == 0
+ || (corsHosts.Length == 1
+ && string.Equals(corsHosts[0], "*", StringComparison.Ordinal)))
+ {
+ builder.AllowAnyOrigin();
+ }
+ else
+ {
+ builder.WithOrigins(corsHosts)
+ .AllowCredentials();
+ }
- // Allow any header
- Headers = { "*" }
- };
+ Policy = builder.Build();
+ }
+
+ ///
+ /// Gets the cors policy.
+ ///
+ public CorsPolicy Policy { get; }
}
-}
\ No newline at end of file
+}
diff --git a/Jellyfin.Server/Startup.cs b/Jellyfin.Server/Startup.cs
index d0dd183c68..76f5e69ced 100644
--- a/Jellyfin.Server/Startup.cs
+++ b/Jellyfin.Server/Startup.cs
@@ -38,7 +38,9 @@ namespace Jellyfin.Server
{
services.AddResponseCompression();
services.AddHttpContextAccessor();
- services.AddJellyfinApi(_serverConfigurationManager.Configuration.BaseUrl.TrimStart('/'));
+ services.AddJellyfinApi(
+ _serverConfigurationManager.Configuration.BaseUrl.TrimStart('/'),
+ _serverConfigurationManager.Configuration.CorsHosts);
services.AddJellyfinApiSwagger();
@@ -78,11 +80,11 @@ namespace Jellyfin.Server
app.UseAuthentication();
app.UseJellyfinApiSwagger(_serverConfigurationManager);
app.UseRouting();
- app.UseCors(ServerCorsPolicy.DefaultPolicyName);
+ app.UseMiddleware(ServerCorsPolicy.DefaultPolicyName);
app.UseAuthorization();
if (_serverConfigurationManager.Configuration.EnableMetrics)
{
- // Must be registered after any middleware that could chagne HTTP response codes or the data will be bad
+ // Must be registered after any middleware that could change HTTP response codes or the data will be bad
app.UseHttpMetrics();
}
diff --git a/MediaBrowser.Model/Configuration/ServerConfiguration.cs b/MediaBrowser.Model/Configuration/ServerConfiguration.cs
index c66091f9d5..a743277d7a 100644
--- a/MediaBrowser.Model/Configuration/ServerConfiguration.cs
+++ b/MediaBrowser.Model/Configuration/ServerConfiguration.cs
@@ -264,6 +264,11 @@ namespace MediaBrowser.Model.Configuration
///
public long SlowResponseThresholdMs { get; set; }
+ ///
+ /// Gets or sets the cors hosts.
+ ///
+ public string[] CorsHosts { get; set; }
+
///
/// Initializes a new instance of the class.
///
@@ -372,6 +377,7 @@ namespace MediaBrowser.Model.Configuration
EnableSlowResponseWarning = true;
SlowResponseThresholdMs = 500;
+ CorsHosts = new[] { "*" };
}
}