I have an API on ASP.net Core 2 (windows authentication) and a front on angular.
I make a cors configuration to querying my backend from the SPA angular, but im blocked in cause of the preflight who are rejected from the IIS server because he don't have identification information.
error message :
Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://XXXXXX' is therefore not allowed access. The response had HTTP status code 401.
code side front :
//MY HEADER
private headers = new Headers({
'Content-Type': 'application/json',
'Access-Control-Allow-Credentials':'true',
'Access-Control-Allow-Origin':'true'
});
//REQUEST
let options = new RequestOptions({headers:this.headers, withCredentials:true});
return this.http.get(this.tasksUrl,options).map(res=>res.json());
code side back : (Startup.cs)
public void ConfigureServices(IServiceCollection services)
{
services.AddCors();
services.AddCors(options =>
{
options.AddPolicy("AllowSpecificOrigin",
builder =>
{
builder.WithOrigins("http://theURLofTheFront:8080" )
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials();
});
});
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
app.UseCors("AllowSpecificOrigin");
app.UseMvc();
}
I try this :
CORS preflight request returning HTTP 401 with windows authentication.
and i added custom header to specify the 'Acces-control-allow-origin' on IIS, dont work for me.
and this dont work for me :
https://blogs.msdn.microsoft.com/friis/2017/11/24/putting-it-all-together-cors-tutorial/
I can't remove the default authorization rule.
I thank you in advance for you
There are several ways to accomplish this, other answers can be found on this similar question --> Angular4 ASP.NET Core 1.2 Windows Authentication CORS for PUT and POST Gives 401
CORS Module
It is possible to configure IIS by using the CORS Module.
As seen here: https://blogs.iis.net/iisteam/getting-started-with-the-iis-cors-module
And further information available here: https://blogs.iis.net/iisteam/getting-started-with-the-iis-cors-module
The IIS CORS module is designed to handle the CORS preflight requests
before other IIS modules handle the same request. The OPTIONS requests
are always anonymous, so CORS module provides IIS servers a way to
correctly respond to the preflight request even if anonymous
authentification needs to be disabled server-wise.
You will need to enable the CORS Module via the Webconfig:
<?xml version="1.0"?>
<configuration>
<system.webServer>
<cors enabled="true">
<add origin="*" allowCredentials="true" />
</cors>
</system.webServer>
</configuration>
for more granular control:
<?xml version="1.0"?>
<configuration>
<system.webServer>
<cors enabled="true">
<add origin="https://readonlyservice.constoso.com" allowCredentials="true">
<allowMethods>
<add method="GET" />
<add method="HEAD" />
</allowMethods>
<allowHeaders>
<add header="content-type" />
<add header="accept" />
</allowHeaders>
</add>
<add origin="https://readwriteservice.constoso.com" allowCredentials="true">
<allowMethods>
<add method="GET" />
<add method="HEAD" />
<add method="POST" />
<add method="PUT" />
<add method="DELETE" />
</allowMethods>
</add>
</cors>
</system.webServer>
</configuration>
Redirect OPTIONS
You can redirect all OPTIONS requests to always give an OK status.
This will however subvert the entire idea of a preflight request, so use this only if it's applicable to your situation.
Install the redirect module in IIS.
Add the following redirect to your Webconfig.
<rewrite>
<rules>
<rule name="CORS Preflight Anonymous Authentication" stopProcessing="true">
<match url=".*" />
<conditions>
<add input="{REQUEST_METHOD}" pattern="^OPTIONS$" />
</conditions>
<action type="CustomResponse" statusCode="200" statusReason="Preflight" statusDescription="Preflight" />
</rule>
</rules>
</rewrite>
Middleware
Alternatively the desired result can be achieved by enabling anonymous authentication in IIS and creating a middleware in the Net Core API that checks if a person is properly authenticated.
Middleware:
public AuthorizationMiddleware(RequestDelegate next, ILogger logger)
{
_next = next;
_log = logger;
}
public async Task Invoke(HttpContext httpContext)
{
//Allow OPTIONS requests to be anonymous
if (httpContext.Request.Method != "OPTIONS" && !httpContext.User.Identity.IsAuthenticated)
{
httpContext.Response.StatusCode = 401;
await httpContext.Response.WriteAsync("Not Authenticated");
}
await _next(httpContext);
}
Preflight request does not send authentication information. So, enable anonymous authentication as well (no need to remove windows authentication). refer to https://stackoverflow.com/a/50354772/946773
Related
Question:
In .Net Core v5.0 apps:
How to catch URL such as: "http://localhost:5000//" or "http://localhost:5000///"??
Is there a way to catch such URL with Route configuration?
Or, is there a way to catch such URL in IIS?
These URLs are OK (200, not 404) in .NET Core v2.1 apps.
Problem:
http://localhost:5000 - //200 OK
http://localhost:5000/ - //200 OK
http://localhost:5000// - //404 (more than 1 slashes: cause 404)
http://localhost:5000/// - //404
Console info: (pay attention to the double slashes "//" after localhost:5000)
info: Microsoft.AspNetCore.Hosting.Diagnostics[2]
Request finished HTTP/1.1 GET http://localhost:5000// - - - 404 0 - 12.1067ms
While, URL with multiple slashes in the middle of the URL path (not right after domain/port) can be caught and handled.
Environment:
1.asp.net core v5.0
2.Route Configuration:
app.UseRouting();
app.UseMvc(routes => //attribute routing is always available
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
3.Program.cs
public static void Main(string[] args)
{
Console.Title = "WebApplication1";
var host = CreateHostBuilder(args).Build();
host.Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
4.web.config
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<location path="." inheritInChildApplications="false">
<system.webServer>
<handlers>
<add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModuleV2" resourceType="Unspecified" />
</handlers>
<aspNetCore processPath="dotnet" arguments=".\WebApplication1.dll" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" hostingModel="inprocess" />
</system.webServer>
</location>
</configuration>
Or, is there a way to catch such URL in IIS?
You could use url rewrite in IIS to rewrite the \\\ to "".
You could install the url rewrite by using this extension.
Then you could add below url rewrite rule:
<rewrite>
<rules>
<rule name="Remove multiple slashes" stopProcessing="true">
<match url=".*" />
<action type="Redirect" url="{REQUEST_URI}" />
<conditions>
<add input="{UNENCODED_URL}" pattern="(.*?)[/]{2,}$" />
</conditions>
</rule>
</rules>
</rewrite>
Result:
Add below codes into Configure method:
var options = new RewriteOptions()
.AddRedirect(#"(.*?)[/]{2,}$", "/");
app.UseRewriter(options);
Result:
I tried to use this middleware:
public class SecurityHeadersMiddleware
{
private readonly RequestDelegate next;
public SecurityHeadersMiddleware(RequestDelegate next)
{
this.next = next;
}
public async Task Invoke(HttpContext context)
{
context.Response.OnStarting(state =>
{
var ctx = (HttpContext)state;
if (!ctx.Response.Headers.ContainsKey("Arr-Disable-Session-Affinity"))
{
ctx.Response.Headers.Add("Arr-Disable-Session-Affinity", "True"); // Disables the Azure ARRAffinity cookie
}
if (ctx.Response.Headers.ContainsKey("Server"))
{
ctx.Response.Headers.Remove("Server"); // For security reasons
}
if (ctx.Response.Headers.ContainsKey("x-powered-by") || ctx.Response.Headers.ContainsKey("X-Powered-By"))
{
ctx.Response.Headers.Remove("x-powered-by");
ctx.Response.Headers.Remove("X-Powered-By");
}
if (!ctx.Response.Headers.ContainsKey("X-Frame-Options"))
{
ctx.Response.Headers.Add("X-Frame-Options", "DENY");
}
return Task.FromResult(0);
}, context);
await next(context);
}
}
x-powered-by is still there in response header which says asp.net
As far as I know, the removal of these headers is facilitated with the Request Filtering module, which is part of IIS.
To remove a header, you need to have a web.config file stored on your site, with the following content:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<!-- To customize the asp.net core module uncomment and edit the following section.
For more info see https://go.microsoft.com/fwlink/?linkid=838655 -->
<system.webServer>
<handlers>
<remove name="aspNetCore"/>
<add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModule" resourceType="Unspecified"/>
</handlers>
<aspNetCore processPath="%LAUNCHER_PATH%" arguments="%LAUNCHER_ARGS%" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" />
<httpProtocol>
<customHeaders>
<remove name="X-Powered-By" />
</customHeaders>
</httpProtocol>
</system.webServer>
</configuration>
Add this web.config to your net core application's root folder.
Then it will remove the x-powered-by header.
The result like this:
In addition to #Brando Zhang answer, To remove "Server:Kestrel" from response header:
-.NET Core 1
var host = new WebHostBuilder()
.UseKestrel(c => c.AddServerHeader = false)
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseStartup<Startup>()
.Build();
-NET Core 2
WebHost.CreateDefaultBuilder(args)
.UseKestrel(c => c.AddServerHeader = false)
.UseStartup<Startup>()
.Build();
If you don't want to create a web.config file in a ASP.NET Core solution, you can remove the X-Powered-By header in IIS Manager.
Click on <ServerName> --> HTTP Response Headers --> X-Powered-By and choose the Remove action.
This will remove the header for all websites on that server. Which is fine because why would you want to share that info in the first place?
As an alternative option to the answers above you can use a configuration transformation. That way the web.config will still be generated via the dotnet publisher sdk but can be mixed with specific tags such as the header removal.
In the root of the project create a new web.Release.config file as such:
<?xml version="1.0" encoding="utf-8"?>
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
<location>
<!-- To customize the asp.net core module uncomment and edit the following section.
For more info see https://go.microsoft.com/fwlink/?linkid=838655 -->
<system.webServer>
<httpProtocol xdt:Transform="InsertIfMissing">
<customHeaders>
<remove name="X-Powered-By" />
</customHeaders>
</httpProtocol>
</system.webServer>
</location>
</configuration>
Note that this is a transformation file, not the actual web.config file.
I am trying to get CORS to work with a new WebAPI project. The project is just the default WebAPI template (i.e. has MVC and WebAPI references) using ActiveDirectoryBearerAuthentication.
Whenever I try and make a request to my API I am met with the following error:
XMLHttpRequest cannot load https://localhost:44385/api/values. Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'https://localhost:44369' is therefore not allowed access.
I have installed Microsoft.Owin.Cors, and Microsoft.AspNet.WebApi.Cors.
My WebApiConfig is this:
public static void Register(HttpConfiguration config)
{
config.EnableCors();
....
My Startup.Auth is this:
public void ConfigureAuth(IAppBuilder app)
{
app.UseCors(CorsOptions.AllowAll);
app.UseWindowsAzureActiveDirectoryBearerAuthentication(
....
Solutions around the net say to add:
<customHeaders>
<!-- Adding the following custom HttpHeader will help prevent CORS from stopping the Request-->
<add name="Access-Control-Allow-Origin" value="*" />
</customHeaders>
to the web.config. However, doing this just allows everything regardless of whether Microsoft.AspNet.WebApi.Cors is installed, enabled, or not.
To allow only a single domain you can do the following:
<system.webServer>
<httpProtocol>
<customHeaders>
<remove name="X-Powered-By" />
<clear />
<add name="Access-Control-Allow-Origin" value="http://www.myalloweddomain.com" />
</customHeaders>
</httpProtocol>
</system.webServer>
However, that's only allowed for a single domain. If you try to add an additional add node your site won't load. If you want to allow multiple domains, then you need to try one of the solutions listed here.
Things to check:
Owin kicks in on IIS (make sure you're including Microsoft.Owin.Host.SystemWeb)
Check that IIS doesn't hijack your request IIS hijacks CORS Preflight OPTIONS request
Use fiddler to fire OPTION request directly to specified url (sometimes the PREFLIGHT request just mask 500 returned by server) - browsers aren't very good in handling that
Put:
<httpProtocol>
<customHeaders>
<add name="Access-Control-Allow-Origin" value="*" />
<add name="Access-Control-Allow-Methods" value="GET, PUT, POST, DELETE, HEAD, OPTIONS" />
<add name="Access-Control-Allow-Headers" value="Origin, X-Requested-With, Content-Type, Accept" />
</customHeaders>
</httpProtocol>
in the Web Config, this literally covers everything and will let through everything according to the "*" wildcard. If you want concrete domains to be whitelisted you put them into:
<add name="Access-Control-Allow-Origin" value="*" />
Enabling you can do either through global configuration as you have been doing or instead with an attribute:
[EnableCors(origins: "*", headers: "*", methods: "*")]
On the relevant controller. Its one of the two, if you use both options it will choke.
Also you can write your own attribute which can handle loading of origins, headers and methods from a web.config appSettings or from a database by overriding EnableCors attribute if you prefer but that is already beyond the scope of your question I think.
I'm getting the following error:
Response to preflight request doesn't pass access control check: No
'Access-Control-Allow-Origin' header is present on the requested
resource.
when making the following request to my web api from my angular2 app:
return this.http.get("http://localhost/api/test", { headers })
.map(res => this.extractData(res))
.catch(this.handleError);
Some considerations:
I'm using Microsoft.Owin.Cors
Requests work fine when they don't include headers. This one includes headers for authentication purposes.
I can make the request using Chrome.
Microsoft.Owin.Cors is installed in the solution
app.UseCors(CorsOptions.AllowAll); is at the start of the configuration method of startup.cs
The following is included in the web.config file:
<system.webServer>
<handlers>
<remove name="ExtensionlessUrlHandler-Integrated-4.0" />
<remove name="OPTIONSVerbHandler" />
<remove name="TRACEVerbHandler" />
<add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionControllerv4.0" />
</handlers>
thanks!
I think the problem is that you don't configure preflight requests on the server side (for OPTIONS methods).
There are two kinds of requests with CORS:
Simple requests. This use case applies if we use HTTP GET, HEAD and POST methods. In the case of POST methods, only content types with the following values are supported: text/plain, application/x-www-form-urlencoded and multipart/form-data.
Preflighted requests. When the ‘simple requests’ use case doesn’t apply, a first request (with the HTTP OPTIONS method) is made to check what can be done in the context of cross-domain requests.
When adding headers, you switch from simple to preflight requests. So an OPTIONS request is executed under the hood before the target request.
See this article for more details:
http://restlet.com/blog/2015/12/15/understanding-and-using-cors/
add these lines to webapiconfig.cs file
var cors = new EnableCorsAttribute("", "", "*");
config.EnableCors(cors);
you can do one more thing in ApplicationOAuthProvider.cs add this line in GrantResourceOwnerCredentials method
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });
I have this App where I would like to set my custom headers in the Web.Config, alas this is not always fool proof.
<customHeaders>
<add name="Access-Control-Allow-Origin" value="*" />
<add name="Access-Control-Allow-Methods" value="*" />
<add name="Access-Control-Allow-Headers" value="*" />
</customHeaders>
The above set and iterations of it such as
<customHeaders>
<add name="Access-Control-Allow-Origin" value="*" />
<add name="Access-Control-Allow-Methods" value="OPTIONS,GET,PUT,DELETE,POST" />
<add name="Access-Control-Allow-Headers" value="Authorization,Content-Type" />
</customHeaders>
has not worked worked for me in all scenario's. As of now this setting works in about 50% of the test machines and gives 405 Method Not Allowed in others.
The alternative is set this in WebApiConfig.cs and uncomment the custom headers in Web.config.
//Web API Cross origin requests - Enable
var cors = new EnableCorsAttribute("*", "*", "*");
config.EnableCors(cors);
Why is there so much ambiguity in this and how do I know for sure where CORS will work all the time? I am really interested in setting CORS on Web.config only as I would like the flexibility of modifying it in the deployed version.
I believe that your 'random' issue occurs because you are not handling the preflight Options requests for PUT and Delete verbs.
For the two verbs mentioned above an extra request is generated, Options, to which Web API needs to respond in order to confirm that it is indeed configured to support CORS.
To handle this, all you need to do is send an empty response back. You can do this inside your actions, or you can do it globally like this:
protected void Application_BeginRequest()
{
if (Request.Headers.AllKeys.Contains("Origin") && Request.HttpMethod == "OPTIONS")
{
Response.Flush();
}
}
This extra check was added to ensure that old APIs that were designed to accept only GET and POST requests will not be exploited. Imagine sending a DELETE request to an API designed when this verb didn't exist. The outcome is unpredictable and the results might be dangerous.
Also, in web.config, you should specify the methods instead of using *
<httpProtocol>
<customHeaders>
<add name="Access-Control-Allow-Origin" value="*" />
<add name="Access-Control-Allow-Headers" value="Content-Type" />
<add name="Access-Control-Allow-Methods" value="GET, POST, PUT, DELETE, OPTIONS" />
</customHeaders>
</httpProtocol>
There is no ambiguity with CORS, you have a few cases that you need to think about
1- if you want to enable CORS for your Web APIs only use "Microsoft.AspNet.WebApi.Cors" library.
2- if you want to enable CORS for the whole website (including the Web APIs, SignalR, ..etc ) use "Microsoft.Owin.Cors" library.
using any library from the above 2 will definitely work and cors will be enabled, now if you want to configure the urls, you can do that from your database/config file, so when your application starts the url that you pass to the EnableCors for example can come from the database/config file, but the bottom line is to avoid adding any cors headers to the web.config.
To know to enable CORS for your Web API, you can have a look to my article here, which enables CORS for the Web APIs and use it from AngularJS client.
Hope that helps.
For anyone reading this, this may help.
Even with the following startup code
var cors = new EnableCorsAttribute("*", "*", "GET, POST, PUT, DELETE, OPTIONS");
config.EnableCors(cors);
I had to explcitly add the verbs to the Web Api action method:
[Route("sanity")]
[HttpOptions]
[HttpPost]
public List<PostImportView> Sanity(SanityFilter filter)
{
....
Pretty pointless and annoying