Open Closed

Preflight CORS policy error when logging-in to tenant #7498


User avatar
0
anurag.tyagi created
  • ABP Framework version: v8.1
  • UI Type: Angular
  • Database System: EF Core MySQL
  • Tiered (for MVC) or Auth Server Separated (for Angular): no
  • Exception message and full stack trace: Access to fetch at 'http://test.localhost:44367/hubroute/negotiate?negotiateVersion=1' from origin 'http://test.localhost:4200' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '' when the request's credentials mode is 'include'.*

HI devs, I am using a multi-tenancy setup and I when I login to the application with the test tenant, i am able to enter the application but I get the above-mentioned error in the dev console. my CorsOrigins in my local appsettings looks like this: "CorsOrigins": "http://localhost:4200,http://test.localhost:4200,http://*.localhost:4200" and I configure the cors in my host module like this:

context.Services.AddCors(options =>
        {
            options.AddDefaultPolicy(builder =>
            {
                builder
                    .WithOrigins(
                        configuration["App:CorsOrigins"]?
                            .Split(",", StringSplitOptions.RemoveEmptyEntries)
                            .Select(o => o.Trim().RemovePostFix("/"))
                            .ToArray() ?? Array.Empty<string>()
                    )
                    .WithAbpExposedHeaders()
                    .SetIsOriginAllowedToAllowWildcardSubdomains()
                    .AllowAnyHeader()
                    .AllowAnyMethod()
                    .AllowCredentials();
            });
        });

Any help with this error would be very appreciated. I'm not sure what cors configuration I'm missing or doing wrong.

thanks in advance!

best wishes, Anurag


