简体   繁体   中英

How to fix reactjs web app to .net webapi authorization error

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 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

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM