1

I have an app where Web UI (reactjs) and Web APIs (.net) are deployed on two different servers. However, when I set my API authentication to Windows, it keeps giving me error: Failed to load resource: the server responded with a status of 401 (Unauthorized)

I am able to browse through the deployed application link when I have following settings:
UI Site: Windows Authentication
API Site: Anonymous Authentication.

But that doesn't help me get the logged-in user-id. That returns iis/app-pool name as logged in user.

So, I changed the settings to following:
UI Site: Windows Authentication
API Site: Windows Authentication and ASP.NET Impersonation.

Now I get the authorization error in UI when it hits first API. Point to note is, if I directly access the API link, it is returning me result and also giving my user ID as logged user-id.

I tried various solutions suggested on internet including the step 1 to 4 for below one: How to pass Windows Authentication credential from client to Web API service but no success yet.

Has anyone faced such issue and found a fix ?

Edit: I went ahead with the steps as suggested by Alexandre Rodrigues in the response below. I enabled CORS as described in the link.

The first issue was: 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'. The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute.

So I went ahead and added the website path in :

[EnableCors(origins: "http:myexample.com", headers: "*", methods: "*", SupportsCredentials = true)] 

It solely did not help, then I added the below lines in web.config:

<httpProtocol>
   <customHeaders>
      <add name="Access-Control-Allow-Methods" value="POST,GET" />
      <add name="Access-Control-Allow-Origin" value="http://myexample.com" /> 
      <add name="Access-Control-Allow-Credentials" value="true" />
      <add name="Access-Control-Allow-Headers" value="cache-control,content-type,man,messagetype,soapaction" />
   </customHeaders>
</httpProtocol>

The next error I got was:

The value of the 'Access-Control-Allow-Credentials' header in the response is 'true, true' which must be 'true'.

and

The value of the 'Access-Control-Allow-Origin' header in the response is 'http://myexample.com, http://myexample(index).com' which must be 'http://myexample.com'

So, as a last resort I commented below line from my WebAPI and redeployed application.

[EnableCors(origins: "http:myexample.com", headers: "*", methods: "*", SupportsCredentials = true)]

That worked! This resulted in application showing result for both the controllers: One where I had set Authorize and the one without Authorize.

The errors were thrown only by controller where I had added [Authorize] because the other one was returning result most of the times.

So my question is, is this how regular authentication and authorization should work or did I comment a very vital part of Authorization ? Update: So this made only GET requests to work and not POST. Request Header: Access-Control-Allow-Credentials: true Access-Control-Allow-Headers: cache-control,content-type,man,messagetype,soapaction Access-Control-Allow-Methods: POST,GET Access-Control-Allow-Origin: http://myexample.com **Response Header** Provisional headers are shown Access-Control-Request-Headers: content-type Access-Control-Request-Method: POST Origin: http://myexample.com Referer: http://myexample.com/form/1001 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36

user1630575
  • 171
  • 1
  • 15

1 Answers1

2

Prerequisite

To access any web API from Reactjs or any Ajax method Web API must enable CORS

Web Config Code snippet

<system.web>  
    <authentication mode="Windows" ></authentication>  
</system.web>  

Use Authorize attribute on the controller or on any action method for security

example

