djoos / EscapeWSSEAuthenticationBundle

Symfony bundle to implement WSSE authentication
http://symfony.com/doc/current/cookbook/security/custom_authentication_provider.html
137 stars 59 forks source link

Allow OPTIONS request from configured clients #6

Closed maria-p closed 12 years ago

maria-p commented 12 years ago

If you're making a call to your app from another domain, OPTIONS preflight request is made before your actual call to ask if your app allows that domain origin and accept X-WSSE headers. (http://www.w3.org/TR/cors/) In this PR I've forced WSSE listener to answer correctly on that OPTIONS request.

djoos commented 12 years ago

Hi Maria,

thanks for your contribution - we will look into the PR and will get back to you ASAP.

Have a great weekend!

Kind regards, David

djoos commented 12 years ago

Hi Maria,

thanks again for your pull request.

We discussed this internally and at first sight we have to conclude that, even though useful, we are not going to implement Cross-Origin Resource Sharing as CORS is part of WSSE. From our point of view it would make sense to develop a separate VendornameCORSBundle, which will do CORS-specific tasks.

There are a few things on our wanted list for our REST API implementations (which use the EscapeWSSEAuthenticationBundle), like eg. Message Integrity Check - but we also decided that those additional items need to go into their own, separate bundle. Symfony2 bundle-wise MIC would be similar to WSSE or CORS as extra header information is pushed and checked...

We'd love to hear your thoughts - thanks in advance!

Kind regards, David

maria-p commented 12 years ago

Hi David,

thank you for your reply.

