CORS Preflight Requests are being blocked .Net Core 2.2

1.2k Views Asked by At

I am trying to make a request from my Angular front end to Canada Posts Get Rate API. Unfortunately my backend .Net Core 2.2 seems to be blocking all preflight requests. If I try to issue these requests using a chrome browser with web security flag disabled I am able to complete the request, but unfortunately on normal browsing I am not able to complete the requests. I have tried reading many threads on here, and making cors exceptions specified in the .NET docs but it seems preflight requests are still being blocked.

Error Message: Access to XMLHttpRequest at 'https://soa-gw.canadapost.ca/rs/ship/price' from origin 'http://localhost:4200' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: It does not have HTTP ok status.

picture of error

Some of the Documents I've reffered to without success, I've tried adding additional cors permissions to C# but preflight requests are still being blocked. https://learn.microsoft.com/en-us/aspnet/core/security/cors?view=aspnetcore-5.0 https://learn.microsoft.com/en-us/aspnet/web-api/overview/security/enabling-cross-origin-requests-in-web-api#how-cors-works

I'm also unable to modify front end http headers to remove preflight check because some of these are required by canada post api. https://origin-www.canadapost.ca/info/mc/business/productsservices/developers/services/rating/getrates/default.jsf

Front End Call

        const httpOptions = {
                    headers: new HttpHeaders({
                      'Content-Type':  'application/vnd.cpc.ship.rate-v4+xml', //<- To SEND XML
                      'Accept':  'application/vnd.cpc.ship.rate-v4+xml',       //<- To ask for XML                                 //<- b/c Angular understands text
                      'Authorization': 'Basic YTUxZGQ1MWY2YjJhNjE4ODowODgzOThiZTUxY2Y4Y2RjZTIzM34',
                      'Accept-language': 'en-CA',
                      'Access-Control-Allow-Origin': '*',
                      "Access-Control-Allow-Methods": "DELETE, POST, GET, OPTIONS",
                    }),
                     
                    responseType: 'text' as 'json'
                  };

    var  postedData = `
                    <mailing-scenario xmlns="https://www.canadapost.ca/ws/ship/rate-v4">
                    <customer-number>0009669089</customer-number>
                    <parcel-characteristics>
                    <weight>5 </weight>
                    </parcel-characteristics>
                    <origin-postal-code>M5V0G1</origin-postal-code>
                    <destination>
                    <domestic>
                    <postal-code>M4R1L8</postal-code>
                    </domestic>
                    </destination>
                    </mailing-scenario>
                    `;
// http post to canada post using angular httpclient
         this.http.post('https://soa-gw.canadapost.ca/rs/ship/price', postedData, httpOptions)
                    .subscribe(
                      result => {    
                console.log(result);                 
                      }, 
                      error => console.log('There was an error: ', error));

Backend Startup.cs

using AutoMapper;
using Domain;
using Domain.Entities;
using Domain.Entities.Projects;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Internal;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Newtonsoft.Json;
using Rolox.Domain.Entities;
using Server.Classes;
using Server.Interfaces;
using Server.Models;
using System;
using System.IO;

namespace Server
{
    public class Startup
    {
        public Startup(IConfiguration configuration, IHostingEnvironment env)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {

            services.AddCors(options =>
            {
                options.AddDefaultPolicy(
                    builder =>
                    {
                        builder.WithOrigins("*")
                               .AllowAnyHeader()
                               .AllowAnyMethod();
                    });
                
                options.AddPolicy("MyAllowAllHeadersPolicy",
                    builder =>
                    {
                        builder.WithOrigins("*").AllowAnyMethod()
                               .AllowAnyHeader();
                    });

            });



            services.Configure<CookiePolicyOptions>(options =>
            {
                // This lambda determines whether user consent for non-essential cookies is needed for a given request.
                options.CheckConsentNeeded = context => false;
                options.MinimumSameSitePolicy = SameSiteMode.None;

            });
         
            services.AddSwaggerDocument();

            services.AddDbContext<RoloxDbContext>(options =>
                 options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"),
                 providerOptions => providerOptions.EnableRetryOnFailure()));

            services.AddDefaultIdentity<User>()
                .AddRoles<Role>()
                .AddEntityFrameworkStores<RoloxDbContext>();

            /// Auto Mapper Configurations
            var mappingConfig = new MapperConfiguration(mc =>
            {
                mc.AddProfile(new MappingProfile());
            });
            IMapper mapper = mappingConfig.CreateMapper();
            services.AddSingleton(mapper);

            ConfigureRepositories(services);

            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
            services.Configure<SettingModel>(Configuration.GetSection("Settings"));

            services.AddMvc().AddJsonOptions(options =>
            {
                options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
                options.SerializerSettings.NullValueHandling = NullValueHandling.Ignore;
                options.SerializerSettings.MissingMemberHandling = MissingMemberHandling.Ignore;


            });

            services.Configure<FormOptions>(options =>
            {
                options.MemoryBufferThreshold = Int32.MaxValue;

            });
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            app.UseCors("MyAllowAllHeadersPolicy");

            app.UseOpenApi();
            app.UseSwaggerUi3();

            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
                app.UseDatabaseErrorPage();
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");
                app.UseHsts();
            }

         
            app.UseHttpsRedirection();
            app.UseStaticFiles();
            app.UseCookiePolicy();
            app.UseStaticFiles();
            app.UseAuthentication();

            app.UseMvc(routes =>
            {
                routes.MapRoute(
                    name: "default",
                    template: "{controller=Home}/{action=Index}/{id?}");
            });


            app.UseSpa(spa =>
            {
            });
        }

        private void ConfigureRepositories(IServiceCollection services)
        {
            /// Repository Injections
            services.AddScoped<IRoloxRepository<ProposalStage>, ProposalStageRepository>();
            services.AddScoped<IProposalRepository, ProposalRepositoy>();
            services.AddScoped<IRoloxRepository<ChecklistItem>, ChecklistRepository>();
            services.AddScoped<IRoloxRepository<Contractor>, ContractorRepository>();
            services.AddScoped<IRoloxRepository<Project>, ProjectRepository>();
            services.AddScoped<IDirectCostRepository, ProjectDirectCostRepository>();
            services.AddScoped<IInvoiceRepository, InvoiceRepositoy>();
            services.AddScoped<ICompanyProfileRepository, CompanyRepository>();
        }
    }
}

Solution: to send message from production, front end preflight request are blocked, send api call from backend server instead. Despite this working on testing with web security disable in live use it must be sent from the back end.

1

There are 1 best solutions below

1
On

Had the same problem with some CORS stuff, for testing purpose i only added those line to get it working. It's probably not the best for the long run but. It might get it running. In your ConfigureServices in startup add this:

services.AddCors();

And then in the Configure add this:

 app.UseCors(options => options.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader());

i put services.AddCors(); at the top in ConfigureServices and the app.UserCors... inbetween app.UseRouting(); and app.UseAuthorization();

Hope this works for you.