10 Answer(s)
  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    hi

    Can you share the cors logs of backend app?

    Thanks.

  • User Avatar
    0
    anurag.tyagi created

    Hi, thanks for your response.

    In the hostApi/Logs.txt when I search for 'cors' I can only find : CORS policy execution successful. and I do not see any errors. Only erroneous log I can spot is the last line of this, but I'm not sure it's related to cors.

    `2024-07-15 10:27:27.926 +02:00 [DBG] The event OpenIddict.Validation.OpenIddictValidationEvents+ProcessAuthenticationContext was successfully processed by OpenIddict.Validation.AspNetCore.OpenIddictValidationAspNetCoreHandlers+ValidateHostHeader.
    2024-07-15 10:27:27.926 +02:00 [DBG] The event OpenIddict.Validation.OpenIddictValidationEvents+ProcessAuthenticationContext was successfully processed by OpenIddict.Validation.OpenIddictValidationHandlers+EvaluateValidatedTokens.
    2024-07-15 10:27:27.926 +02:00 [DBG] The event OpenIddict.Validation.OpenIddictValidationEvents+ProcessAuthenticationContext was successfully processed by OpenIddict.Validation.AspNetCore.OpenIddictValidationAspNetCoreHandlers+ExtractAccessTokenFromAuthorizationHeader.
    2024-07-15 10:27:27.926 +02:00 [DBG] The event OpenIddict.Validation.OpenIddictValidationEvents+ProcessAuthenticationContext was successfully processed by OpenIddict.Validation.AspNetCore.OpenIddictValidationAspNetCoreHandlers+ExtractAccessTokenFromBodyForm.
    2024-07-15 10:27:27.926 +02:00 [DBG] The event OpenIddict.Validation.OpenIddictValidationEvents+ProcessAuthenticationContext was successfully processed by OpenIddict.Validation.AspNetCore.OpenIddictValidationAspNetCoreHandlers+ExtractAccessTokenFromQueryString.
    2024-07-15 10:27:27.926 +02:00 [DBG] The event OpenIddict.Validation.OpenIddictValidationEvents+ProcessAuthenticationContext was successfully processed by OpenIddict.Validation.OpenIddictValidationHandlers+ValidateRequiredTokens.
    2024-07-15 10:27:27.926 +02:00 [DBG] The event OpenIddict.Validation.OpenIddictValidationEvents+ProcessAuthenticationContext was marked as rejected by OpenIddict.Validation.OpenIddictValidationHandlers+ValidateRequiredTokens.
    2024-07-15 10:27:27.926 +02:00 [DBG] AuthenticationScheme: OpenIddict.Validation.AspNetCore was not authenticated.`
    

    Thanks

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    hi

    Please set log level to debug and share full logs.txt

    Thanks

    public class Program
    {
        public async static Task<int> Main(string[] args)
        {
            Log.Logger = new LoggerConfiguration()
                .MinimumLevel.Debug()
                .MinimumLevel.Override("Microsoft.EntityFrameworkCore", LogEventLevel.Warning)
                .Enrich.FromLogContext()
                .WriteTo.Async(c => c.File("Logs/logs.txt"))
                .WriteTo.Async(c => c.Console())
                .CreateLogger();
    
  • User Avatar
    0
    anurag.tyagi created

    Hi again, how can I attach a file here? I am unable to attach the whole txt and the character limit also doesn't allow me to paste the relevant logs thanks again.

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    hi

    You can share the logs by https://wetransfer.com/

  • User Avatar
    0
    anurag.tyagi created

    hi, please find the logs file here

    in addition I noticed that the homepage takes 10 seconds to lead the page after refresh, which is much slower than usual.

    thank you very much the effort and quick responses!

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    hi

    https://learn.microsoft.com/en-us/aspnet/core/security/cors?view=aspnetcore-8.0#set-the-allowed-origins

    The logs are no problem.

    Access to fetch at 'http://test.localhost:44367/hubroute/negotiate?negotiateVersion=1'

    Does only the route/negotiate have a CORS error or all requests?

    Please share the code of your host module.

    Thanks again.

  • User Avatar
    0
    anurag.tyagi created

    Hi, yes I only get the CORS error for the route/negotiate. Here is our module code:

    public class GovernmentGptHttpApiHostModule : AbpModule
    {
        public override void PreConfigureServices(ServiceConfigurationContext context)
        {
            var hostingEnvironment = context.Services.GetHostingEnvironment();
            var configuration = context.Services.GetConfiguration();
    
            PreConfigure<OpenIddictBuilder>(builder =>
            {
                builder.AddValidation(options =>
                {
                    options.AddAudiences("GovernmentGpt");
                    options.UseLocalServer();
                    options.UseAspNetCore();
                });
            });
            
            PreConfigure<AbpOpenIddictWildcardDomainOptions>(options =>
            {
                options.EnableWildcardDomainSupport = true;
                options.WildcardDomainsFormat.Add(configuration["App:TenantResolverSelfUrl"]);
                options.WildcardDomainsFormat.Add(configuration["App:TenantResolverSignInOIDUrl"]);
                options.WildcardDomainsFormat.Add(configuration["App:TenantResolverSignOutOIDUrl"]);
                options.WildcardDomainsFormat.Add(configuration["App:TenantResolverLoginOIDUrl"]);
                options.WildcardDomainsFormat.Add(configuration["App:TenantResolverAngularUrl"]);
            });
    
    #if DEBUG
            PreConfigure<OpenIddictServerBuilder>(options => { options.UseAspNetCore().DisableTransportSecurityRequirement(); });
    #endif
            
            if (!hostingEnvironment.IsDevelopment())
            {
                PreConfigure<AbpOpenIddictAspNetCoreOptions>(options =>
                {
                    options.AddDevelopmentEncryptionAndSigningCertificate = false;
                });
    
                PreConfigure<OpenIddictServerBuilder>(builder =>
                {
                    builder.AddSigningCertificate(GetSigningCertificate(hostingEnvironment, configuration));
                    builder.AddEncryptionCertificate(GetSigningCertificate(hostingEnvironment, configuration));
                   // builder.SetIssuer(new Uri(configuration["AuthServer:Authority"]!));
                });
            }
            
            PreConfigure<IdentityBuilder>(identityBuilder=>{identityBuilder.AddSignInManager<EnaioSignInManager>();});
        }
    
        public override void ConfigureServices(ServiceConfigurationContext context)
        {
            var configuration = context.Services.GetConfiguration();
            var hostingEnvironment = context.Services.GetHostingEnvironment();
    
            if (!Convert.ToBoolean(configuration["App:DisablePII"]))
            {
                Microsoft.IdentityModel.Logging.IdentityModelEventSource.ShowPII = true;
            }
    
            Configure<AbpTenantResolveOptions>(options =>
            {
                options.AddDomainTenantResolver(configuration["App:Domain"]!);
            });
            
            if (!Convert.ToBoolean(configuration["AuthServer:RequireHttpsMetadata"]))
            {
                Configure<OpenIddictServerAspNetCoreOptions>(options =>
                {
                    options.DisableTransportSecurityRequirement = true;
                });
            }
            
            Configure<AbpTenantResolveOptions>(options =>
            {
                options.AddDomainTenantResolver(configuration["App:TenantResolverDomain"]!);
            });
            
            ConfigureAuthentication(context);
            ConfigureUrls(configuration);
            ConfigureBundles();
            ConfigureConventionalControllers();
            ConfigureImpersonation(context, configuration);
            ConfigureSwagger(context, configuration);
            ConfigureVirtualFileSystem(context);
            ConfigureCors(context, configuration);
            ConfigureExternalProviders(context);
            ConfigureHealthChecks(context);
            ConfigureTheme();
            
            // Add session services
            context.Services.AddSession(options =>
            {
                options.IdleTimeout = TimeSpan.FromMinutes(30); // Set session timeout as needed
                options.Cookie.HttpOnly = true;
                options.Cookie.IsEssential = true;
            });
            
            context.Services.AddSignalR(hubOptions =>
            {
                hubOptions.MaximumParallelInvocationsPerClient = 5;
            });
        }
    
        private void ConfigureTheme()
        {
            Configure<LeptonXThemeOptions>(options =>
            {
                options.DefaultStyle = LeptonXStyleNames.System;
            });
        }
    
        private void ConfigureAuthentication(ServiceConfigurationContext context)
        {
            context.Services.ForwardIdentityAuthenticationForBearer(OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme);
        }
    
        private void ConfigureHealthChecks(ServiceConfigurationContext context)
        {
            context.Services.AddGovernmentGptHealthChecks();
        }
    
        private void ConfigureUrls(IConfiguration configuration)
        {
            Configure<AppUrlOptions>(options =>
            {
                options.Applications["MVC"].RootUrl = configuration["App:SelfUrl"];
                options.Applications["Angular"].RootUrl = configuration["App:AngularUrl"];
                options.Applications["Angular"].Urls[AccountUrlNames.PasswordReset] = "account/reset-password";
                options.Applications["Angular"].Urls[AccountUrlNames.EmailConfirmation] = "account/email-confirmation";
                options.RedirectAllowedUrls.AddRange(configuration["App:RedirectAllowedUrls"]?.Split(',') ?? Array.Empty<string>());
            });
        }
    
        private void ConfigureBundles()
        {
            Configure<AbpBundlingOptions>(options =>
            {
                options.StyleBundles.Configure(
                    LeptonXThemeBundles.Styles.Global,
                    bundle =>
                    {
                        bundle.AddFiles("/global-styles.css");
                    }
                );
            });
        }
    
    
        private void ConfigureVirtualFileSystem(ServiceConfigurationContext context)
        {
            var hostingEnvironment = context.Services.GetHostingEnvironment();
    
            if (hostingEnvironment.IsDevelopment())
            {
                Configure<AbpVirtualFileSystemOptions>(options =>
                {
                    options.FileSets.ReplaceEmbeddedByPhysical<GovernmentGptDomainSharedModule>(Path.Combine(hostingEnvironment.ContentRootPath, $"..{Path.DirectorySeparatorChar}Pm.GovernmentGpt.Domain.Shared"));
                    options.FileSets.ReplaceEmbeddedByPhysical<GovernmentGptDomainModule>(Path.Combine(hostingEnvironment.ContentRootPath, $"..{Path.DirectorySeparatorChar}Pm.GovernmentGpt.Domain"));
                    options.FileSets.ReplaceEmbeddedByPhysical<GovernmentGptApplicationContractsModule>(Path.Combine(hostingEnvironment.ContentRootPath, $"..{Path.DirectorySeparatorChar}Pm.GovernmentGpt.Application.Contracts"));
                    options.FileSets.ReplaceEmbeddedByPhysical<GovernmentGptApplicationModule>(Path.Combine(hostingEnvironment.ContentRootPath, $"..{Path.DirectorySeparatorChar}Pm.GovernmentGpt.Application"));
                });
            }
        }
    
        private void ConfigureConventionalControllers()
        {
            Configure<AbpAspNetCoreMvcOptions>(options =>
            {
                options.ConventionalControllers.Create(typeof(GovernmentGptApplicationModule).Assembly);
            });
        }
    
        private static void ConfigureSwagger(ServiceConfigurationContext context, IConfiguration configuration)
        {
            context.Services.AddAbpSwaggerGenWithOAuth(
                configuration["AuthServer:Authority"]!,
                new Dictionary<string, string>
                {
                        {"GovernmentGpt", "GovernmentGpt API"}
                },
                options =>
                {
                    options.SwaggerDoc("v1", new OpenApiInfo { Title = "GovernmentGpt API", Version = "v1" });
                    options.DocInclusionPredicate((docName, description) => true);
                    options.CustomSchemaIds(type => type.FullName);
                });
        }
    
        private void ConfigureCors(ServiceConfigurationContext context, IConfiguration configuration)
        {
            context.Services.AddCors(options =>
            {
                options.AddDefaultPolicy(builder =>
                {
                    builder
                        .WithOrigins(
                            configuration["App:CorsOrigins"]?
                                .Split(",", StringSplitOptions.RemoveEmptyEntries)
                                .Select(o => o.Trim().RemovePostFix("/"))
                                .ToArray() ?? Array.Empty<string>()
                        )
                        .WithAbpExposedHeaders()
                        .SetIsOriginAllowedToAllowWildcardSubdomains()
                        .AllowAnyHeader()
                        .AllowAnyMethod()
                        .AllowCredentials();
                });
            });
        }
    
        private void ConfigureExternalProviders(ServiceConfigurationContext context)
        {
            var configuration = context.Services.GetConfiguration();
            context.Services.AddAuthentication()
                .AddJwtBearer(options =>
                {
                    options.Authority = configuration["AuthServer:Authority"];
                    options.RequireHttpsMetadata = Convert.ToBoolean(configuration["AuthServer:RequireHttpsMetadata"]);
                    options.Audience = "GovernmentGpt";
            
                    options.TokenValidationParameters.IssuerValidator = TokenWildcardIssuerValidator.IssuerValidator;
                    options.TokenValidationParameters.ValidIssuers = new[]
                    {
                        configuration["App:CommonUrl"]
                    };
                })
                .AddGoogle(GoogleDefaults.AuthenticationScheme, _ => { })
                .WithDynamicOptions<GoogleOptions, GoogleHandler>(
                    GoogleDefaults.AuthenticationScheme,
                    options =>
                    {
                        options.WithProperty(x => x.ClientId);
                        options.WithProperty(x => x.ClientSecret, isSecret: true);
                    }
                )
                .AddMicrosoftAccount(MicrosoftAccountDefaults.AuthenticationScheme, options =>
                {
                        //Personal Microsoft accounts as an example.
                        options.AuthorizationEndpoint = "https://login.microsoftonline.com/consumers/oauth2/v2.0/authorize";
                    options.TokenEndpoint = "https://login.microsoftonline.com/consumers/oauth2/v2.0/token";
                })
                .WithDynamicOptions<MicrosoftAccountOptions, MicrosoftAccountHandler>(
                    MicrosoftAccountDefaults.AuthenticationScheme,
                    options =>
                    {
                        options.WithProperty(x => x.ClientId);
                        options.WithProperty(x => x.ClientSecret, isSecret: true);
                    }
                )
                .AddTwitter(TwitterDefaults.AuthenticationScheme, options => options.RetrieveUserDetails = true)
                .WithDynamicOptions<TwitterOptions, TwitterHandler>(
                    TwitterDefaults.AuthenticationScheme,
                    options =>
                    {
                        options.WithProperty(x => x.ConsumerKey);
                        options.WithProperty(x => x.ConsumerSecret, isSecret: true);
                    }
                );
        }
    
        private void ConfigureImpersonation(ServiceConfigurationContext context, IConfiguration configuration)
        {
            context.Services.Configure<AbpAccountOptions>(options =>
            {
                options.TenantAdminUserName = "admin";
                options.ImpersonationTenantPermission = SaasHostPermissions.Tenants.Impersonation;
                options.ImpersonationUserPermission = IdentityPermissions.Users.Impersonation;
            });
        }
    
        public override void OnApplicationInitialization(ApplicationInitializationContext context)
        {
            var app = context.GetApplicationBuilder();
            var env = context.GetEnvironment();
    
            var forwardOptions = new ForwardedHeadersOptions {
                ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto | ForwardedHeaders.XForwardedHost, RequireHeaderSymmetry = false
            };
    
            forwardOptions.KnownNetworks.Clear();
            forwardOptions.KnownProxies.Clear();
    
        // ref: https://github.com/aspnet/Docs/issues/2384
            app.UseForwardedHeaders(forwardOptions);
    
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
    
            app.UseAbpRequestLocalization();
    
            if (!env.IsDevelopment())
            {
                app.UseErrorPage();
            }
    
            app.UseAbpSecurityHeaders();
            app.UseStaticFiles();
            app.UseRouting();
            app.UseCors();
            app.UseAuthentication();
            app.UseAbpOpenIddictValidation();
    
            if (MultiTenancyConsts.IsEnabled)
            {
                app.UseMultiTenancy();
            }
    
            app.UseUnitOfWork();
    
            app.UseAuthorization();
            
            // Add session middleware
            app.UseSession();
    
            if (!env.IsDevelopment())
            {
                app.Use(async (httpContext, next) =>
                {
                    if (httpContext.Request.Path.StartsWithSegments("/swagger") && !httpContext.User.IsInRole("admin"))
                    {
                        httpContext.Response.StatusCode = StatusCodes.Status403Forbidden;
                        return;
                    }
    
                    await next.Invoke();
                });
            }
    
    
            app.UseSwagger();
            app.UseAbpSwaggerUI(options =>
            {
                options.SwaggerEndpoint("/swagger/v1/swagger.json", "GovernmentGpt API");
    
                var configuration = context.ServiceProvider.GetRequiredService<IConfiguration>();
                options.OAuthClientId(configuration["AuthServer:SwaggerClientId"]);
            });
            
            app.UseAuditing();
            app.UseAbpSerilogEnrichers();
            app.UseConfiguredEndpoints();
        }
    
        private X509Certificate2 GetSigningCertificate(IWebHostEnvironment hostingEnv, IConfiguration configuration)
        {
            var fileName = "authserver.pfx";
            var passPhrase = "f002a3f4-185a-4b4d-a2c4-3f62b6033d4d";
            var file = Path.Combine(hostingEnv.ContentRootPath, fileName);
    
            if (!File.Exists(file))
            {
                throw new FileNotFoundException($"Signing Certificate couldn't found: {file}");
            }
    
            return new X509Certificate2(file, passPhrase);
        }
    }
    
  • User Avatar
    0
    anurag.tyagi created

    Hi,

    I have realized the issue is somewhere in my browser, and it works fine on firefox. Probably there is an extension on my google chrome that's causing this.

    Sorry to have wasted your time. I will close this issue here. Thanks for the wonderful support nonetheless.

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    Great.

Made with ❤️ on ABP v9.0.0-preview. Updated on October 07, 2024, 08:52