I can move that functionality to separate bundle, and moreover, there is such bundle (https://github.com/nelmio/NelmioCorsBundle), but I made this PR because I believe that it belongs here.

In most cases when you're making API with WSSE authentication, you're going to have some clients talking to that API, and there are big chances that client will have different domain name. And in that case that whole bundle won't work If we're not having ability to accept WSSE header.

Thanks, Maria

djoos commented 12 years ago

Hi Maria,

thanks for your feedback!

Now, I'm not familiar with the NelmioCorsBundle, but according to me this should just do the trick: each bundle's listener handles its own specific headers (WSSE/CORS/MIC/...), eg. "if($request->headers->has('X-WSSE'))" in EscapeWSSEAuthenticationBundle/Security/Firewall/Listener.php, line 75. If each bundle does this for its own specific headers, I don't see any issue with mixing and matching different bundles to get to an application that implements WSSE, CORS, MIC,...

Let me know your thoughts!

Kind regards, David

maria-p commented 12 years ago

The problem is that preflight OPTIONS request that is made before real request from client does not contain X-WSSE header itself. It just asking that this header is allowed on API side. But this request is caught by bundles firewall and considered unauthorised before I'm able to pass my headers.

djoos commented 12 years ago

Hi Maria,

"The problem is that preflight OPTIONS request that is made before real request from client does not contain X-WSSE header itself" So, what about pushing the X-WSSE header in the OPTIONS request?

CORS allows the usage of custom headers, by explicitly allowing them. The OPTIONS request can specify the headers it wants to add: eg. OPTIONS http://othersite.com/method.json Origin: http://mysite.com Access-Control-Request-Method: GET Access-Control-Request-Headers: X-WSSE ...and then you can just add in the X-WSSE header on the OPTIONS request, without the EscapeWSSEAuthenticationBundle even knowing anything about your application's use of CORS. (see http://www.tsheffler.com/blog/?p=428 > "Custom Headers" for more information)

FYI: the "Access-Control-Request-Headers" feature seems to have been put into the NelmioCorsBundle a couple of months ago.

What do you think? Thanks in advance for your feedback!

Kind regards, David

maria-p commented 12 years ago

Hi, David

Thank you for digging into the problem.

It surely will be better to change my if ($request->getMethod() == 'OPTIONS') { condition to smth like if (in_array('x-wsse', $requestHeaders)) { (just didn't notice 'Access-Control-Request-Headers' header before...)

But I don't see a way to add X-WSSE header itself to the OPTIONS request (I would be happy to..), because as I understand it, this OPTIONS request is supposed to ask server about the possibility of adding that X-WSSE header. In fact, I don't event send that OPTIONS request myself, it is done when I'm asking jquery to fetch data from server with another domain name.

Please tell me if I'm wrong, or if you agree, I will commit a fix that I've described above.

Thanks, Maria

djoos commented 12 years ago

Hi Maria,

sorry for the late reply - last week has been very busy...

Correct me if I'm wrong, but from my point of view no change needs to be made to this (EscapeWSSEAuthenticationBundle), as adding the additional X-WSSE header is all done on the NelmioCorsBundle side of things. As far as pushing the actual X-WSSE header in the request: which jQuery code to you make use of to fetch the data from another server? It might be worth looking into eg. the jQuery.ajax/jQuery.post parameters (what do you use exactly?):

eg. (some dummy information)

    $.ajax({
      beforeSend: function(XMLHttpRequest) {
            XMLHttpRequest.setRequestHeader("Authorization", "Basic eWldWRbmdZ21haWwu29OmV2YTldwEs");
      },
      type: "GET",
      url: url,
      dataType: "jsonp",
      success: function(data){
            alert( "Data Saved: " + data["friends_count"] );
       }
    });

=>

...and instead of the "Authorization"-header, push the "X-WSSE"-header: XMLHttpRequest.setRequestHeader("X-WSSE", yourwsseheader goes over here);

What do you think?

Kind regards, David

P.S. Once we figure this one out, it might be good to get in touch with ptitcub (http://forum.jquery.com/topic/authentication-with-symfony-wsse) as (s)he gave up, but I don't see why it shouldn't be possible...

maria-p commented 12 years ago

David,

I think we have some misunderstanding here :) It is correct, that if I use NelmioCorsBundle, I don't need to add anything in WSSE bundle, as I can specify there what headers from what domains I want to be able to accept. But I think that WSSE bundle itself should allow to do everything related to WSSE - in particular it should allow to accept WSSE header.

If you make a jquery request you described above you will get 2 requests to you WSSE secured application: first is OPTIONS, asking about ability of accepting WSSE header, and if the first will have 200 status response, second GET request will be made with WSSE header info attached.

But what is happening now is that OPTIONS request is caught by your WSSE bundle firewall, considered unauthorised (because it don't have WSSE header, it is just asking about ability to accept it) and this request is denied. And second GET request is not made, because the first one don't have 200 status response. So the real problem is that OPTIONS request.

Regards, Maria

djoos commented 12 years ago

Hi Maria,

thanks for your feedback!

"But I think that WSSE bundle itself should allow to do everything related to WSSE - in particular it should allow to accept WSSE header." I'm not 100% sure whether I understood what you meant - at the moment the WSSEAuthenticationBundle does only things related to WSSE, nothing more, which from my point of view is correct.

"But what is happening now is that OPTIONS request is caught by your WSSE bundle firewall, considered unauthorised (because it don't have WSSE header, it is just asking about ability to accept it)" Not sure why you don't have a WSSE header... Just to be clear (sorry if I misunderstood): the OPTIONS request is not asking about the ability to accept the WSSE header, but the ability to accept the actual request past the preflight OPTIONS request. By allowing the X-WSSE header on the OPTIONS request and pushing the actual X-WSSE header, you're basically mixing both CORS AND WSSE together, see my previous post + CORS/NelmioCorsBundle's "Access-Control-Request-Headers". When pushing the actual WSSE header along with the OPTIONS request, the WSSEAuthenticationBundle shouldn't complain unless the header is wrong.

This is the only way that I can see the CORS OPTIONS request succeed, while also making use of WSSE... I can try to set up a dummy project that implements this idea - or maybe you could strip any non-public stuff out of your project and share it so I can have a look?

Let me know your thoughts!

Kind regards, David

maria-p commented 12 years ago

Hi David,

I'm unable to attach original WSSE header to OPTIONS request. WSSE header is attached to GET request I'm making, and OPTIONS request is checking the ability to accept my GET request.

Here is how I'm making a request:

        $.ajax({
            type: "GET",
            crossDomain: true,
            dataType: "json",

            headers: {'X-WSSE': wsseHeader(username, password)},
            url: restapiUrl + '/test.json',
            success: function(message, text, response) {
                console.log(message);
            },
            error: function(response, status, error) {
                console.log(response);
            }
        });

Here is the request that is actually made

Request URL: http://my-api/restapi/test.json
Request Method: OPTIONS
Status Code: 401 Unauthorized

Request Headers
OPTIONS /restapi/test.json HTTP/1.1
Host: my-api
Connection: keep-alive
Access-Control-Request-Method: GET
Origin: http://my-client
User-Agent: Mozilla/5.0 (X11; Linux i686) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.46 Safari/536.5
Access-Control-Request-Headers: origin, accept, x-wsse
Accept: */*
Referer: http://my-api/login
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3

As you can see OPTIONS request doesn't conatin WSSE header itself, but it specify what headers my original GET request will pass:

Access-Control-Request-Headers: origin, accept, x-wsse

But my-api app redirects it to login page, as the request is considered unauthorised as it don't contain WSSE header.

Please tell me if I'm somehow wrong in the way I'm making a request, and if there is a way to attach WSSE header to my OPTIONS request so that it could be authorised.

Thanks, Maria

djoos commented 12 years ago

Hi Maria,

sorry for not getting back to you earlier - I haven't forgotten about this, but I think the only way to fix this is by me setting up a dummy application and have a go. Unfortunately I haven't got the time for this right now, but will try to get to it ASAP...

Kind regards, David

peschee commented 12 years ago

Any updates on this @djoos ? /cc @maria-plyasunova

maria-p commented 12 years ago

@peschehimself, I ended up with using NelmioCorsBundle mentioned above. I'm still not sure that it doesn't belong here, but I'm also not sure in the opposite. I believe that PR can be closed.

peschee commented 12 years ago

Yes, that's what I ended up using as well, thanks. I believe since the OPTIONS case is rather related to CORS in general, and since the cors bundle is quite flexible, this PR does not necessarily belong to the WSSE bundle only.

djoos commented 12 years ago

Hi,

@maria-plyasunova: sorry for still not having been able to put something together. We're extremely busy rewriting a legacy application and it hasn't been possible to free up time for this particular issue.

Having read both of your comments I think we all agree that the WSSE bundle is probably not the place for the PR. Is it ok for me to go ahead and close it?

Thanks in advance for your feedback!

Kind regards, David

peschee commented 12 years ago

Yes, the CORS bundle solves the missing piece from this PR.

djoos commented 12 years ago

If at any point any changes are required to the WSSE bundle, don't hesitate to get in touch!

Kind regards, David