Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cookies disappear after redirect

I have:

1) A client side app that has its own domain: http://client.com

2) A server side app that has a separate domain: http://server.com

Now,

the scenario is:

1) Opening http://client.com/home in the browser, which displays an HTML page.

2) http://client.com/home redirects to http://server.com/login

3) http://server.com/login stores a cookie 'auth' and sends a redirect instruction to http://client.com/welcome

Response:

Access-Control-Allow-Origin: *

Connection: keep-alive

Content-Length: 104

Content-Type: text/html; charset=utf-8

Date: Wed, 16 Jan 2019 10:47:11 GMT

Location: http://client.com/welcome

Set-Cookie: auth=1479da80-197c-11e9-ba74-59606594e2fb; Path=/

Vary: Accept

X-Powered-By: Express

4) The browser receives the response, which does contain the cookie 'auth'

5) The browser redirects itself to http://client.com/welcome

6) 'auth' cookie is sent to http://client.com/welcome

Request:

Cookie: auth=1479da80-197c-11e9-ba74-59606594e2fb

7) http://client.com/welcome returns HTML but does not return the cookie 'auth'

enter image description here

enter image description here

8) http://client.com/welcome makes an AJAX request to http://server.com/data (CORS enabled), but the cookie 'auth' is not sent

9) http://server.com/data doesn't recognize the user because there is no cookie

The client side is an angular app hosted by Node.js

Edit:

As suggested, I've added to the response of server.com:

Access-Control-Allow-Credentials: true

but nothing has been changed.

Relevant client side code:

const headerOptions = new HttpHeaders({
      'Content-Type': 'application/json', 'withCredentials': 'true', 'Access-Control-Allow-Origin': 'true', 'Access-Control-Allow-Credentials': 'true'
    });

this.httpClient.get<any>(this.baseUrl + "data", { headers: headerOptions }).subscribe((res) => {
like image 686
Alon Avatar asked Jan 16 '19 10:01

Alon


2 Answers

You should use the withCredentials option when sending your ajax request to your http://server.com and your server.com should have the Access-Control-Allow-Credentials set to true.

Example code in Node.JS server:

var cors = require('cors');
var corsOptions = {
    origin: '*',
    credentials: true };

app.use(cors(corsOptions));

More on this here: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Credentials

Example code in Angular.JS client
import {RequestOptions, Request, RequestMethod} from '@angular/http';

const options = new RequestOptions({
  method: RequestMethod.Post,
  url: 'https://google.com',
  withCredentials: true
});

More on this here: https://angular.io/api/http/RequestOptions

Also check this out: https://github.com/angular/angular/issues/24283 - it looks like a particular version of Angular had problems with this flag, so unless you're using a more up-to-date version, you might need to set the header explicitly.

The reasoning of this is, unless the server explicitly tells the client "I will accept cookies (set previously on my domain) passed by another domain" - accepting the cookies would be a security issue. More here: https://en.wikipedia.org/wiki/Cross-site_request_forgery

like image 51
Mavi Domates Avatar answered Nov 06 '22 04:11

Mavi Domates


Your description of what is happening does not seem right.

  • http://server.com/login stores a cookie 'auth' and sends a redirect instruction to http://client.com/welcome
  • 'auth' cookie is sent to http://client.com/welcome

That is not (or at least should not be) what is happening. When the browser requests http://server.com/login and gets back in the response a Set-Cookie header, the cookie is set on and restricted to the server.com domain, even if the response is a redirect. If you are seeing the 'auth' cookie sent to client.com then that is a cookie that client.com previously set.

Anyway, it seems that what you really care about is

  • http://client.com/welcome makes an AJAX request to http://server.com/data (CORS enabled), but the cookie 'auth' is not sent

There are a bunch of reasons this can happen.

  • CORS. You mentioned it was CORS enabled, but for the sake of others reading this, you must have the following CORS headers set on server.com
    • Access-Control-Allow-Origin: http://client.com
    • Access-Control-Allow-Credentials: true
  • Note that you cannot get away with using a wildcard for Access-Control-Allow-Origin when you are sending credentials. Also note that the origin has to be an exact match, including scheme (http or https). In practice, what servers generally do is read the Origin header of the request, check it against a white list, and if it allowed, copy the Origin header value from the request to the Access-Control-Allow-Origin header in the response.
  • You must set xhr.withCredentials = true in your XHR request. (See MDN for more details.)

Then after you have done all that, you have one other hurdle in your way. Because you are on client.com and trying to send a cookie to server.com, the server.com cookie is considered a "third-party" cookie. AFAIK all the major browsers have a setting that blocks third-party cookies for privacy, because they are most often used by trackers to gather marketing data for advertising. I believe most of them block third-party cookies by default, but I am not sure of that. For sure lots of people have set their browsers to block third-party cookies.

So you have to tell your visitors to configure their browser to allow third-party cookies from server.com.

BTW, it is not safe to set a cookie on a redirect to a different domain. While it is allowed under the specification AFAIK, there have been issues with browser support. See, for example, this Chrome bug.

like image 44
Old Pro Avatar answered Nov 06 '22 04:11

Old Pro