I'm trying to write and read cookies and falling into a problem below.
This is my basic server side:
server.js
const app = express();
app.use(cors());
app.use(cookieParser());
import routes from '...';
app.use("/foo", routes);
app.listen(8888);
routes.js
const routes = express.Router();
routes.post('/', (req, res) => {
res.cookie("myFoo", "abcd");
res.send("Cookie added");
}
});
routes.get('/', (req, res) => {
res.send(req.cookies.myFoo);
}
});
export default routes;
And my client side at "http://localhost:3000".
I do two HTTP request
POST http://localhost:8888/foo
GET http://localhost:8888/foo
And get the response exactly what I expected abcd. Also, the cookie exists in the browser tab Application > Cookies too.
The problem cases when axios is used in the client.
const api = axios.create({
baseURL: "http://localhost:8888/foo"
});
async function setCookie(object) {
return api.post("/", object)
.then((res) => {
return res;
});
}
function getCookie() {
return api.get("/")
.then((res) => {
return res;
});
}
setCookie({})
.then((res) => {
getCookie();
})
The api.post() run usually and the header response Set-Cookie is correct. But cookies in the browser tab Application > Cookies are empty. Also, api.get() get the undefined.
I did try to move res.cookie() or the set cookie job in server side to GET route it WORKS on both HTTP and axios
routes.get('/', (req, res) => {
res.cookie("myFoo", "abcd");
});
tldr: Set cookie in HTTP POST method work fine but when client use axios to call so it causes problems.
Can you show me why this happened? And which code part went wrong that caused me into this?
Cookies are only used in cross-origin Ajax requests when:
The client asks to use them
The server grants permission to use them cross origin
So you need to change the client side code to ask for them:
const api = axios.create({
baseURL: 'http://localhost:8888/',
withCredentials: true,
});
And the server code to grant permission (note that you can't use credentials at the same time as the wildcard for origins).
app.use(cors({
origin: 'http://localhost:3000',
credentials: true
}));
Related
I have a project where I set a cookie with universal-cookie.
cookies.set('auth-token', data);
I then have a fetch request:
const getMeals = async (date: Date) => {
let res= await fetch("http://127.0.0.1:5000/meals/", {
method: "POST",
credentials: "same-origin",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
date: date
}),
});
const data = await res.json();
console.log(data);
}
And a backend where it gets checked (NodeJs, ExpressJs):
module.exports = function(req, res, next){
const token = req.header('auth-token');
if(!token){
return res.status(401).json('Access denied!');
}
}
I see the cookie in my mozilla:
But I always get "Access denied!" -> that means the auth-token is not there...
I hardcoded the auth-token into the fetch and it worked.
I checked several websites and almost all stackoverflow posts on this theme. I also checked the fetch-api documentation, but I couldnt come up with a solution...
Both the client and the server are running on localhost.
I hope someone can help me with this topic.
UPDATE
Andreas suggested that I should either set the header(frontend) or look for cookie- instead of header-values(backend).
I decided to do the second approach.
Server.js
var cookieParser = require('cookie-parser')
const usersRouter = require('./routes/users'); //where auth.js gets imported
app.use(cors());
app.use(express.json());
app.use(cookieParser());
app.use('/users', usersRouter);
I tried changing the position of app.use(cookieParser()); to above all others - didnt help.
auth.js
module.exports = function(req, res, next){
const cookie = req.cookies['auth-token'];
console.log(cookie);
}
The problem I now have is that the cookie value is undefined even though the cookie gets displayed in FireFox.
I tried to do the steps of this post, but this doesnt work afterwards I went to their official documentation and found nothing.
I also stumbled upon this one, which makes me think that something is wrong in the frontend...
Frontend-File where I set the cookie:
import Cookies from 'universal-cookie';
const cookies = new Cookies();
const login = async () => {
let res= await fetch("http://127.0.0.1:5000/users/login", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
email: (e.target as HTMLFormElement).email.value,
password: (e.target as HTMLFormElement).password.value,
}),
});
const data = await res.json()
if(res.status === 200){
cookies.set('auth-token', data);
setUserId((jwt_decode(data) as any)._id);
navigate("/");
}else{
alert(data);
}
}
Does someone have an idea?
I could fix the problem with the help of a friend. I had to add "proxy": "http://localhost:5000/" to my package.json in my React-Project, because the port is important for the cookie so localhost:5000 and localhost:3000 are not the same for it. I could also remove the app.use(cors()) from my NodeJs-Project, because of it.
I would suggest you run your front end on http://127.0.0.1:3000. If you are already doing that then you have to call fetch with credentials options set to include.
This is because unless fetch() is called with the credentials option set to include, fetch():
won't send cookies in cross-origin requests
won't set any cookies sent back in cross-origin responses
As of August 2018, the default credentials policy changed to same-origin. Firefox was also modified in version 61.0b13)
My server code as following:
var app = express();
app.use(express.urlencoded());
app.use(express.json());
app.post('/user', function (req, res) {
console.log("request body:"+req.body+" params:"+req.params);
})
my client code using react js as following:
handleSubmit(event) {
event.preventDefault();
const post ={
name:"Tom"
}
axios.post('http://localhost:9111/user', { post })
.then(res => {
this.setState({response:res.data})
})
}
I'm sure the server side did get the request from client, but I got 'undefined' when I tried to get the data from client request via req.body or req.params in server side code.
The second parameter to axios.post needs to have a data key. eg { data: post }
I am new to Node, and I can't pass request parameters when using http-proxy-middleware..
It throws me 404 error
This is the express listener:
app.put("/api/markets/:id",()=>{..code..});
This is the axios PUT request:
axios
.put("/api/markets/" + idToPass, {..Object..})
.then(res => {
console.log(res);
})
.catch(err => {
console.log(err);
});
And this is the proxyMiddleware:
const proxy = require("http-proxy-middleware");
module.exports = function(app) {
app.use(proxy("/api/*", { target: "http://localhost:3050" }));
};
My client runs on localhost:3000 (react app)
And my server is on localhost:3050
When I open my browser network tab to inspect requests I see that call is being made to
localhost:3000/api/markets/idContent
not
localhost:3050/api/markets/idContent
as it is supposed to.
It works when I send it manually to
localhost:3050/api/markets/idContent
How can I fix this, any ideas?
Thx in advance
It seems the problem is with the proxy configuration. As of now it matches urls only one level deep.
Try /api or /api/** instead
const proxy = require("http-proxy-middleware");
module.exports = function(app) {
app.use(proxy("/api", { target: "http://localhost:3050" }));
};
You are not specifying the base URL. Create an axios instance and specify the correct base URL:
const instance = axios.create({
baseURL: 'http://localhost:3050'
});
And then make every axios requests using that instance:
instance
.put("/api/markets/" + idToPass, {..Object..})
.then(res => {
console.log(res);
})
.catch(err => {
console.log(err);
});
I have just deployed my Create-React-App project to Heroku. In development, I was running two separate ports - the local HTML + JS were being served from the React WebpackDevServer using the npm/yarn start scripts on Port 3000. The Backend was Express + NodeJS running on port 3001. I configured all fetch requests to use mode:'cors' and provided a handler on the API so to avoid CORS errors.
A typical fetch request would look like this:
When I deployed to Heroku, everything is now kept together on a single Dyno, and the Express app serves the React files (bundle + index.html) and also handles backend routing logic.
Here is a sample of my API code so-far:
const express = require('express');
const mongoose = require('mongoose');
const path = require('path');
const bodyParser = require('body-parser');
const config = require('./models/config');
require('dotenv').config()
const app = express();
const server = require('http').createServer(app);
const storeItems= require('./controllers/storeItems')
const authorize= require('./controllers/authorize')
const router = express.Router();
mongoose.Promise = global.Promise;
mongoose.connect(`mongodb://${process.env.MLABS_USER}:${process.env.MLABS_PW}#ds113000.mlab.com:13000/omninova`, { useMongoClient: true });
app.use(bodyParser.json());
// Middleware to handle CORS in development:
app.use('/*', function(req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "Origin, x-access-token, x-user-pathway, x-mongo-key, X-Requested-With, Content-Type, Accept");
res.header("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE, PUT");
next();
});
app.use(express.static(path.join(__dirname, 'client/build')));
router.route('/api/storeItem/')
.get(storeItems.getAllStoreItems)
.post(storeItems.createNewStoreItem);
router.route('/authorize')
.post(authorize.login);
// Catch-All Handler should send Client index.html for any request that does not match previous routes
router.route('*')
.get((req, res) => {
res.sendFile(path.join(__dirname+'/client/build/index.html'));
});
app.use('/', router);
server.listen(config.port);
module.exports = app;
I'm having some issues, ALL of my get requests are returning my index.html page with the following Error: Unexpected token < in JSON at position 0
I have the following Fetch Request:
return fetch(`/api/storeItem`, {
headers:{
'Content-Type': 'application/json',
},
method: 'GET',
mode: 'no-cors',
})
.then(response => response.ok ? response.json() : Promise.reject(response))
.then(json => {
dispatch(receiveItems(json))
})
.catch(err => console.log(err))
This is failing, because instead of triggering the Express Middleware that should be running storeItems.getAllStoreItems on the backend, It's passing that route and triggering the catch-all handler, which I use to serve the index.html upon initial request:
router.route('*')
.get((req, res) => {
res.sendFile(path.join(__dirname+'/client/build/index.html'));
});
Another confusion is that this following fetch request returns a 404, even though the /authorize route is expecting a POST request in the API code:
export function attemptLogIn(credentials) {
return dispatch => {
return fetch('/authorize', {
headers:{
'Content-Type': 'application/json'
},
method: 'POST',
mode: 'no-cors'
body: JSON.stringify(credentials)
})
.then(response => response.ok ? response.json() : Promise.reject(response.statusText))
.then(json => {
dispatch(routeUserAfterLogin(json.accountType))
})
.catch(err => dispatch(authFail(err.message)))
}
}
Any help with this would be highly appreciated. I assume I am doing something wrong with the Express Router, since the authorize route is just not being picked up.
I followed the instructions in this blog post to help me set up my new project: https://daveceddia.com/deploy-react-express-app-heroku/
Edit: This is Fetch code from my Development branch. This successfully logs the user in, without returning a 404. However, I do not use the catch-all handler at all, or the express.static middleware:
return fetch('http://localhost:3001/authorize', {
headers:{
'Content-Type': 'application/json'
},
method: 'POST',
mode: 'cors',
body: JSON.stringify(credentials)
})
Edit: I just changed the URL which points to the bundle.js to
app.use(express.static(path.join(__dirname, '/../../build')));
I'm not sure how I was even sending out the HTML before, since that's the actual location of build files..I'm not sure how they were being found before.
Edit2: Found my problem, I left in the start script for my React project (which actually started the webpack dev server...)
For the first part
Error: Unexpected token < in JSON at position 0
My guess is that without the dev server, express can't handle the bundle download automatically. So, when your index.html hits /path/to/bundle.js, it falls under the wildcard ("*") route, which returns the HTML itself. Your browser then tries to parse it as JS, but it can't since it's HTML, hence the error.
I would try something like:
app.get("path/to/bundle.js", (req, res) => {
res.sendFile("path/to/bundle.js");
});
Your server is not sending json response but HTML content. Change response to res.text() and log it. https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch
router.route('*')
.get((req, res) => {
res.sendFile(path.join(__dirname+'/client/build/index.html'));
});
This should fix the problem. Instead of "*" there should be "/". Now my GET request works like the POST on Heroku.
https://stackoverflow.com/a/37878281/12839684
When attempting to login any user, the following POST error results:
XMLHttpRequest cannot load https://api.stormpath.com/v1/applications/[APP_HREF]/login. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:8080' is therefore not allowed access. The response had HTTP status code 401.
In my server.js file, I have put the following:
var express = require('express');
var stormpath = require('express-stormpath');
var cors = require('cors');
var app = express();
app.use(stormpath.init(app, {
apiKey: {
id: 'xyz',
secret: 'abc' // Using the unsafe inline option for example purposes
},
application: {
href: `[APP_HREF]`
},
web: {
produces: ['application/json']
},
debug: 'info'
}));
app.use(cors({
origin: 'http://localhost:8080',
credentials: true
}));
app.post('/', stormpath.loginRequired, function (req, res) {
function writeError(message) {
res.status(400);
res.json({ message: message, status: 400 });
res.end();
}
});
app.on('stormpath.ready', function () {
var server = app.listen(process.env.PORT, function() {
try {
process.send('CONNECTED');
} catch(e) {}
});
});
In my login.jsx file, I have included:
login: function(e) {
e.preventDefault();
e.stopPropagation();
this.serverRequest = $.post('https://api.stormpath.com/v1/applications/[APP_HREF]/login',
{
"username": document.getElementById("email").value,
"password": document.getElementById("pass").value
}, function (result) {
console.log(result);
});
I also have saved my stormpath.yml file.
I'm not using React-Stormpath because I already created my views for Login and Registering. It looks like I only need to access the REST api with Stormpath, but I'm not sure what I need to add in order to get the API key validated.
On the actual login.jsx file, would I have to send the ID:SECRET pair as well as part of the POST request?
I see that in your login.jsx you are trying to post directly to the Stormpath REST API, but unfortunately that isn't possible yet. Instead you will make the post to your Express server, and in turn it will communicate with Stormpath.
You already have express-stormpath in your Express server, so you just need to post your login form to /login and Stormpath will take care of the rest :)
Let us know if you run into any issues! FYI, we will be adding a "serverless" feature soon, you can follow that here: http://ideas.stormpath.com/ideas/IAM-I-59