[EnableCors(origins: "*", headers: "*", methods: "*", SupportsCredentials = true)]  
    public partial class WebAPIController : ApiController  
    {  
   
        [HttpGet]  
        [Authorize]  
        [Route("api/AuthenticateUser")]  
   
        public HttpResponseMessage AuthenticateUser()  
        {  
            if (User != null)  
            {  
                 return Request.CreateResponse(HttpStatusCode.OK, new  
                {  
                    status = (int)HttpStatusCode.OK,  
                    isAuthenticated = true,  
                    isLibraryAdmin = User.IsInRole(@"domain\AdminGroup"),  
                    username = User.Identity.Name.Substring(User.Identity.Name.LastIndexOf(@"\") + 1)  
                });  
            }  
            else  
            {  
//This code never execute as we have used Authorize attribute on action method  
                return Request.CreateResponse(HttpStatusCode.OK, new  
                {  
                    status = (int)HttpStatusCode.BadRequest,  
                    isAuthenticated = false,  
                    isLibraryAdmin = false,  
                    username = ""  
                });  
   
            }  
         }  
    }

This [Authorize] attribute makes sure that action is only executed if the user entered a valid credential otherwise it will display 401 Unauthorized access.

If you get"Authorization has been denied for this request" check this post

So, based on the provided solution from above Stack overflow question needs a bit more configuration for windows authentication () in “applicationhost.config” file which resides at Project root directory “.vs\config”, this folder is hidden you must enable the show all hidden files and folder option.

<windowsAuthentication enabled="true">
   <providers>
       <add value="Negotiate" />
       <add value="NTLM" />
       </providers>
</windowsAuthentication>

CORS is enabled from the server side. Now while requesting API, pass flag withCredentials: true from the frontend.

For jQuery Ajax you must pass the request as below.

$.ajax({url:apiURL ,xhrFields: {   withCredentials: true }, success:successHandler }); 

For Reactjs you must pass the request as below.

private options = new RequestOptions({ withCredentials: true });  
this.http.get(this.baseUrl, this.options) 

Ajax Snippet

var apiURL="http://localhost:51647/api/AuthenticateUser";  
$.ajax({
    url:apiURL ,
    xhrFields: { withCredentials: true }, 
    success: function(result){  
    console.log(JSON.stringify(result));  
}});    

Edited

Let's enabling CORS

Preflight requests (OPTIONS)

<system.web>
    <authentication mode="Windows" />
    <authorization>
        <allow verbs="OPTIONS" users="*"/>
        <deny users="?" />
    </authorization>
</system.web>

global.asax.cs

protected void Application_AuthenticateRequest(object sender, EventArgs e)
{
    if (Context.Request.HttpMethod == "OPTIONS")
    {
        if (Context.Request.Headers["Origin"] != null)
            Context.Response.AddHeader("Access-Control-Allow-Origin", Context.Request.Headers["Origin"]);

        Context.Response.AddHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, MaxDataServiceVersion");
        Context.Response.AddHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
        Context.Response.AddHeader("Access-Control-Allow-Credentials", "true");

        Response.End();
    }
}

CORS enabling

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        // all requests are enabled in this example. SupportsCredentials must be here to allow authenticated requests          
        var corsAttr = new EnableCorsAttribute("*", "*", "*") { SupportsCredentials = true };
        config.EnableCors(corsAttr);
    }
}

protected void Application_Start()
{
    GlobalConfiguration.Configure(WebApiConfig.Register);
}

example

Community
  • 1
  • 1
  • I have two controllers, to the first one I added [Authorize] and did not add in the second one. So only second is returning result, the first is still not returning any result. – user1630575 May 24 '19 at 12:48
  • @user1630575 have un enable CORS? If you open the browser console have you have any errors? In the network tab what the detail of the request? – Alexandre Rodrigues May 24 '19 at 13:33
  • Just edited response in my question above because the description was long. Do you think commenting the `[EnableCors(origins: "*", headers: "*", methods: "*", SupportsCredentials = true)]` is a risk to code ? – user1630575 May 24 '19 at 14:08
  • @user1630575 if you have setup the configuration for cors on ` web.config` and with commented `EnableCors` works, that's totally fine. no worries. can you accept my answer :) – Alexandre Rodrigues May 24 '19 at 14:27
  • I just realized that only "Get" requests are working and "Post" is not. – user1630575 May 24 '19 at 16:04
  • that's strange... let's try add more configuration to your `web.config` and `WebApiConfig` – Alexandre Rodrigues May 24 '19 at 17:12
  • Well, a lot of setting changes to and fro but finally it is working. Few from you and few from other posts so I'll mark your response as answer. – user1630575 May 30 '19 at 15:14