Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JQuery cross domain basic auth call

I have built a REST API backend whith Spring MVC and secured with basic Auth with Spring Security.

I would like to do cross domain ajax call to the REST API from Javascript clients. I don't want to use JSONP because i don't want to be limited to GET calls. I use CORS and i have put the right headers on server side.

Suppose my REST API is on domain localhost:8087 and my client on localhost:8086, which is cross domain call.

In my Javascript client, i make ajax call with jQuery :

<script>
        $.ajax ({
            url: "http://localhost:8087/SpringMVC/users/user1",
            beforeSend: function (xhr) { xhr.setRequestHeader ("Authorization", "Basic xxxxxxxxxxxx"); },
            success: function(val) { console.log(val); alert("success" + val); },
            error: function(val) { console.log(val); alert("error" + val); }
        });
</script>

My problem is that jQuery does not send the Authorization header in the HTTP request and i don't know why. I don't understand because i do it in the beforeSend method, so it should be inside the HTTP request. Result : i have a 401 error.

When i try the script from the same domain localhost:8087, which is not cross domain anymore, i have no problem.

How is it possible ?

My script is just a test. I don't intend to put my username/password on client side. But i want to test how to do ajax calls to a basic auth protected REST API. I imagine i have to send on server side to be secure my username/password, the REST API sends me back a cookie and i don't need to pass the username/password anymore for my next ajax calls to the REST API. Am i right ?

I have tested my REST API with Chrome Advanced REST client and it is working like that. For the first request i need to pass the authorization header. Then it is not needed. Should it work also like that with my javascript web client ? I intend to use Node.JS with Backbone to build it.

Thanks a lot.

EDIT2 : Seems really to be a CORS Browser problem. I have added the header Access-Control-Allow-Methods for OPTIONS method on server side and it works on Chrome. I have access to the JSON response with no error anymore. But i still need to use the authorization header for the next requests. How to tell jQuery to use the cookie sent ?

And when i try with Firefox 11, i have no access to the json response and i have the error :

"NetworkError: 401 Non-Autorisé - http://localhost:8087/SpringMVC/users/user1"
like image 315
rico Avatar asked Apr 08 '12 14:04

rico


2 Answers

Apparently, Chrome and Firefox treat Cross Domain requests a bit differently. Before doing the cross domain request, they do what is called a 'preflight' request with HTTP OPTIONS method. The difference between Chrome and Firefox is that Chrome sends also the Authorization header with the credentials whereas Firefox do not.

Then, it remains a Spring Security config problem. My url /users/* is secured for all HTTP methods, including OPTIONS. In the case of Firefox, as the Authorization header is not sent, my request is not authorized. If i restrict my secure url /users/* to the GET method only, then it perfectly works for Firefox. So i had to add only that in my Spring security config :

<intercept-url pattern="/users/*" access="isAuthenticated()" method="GET"/>

Afterwards, i have the choice : i can add other methods to be secured in the intercept-url, except OPTIONS, or i can restrict the HTTP method call to GET in my Spring MVC controller, which will even treat my OPTIONS calls according to the Javadoc. I chose the second solution. But if somebody finds a solution to force Firefox to send the credentials like Chrome, it would be great and i would chose this one.

like image 94
rico Avatar answered Dec 03 '22 07:12

rico


Another option for the Spring Security configuration scenario presented by rico would be:

<http ... use-expressions="true">
    <intercept-url pattern="/users/*" access="permitAll" method="OPTIONS"/>
    <intercept-url pattern="/users/*" access="isAuthenticated()"/>
    ...
</http>

HTTP OPTIONS requests will always pass through authentication and any other HTTP method will not.

Note the use-expressions XML attribute set to true in <http> element. Spring Security will then expect the access attributes of the <intercept-url> elements to contain Spring EL expressions, like permitAll and isAuthenticated().

like image 28
José Andias Avatar answered Dec 03 '22 08:12

José Andias