Click here to Skip to main content
14,691,039 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
I have two separate project, one is WebAPI developed in .net Core 2.2 with Windows Authentication and other is Angular. I am stuck in CORS issue. I was able to handle GET request by using withCredentials: true in GET method option as mentioned below, where httpClient is from import { HttpClient } from '@angular/common/http':
httpClient.get('url'), { withCredentials: true }) as Observable<Type>;

But in case of POST, the request is going as OPTION. And every time it is failing with error code 401 UNAUTHORIZED in Network tab of Chrome Developer Tools window. And in Console it is showing the below error

Access to XMLHttpRequest at 'http://localhost:5000/api/xxx/xxxMethod' 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.

What I have tried:

To resolve this issue I made few changes in the following files of Web API and Angular project:

Web.config:I added the below code under system.webserver tag
<handlers>
   <add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModuleV2" resourceType="Unspecified" />
   <remove name="ExtensionlessUrlHandler-Integrated-4.0" />
   <remove name="OPTIONSVerbHandler" />
   <add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
</handlers>
<httpProtocol>
   <customHeaders>
     <add name="Access-Control-Allow-Origin" value="http://localhost:4200" />
     <add name="Accept" value="application/json, text/plain, */*"/>
   </customHeaders>
</httpProtocol>


PreflightRequestMiddleware.cs: I created this middleware to handle all the incoming request and to bypass the OPTIONS request with OK status
public class PreflightRequestMiddleware
{
  private readonly RequestDelegate Next;
  public PreflightRequestMiddleware(RequestDelegate next)
  {
    Next = next;
  }
  public Task Invoke(HttpContext context)
  {
    return BeginInvoke(context);
  }
  private Task BeginInvoke(HttpContext context)
  {
    context.Response.Headers.Add("Access-Control-Allow-Credentials", new[] { "true" });
    context.Response.Headers.Add("Access-Control-Allow-Headers", new[] { "Origin, X-Requested-With, Content-Type, Accept, Athorization, ActualUserOrImpersonatedUserSamAccount, IsImpersonatedUser" });
    context.Response.Headers.Add("Access-Control-Allow-Methods", new[] { "GET, POST, PUT, DELETE, OPTIONS" });
    if (context.Request.Method == HttpMethod.Options.Method)
    {
      context.Response.StatusCode = (int)HttpStatusCode.OK;
      return context.Response.WriteAsync("OK");
    }
    return Next.Invoke(context);
  }
}

public static class PreflightRequestExtensions
{
  public static IApplicationBuilder UsePreflightRequestHandler(this IApplicationBuilder builder)
  {
    return builder.UseMiddleware<PreflightRequestMiddleware>();
  }
}


Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
  services.AddCors(o => o.AddPolicy("CorePolicy", builder =>
  {
    builder.AllowAnyMethod();
  }));
  .......
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
  app.UsePreflightRequestHandler();
  .....
  app.UseCors("CorePolicy"); //Tried to put this first line too, but no luck
  .....
}


Then in Angular Project, for POST method call I first create headers:
<pre lang="TypeScript">
HttpHeaders() with below values:
'Content-Type', 'application/json'
'Content-Type', 'application/json'
'Accept-Language', ['en-US', 'en', 'q=0.9']
'Accept', ['application/json', 'text/plain', '*/*']
'Athorization', 'Include'

Calling POST Method
client.post(url, postParam, optionsWithHeader).pipe(
            map((response: any) => response as Result[]),
            catchError(#HandleError)


But, there is one strange thing which I noticed, When I run Fiddler first and then when I run Web API and Angular app all the OPTIONS request is handled in PreflightRequestMiddleware. But when I am running without Fiddler the request is not even reaching to PreflightRequestMiddleware. I have spent 4 days but still has no idea what is wrong. Few people may suggest me to check the header which is received when running Fiddler in Request, but I tried that too with no luck. Does anyone has any clue??
Posted
Updated 23-Sep-20 9:40am
v5
Comments
Mahadevan Annamalai 13-Mar-20 23:32pm
   
Solution 1 is resolved my issue. Thanks for the post.

Finally, I was able to figure out how to resolve the above Issue. Since I cannot answer my own question here so I am posting the solution here:

Solution: I removed all the above code and started fresh, as mentioned below with files:

Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
  services.AddCors(options =>
  {
    options.AddPolicy(
      "CorsPolicy",
      builder => builder.WithOrigins("http://localhost:4200")
      .AllowAnyMethod()
      .AllowAnyHeader()
      .AllowCredentials());
    });
  services.AddAuthentication(IISDefaults.AuthenticationScheme);
  .....
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
  app.UseCors("CorsPolicy");

  app.UsePreflightRequestHandler();
  .......
}


launchSettings.json: I set Anonymous and Windows Authentication to true
{
  "iisSettings": {
    "windowsAuthentication": true,
    "anonymousAuthentication": true,
    "iisExpress": {
      "applicationUrl": "http://localhost:5000",
      "sslPort": 0
    }
  },
  "$schema": "http://json.schemastore.org/launchsettings.json",
  "profiles": {
    "IIS Express": {
      "commandName": "IISExpress",
      "launchBrowser": true,
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
      }
    },
    "TPIGO.WebAPI": {
      "commandName": "Project",
      "launchBrowser": true,
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
      },
      "applicationUrl": "http://localhost:5000"
    }
  }
}


Then, in Angular:
//For GET & POST

