Skip to content
This repository has been archived by the owner on Jun 7, 2019. It is now read-only.

Can not set .withCredentials using "mixin" #79

Closed
fidian opened this issue Jul 8, 2014 · 12 comments
Closed

Can not set .withCredentials using "mixin" #79

fidian opened this issue Jul 8, 2014 · 12 comments
Milestone

Comments

@fidian
Copy link

fidian commented Jul 8, 2014

I've read issue #40 and I'm trying to get the withCredentials flag passed by using a mixin. My client is wrapped using code like this:

.wrap(defaultRequest, {
    headers: {
        Accept: "application/hal+json,application/json",
        "Content-Type": "application/json"
    },
    mixin: {
        withCredentials: true
    }
})

However, this can't work. If you look at xhr.js on line 81 you will see that the client is created, opened, then the mixins are all added. Unfortunately, the withCredentials property needs to be set before the call to client.open().

Would moving the client.open() line down to after the mixins allow this problem to get solved?

I've been trying to search for more information of using this REST library with CORS-compliant browsers and found no examples of it working elsewhere. In fact, I've only seen issue #40, which didn't list a clear chunk of code defining how one could make the requests work. Am I doing this the wrong way and should the documentation get updated? Or perhaps is my solution correct (and maybe documentation could still get written)?

@scothis
Copy link
Member

scothis commented Jul 8, 2014

@fidian It looks like your suggestion should work, I'll give it a try.

I've used rest.js successfully in CORS environments, however, I have not had to use withCredentials. So that specific bit of functionality is admittedly under tested. I'd gladly accept additional documentation, or perhaps a blog post.

scothis added a commit that referenced this issue Jul 8, 2014
As suggested by @fidian in #79, withCredentials needs to be mixed into
the request before opening.
@scothis
Copy link
Member

scothis commented Jul 9, 2014

@fidian I've been fighting our test environment recently which is making it difficult to verify the correctness of this change. Until I get things sorted out, I'd rather not merge the change.

What you can do in the mean time is create your own fork of the rest/client/xhr module and install that fork as the default client. Let me know how it goes.

@fidian
Copy link
Author

fidian commented Jul 9, 2014

I have another thing setting the default engine so that IE8 works with CORS. It supplies libxdr when the .withCredentials property does not exist. So I keep track of a CORS engine and a non-CORS engine. This is fed to an interceptor that detects cross-origin requests and provides an appropriate engine to your library.

So I just replaced a bit of code with a wrapper. When the .withCredentials property exists and when performing a CORS request, this is what's provided to your library as the engine:

function CorsXmlHttpRequest() {
    var xhr;

     if (!(this instanceof CorsXmlHttpRequest)) {
        return new CorsXmlHttpRequest();
    }

    xhr = new DefaultEngine();
    xhr.withCredentials = true;

    return xhr;
}

@fidian
Copy link
Author

fidian commented Jul 9, 2014

Sorry. I had forgotten to mention how I simulated this test. I started a new express server and made it always set a cookie with a random number. I needed to also set the Access-Control-Allow-Origin header to request.origin and Access-Control-Allow-Credentials to true. Next I have the JS and HTML on site 1 and the express server on site 2 to force CORS requests. I just make two calls to GET / and check if the second one sent the cookie.

The above is all from memory. Forgive me if I have headers slightly wrong. I hope this helps your diagnostic effort. If you have tests and they hit against another server, this could be something that you can automate and add to the test suite.

@scothis
Copy link
Member

scothis commented Jul 9, 2014

@fidian
Copy link
Author

fidian commented Jul 9, 2014

Somewhat, but I need the headers and the content-type must not be
text/plain. I can't use XDomainRequest. Instead the server has
implemented pmxdr's solution. An iframe is created and controlled via
window.postmessage. The iframe fires a standard XmlHttpRequest over to the
server at the right endpoint. The response is validated against the CORS
headers and, when those pass, response information is sent back to the
iframe parent. The iframe is then destroyed. A little more overhead but
that means IE8 now supports CORS.

