cors.net-6.0

Is there a way to update a Cors policy after startup?


I have an API with a CORS policy that sets allowed origins based on values in a database. (it's for a white labelled app that users can configure their own domains for).

In Startup.cs I'm currently adding the CORS policy as normal, accept the list of Allowed Origins is looked up from the database.

I was looking for a way to update that CORS policy at run time without restarting the API, perhaps on a timer, or perhaps after a specific POST API request.

Does anybody know if this is possible? I can't seem to find much about this particular scenario. I am using .Net 6.


Solution

  • In the end I ended up doing something similar to this tutorial, using the CorsPolicyAccessor as inspiration.

    https://dzone.com/articles/reconfiguring-cors-policy-in-aspnet-core-at-runtim

    First I created a policy provider class that is registered as a hosted service and inherits the BackgroundService class. This runs a timer that refreshes my CORS origins after a period of time.

    public class CorsPolicyProvider : BackgroundService
    {
        private readonly CorsOptions options;
        private readonly Microsoft.Extensions.Configuration.IConfiguration configuration;
    
        public CorsPolicyProvider(IOptions<CorsOptions> options, Microsoft.Extensions.Configuration.IConfiguration configuration)
        {
            this.options = options.Value;
            this.configuration = configuration;
        }
    
        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            await CreatePolicy();
    
            var timer = new PeriodicTimer(TimeSpan.FromMinutes(15));
            while (await timer.WaitForNextTickAsync())
            {
                await UpdatePolicy();
            }
        }
    
        private async Task CreatePolicy()
        {
            var allowedOrigins = new List<string>();
    
            // get static origins from appsettings.json
            configuration.GetSection("AllowedOrigins").Bind(allowedOrigins);
            
            var configurableOrigins = await GetConfigurableOrigins()
            allowedOrigins.AddRange(configurableOrigins);
    
            options.AddPolicy("AllowedSpecificOrigins", policy => policy.WithOrigins(allowedOrigins.ToArray()).AllowAnyHeader().AllowAnyMethod().AllowCredentials());
        }
    
        private async Task UpdatePolicy()
        {
            var origins = await GetConfigurableOrigins();
            var policy = options.GetPolicy("AllowedSpecificOrigins");
    
            // filter all origins to ones that aren't already in the policy
            foreach (var origin in origins.Where(x => !policy.Origins.Any(y => y == x)))
            {
                policy.Origins.Add(origin);
            }
        }
    
        private async Task<List<string>> GetConfigurableOrigins()
        {
            // get configurable origins from other source (database/config file, etc.)
        }
    }
    

    And then register it as a Hosted Service in Startup.cs in place of the existing .AddPolicy method (they conflicted for me).

    public void ConfigureServices(IServiceCollection services)
    {
        /* ... */
        services.AddHostedService<CorsPolicyProvider>();
    }
    
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        /* ... */
        app.UserCors("AllowedSpecificOrigins");
        /* ... */
    }