Sending Credentials with Cross-Domain Posts

Sending credentials with cross-domain posts?

Functionality is supposed to be broken in jQuery 1.5.

Since jQuery 1.5.1 you should use xhrFields param.

$.ajaxSetup({
type: "POST",
data: {},
dataType: 'json',
xhrFields: {
withCredentials: true
},
crossDomain: true
});

Docs: http://api.jquery.com/jQuery.ajax/

Reported bug: http://bugs.jquery.com/ticket/8146

Cross domain jQuery ajax call with credentials

My best guess is that this is a problem not with your Javascript but with your CORS configuration. Did you set up your server with the Access-Control-Allow-Credentials: true header? http://www.w3.org/TR/cors/#access-control-allow-credentials-response-header

Also note that, even when the allow-credentials header is set, the browser will not allow responses to credentialed requests if Access-Control-Allow-Origin is *, according to these docs: https://developer.mozilla.org/en-US/docs/HTTP/Access_control_CORS?redirectlocale=en-US&redirectslug=HTTP_access_control#Requests_with_credentials.

Edit: Since the OP has the CORS headers set up properly, the problem seems to be that the server is rejecting OPTIONS requests with a 403 status code. OPTIONS requests (known as the "preflight request") are sent before certain cross-domain requests (such as POSTs with application/xml content types), to allow the server to notify the browser of what types of requests are allowed. Since the browser doesn't see the 200 response that it expects from the OPTIONS request, it doesn't fire the actual POST request.

JQuery $.post cross domain and credentials

You can use jQuery.ajaxSetup() to set default options that each ajax request will use (including $.post and $.get)

$.ajaxSetup({
crossDomain: true,
xhrFields: {
withCredentials: true
},
username: 'test',
password: 'test'
});

$.post('http://example.com/server/api.php', {
username: 'test',
password: 'test'
}, function (d) {
$('body').html(d.status);
}, 'json');

Also the warning regarding this API

Note: The settings specified here will affect all calls to $.ajax or
Ajax-based derivatives such as $.get(). This can cause undesirable
behavior since other callers (for example, plugins) may be expecting
the normal default settings. For that reason we strongly recommend
against using this API. Instead, set the options explicitly in the
call or define a simple plugin to do so.

from jQuery documentation

Set cookies for cross origin requests

Cross site approach

To allow receiving & sending cookies by a CORS request successfully, do the following.

Back-end (server) HTTP header settings:

  • Set the HTTP header Access-Control-Allow-Credentials value to true.

  • Make sure the HTTP headers Access-Control-Allow-Origin and Access-Control-Allow-Headers are set. Don't use a wildcard *. When you set the allowed origin make sure to use the entire origin including the scheme, i.e. http is not same as https in CORS.

For more info on setting CORS in express js read the docs here.

Cookie settings:
Cookie settings per Chrome and Firefox update in 2021:

  • SameSite=None
  • Secure

When doing SameSite=None, setting Secure is a requirement. See docs on SameSite and on requirement of Secure. Also note that Chrome devtools now have improved filtering and highlighting of problems with cookies in the Network tab and Application tab.

Front-end (client): Set the XMLHttpRequest.withCredentials flag to true, this can be achieved in different ways depending on the request-response library used:

  • ES6 fetch() This is the preferred method for HTTP. Use credentials: 'include'.

  • jQuery 1.5.1 Mentioned for legacy purposes. Use xhrFields: { withCredentials: true }.

  • axios As an example of a popular NPM library. Use withCredentials: true.

Proxy approach

Avoid having to do cross site (CORS) stuff altogether. You can achieve this with a proxy. Simply send all traffic to the same top level domain name and route using DNS (subdomain) and/or load balancing. With Nginx this is relatively little effort.

This approach is a perfect marriage with JAMStack. JAMStack dictates API and Webapp code to be completely decoupled by design. More and more users block 3rd party cookies. If API and Webapp can easily be served on the same host, the 3rd party problem (cross site / CORS) dissolves. Read about JAMStack here or here.

Sidenote

It turned out that Chrome won't set the cookie if the domain contains a port. Setting it for localhost (without port) is not a problem. Many thanks to Erwin for this tip!

Cross Origin Resource Sharing with Credentials

Two thoughts:

1) are you also including the "Access-Control-Allow-Credentials: true" header? This is needed for passing cookie credentials (and the corresponding XHR client must set .withCredentials = true)

2) Have you tried the suggestion from your link and only include the origin for the current request. For example, if a request comes in with the header "Origin: http://blog.example.com", you would respond with "Access-Control-Allow-Origin: http://blog.example.com", and not a list of origins. This requires a little more work on your server side implementation.

3) One other thought, you mention that you have a single login form that must be shared by various domains. Well, if it is a standard HTML form, you can do a regular form-post across domains. You don't need to use CORS. Just set the "action" property of the form to the url you wish to post to. For example:

<form name="login" action="http://login.example.com/doLogin">

How to make XMLHttpRequest cross-domain withCredentials, HTTP Authorization (CORS)?

I've written an article with a complete CORS setup.

I found several issues that can result in this problem:

  1. The Access-Control-Allow-Origin cannot be a wildcard if credentials are being used. It's easiest just to copy the Origin header of the request to this field. It's entirely unclear why the standard would disallow a wildcard.
  2. Firefox caches the Access-Control results even if you clear the cache (perhaps for the session). Restarting forced it to do a new OPTIONS request. To aid in debugging I added the header Access-Control-Max-Age: 1
  3. The username/password of the open command is apparently not usable as the credentials. You must add an Authorization header yourself. xhr.setRequestHeader( 'Authorization', 'Basic ' + btoa( user + ':' + pass ) )

Overall the withCredentials system is rather braindead. It's easier to simply write a server that accepts the authorization as part of the body of the request.



Related Topics



Leave a reply



Submit