Related
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/
)
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 :
Access-Control-Allow-Origin wildcard subdomains, ports and protocols
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.
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'}));
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.
try it:
const cors = require('cors')
const corsOptions = {
origin: 'http://localhost:4200',
credentials: true,
}
app.use(cors(corsOptions));
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');
});
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
}));
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.
(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: *://*
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())
}
}
}
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.
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.
I'm trying to set cookies on the frontend firebase from nodejs app on a different server.
I have allowed whitelisted the frontend URL from cors.
Also, set the headers.
appRouter.use(function (req, res, next) {
// Website you wish to allow to connect
res.setHeader('Access-Control-Allow-Origin','*');
// Request methods you wish to allow
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE');
// Request headers you wish to allow
res.setHeader('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept');
// Set to true if you need the website to include cookies in the requests sent
// to the API (e.g. in case you use sessions)
res.setHeader('Access-Control-Allow-Credentials', true);
res.cookie('cokkieName',138932843821341, { maxAge: 900000, httpOnly: false, sameSite: 'none', secure: true })
// Pass to next layer of middleware
next();
}
)
appRouter.use(cors(
{
credentials: true,
origin: [
'http://localhost:3000',
'https://mywebsitename.com',
'https://localhost:3000'
]
}));
// parse application/x-www-form-urlencoded
appRouter.use(bodyParser.urlencoded({ extended: false }))
appRouter.use(cookieParser("secret"));
appRouter.use(session({
cookie:{
httpOnly:false,
maxAge: 1*60*60*1000},
resave: false,
saveUninitialized: true,
secret: 'secret' ,
sameSite:false,
secure: true
}));
Through postman or even through running the backend(which is on a different server) URL, I do get the cookies. But on the firebase frontend, I don't get the cookies.
There was a cors error but I resolved it by adding the frontend URL to the origin whitelist.
The backend is communicating with the frontend. I say this because the backend response is logged on the browser and the data is visible. However, the cookies are not.
I also have google authentication which, after successfully closing the window of account selection, sets the various google auth cookies to the frontend.
Having issues with node express-session and basically just trying to understand how it all works with regards to cookies and my session store within my postgres database.
For starters, I'm not sure why I don't receive a session id cookie within my chrome browser where my react app is running on localhost:3000. If I call the route localhost:5000/login from postman, a cookie is received but when calling the same route from Chrome: localhost:5000/login and then check my cookies, nothing is created when using the fetch API.
The session is created fine within postgres.
Here is my middleware for session setup:
app.use(session({
store: new pgSession({
pool : pool, // Connection pool
schemaName: 'my_schema',
tableName : 'user_sessions'
}),
secret: randomString.generate({
length: 14,
charset: 'alphanumeric'
}),
resave: false,
saveUninitialized: false,
cookie: { maxAge: 1000 * 60 * 60 * 24,
httpOnly: true }
}))
app.use(function (req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
res.header('Access-Control-Allow-Methods', 'PUT, POST, GET, DELETE, OPTIONS');
next();
});
My other question is, within my react app, how can I use the session info within my postgres db to check that all requests to all routes are still coming from the same user on the client side?
try this , change your cors middleware this
app.use(cors({ origin: true, credentials: true }));
and add this when you make your request
withCredentials: true,
credentials: "include",
I had same problem I was recieving the cooking when making post from postman, but not from the browser when making request with axios, hopefully it works for you too as well
I have a project that sends HTTP requests from the client using Axios
axios.create({
baseURL: `http://localhost:8081/`,
withCredentials: true
})
And I suppose this allows cookies -Which I am sure it shows in the browser before you ask- to be sent with the requests.
The problem occurs in the back-end, when this error shows in the log:
Response to preflight request doesn't pass access control check: 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'. Origin 'http://localhost:8080' is therefore not allowed
access. The credentials mode of requests initiated by the
XMLHttpRequest is controlled by the withCredentials attribute.
I tried this:
app.use(cors({
//origin : to be set later
credentials: true,
}))
and this instead:
app.use(function(req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, Authorization");
res.header("Access-Control-Allow-Credentials", true);
next();
});
but neither seems to work.
EDIT - Here is the answer for future visitors
With the help of the participants in comments, I found out I had to set the origin value:
app.use(cors({
origin : "http://localhost:8080",
credentials: true,
}))
I know it's too late for OP, but for those who keep comming here - what helped me is:
app.use(cors({
preflightContinue: true,
credentials: true,
}));
source: https://expressjs.com/en/resources/middleware/cors.html
I got this problem and I fixed it by setting the header "Access-Control-Allow-Origin" to the URL of my frontend instead of "*"
More details: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS/Errors/CORSNotSupportingCredentials
I have problem with setting a cookies via express. I'm using Este.js dev stack and I try to set a cookie in API auth /login route. Here is the code that I use in /api/v1/auth/login route
res.cookie('token', jwt.token, {expires: new Date(Date.now() + 9999999)});
res.status(200).send({user, token: jwt.token});
In src/server/main.js I have registered cookie-parser as first middleware
app.use(cookieParser());
The response header for /api/v1/auth/login route contains
Set-Cookie:token=eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJ..
but the cookie isn't saved in browser (document.cookie is empty, also Resources - Cookies tab in develepoers tools is empty) :(
EDIT:
I'm found that when I call this in /api/v1/auth/login (without call res.send or res.json)
res.cookie('token', jwt.token, {expires: new Date(Date.now() + 9999999), httpOnly: false});
next();
then the cookie is set AND response header has set X-Powered-By:Este.js ... this sets esteMiddleware in expres frontend rendering part.
When I use res.send
res.cookie('token', jwt.token, {expires: new Date(Date.now() + 9999999), httpOnly: false}).send({user, token: jwt.token});`
next();
then I get error Can't set headers after they are sent. because send method is used, so frontend render throw this error.
But I have to send a data from API, so how I can deal with this?
I had the same issue. The server response comes with cookie set:
Set-Cookie:my_cookie=HelloWorld; Path=/; Expires=Wed, 15 Mar 2017 15:59:59 GMT
But the cookie was not saved by a browser.
This is how I solved it.
I use fetch in a client-side code. If you do not specify credentials: 'include' in fetch options, cookies are neither sent to server nor saved by a browser, although the server response sets cookies.
Example:
var headers = new Headers();
headers.append('Content-Type', 'application/json');
headers.append('Accept', 'application/json');
return fetch('/your/server_endpoint', {
method: 'POST',
mode: 'same-origin',
redirect: 'follow',
credentials: 'include', // Don't forget to specify this if you need cookies
headers: headers,
body: JSON.stringify({
first_name: 'John',
last_name: 'Doe'
})
})
Struggling with this for a 3h, and finally realized, with axios, I should set withCredentials to true, even though I am only receiving cookies.
axios.defaults.withCredentials = true;
I work with express 4 and node 7.4 and Angular, I had the same problem this helped me:
a) server side: in file app.js I give headers to all responses like:
app.use(function(req, res, next) {
res.header('Access-Control-Allow-Origin', req.headers.origin);
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
next();
});
This must have before all routers.
I saw a lot of added this header:
res.header("Access-Control-Allow-Headers","*");
res.header('Access-Control-Allow-Credentials', true);
res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE');
but I don't need that.
b) when you define cookie you need to add httpOnly: false, like:
res.cookie( key, value,{ maxAge: 1000 * 60 * 10, httpOnly: false });
c) client side: in send ajax you need to add: withCredentials: true, like:
$http({
method: 'POST',
url: 'url',
withCredentials: true,
data : {}
}).then(function(response){
// do something
}, function (response) {
// do something else
});
There's a few issues:
a cookie that isn't explicitly set with httpOnly : false will not be accessible through document.cookie in the browser. It will still be sent with HTTP requests, and if you check your browsers' dev tools you will most likely find the cookie there (in Chrome they can be found in the Resources tab of the dev tools);
the next() that you're calling should only be used if you want to defer sending back a response to some other part of your application, which—judging by your code—is not what you want.
So, it seems to me that this should solve your problems:
res.cookie('token', jwt.token, {
expires : new Date(Date.now() + 9999999),
httpOnly : false
});
res.status(200).send({ user, token: jwt.token });
As a side note: there's a reason for httpOnly defaulting to true (to prevent malicious XSS scripts from accessing session cookies and the like). If you don't have a very good reason to be able to access the cookie through client-side JS, don't set it to false.
I had the same issue with cross origin requests, here is how I fixed it. You need to specifically tell browser to allow credentials. With axios, you can specify it to allow credentials on every request like
axios.defaults.withCredentials = true
however this will be blocked by CORS policy and you need to specify credentials is true on your api like
const corsOptions = {
credentials: true,
///..other options
};
app.use(cors(corsOptions));
Update: this only work on localhost
For detail answer on issues in production environment, see my answer here
I was also going through the same issue.
Did code changes at two place :
At client side :
const apiData = await fetch("http://localhost:5000/user/login",
{
method: "POST",
body: JSON.stringify(this.state),
credentials: "include", // added this part
headers: {
"Content-Type": "application/json",
},
})
And at back end:
const corsOptions = {
origin: true, //included origin as true
credentials: true, //included credentials as true
};
app.use(cors(corsOptions));
Double check the size of your cookie.
For me, the way I was generating an auth token to store in my cookie, was causing the size of the cookie to increase with subsequent login attempts, eventually causing the browser to not set the cookie because it's too big.
Browser cookie size cheat sheet
There is no problem to set "httpOnly" to true in a cookie.
I am using "request-promise" for requests and the client is a "React" app, but the technology doesn't matter. The request is:
var options = {
uri: 'http://localhost:4000/some-route',
method: 'POST',
withCredentials: true
}
request(options)
.then(function (response) {
console.log(response)
})
.catch(function (err) {
console.log(err)
});
The response on the node.js (express) server is:
var token=JSON.stringify({
"token":"some token content"
});
res.header('Access-Control-Allow-Origin', "http://127.0.0.1:3000");
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
res.header( 'Access-Control-Allow-Credentials',true);
var date = new Date();
var tokenExpire = date.setTime(date.getTime() + (360 * 1000));
res.status(201)
.cookie('token', token, { maxAge: tokenExpire, httpOnly: true })
.send();
The client make a request, the server set the cookie , the browser (client) receive it (you can see it in "Application tab on the dev tools") and then I again launch a request to the server and the cookie is located in the request: "req.headers.cookie" so accessible by the server for verifying.
I had same problem in Angular application. The cookies was not set in browser although I used
res.cookie("auth", token, {
httpOnly: true,
sameSite: true,
signed: true,
maxAge: 24 * 60 * 60 * 1000,
});
To solve this issue, I added app.use(cors({ origin:true, credentials:true })); in app.js file of server side
And in my order service of Angular client side, I added {withCredentials: true} as a second parameter when http methods are called like following the code
getMyOrders() {
return this.http
.get<IOrderResponse[]>(this.SERVER_URL + '/orders/user/my-orders', {withCredentials: true})
.toPromise();}
vue axios + node express 2023
server.ts (backend)
const corsOptions = {
origin:'your_domain',
credentials: true,
optionSuccessStatus: 200,
}
auth.ts (backend)
res.cookie('token', JSON.stringify(jwtToken), {
secure: true,
httpOnly: true,
expires: dayjs().add(30, "days").toDate(),
sameSite: 'none'
})
authService.ts (frontend)
export class AuthService {
INSTANCE = axios.create({
withCredentials: true,
baseURL: 'your_base_url'
})
public Login = async (value: any): Promise<void> => {
try {
await this.INSTANCE.post('login', { data: value })
console.log('success')
} catch (error) {
console.log(error)
}
}
it works for me, the cookie is set, it is visible from fn+F12 / Application / Cookies and it is inaccessible with javascript and the document.cookie function. Screenshot Cookies Browser
One of the main features is to set header correctly.
For nginx:
add-header Access-Control-Allow-Origin' 'domain.com';
add_header 'Access-Control-Allow-Credentials' 'true';
Add this to your web server.
Then form cookie like this:
"cookie": {
"secure": true,
"path": "/",
"httpOnly": true,
"hostOnly": true,
"sameSite": false,
"domain" : "domain.com"
}
The best approach to get cookie from express is to use cookie-parser.
A cookie can't be set if the client and server are on different domains. Different sub-domains is doable but not different domains and not different ports.
If using Angular as your frontend you can simply send all requests to the same domain as your Angular app (so the app is sending all API requests to itself) and stick an /api/ in every HTTP API request URL - usually configured in your environment.ts file:
export const environment = {
production: false,
httpPhp: 'http://localhost:4200/api'
}
Then all HTTP requests will use environment.httpPhp + '/rest/of/path'
Then you can proxy those requests by creating proxy.conf.json as follows:
{
"/api/*": {
"target": "http://localhost:5200",
"secure": false,
"changeOrigin": true,
"pathRewrite": {
"^/api": ""
}
}
}
Then add this to ng serve:
ng serve -o --proxy-config proxy.conf.json
Then restart your app and it should all work, assuming that your server is actually using Set-Cookie in the HTTP response headers. (Note, on a diff domain you won't even see the Set-Cookie response header, even if the server is configured correctly).
Most of these answers provided are corrections, but either of the configuration you made, cookies won't easily be set from different domain. In this answer am assuming that you are still in local development.
To set a cookie, you can easily use any of the above configurations or
res.setHeader('Set-Cookie', ['foo=bar', 'bar=baz']); // setting multiple cookies or
res.cookie('token', { maxAge: 5666666, httpOnly: true })
Both of the will set your cookie while to accessing your cookie from incoming request req.headers.
In my case, my cookie were not setting because my server was running on http://localhost:7000/ while the frontend was running on http://127.0.0.1:3000/ so the simple fix was made by making the frontend run on http://localhost:3000 instead.
I struggle with it a lot so follow below solution to get through this
1 check if you are getting token with response with postmen in my case i was getting token in postmen but it wasn't being saved in cookies.
I was using a custom publicRequest which looks like below
try {
const response = await publicRequest.post("/auth/login", user, {withCredentials: true});
dispatch(loginSuccess(response.data));
} catch (error) {
dispatch(loginFail());
dispatch(reset());
}
I was using this method in other file to handle login
I added {withCredentials: true} in both methods as option and it worked for me.
I am late to the party but nothing fixed it for me. This is what I was missing (and yeah, it's stupid):
I had to add res.send() after res.cookie() - so apperently sending a cookie is not enough to send a response to the browser.
res.cookie("testcookie", "text", cookieOptions);
res.send();
You have to combine:
including credentials on the request with, for example withCredentials: true when using axios.
including credentials on the api with, for example credentials: true when using cors() mw.
including the origin of your request on the api, for example origin: http://localhost:3000 when using cors() mw.
app.post('/api/user/login',(req,res)=>{
User.findOne({'email':req.body.email},(err,user)=>{
if(!user) res.json({message: 'Auth failed, user not found'})
user.comparePassword(req.body.password,(err,isMatch)=>{
if(err) throw err;
if(!isMatch) return res.status(400).json({
message:'Wrong password'
});
user.generateToken((err,user)=>{
if(err) return res.status(400).send(err);
res.cookie('auth',user.token).send('ok')
})
})
})
});
response
res.cookie('auth',user.token).send('ok')
server gives response ok but the cookie is not stored in the browser
Solution :
Add Postman Interceptor Extension to chrome which allows postman to store cookie in browser and get back useing requests.