Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Bad Request returned only with IE 11 on remote machine for XHR that passes credentials

I've been trying to figure out what piece I'm missing when making an XHR to an MS Web API that requires windows auth.

This request works locally on both Chrome and IE 11 as well as Chrome on a remote box (not the server). The problem is IE 11 on the remote box.

According to the dev tools, IE makes 3 requests. The first two requests pass an Authorization: Negotiate header and return 401s (preflights for CORS?). However, the third returns a 400. It seems like it fails to authenticate in a way that I don't understand, especially since other browsers and local tests work.

The API is a self-hosted OWIN console app. Here's the startup:

public void Configuration(IAppBuilder appBuilder)
{
    appBuilder.UseCors(CorsOptions.AllowAll);

    var listener = (HttpListener)appBuilder.Properties["System.Net.HttpListener"];

    if (listener != null)
    {
        listener.AuthenticationSchemeSelectorDelegate = request =>
        {
            if (string.Compare(request.HttpMethod, "OPTIONS", StringComparison.OrdinalIgnoreCase) == 0)
            {
                return AuthenticationSchemes.Anonymous;
            }
            else
            {
                return AuthenticationSchemes.IntegratedWindowsAuthentication;
            }
        };
    }

    var config = new HttpConfiguration();
    config.Routes.MapHttpRoute("DefaultApi", "api/{controller}/{action}/{id}", new { id = RouteParameter.Optional });
    appBuilder.UseWebApi(config);
}

Here's the client-side XHR call:

var request = new XMLHttpRequest();
request.open('GET', 'http://xxxx:9000/api/test/something', true);
request.timeout = 10000;
request.withCredentials = true;
request.onload = function() {
    if (request.status >= 200 && request.status < 400) {
        console.log('done');
    } else {
        console.error('error');
    }
};

request.onerror = function() {
    // There was a connection error of some sort
};

request.send();

And the API Controller:

[Authorize]
[RoutePrefix("api/test")]
public class TestController : ApiController
{
    [HttpGet]
    [ActionName("something")]
    public IHttpActionResult Something()
    {
        return Ok();
    }
}

2 Requests that return 401 and the one that returns a 400:

First 401:

   Request URL: http://xxxx:9000/xxxx
   Request Method: GET
   Status Code: 401 / Unauthorized

Request Headers
   Accept: */*
   Accept-Encoding: gzip, deflate
   Accept-Language: en-US
   Authorization: Negotiate [token]
   Connection: Keep-Alive
   Host: xxxx:9000
   Referer: http://xxxx/xxxx.html
   User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 10.0; WOW64; Trident/7.0; .NET4.0C; .NET4.0E; .NET CLR 2.0.50727; .NET CLR 3.0.30729; .NET CLR 3.5.30729; InfoPath.3)

Response Headers

   Content-Length: 0
   Date: Fri, 22 Dec 2017 14:03:09 GMT
   Server: Microsoft-HTTPAPI/2.0
   WWW-Authenticate: Negotiate [token]

-------------
Second 401

   Request URL: http://xxxx:9000/xxxx
   Request Method: GET
   Status Code: 401 / Unauthorized

Request Headers

   Accept: */*
   Accept-Encoding: gzip, deflate
   Accept-Language: en-US
   Authorization: Negotiate [token]
   Connection: Keep-Alive
   Host: xxxx:9000
   Referer: http://xxxx/xxxx.html
   User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 10.0; WOW64; Trident/7.0; .NET4.0C; .NET4.0E; .NET CLR 2.0.50727; .NET CLR 3.0.30729; .NET CLR 3.5.30729; InfoPath.3)

Response Headers

   Content-Length: 0
   Date: Fri, 22 Dec 2017 14:03:09 GMT
   Server: Microsoft-HTTPAPI/2.0
   WWW-Authenticate: Negotiate [token]

-----------
400

   Request URL: http://xxxx:9000/xxxx
   Request Method: GET
   Status Code: 400 / Bad Request

Request Headers

   Accept: */*
   Accept-Encoding: gzip, deflate
   Accept-Language: en-US
   Authorization: Negotiate [token]
   Connection: Keep-Alive
   Host: xxxx:9000
   Referer: http://xxxx/xxxx.html
   User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 10.0; WOW64; Trident/7.0; .NET4.0C; .NET4.0E; .NET CLR 2.0.50727; .NET CLR 3.0.30729; .NET CLR 3.5.30729; InfoPath.3)

Response Headers

   Content-Length: 0
   Date: Fri, 22 Dec 2017 14:03:12 GMT
   Server: Microsoft-HTTPAPI/2.0
like image 426
Jordan Avatar asked Dec 20 '17 01:12

Jordan


2 Answers

Trusted site

My guess is that the remote machine is restricted by domain policies and your site is not seen as a trusted site on that machine.

Add (or ask to add, if policies are set by IT) your website the appropriate security zone (Local intranet or Trusted sites) and the problem should be fixed.

Document mode

While you're at it, also see if they're pushing any old document mode for IE11 to run in).

Enable CORS for WebAPI

I see you already call UseCors on OWIN's IAppBuilder, but additionally you can try to enable CORS on the HttpConfiguration instance:

config.EnableCors(new EnableCorsAttribute("*", "*", "GET, POST, OPTIONS, PUT, DELETE"));
like image 123
huysentruitw Avatar answered Sep 28 '22 15:09

huysentruitw


I did the following to pass a HttpOnly cookie/token using XHR:

  1. Use axios or similar library on the client and use a config containing withCredentials:

        import axios from 'axios';
        const config = {
            method: 'get',
            url: 'http://xxxx:9000/api/test/something',
            timeout: 5000,
            withCredentials: true
        };
        //if (setCookie === true) {
            //config.mode = 'no-cors'; // <= THIS IS IMPORTANT IF SETTING COOKIE!
        //}
        const response = await axios(config);
    
  2. On the server set cors options:

    const corsOptions = {
        origin: 'http://xxxx:9000', // Set explicit origin!
        methods: ['GET', 'POST', 'PUT', 'DELETE'], // Allow method(s)!
        credentials: true //enable server to see credentials.
    };
    

source: xhr.spec.whatwg.org

I hope it works for you too, it took some time before it worked. If it fails then window.postMessage long shot might work. Finally if you are using windows it is also possible it is a local problem:

Whatever security zone your website is in (Internet Options > Security) make sure you ENABLE the following setting in your zone: Miscellaneous > Access data sources across domains.

like image 37
Gillsoft AB Avatar answered Sep 28 '22 15:09

Gillsoft AB