Tyler Akins

On Wed, Jul 9, 2014 at 9:43 AM, Scott Andrews [email protected]
wrote:

@fidian https://github.com/fidian looks like you're trying to
reimplement
https://github.com/cujojs/rest/blob/master/docs/interceptors.md#module-rest/interceptor/ie/xdomain

:)


Reply to this email directly or view it on GitHub
#79 (comment).

@scothis
Copy link
Member

scothis commented Jul 9, 2014

Interesting. You could create a custom client to manage the iframe and create an interceptor similar to the one I linked. You can take a look at the JSONP client and JSONP interceptor for further inspiration as "non-standard" clients.

@fidian
Copy link
Author

fidian commented Jul 9, 2014

I can't use JSONP because the server doesn't support it. I'd rather not write the code that does iframes myself and would rather use libxdr + pmxdr. I've tested it with other projects and have already made sure it works. My interceptor currently looks like this. I use a module loading sytem so interceptor is rest/interceptor, etc.

return interceptor({
    request: function (request) {
        if (request.engine) {
            return request;
        }

        if (new UrlBuilder(request.path, request.params).isCrossOrigin()) {
            request.engine = appConfig.restEngineCors;
        } else {
            request.engine = appConfig.restEngineSame;
        }

        return request;
    }
});

And my app config sets the engine pretty much like this

if ((new XmlHttpRequest()).withCredentials === undefined) {
    appConfig.restEngineCors = XDR;
    appConfig.restEngineSame = XmlHttpRequest;
} else {
    appConfig.restEngineCors = CorsXmlHttpRequest;
    appConfig.restEngineSame = XmlHttpRequest;
}

Personally I see this as a really good solution. Using XDomainRequest doesn't give me headers nor the ability to use the right verbs nor use alternate content types. JSONP is a good way to use GET and one might work POST, but method tunneling will be needed for PUT and DELETE. I'm working with a Hypermedia endpoint and it's trying to stick with the principles as strictly as possible.

My code to set up the engine is quite minimal and would get even smaller once mixins are added before the call to .open().

@scothis
Copy link
Member

scothis commented Jul 9, 2014

Didn't mean to infer that you should switch to JSONP, merely suggesting to look at that client and interceptor for inspiration. JSONP as a technology is very flawed (as is XDomainRequest).

@fidian
Copy link
Author

fidian commented Jul 11, 2014

No worries - I didn't take your message poorly. :-)

scothis added a commit that referenced this issue Dec 24, 2014
As suggested by @fidian in #79, withCredentials needs to be mixed into
the request before opening.
scothis added a commit that referenced this issue Dec 24, 2014
As suggested by @fidian in #79, withCredentials needs to be mixed into
the request before opening. IE requires that timeout be set after opening
the request.

Now request.mixin values are mixed into the XHR object before before and
after calls to xhr.open(). Errors setting properties (like timeout in IE
before calling .open()) are ignored.
scothis added a commit that referenced this issue Dec 24, 2014
As suggested by @fidian in #79, withCredentials needs to be mixed into
the request before opening. IE requires that timeout be set after opening
the request.

Now request.mixin values are mixed into the XHR object before before and
after calls to xhr.open(). Errors setting properties (like timeout in IE
before calling .open()) are ignored.
@scothis
Copy link
Member

scothis commented Dec 30, 2014

@fidian this is fixed on the dev branch and will be in the 1.3.0 release. Please reopen if you continue to see issues

@scothis scothis closed this as completed Dec 30, 2014
@scothis scothis added this to the 1.3.0 milestone Dec 30, 2014
@fidian
Copy link
Author

fidian commented Dec 31, 2014

Thanks Scott, I shall do that.

Tyler Akins

On Tue, Dec 30, 2014 at 12:50 PM, Scott Andrews [email protected]
wrote:

@fidian https://github.com/fidian this is fixed on the dev branch and
will be in the 1.3.0 release. Please reopen if you continue to see issues


Reply to this email directly or view it on GitHub
#79 (comment).

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants