2

Greetings

I have one web application with following architecture: Web api: ASP.net core 2.1 (Windows Authentication) UI: angular 8

UI is able to get data but unable to send data. I mean GET method is working fine but POST, PUT, DELETE options are not working . And all the methods are working using POSTMAN.

ERROR is: Access to XMLHttpRequest at 'http://xx.xxx.xxx.xx:xxyy/xxx/xxxxxx/Method' from origin 'http://localhost:xxxx' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: It does not have HTTP ok status.

Any help will be appreciated .

Thanks in advance :)

DURGESH KUMAR
  • 57
  • 1
  • 4
  • 1
    Have you configured Cross Origin Resource Sharing (CORS)? Here's the docs for that: https://learn.microsoft.com/en-us/aspnet/core/security/cors?view=aspnetcore-2.1 – juunas Feb 24 '20 at 12:28
  • hi Yes i have added that and also added headers in wen.config ...Get is working fine with browser and postman . but post is working with postman only. – DURGESH KUMAR Feb 25 '20 at 04:42
  • You can check [this SO thread](https://stackoverflow.com/questions/49450854/how-to-authorize-cors-preflight-request-on-iis-with-windows-authentication) that discussed same issue. And if possible, you can use [IIS CORS module](https://learn.microsoft.com/en-us/iis/extensions/cors-module/cors-module-configuration-reference#cors-preflight-request) that provides IIS servers a way to correctly respond to the preflight request. – Fei Han Feb 25 '20 at 09:51
  • HI, are you able to solve this issue? I am also facing same. – Vikas Garg Sep 14 '20 at 17:52

2 Answers2

2

This is what i use and it should work i hope for your case.

My startup.cs ConfigureServices() decorated with:

services.AddCors(feature =>
                feature.AddPolicy(
                    "CorsPolicy",
                    apiPolicy => apiPolicy
                                    //.AllowAnyOrigin()
                                    //.WithOrigins("http://localhost:4200")
                                    .AllowAnyHeader()
                                    .AllowAnyMethod()
                                    .SetIsOriginAllowed(host => true)
                                    .AllowCredentials()
                                ));

And, Configure() method with:

app.UseCors("CorsPolicy");

Notice the SetIsOriginAllowed() and allowCreds() along with other policy settings, this works for me with POST calls to my api from my angular, which are running on two different port#s.

UPDATE:

Following the questions on the comments, adding additional information on how do we check the logged in user (windows auth) btwn api and the angular (frontend).

You can check the incoming User on a specific route that would only expect the authenticated user using the decoration [Authorize]. In my case, i would have only one method that would expect the windows user in the api:

[HttpGet("UserInfo")]
[Authorize]
public IActionResult GetUserInfo()
{
    string defaultCxtUser = HttpContext?.User?.Identity?.Name;

    if (defaultCxtUser != null && !string.IsNullOrEmpty(defaultCxtUser))
    {
        _logger.LogDebug($"START - Get Context user details for {defaultCxtUser}");
        ADHelper.logger = _logger;
        var userFullName = ADHelper.GetUserIdentityInfo(defaultCxtUser);
        _logger.LogInformation($"Context user {defaultCxtUser} with name: {userFullName}");
        var userInfo = new { Name = userFullName };
        //_logger.LogDebug($"END - GetUserInfo({defaultCxtUser} for {userFullName}");
        return Ok(userInfo);
    }
    else
        return Ok(new { Name = defaultCxtUser });
}

then i would call this from my angular with the service call as,

// Get the Logged in user info
GetCurrentUserInfo(): Observable<string> {
const httpOptions = {
  headers: new HttpHeaders({
    'Content-Type': 'application/json'
  }),
  withCredentials: true
 };

// return this.http.get<string>(`${ApiPath}UserInfo`, httpOptions)
// .pipe(map(v => v as string));
return this.http.get<UserInfo>(`${ApiPath}UserInfo`, httpOptions)
.pipe(map(data => {
  // console.log(data, data.Name);
  return data.Name;
}))
;
}

Please see the headers with 'withCredentials: true' line that would trigger to pass the current user info, and it would be read and understood only if it has the authorize attr to read the User.Identity object in c# side. The reason we do this on a specific method is that, there should be some other parental method in the api like ApiStatus() or anything that could be, should be called first. This would ensure to also invoke the preflight check with OPTIONS that would require anonymous auth. Like in my case, getting whether the api is available and running, and some other app environment info before i get the userInfo() from my angular app.

Ak777
  • 346
  • 7
  • 18
  • Is this works when we set WindowsAuthentication =true and anonymusauthentication =false in launchersetting.json? – Vikas Garg Sep 18 '20 at 13:38
  • dont set anonymousAuth = false. Set both as true. That way, it will work. My requirement is also needing windows auth. But we still need to enable the AnonymousAuth for the preflight request to go through. `"windowsAuthentication": true, "anonymousAuthentication": true,` – Ak777 Sep 18 '20 at 15:38
  • If I set anonymousAuthentication = true then WindowsIdentity is coming null where I have some custom logic to check whther the user is part of some AD group or not. Could you please try anonymousAuthentication = false? – Vikas Garg Sep 21 '20 at 06:17
  • I have updated my answer with the details as i could not provide all those in the comments section with formatted view of the code block. – Ak777 Sep 21 '20 at 13:01
1

That's because your API is on different domain than your SPA angular application.

Please at this at the start of your Configure method in Startup.cs

if (env.IsDevelopment())
{
    app.UseCors(opts =>
    {
        opts.WithOrigins(new string[]
        {
            "http://localhost:3000",
            "http://localhost:3001"
            // whatever domain/port u are using
        });

        opts.AllowAnyHeader();
        opts.AllowAnyMethod();
        opts.AllowCredentials();
    });
}

Please note that this will handle only CORS for local development since you'll probably have same domain in production - if not, you'll need to reconfigure this for production also.

CORS blocking is browser specific and that's why it's working in PostMan but not in browser.

zhuber
  • 5,364
  • 3
  • 30
  • 63
  • Facing issue with POST method after setting Header's Content-Type = 'application/json' – Vikas Garg Sep 16 '20 at 08:48
  • @VikasGarg If you have AllowAnyMethod, every method should be supported. – zhuber Sep 16 '20 at 10:53
  • Yes ideally it should but it give error for POST with window auth at API end. – Vikas Garg Sep 16 '20 at 14:58
  • @VikasGarg, look at my updated answer for the same. AS i said, you cannot have the whole api requires authenticated, like the controller to have [authorize]. It should have atleast one initial call for the app to make preflight request using OPTIONS. Ideally, you can have specific methods or controllers protected but have some basic controller or base, like HealthCheck, Status or whatever might fit to your needs without auth. At least, the loginController or something.. – Ak777 Sep 21 '20 at 15:06