I am new to Express and I have problems with sending cookies.
I made a simple express app that needs to set a cookie to the browser. This is the server:
const express = require('express');
const cookieParser = require('cookie-parser');
const cors = require('cors');
const app = express();
//app.use(cors());
app.use((req, res, next) => {
res.append('Access-Control-Allow-Origin', ['http://127.0.0.1:5500']);
res.append('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE');
res.append('Access-Control-Allow-Headers', 'Content-Type');
res.append('Access-Control-Allow-Credentials', 'true');
next();
});
app.use(cookieParser());
app.use(express.json());
const PORT = 9000;
app.get('/', (req, res) => {
res.setHeader('Content-Type', 'application/json');
res.cookie('testCookie', 'random value', {httpOnly: false, secure: false});
res.send({user: "test", password: "test123"});
})
app.listen(PORT, console.log(`Server started on port ${PORT}`));
So it successfully sends to the browser the testCookie on request with fetch:
let response = await fetch('http://localhost:9000/', {
method: 'GET',
mode: 'cors',
credentials: 'include',
headers: {
'Content-Type': 'application/json'
}
}).then(response => response.json());
console.log(response);
After the request the cookie is successfully send because is in the Chrome cookie tab but document.cookie returns an empty string. And also when i make request to a page the request doesnt contains the Cookie header.
How can I make the cookie to be visible to document.cookie and also to the browser to send his Cookie header?
I strongly suggest you to use an npm package: jsonwebtoken, https://www.npmjs.com/package/jsonwebtoken
This way it's much cleaner:
const jwt = require ('jsonwebtoken');
// Create login logic here (check password etc.);
const token = jwt.sign(user, secret, expiration);
res.status(201).json({
status: 'success',
token
});
Try using JSON format instead of res.send since you have a body parser in place already and it's a best practice in modern APIs.
Related
I'm blocked since 3 days and did my research on the internet. Here' is the code.
api.js
const express = require('express');
const router = express.Router();
var http = require('http');
var https = require("https");
router.get('/github', async function (req, res) {
https.get('https://api.github.com/v3/users/tmtanzeel', function (apiRes) {
apiRes.pipe(res);
}).on('error', (e) => {
console.error(e);
res.status(500).send('Something went wrong');
});
});
Output:
Request forbidden by administrative rules. Please make sure your request has a User-Agent header (http://developer.github.com/v3/#user-agent-required). Check https://developer.github.com for other possible causes.
I found something useful here:
In Node.js/Express, how do I automatically add this header to every "render" response?
Requesting https://api.github.com/users (api) returns System.Net.HttpStatusCode.Forbidden IsSuccessStatusCode = false
But these were not very helpful.
I tried: res.setHeader("User-Agent", "My App"); but I'm not very sure about the second argument.
Modified server.js
const express = require('express');
const app = express();
const api = require('./routes/api');
const cors = require('cors');
app.use(cors());
app.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', 'X-Requested-With,content-type');
// 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.setHeader("User-Agent", "My App"); // <-------------ADDED HEADER HERE
// Pass to next layer of middleware
next();
});
app.use('/api', api);
app.get('/', function (req, res) {
res.send('Server is up and running!');
})
app.listen(3000, function () {
console.log('Server listening on 3000 port');
});
Have you ever face this kind of issue. Please help.
You are setting headers to your response. Instead, you must set headers in the API call you make. You can pass options object to the http.get() method and set headers there.
router.get('/github', async function (req, res) {
const options = {
hostname: 'api.github.com',
path: '/v3/users/tmtanzeel',
headers: {
'User-Agent' : 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1521.3 Safari/537.36'
}
}
https.get(options, function (apiRes) {
apiRes.pipe(res);
}).on('error', (e) => {
console.error(e);
res.status(500).send('Something went wrong');
});
});
See this answer on setting github user-agent header:
https://stackoverflow.com/a/16954033/4185234
I'm new to server-side development.
I'm trying to set up a node.js server that can receive posts.
My client-server code sends the post request:
function post(){
fetch('/clicked', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({a: 1, b: 'Text'})
})
.then(function(response){
if(response.ok){
console.log('POST success.');
return;
}
throw new Error('POST failed.');
})
.catch(function(error){
console.log(error);
});
}
And my Node.js server receives it:
const express = require('express');
const app = express();
app.use(express.json());
app.post('/clicked', (req, res) => {
console.log(req.a);
console.log(req.b);
console.log(req.body);
res.sendStatus(201);
})
However, my server console logs all undefined.
What should I do to receive my POST request body?
Try setting up express.json() inside the app:
const express = require('express');
const app = express();
app.use(express.json())
app.post('/clicked', (req, res) => {
console.log(req.a);
console.log(req.b);
console.log(req.body);
res.sendStatus(201);
});
Add this before handling post request.
app.use(require('body-parser').json());
What body-parser does is, it will add all the information we pass to the API to the 'request.body' object.
In my app I have a code from official docs, except one difference: I send xsrfToken in response to POST request, not GET.
var cookieParser = require('cookie-parser')
var csrf = require('csurf')
var bodyParser = require('body-parser')
var express = require('express')
// setup route middlewares
var csrfProtection = csrf({ cookie: true })
var parseForm = bodyParser.urlencoded({ extended: false })
var app = express()
// we need this because "cookie" is true in csrfProtection
app.use(cookieParser())
app.post('/getCsrfToken', /*csrfProtection,*/ function (req, res) {
// check credentials from request.body
// and then
res.render('send', { csrfToken: req.csrfToken() }) //EXCEPTION: csrfToken is not a function
})
app.post('/process', parseForm, csrfProtection, function (req, res) {
res.send('data is being processed')
})
I'm facing the egg-hen problem: if I enable csrfProtection, I cannot get into the endpoint's code without the token, but if I disable it, req.csrfToken becomes undefined.
I need the gerCsrfToken endpoint to be POST, because I don't want to expose password as url parameter.
Question was answered by csurf maintainer, thanks for a quick response!
https://github.com/expressjs/csurf/issues/133
The (tricky) solution is to ignore POST method for this particular endpoint
app.post('/authenticate', csrf({ cookie: true, ignoreMethods: ['POST'] }), function (req, res) {
How do I protect a post call from a angular2 application to a Express server?
In my angular2 application I have a the following HTTP Post.
const headers = new Headers();
headers.append('Content-Type', 'application/json');
const data = {
email: this.form.value.email
};
this.http.post('http://localhost:8080/api/user/email', data, {
headers: headers
})
Now I want to make sure that only my angular 2 application can make the post call to the user api. I did some research about csrf in combination with Express and Angular 2. In my Angular 2 application I made the following implementation to the app.module.ts file.
import { HttpModule, XSRFStrategy, CookieXSRFStrategy } from '#angular/http';
providers: [ {
provide: XSRFStrategy,
useValue: new CookieXSRFStrategy('csrftoken', 'X-CSRFToken')
} ]
I think this is the right way to implement a XSRFStrategy to Angular 2?
For the implementation in Express I followed a few tutorials, but without any success. Most of the time I received:
ForbiddenError: invalid csrf token
How do I implement the CSRF part in my Express api. Here is my Express config:
// call the packages we need
var express = require('express'); // call express
var app = express(); // define our app using express
var bodyParser = require('body-parser');
var cookieParser = require('cookie-parser');
var csrf = require('csurf');
app.use(bodyParser.urlencoded({
extended: false
}));
app.use(bodyParser.json());
var port = process.env.PORT || 8080; // set our port
// ROUTES FOR OUR API
// =============================================================================
var router = express.Router();
router.use(function (req, res, next) {
console.log('Something is happening.');
res.setHeader("Access-Control-Allow-Origin", "http://localhost:4200");
res.setHeader("Access-Control-Allow-Credentials", "true");
res.setHeader("Access-Control-Allow-Methods", "POST");
res.setHeader("Access-Control-Allow-Headers", "Access-Control-Allow-Headers, Origin,Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers");
res.setHeader('Content-Type', 'application/json');
next();
});
app.use('/api', router);
router.post('/user/email', function (req, res) {
..... [how to make sure that this post can only be fired from my angular 2 application ]
}
Update #1
Did some more research and found the following in the Angular 2 docs:
//By default, Angular will look for a cookie called `'XSRF-TOKEN'`, and set
//* an HTTP request header called `'X-XSRF-TOKEN'` with the value of the cookie on each request,
So in my Express application I added the following parts:
const cookieOptions = {
key: 'XSRF-TOKEN',
secure: false,
httpOnly: false,
maxAge: 3600000
}
var csrfProtection = csrf({
cookie: cookieOptions
})
and in the post route I implemented the protection as follow:
router.post('/user/email', csrfProtection, function (req, res) {
console.log('post incomming');
}):
I got the following response headers back
Access-Control-Allow-Credentials:true
Access-Control-Allow-Headers:Access-Control-Allow-Headers, Origin,Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers
Access-Control-Allow-Methods:POST
Access-Control-Allow-Origin:http://localhost:4200
Connection:keep-alive
Content-Length:1167
Content-Type:text/html; charset=utf-8
Date:Mon, 21 Nov 2016 20:07:12 GMT
set-cookie:XSRF-TOKEN=O4JKkjAZRik2H7ml0DoxDc8s; Max-Age=3600000; Path=/
X-Content-Type-Options:nosniff
X-Powered-By:Express
And the request headers:
Accept:*/*
Accept-Encoding:gzip, deflate, br
Accept-Language:en-US,en;q=0.8,nl;q=0.6
Connection:keep-alive
Content-Length:38
content-type:application/json
Host:localhost:8080
Origin:http://localhost:4200
Referer:http://localhost:4200/profile/users
How to implement CSRF protection with Angular2 and Express
By default, Angular will look for a cookie called 'XSRF-TOKEN', and set
an HTTP request header called 'X-XSRF-TOKEN' with the value of the cookie on each request.
To make sure that our backend can set a XSRF-TOKEN cookie, we have to proxy our calls to the api running on port 8080. We can do that with a proxy.config.json file.
{
"/api/*" : {
"target": "http://localhost:8080",
"secure": false,
"logLevel": "debug"
}
}
And in our package.json file we modify the scripts/start function to use our proxy.config.json file:
"scripts": {
"start": "ng serve --proxy-config proxy.config.json",
}
Now every time we run npm start our calls to /api are proxied to localhost:8080. Now we are ready to make a post call to our api server.
In our component we make a http post call and we set the headers to use content-type application/json.
ourfunction() {
const headers = new Headers();
headers.append('Content-Type', 'application/json');
data = { key:value }
this.http.post('/api/user/email', data, {
headers: headers
}).subscribe( (resp: any) => console.log('resp', resp));
}
That is everything we need to do at the Angular2 side. Now we are implement the Express side.
var express = require('express');
var app = express();
var bodyParser = require('body-parser');
var cookieParser = require('cookie-parser');
var csrf = require('csurf');
var cors = require('cors')
We initialise our app and defining some middleware to use in our application.
const cookieOptions = {
key: 'XSRF-TOKEN',
secure: false,
httpOnly: false,
maxAge: 3600
}
const corsOptions = {
origin: 'http://localhost:4200',
optionsSuccessStatus: 200 // some legacy browsers (IE11, various SmartTVs) choke on 204
};
Here we are setting the options to use for csrf and cors middleware.
const port = process.env.PORT || 8080; // set our port
const csrfProtection = csrf({ cookie: cookieOptions })
const router = express.Router();
Implementing the middelware. The order is very important to get the correct results.
app.use(bodyParser.urlencoded({extended: false}));
app.use(bodyParser.json());
app.use('/api', router);
app.use(cors(corsOptions));
app.use(cookieParser());
app.use(csrfProtection);
router.use(function (req, res, next) {
res.setHeader('Content-Type', 'application/json');
next();
});
Thats all we need to do on the Express side. Now we can secure our post calls with a CSRF token.
Compleet express server file
var express = require('express');
var app = express();
var bodyParser = require('body-parser');
var cookieParser = require('cookie-parser');
var csrf = require('csurf');
var cors = require('cors')
const cookieOptions = {
key: 'XSRF-TOKEN',
secure: false,
httpOnly: false,
maxAge: 3600
}
const corsOptions = {
origin: 'http://localhost:4200',
optionsSuccessStatus: 200 // some legacy browsers (IE11, various SmartTVs) choke on 204
};
const port = process.env.PORT || 8080; // set our port
const csrfProtection = csrf({ cookie: cookieOptions })
const router = express.Router();
app.use(bodyParser.urlencoded({extended: false}));
app.use(bodyParser.json());
app.use('/api', router);
app.use(cors(corsOptions));
app.use(cookieParser());
app.use(csrfProtection);
router.use(function (req, res, next) {
res.setHeader('Content-Type', 'application/json');
next();
});
router.post('/user/email', function (req, res) {
console.log('post incomming');
console.log('req', req.body);
res.send('testing..');
});
app.listen(port);
console.log('Magic happens on port ' + port);
I am having a strange issue with cookies in my node app. It is hosted on Heroku and I use JSON Web Tokens stored in a cookie that is authenticated by my express middleware. When I login on my Macbook pro, the cookie is successfully stored. However, when I use Linux Mint desktop, or an Android tablet, the site logs in but then redirects on protected routes and the cookie is never set.
This is where the cookie is set on login:
let token = jwt.sign({
username: user.username,
email: user.email
}, config.privateKey, {
expiresIn: '7d'
});
let userResponse = {
success: true,
message: 'Successfully logged in!',
id: user._id,
email: user.email,
username: user.username
}
// set cookie for 7 days
res.cookie('auth_token',
token,
{maxAge: 604800000, path: "/"}).json(userResponse);
Here is my server.js file:
'use strict';
const express = require('express');
const app = express();
const bodyParser = require('body-parser');
const env = process.env.NODE_ENV || "development";
const mongoose = require('mongoose');
const cookieParser = require('cookie-parser');
const config = require('./app/config/config.js');
process.env.PWD = process.cwd();
// Establish connection with MongoDB
mongoose.connect(config.db.connectString);
app.use(cookieParser());
// Allowing X-domain request
var allowCrossDomain = function(req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE');
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, Cache-Control");
// intercept OPTIONS method
if ('OPTIONS' == req.method) {
res.send(200);
}
else {
next();
}
};
app.use(allowCrossDomain);
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
app.use(express.static('public'));
const db = mongoose.connection;
db.on('error', console.error.bind(console, 'connection error: '));
db.once('open', () => {
console.log('Connected to sondage database');
});
// ===== Import Routers ======
const userRouter = require('./app/routes/user.routes')(express, app);
const pollRouter = require('./app/routes/poll.routes')(express, app);
const authRouter = require('./app/routes/auth.routes')(express, app);
app.use('/api/users', userRouter);
app.use('/api/polls', pollRouter);
app.use('/api/', authRouter);
// For all other requests, use React Router
app.get('*', function (request, response){
response.sendFile(process.env.PWD + '/public/index.html');
});
app.listen(process.env.PORT || 3000, () => {
console.log('Server running');
});
EDIT I have traced this down to a http vs https issue. If I use https in the request, the cookies work. Otherwise cookies aren't set. So I need a way to force the user to do HTTPS.
I was able to fix this using the heroku-ssl-redirect node package. This takes requests and forces the browser to use https for each request.