CORS: Cannot use wildcard in Access-Control-Allow-Origin when credentials flag is true
Asked Answered
F

12

499

I have a setup involving

Frontend server (Node.js, domain: localhost:3000) <---> Backend (Django, Ajax, domain: localhost:8000)

Browser <-- webapp <-- Node.js (Serve the app)

Browser (webapp) --> Ajax --> Django(Serve ajax POST requests)

Now, my problem here is with CORS setup which the webapp uses to make Ajax calls to the backend server. In chrome, I keep getting

Cannot use wildcard in Access-Control-Allow-Origin when credentials flag is true.

doesn't work on firefox either.

My Node.js setup is:

var allowCrossDomain = function(req, res, next) {
    res.header('Access-Control-Allow-Origin', 'http://localhost:8000/');
    res.header('Access-Control-Allow-Credentials', true);
    res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE');
    res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
    next();
};

And in Django I'm using this middleware along with this

The webapp makes requests as such:

$.ajax({
    type: "POST",
    url: 'http://localhost:8000/blah',
    data: {},
    xhrFields: {
        withCredentials: true
    },
    crossDomain: true,
    dataType: 'json',
    success: successHandler
});

So, the request headers that the webapp sends looks like:

Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: "Origin, X-Requested-With, Content-Type, Accept"
Access-Control-Allow-Methods: 'GET,PUT,POST,DELETE'
Content-Type: application/json 
Accept: */*
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8
Cookie: csrftoken=***; sessionid="***"

And here's the response header:

Access-Control-Allow-Headers: Content-Type,*
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: POST,GET,OPTIONS,PUT,DELETE
Content-Type: application/json

Where am I going wrong?!

Edit 1: I've been using chrome --disable-web-security, but now want things to actually work.

Edit 2: Answer:

So, solution for me django-cors-headers config:

CORS_ORIGIN_ALLOW_ALL = False
CORS_ALLOW_CREDENTIALS = True
CORS_ORIGIN_WHITELIST = (
    'http://localhost:3000' # Here was the problem indeed and it has to be http://localhost:3000, not http://localhost:3000/
)
Futrell answered 2/11, 2013 at 15:18 Comment(7)
For me it is localhost:3000 without http, like this: CORS_ORIGIN_WHITELIST = ( 'localhost:3000', )Rodmur
Do you mean you use develop the frontend and backend in one PC?Kristikristian
how about the frontend and backend in different PC?Kristikristian
@Futrell why you say with the http works for you? we all only ` 'localhost:3000'` works.Sphalerite
@244boy yeah the point is not the http, it is the / at the end. I suppose omitting http could work, but I've not really worked on this stuff for some years, so don't really know what works now!Futrell
omitting http:// just throws: ERRORS: ?: (corsheaders.E013) Origin 'localhost:3000' in CORS_ORIGIN_WHITELIST is missing scheme or netloc in Django 2.2Web
Spent hours debugging this - and this thread was the fix: stackoverflow.com/questions/35713682/…Hitherto
E
369

This is a part of security, you cannot do that. If you want to allow credentials then your Access-Control-Allow-Origin must not use *. You will have to specify the exact protocol + domain + port. For reference see these questions :

  1. Access-Control-Allow-Origin wildcard subdomains, ports and protocols
  2. Cross Origin Resource Sharing with Credentials

Besides * is too permissive and would defeat use of credentials. So set http://localhost:3000 or http://localhost:8000 as the allow origin header.

Eyrie answered 2/11, 2013 at 17:31 Comment(18)
But what if there's more than one domain?Basso
@Basso You can give a list of domains. Related question: stackoverflow.com/questions/1653308/…Eyrie
What if I'm getting the same error message but there is no Access-Control-Allow-Headers in the response?L
@Eyrie Could you explain "Besides * is too permissive and would defeat use of credentials."?Lamentation
What is the "exact domain" if the request comes from mobile device, like it can happen with Cordova?Laughton
Ok, so how exactly does the server know whether or not it should send "*"?Ecto
Is it possible to provide localhost of a different computer than the server? I got this error: "The 'Access-Control-Allow-Origin' header has a value 'localhost:3000' that is not equal to the supplied origin. Origin 'localhost:3000' is therefore not allowed access."Plumlee
@HugoWood, a man in the middle attack would cause one to send credentials to any (*) serverUnijugate
@Laughton kinda old, but if anyone still curious, this problem happens only for applications running on browsers, because this error is thrown by the browser for security reasons. Other clients such as a mobile app, postman or any other backend code using http client to make a request won't have this problem, so you don't have to worry about the origin and the exact domain.Amplifier
For more than one domain use it like this response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin"));Manic
@Alisson Reinaldo Silva Sorry, I don't get it. If my backend (which previously allows wildcard) has to allow exact domains of frontend sites now, so that my frontend sites (which wants to send a few requests with credentials to backend) can work. My mobile apps which directly call backend obviously do not have exact domain names, how can they work in the ruleset?Vespers
@LamLe there is nothing in your backend that would block requests from your mobile apps, the backend doesn't block anything here. You only need to whitelist the domain from your frontend web app, because otherwise the browser would block the requests from the frontend web app. So it's like the browser is asking the backend "hey, is it ok if I send you this request from this domain?" and the backend can reply with "yes, whatever" or "nope, I don't recognize this domain", but the browser blocks the requests, the backend won't reject the exact same request coming from other clients.Amplifier
@LamLe so CORS is a security mechanism adopted by browsers, they are the ones blocking requests if the backend doesn't reply with proper rulesets. Other clients may decide to apply CORS too and block requests, one such example is Unity. You can create mobile games with Unity, they are transpiled into native code for Android/iOS and other platforms, and if your app needs to send requests to a backend, Unity requires a similar CORS validation (may not be exactly CORS, but it's the same idea).Amplifier
@ShivamYadav That's the worst thing that you can do in this situation and it completely bypasses the security that CORS is intended to provide. If you reflect the origin of the request and allow credentials the requests to your application would be allowed from any domain which would leave the application vulnerable to CSRF.Petiolate
@AlissonReinaldoSilva What should I do if the client requests do come from browsers?Cooke
@Cooke it depends on what are you trying to achieve. If you want to use credentials, you can't use wildcard * for the origin, as per the accepted answer. Why do you need to use credentials true?Amplifier
@AlissonReinaldoSilva My goal is to design a public API for which it would be possible for users to create their own frontends. I'm currently using a cookie oauth scheme and trying to pass the authorization cookie through CORS to the user on a successful login.Cooke
@Cooke then if you need credentials: true, you cannot use wildcard * for the origin in the cors response. So either you need to 1) keep a list of your users' origins (you could have an input where they provide their domain for you) and return their domains in the CORS origin response, OR 2) use the request's origin header as the origin for the cors response (which has the same effect as using *) but this kinda defeats the purpose of using cookie through cors, OR 3) use another auth mechanism instead of cookies.Amplifier
D
97

If you are using CORS middleware and you want to send withCredential boolean true, you can configure CORS like this:

var cors = require('cors');    
app.use(cors({credentials: true, origin: 'http://localhost:3000'}));
Despite answered 26/8, 2017 at 0:30 Comment(1)
It worked. Thanks @hamidAirline
C
66

Expanding on @Renaud idea, cors now provides a very easy way of doing this:

From cors official documentation found here:

" origin: Configures the Access-Control-Allow-Origin CORS header. Possible values: Boolean - set origin to true to reflect the request origin, as defined by req.header('Origin'), or set it to false to disable CORS. "

Hence we simply do the following:

const app = express();
const corsConfig = {
    credentials: true,
    origin: true,
};
app.use(cors(corsConfig));

Lastly I think it is worth mentioning that there are use cases where we would want to allow cross origin requests from anyone; for example, when building a public REST API.

Copybook answered 23/10, 2020 at 16:47 Comment(1)
I like this answer! especially about the fact that there are use cases where you want to allow all origins (many answers here seem to assume that it is always a bad practice).Alexandretta
A
21

try it:

const cors = require('cors')

const corsOptions = {
    origin: 'http://localhost:4200',
    credentials: true,

}
app.use(cors(corsOptions));
Abele answered 24/12, 2017 at 10:41 Comment(0)
C
18

If you are using express you can use the cors package to allow CORS like so instead of writing your middleware;

var express = require('express')
, cors = require('cors')
, app = express();

app.use(cors());

app.get(function(req,res){ 
  res.send('hello');
});
Crewelwork answered 2/11, 2013 at 15:22 Comment(7)
Ah, now that's more convenient, however, the result's the same :( BTW, I'm using app.use(cors({credentials: true}));Futrell
You might want to look into this Django CORS middleware that is tested.Crewelwork
So you have two Django middlewares ? I would only use django-cors-header app. Make sure you add localhost to CORS_ORIGIN_WHITELIST setting and set CORS_ALLOW_CREDENTIALS to TrueCrewelwork
Yeah man, tried that before to no avail, had CORS_ORIGIN_ALLOW_ALL = True, CORS_ORIGIN_WHITELIST = ( 'localhost' ) and CORS_ALLOW_CREDENTIALS = True I get these headers: Access-Control-Allow-Credentials: true Access-Control-Allow-Origin: http://localhost:3000/ Access-Control-Allow-Methods: POST,GET,OPTIONS,PUT,DELETE Content-Type: application/jsonFutrell
BTW, I was using django-cors-headers, but things were not working, so I coupled it with a custom middleware as well.Futrell
After reading this documentation: github.com/expressjs/corsuse i using this config: app.use(cors({credentials: true, origin: 'localhost:3001'})); is working for me.Dace
@Dace - that url is no more in use - find the following working url https://github.com/expressjs/corsCora
G
14

If you want to allow all origins and keep credentials true, this worked for me:

app.use(cors({
  origin: function(origin, callback){
    return callback(null, true);
  },
  optionsSuccessStatus: 200,
  credentials: true
}));
Glassware answered 17/5, 2019 at 15:22 Comment(3)
@TSlegaitis Haha yeah that's why it works for all origins but keeps credentials. I wouldn't recommend it for security but it does work.Glassware
This worked for me while keeping credentials true, in my case origin was null so nothing else worked except this.Pikeman
This is terribly insecure: it exposes users to cross-origin attacks. See portswigger.net/research/…Millar
L
9

This works for me in development but I can't advise that in production, it's just a different way of getting the job done that hasn't been mentioned yet but probably not the best. Anyway here goes:

You can get the origin from the request, then use that in the response header. Here's how it looks in express:

app.use(function(req, res, next) {
  res.header('Access-Control-Allow-Origin', req.header('origin') );
  next();
});

I don't know what that would look like with your python setup but that should be easy to translate.

Linen answered 26/8, 2019 at 9:57 Comment(5)
Mozilla Dev Docs expand on the idea of changing the allowed origin to the one from the request. It is suggested to add a 'Vary: Origin' HTTP response header and whitelist allowed domains.Fourchette
Whats wrong with this solution in production?Metasomatism
@Metasomatism this allows all origins so it wouldn't be considered safeLinen
Yeah of course.. Was talking about situations where you want your server to be open to all websitesMetasomatism
@Linen origin is null. do you mean host? Calling request.headers["host"] returns localhost:3000 but doesn't include the protocol, http://Elenoraelenore
C
4

(Edit) The previously recomended add-on is not available any longer, you may try this other one


For development purposes in Chrome, installing this add on will get rid of that specific error:

Access to XMLHttpRequest at 'http://192.168.1.42:8080/sockjs-node/info?t=1546163388687' 
from origin 'http://localhost:8080' has been blocked by CORS policy: The value of the 
'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' 
when the request's credentials mode is 'include'. The credentials mode of requests 
initiated by the XMLHttpRequest is controlled by the withCredentials attribute.

After installing, make sure you add your url pattern to the Intercepted URLs by clicking on the AddOn's (CORS, green or red) icon and filling the appropriate textbox. An example URL pattern to add here that will work with http://localhost:8080 would be: *://*

Crouch answered 30/12, 2018 at 9:58 Comment(4)
I got it just after installing it, any ideas?Medicament
It worked for me. Warning if you have other similar add-ons you have to uninstall it before try this one.Marlin
please fix the broken linkAdditive
Seems like the original add on was removed, I added a new recommendation as an (Edit) at the topCrouch
R
2

Though we have many solutions regarding the cors origin, I think I may add some missing part. Generally using cors middlware in node.js serves maximum purpose like different http methods (get, post, put, delete).

But there are use cases like sending cookie response, we need to enable credentials as true inside the cors middleware Or we can't set cookie. Also there are use cases to give access to all the origin. in that case, we should use,

{credentials: true, origin: true}

For specific origin, we need to specify the origin name,

{credential: true, origin: "http://localhost:3000"}

For multiple origins,

{credential: true, origin: ["http://localhost:3000", "http://localhost:3001" ]}

In some cases we may need multiple origin to be allowed. One use case is allowing developers only. To have this dynamic whitelisting, we may use this kind of function

const whitelist = ['http://developer1.com', 'http://developer2.com']
const corsOptions = {
origin: (origin, callback) => {
    if (whitelist.indexOf(origin) !== -1) {
      callback(null, true)
    } else {
      callback(new Error())
    }
  }
}
Rainout answered 3/8, 2022 at 17:27 Comment(1)
{credentials: true, origin: true} is insecure. See portswigger.net/research/…Millar
B
1

CORS ERROR With NETLIFY and HEROKU

Actually, if none of the above solutions worked for you then you might wanna try this. In my case, the backend was running on Heroku and the frontend was hosted on netlify. in the .env file, of the frontend, the server_url was written as

REACT_APP_server_url = "https://ci-cd-backend.herokuapp.com"

and in the backend, all my api calls where written as,

app.get('/login', (req, res, err) => {});

So, Only change you need to do is, add /api at the end of the routes,

so, frontend base url will look like,

REACT_APP_server_url = "https://ci-cd-backend.herokuapp.com/api"

and backend apis should be written as,

app.get('/api/login', (req, res, err) => {})

This worked in my case, and I believe this problem is specifically related when the front end is hosted on netlify.

Butterfingers answered 22/11, 2022 at 2:22 Comment(0)
M
0

Had this problem with angular, using an auth interceptor to edit the header, before the request gets executed. We used an api-token for authentification, so i had credentials enabled. now, it seems it is not neccessary/allowed anymore

@Injectable()
export class AuthInterceptor implements HttpInterceptor {
  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    req = req.clone({
      //withCredentials: true, //not needed anymore
      setHeaders: {
        'Content-Type' : 'application/json',
        'API-TOKEN' : 'xxx'
      },
    });
    
    return next.handle(req);
  }

Besides that, there is no side effects right now.

Matrilateral answered 30/6, 2020 at 12:49 Comment(0)
S
0

set 'supports_credentials' => true, at config/cors.php it works like a charm

Shown answered 10/4, 2023 at 10:50 Comment(1)
The question uses Node.js. This is not useful at all.Lippe

© 2022 - 2024 — McMap. All rights reserved.