Add, withCredentials: true as otions


Now, comes the explanation to this solution.

As I mentioned in my problem statement, the GET request was working fine, but the issue was with the POST request.

In case of POST request, the browser was sending OPTIONS request first to authenticate with the server but this request will never reach the Server and that was the reason I was not able to handle the request in PreflightRequestMiddleware.

The OPTIONS request was failing because the API was configured for Windows Authentication and OPTIONS request was not carrying any Authentication with them.

That is the reason I enabled Anonymous Authentication in launchSettings.json.

But, when I enable Anonymous Authentication I started getting 500 Internal Server errors, and investigating it further I came to know that I need to provide Authentication Scheme.

Then I added services.AddAuthentication(IISDefaults.AuthenticationScheme); in Startup.cs files ConfigureServices(IServiceCollection services) method. and everything works like a charm.

Remember to add withCredentials: true with every request you send to the API that needs Authentication.

If anyone has any doubts or confusion feel free to ask here. I am not closing this post so that others can share their doubts here wrt the solution I mentioned.

NOTE: I am still using PreflightRequestMiddleware just to do some additional stuff on Request and Response, but this middleware is not required.
public class PreflightRequestMiddleware
    {
        private readonly RequestDelegate Next;

        public PreflightRequestMiddleware(RequestDelegate next)
        {
            Next = next;
        }

        public Task Invoke(HttpContext context)
        {
            return BeginInvoke(context);
        }

        private Task BeginInvoke(HttpContext context)
        {
            // Do stuff here
            return Next.Invoke(context);
        }
    }

    public static class PreflightRequestExtensions
    {
        public static IApplicationBuilder UsePreflightRequestHandler(this IApplicationBuilder builder)
        {
            return builder.UseMiddleware<PreflightRequestMiddleware>();
        }
    }
   
v5
Comments
Member 14558413 15-Aug-19 14:34pm
   
Where does this angular part go?

Do I add it a ts file?

Thanks


AbstractReadOnlyService.httpHeaders = new HttpHeaders().set('Content-Type', 'application/json');
AbstractReadOnlyService.httpHeaders = AbstractReadOnlyService.httpHeaders.set('Accept', 'application/json');

AbstractReadOnlyService.optionsStatic = {
headers: AbstractReadOnlyService.httpHeaders,
params: new HttpParams(),
withCredentials: true
};

//For GET
return this.httpClient.get(this.getWebApiServiceUrl('/applicationusers/userprofile'), { params: this.httpParams, withCredentials: true }) as Observable<userprofile>;

//For POST
return this.httpClient.post(url, firmDashboardSearchCriteria, AbstractReadOnlyService.optionsStatic).pipe(
map((response: any) => response as xxxxSearchResult[]),
catchError(this.handleError)
Mahadevan Annamalai 13-Mar-20 23:34pm
   
in Angular yourService.ts file like below

this.http.get(your URL',{withCredentials: true}).pipe(
map((res: any) => {res; console.log('success'); console.log(res);}),
catchError(<t>(error: any, result?: T) => {
console.log('error'); console.log(error);
return of(result as T);
})
);
tripe3way 11-Dec-19 9:52am
   
shivangkaul , Man you are such a great developer your solution saved me after like 6 hours continuous working I OWE YOU ALOT , can i have your linkedin , and promise if you are living in north america i will invite you for beer and dinner
THANK YOU
[no name] 17-Apr-20 5:27am
   
Thanks, this is exactly what i was struggling with. I just had to enable anonymous authentication and the post requests started to work again.
This works for me:

Web.config

<httpProtocol>
  <customHeaders>
	<add name="Access-Control-Allow-Origin" value="*" />
	<add name="Access-Control-Allow-Methods" value="*" />
	<add name="Access-Control-Allow-Headers" value="x-requested-with, Content-Type, origin, authorization, accept, client-security-token" />
  </customHeaders>
</httpProtocol>  

Global.asax

protected void Application_BeginRequest()
{
	if (Request.Headers.AllKeys.Contains("Origin") && Request.HttpMethod == "OPTIONS")
	{
		// These headers are handling the "pre-flight" OPTIONS call sent by the browser
		HttpContext.Current.Response.AddHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE");
		HttpContext.Current.Response.AddHeader("Access-Control-Allow-Headers", "Content-Type, Accept");
		HttpContext.Current.Response.AddHeader("Access-Control-Allow-Credentials", "true");
		HttpContext.Current.Response.AddHeader("Access-Control-Max-Age", "1728000");
		HttpContext.Current.Response.End();
	}
}
   
I have the same problem with asp.net core 3.1 and angular 9.
The solution you have illustrated, in my case, does not work.

I keep getting the same error:
"Access to XMLHttpRequest at 'http://localhost:5000/api/......' from origin 'http://localhost:4200' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource."

I also noticed that, during debugging, for GET requests the PreflightRequestMiddleware class is processed, while when it comes to OPTIONS requests it is not.
   
Hola buenas tardes, en este fragmento de codigo
builder => builder.WithOrigins("http://localhost:4200")
remplazo por el puerto de mi localhost?
   
Comments
CHill60 29-Sep-20 11:46am
   
Si desea hacer una pregunta, use el enlace rojo 'Ask a Question' en la parte superior de la página.
Debe escribirlo en inglés en este sitio por favor. Utilice la herramienta 'Traductor de Google'

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)




CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900