Reputation: 631
I'm building a web site with Angular and ASP.NET Core.
On some pages I want to get data from a Web API. When I run the app, the browser (Firefox) shows that
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at ...(the url) (Reason: missing token ‘authorization’ in CORS header ‘Access-Control-Allow-Headers’ from CORS preflight channel).
I tried other browsers, got the same error.
For authorization consideration, I use a HttpInterceptor
to insert an authorization header for each request from Angular frontend.
Then I looked into my ASP.NET Core backend. I set the CORS policy as app.UseCors(builder => { builder.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader(); });
, but it still doesn't work.
I tested the API with Postman, it works fine.
Where's going wrong?
The Startup.cs
file.
public class Startup
{
public Startup(IConfiguration configuration)
{
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.AddMvc().AddJsonOptions(
opt => opt.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
);
// In production, the Angular files will be served from this directory
services.AddSpaStaticFiles(configuration =>
{
configuration.RootPath = "ClientApp/dist";
});
services.AddEntityFrameworkSqlServer();
services.AddCors();
services.AddSignalR();
services.AddDbContext<ApplicationDbContext>(opt =>
{
opt.UseSqlServer(Configuration.GetConnectionString("Remote"));
});
services.AddIdentity<ApplicationUser, IdentityRole>(opts =>
{
opts.Password.RequireDigit = true;
opts.Password.RequireLowercase = true;
opts.Password.RequireUppercase = true;
opts.Password.RequireNonAlphanumeric = false;
opts.Password.RequiredLength = 7;
}).AddEntityFrameworkStores<ApplicationDbContext>();
services.AddAuthentication(opts =>
{
opts.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
opts.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
opts.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(cfg =>
{
cfg.RequireHttpsMetadata = false;
cfg.TokenValidationParameters = new TokenValidationParameters()
{
ValidIssuer = Configuration["Auth:Jwt:Issuer"],
ValidAudience = Configuration["Auth:Jwt:Audience"],
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Auth:Jwt:Key"])),
ClockSkew = TimeSpan.Zero,
RequireExpirationTime = true,
ValidateIssuerSigningKey = true,
ValidateAudience = true
};
});
services.AddAuthorization(options =>
{
options.AddPolicy("NonUser", policy => policy.RequireRole("RestrauntOwner", "RestrauntAdmin", "SystemAdmin"));
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseCors(builder => { builder.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader(); });
app.UseStaticFiles();
app.UseSpaStaticFiles();
app.UseAuthentication();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller}/{action=Index}/{id?}");
});
app.UseSignalR(route =>
{
route.MapHub<OrderHub>("/orderhub");
});
app.UseCookiePolicy();
app.UseSpa(spa =>
{
// To learn more about options for serving an Angular SPA from ASP.NET Core,
// see https://go.microsoft.com/fwlink/?linkid=864501
spa.Options.SourcePath = "ClientApp";
if (env.IsDevelopment())
{
spa.UseAngularCliServer(npmScript: "start");
}
});
}
}
It's weird. I heve been developing this on my Windows PC for some time. I cloned the project on my MacBook, it worked fine without any problems on macOS.
Upvotes: 5
Views: 5027
Reputation: 145910
If you can't quite get it working here's a couple tips:
If it's HTTP you must put HTTP, and HTTPS must be HTTPS.
Port number must be included too and correct.
http://localhost:5000
https://localhost:5001
So don't put a CORS rule for "http://localhost:5001"
if you're using the above settings because that isn't the same URL!
Look in the browser, or Fiddler to get it exactly right and make sure it is what you expect. If switching between HTTP and HTTPS it's very easy to get confused.
"authorization"
or "Authorization"
or "aUtHoRiZaTiOn"
if you want.WithMethods
or AllowAnyMethod
it won't work. This is very easy to miss - especially for just a GET request.For app.UseSpa
(if you're using Microsoft's hosting mechanism for SPA) you can do this
app.UseSpa(spa =>
{
// see https://go.microsoft.com/fwlink/?linkid=864501
// CORS just for the SPA
spa.ApplicationBuilder.UseCors(builder =>
{
// Must specify Methods
builder.WithMethods("GET");
// Case insensitive headers
builder.WithHeaders("AuthoriZatioN");
// Can supply a list or one by one, either is fine
builder.WithOrigins("http://localhost:5000");
builder.WithOrigins("https://localhost:5001");
});
}
Fiddler will show you the required conditions for CORS to succeed. Here it is authorization
header and GET
method.
You can hit R
on a previous request to rerun it after changing server configuration. That way you don't have to keep firing up your browser.
Make sure to look at Headers
in the response section because the actual content will be 0 bytes even when successful.
Upvotes: 4
Reputation: 1928
Reason behind this error is : Your client project and webapi are on different domain( or port ).
Browser security prevents a web page from making AJAX requests to another domain. This restriction is called the same-origin policy, and prevents a malicious site from reading sensitive data from another site.
To set up CORS for your application add the Microsoft.AspNetCore.Cors package to your project.
Then Enabling CORS with middleware : in startup > ConfigureServices method
//For any origin
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(options =>
{
options.AddPolicy("AllowSpecificOrigin",
builder1 => builder1.WithOrigins("http://web.user.local:44352"));
});
}
And in Configure method :
public void Configure(IApplicationBuilder app)
{
// Shows UseCors with named policy.
app.UseCors("AllowSpecificOrigin");
//...rest
}
Note: The URL must be specified without a trailing slash (/). If the URL terminates with /, the comparison will return false and no header will be returned.
For more info read here
Upvotes: 2
Reputation: 631
OK. I found what't wrong, it's kinda silly though.
I asked the web API provider. He told me that it was because the authorization header wasn't allowed.
I use a HttpIntercepter
to insert a authorization header into every request fired form Angular frontend. I did this to let my ASP.NET Core Web API to authenticate the users of the site. But the open web API doesn't allow this header.
It works if I didn't log in on my website, but if I do perform the login procedure, it will get a token and insert an authorization into the request, and it will fire a OPTIONS preflight request, well, then the request will be blocked.
Such a silly question :) I gotta find a new way to do this.
Upvotes: 2