[authentication] Set cookies for cross origin requests

How to share cookies cross origin? More specifically, how to use the Set-Cookie header in combination with the header Access-Control-Allow-Origin?

Here's an explanation of my situation:

I am attempting to set a cookie for an API that is running on localhost:4000 in a web app that is hosted on localhost:3000.

It seems I'm receiving the right response headers in the browser, but unfortunately they have no effect. These are the response headers:

HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://localhost:3000
Vary: Origin, Accept-Encoding
Set-Cookie: token=0d522ba17e130d6d19eb9c25b7ac58387b798639f81ffe75bd449afbc3cc715d6b038e426adeac3316f0511dc7fae3f7; Max-Age=86400; Domain=localhost:4000; Path=/; Expires=Tue, 19 Sep 2017 21:11:36 GMT; HttpOnly
Content-Type: application/json; charset=utf-8
Content-Length: 180
ETag: W/"b4-VNrmF4xNeHGeLrGehNZTQNwAaUQ"
Date: Mon, 18 Sep 2017 21:11:36 GMT
Connection: keep-alive

Furthermore, I can see the cookie under Response Cookies when I inspect the traffic using the Network tab of Chrome's developer tools. Yet, I can't see a cookie being set in in the Application tab under Storage/Cookies. I don't see any CORS errors, so I assume I'm missing something else.

Any suggestions?

Update I:

I'm using the request module in a React-Redux app to issue a request to a /signin endpoint on the server. For the server I use express.

Express server:

res.cookie('token', 'xxx-xxx-xxx', { maxAge: 86400000, httpOnly: true, domain: 'localhost:3000' })

Request in browser:

request.post({ uri: '/signin', json: { userName: 'userOne', password: '123456'}}, (err, response, body) => {
    // doing stuff
})

Update II:

I am setting request and response headers now like crazy now, making sure that they are present in both the request and the response. Below is a screenshot. Notice the headers Access-Control-Allow-Credentials, Access-Control-Allow-Headers, Access-Control-Allow-Methods and Access-Control-Allow-Origin. Looking at the issue I found at Axios's github, I'm under the impression that all required headers are now set. Yet, there's still no luck...

enter image description here

This question is related to authentication cookies cors http-headers localhost

The answer is


Note for Chrome Browser released in 2020.

A future release of Chrome will only deliver cookies with cross-site requests if they are set with SameSite=None and Secure.

So if your backend server does not set SameSite=None, Chrome will use SameSite=Lax by default and will not use this cookie with { withCredentials: true } requests.

More info https://www.chromium.org/updates/same-site.

Firefox and Edge developers also want to release this feature in the future.

Spec found here: https://tools.ietf.org/html/draft-west-cookie-incrementalism-01#page-8


For express, upgrade your express library to 4.17.1 which is the latest stable version. Then;

In CorsOption: Set origin to your localhost url or your frontend production url and credentials to true e.g

  const corsOptions = {
    origin: config.get("origin"),
    credentials: true,
  };

I set my origin dynamically using config npm module.

Then , in res.cookie:

For localhost: you do not need to set sameSite and secure option at all, you can set httpOnly to true for http cookie to prevent XSS attack and other useful options depending on your use case.

For production environment, you need to set sameSite to none for cross-origin request and secure to true. Remember sameSite works with express latest version only as at now and latest chrome version only set cookie over https, thus the need for secure option.

Here is how I made mine dynamic

 res
    .cookie("access_token", token, {
      httpOnly: true,
      sameSite: app.get("env") === "development" ? true : "none",
      secure: app.get("env") === "development" ? false : true,
    })

Pim's answer is very helpful. In my case, I have to use

Expires / Max-Age: "Session"

If it is a dateTime, even it is not expired, it still won't send the cookie to the backend:

Expires / Max-Age: "Thu, 21 May 2020 09:00:34 GMT"

Hope it is helpful for future people who may meet same issue.


In order for the client to be able to read cookies from cross-origin requests, you need to have:

  1. All responses from the server need to have the following in their header:

    Access-Control-Allow-Credentials: true

  2. The client needs to send all requests with withCredentials: true option

In my implementation with Angular 7 and Spring Boot, I achieved that with the following:


Server-side:

@CrossOrigin(origins = "http://my-cross-origin-url.com", allowCredentials = "true")
@Controller
@RequestMapping(path = "/something")
public class SomethingController {
  ...
}

The origins = "http://my-cross-origin-url.com" part will add Access-Control-Allow-Origin: http://my-cross-origin-url.com to every server's response header

The allowCredentials = "true" part will add Access-Control-Allow-Credentials: true to every server's response header, which is what we need in order for the client to read the cookies


Client-side:

import { HttpInterceptor, HttpXsrfTokenExtractor, HttpRequest, HttpHandler, HttpEvent } from "@angular/common/http";
import { Injectable } from "@angular/core";
import { Observable } from 'rxjs';

@Injectable()
export class CustomHttpInterceptor implements HttpInterceptor {

    constructor(private tokenExtractor: HttpXsrfTokenExtractor) {
    }

    intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        // send request with credential options in order to be able to read cross-origin cookies
        req = req.clone({ withCredentials: true });

        // return XSRF-TOKEN in each request's header (anti-CSRF security)
        const headerName = 'X-XSRF-TOKEN';
        let token = this.tokenExtractor.getToken() as string;
        if (token !== null && !req.headers.has(headerName)) {
            req = req.clone({ headers: req.headers.set(headerName, token) });
        }
        return next.handle(req);
    }
}

With this class you actually inject additional stuff to all your request.

The first part req = req.clone({ withCredentials: true });, is what you need in order to send each request with withCredentials: true option. This practically means that an OPTION request will be send first, so that you get your cookies and the authorization token among them, before sending the actual POST/PUT/DELETE requests, which need this token attached to them (in the header), in order for the server to verify and execute the request.

The second part is the one that specifically handles an anti-CSRF token for all requests. Reads it from the cookie when needed and writes it in the header of every request.

The desired result is something like this:

response request


Examples related to authentication

Set cookies for cross origin requests How Spring Security Filter Chain works What are the main differences between JWT and OAuth authentication? http post - how to send Authorization header? ASP.NET Core Web API Authentication Token based authentication in Web API without any user interface Custom Authentication in ASP.Net-Core Basic Authentication Using JavaScript Adding ASP.NET MVC5 Identity Authentication to an existing project LDAP: error code 49 - 80090308: LdapErr: DSID-0C0903A9, comment: AcceptSecurityContext error, data 52e, v1db1

Examples related to cookies

SameSite warning Chrome 77 How to fix "set SameSite cookie to none" warning? Set cookies for cross origin requests Make Axios send cookies in its requests automatically How can I set a cookie in react? Fetch API with Cookie How to use cookies in Python Requests How to set cookies in laravel 5 independently inside controller Where does Chrome store cookies? Sending cookies with postman

Examples related to cors

Axios having CORS issue Cross-Origin Read Blocking (CORB) Jquery AJAX: No 'Access-Control-Allow-Origin' header is present on the requested resource How to allow CORS in react.js? Set cookies for cross origin requests XMLHttpRequest blocked by CORS Policy How to enable CORS in ASP.net Core WebAPI No 'Access-Control-Allow-Origin' header is present on the requested resource—when trying to get data from a REST API How to overcome the CORS issue in ReactJS Trying to use fetch and pass in mode: no-cors

Examples related to http-headers

Set cookies for cross origin requests Adding a HTTP header to the Angular HttpClient doesn't send the header, why? Passing headers with axios POST request What is HTTP "Host" header? CORS error :Request header field Authorization is not allowed by Access-Control-Allow-Headers in preflight response Using Axios GET with Authorization Header in React-Native App Axios get access to response header fields Custom header to HttpClient request Send multipart/form-data files with angular using $http Best HTTP Authorization header type for JWT

Examples related to localhost

Xampp localhost/dashboard Set cookies for cross origin requests Invalid Host Header when ngrok tries to connect to React dev server How to turn on/off MySQL strict mode in localhost (xampp)? What is IPV6 for localhost and 0.0.0.0? How do I kill the process currently using a port on localhost in Windows? How to run html file on localhost? Can't access 127.0.0.1 Server http:/localhost:8080 requires a user name and a password. The server says: XDB How to solve ERR_CONNECTION_REFUSED when trying to connect to localhost running IISExpress - Error 502 (Cannot debug from Visual Studio)?