express does not set cookies in Production but they are visible in respoonse tab - express

II have an exppress app witch works correctly in dev.
However, when i do try to set cookies in prod, cookies are visible in network tab, but do not present in the browser.
I did a research and i think i covered most common problems, still cookies are not set
You may see my express app
I do add express configuration file, which i post here as well
const app = require("express")();
require("./config/express")(app);
app.disable("x-powered-by");
app.use((req, res, next) => {
res.setHeader("Access-Control-Allow-Origin", "example.com");
res.setHeader("Access-Control-Allow-Methods", "POST,GET,OPTIONS");
res.setHeader("Access-Control-Allow-Headers", "*");
res.setHeader("Access-Control-Allow-Credentials", true);
res.setHeader(
"Access-Control-Allow-Headers",
"Origin, X-Requested-With, Content-Type, Accept"
);
if (req.method === "OPTIONS") {
// return res.sendStatus(200);
}
next();
});
//express config.js
const express = require("express");
const path = require("path");
const cookieParser = require("cookie-parser");
const bodyParser = require("body-parser");
const jwt = require("express-jwt");
const jwks = require("jwks-rsa");
const cookieSecret =
process.env.COOKIESECRET ||
"aabbcc";
// const { errorHandler } = require('../utils')
const expressSession = require("express-session");
const config = require("../config/config");
const helmet = require("helmet");
const morgan = require("morgan");
app.use(express.json());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true, limit: "50mb" }));
app.use(cookieParser()); // TRY to use it with secret as in the __express.js file
app.use(express.static("uploads"));
app.use(express.static(path.join(__dirname, "static")));
app.use(express.static(path.resolve(__basedir, "static")));
app.use("/static/uploads", express.static("static/uploads"));
app.use("/files", express.static("files"));
app.use(helmet());
app.use(morgan("combined"));
// app.use(errorHandler(err, req, res, next));
app.use(
expressSession({
secret:
"aabbcc",
resave: false,
saveUninitialized: true, cookies,
cookie: { secure: true, sameSite: "none", domain: 'example.com' },
})
);
app.set("trust proxy", 1);
};
const expiryDate = new Date(Date.now() + 60 * 60 * 1000);
res.cookie(authCookieName, token, {
expires: expiryDate,
httpOnly: true,
secure: true,
domain: "example.com",
});
res.cookie(secondCookieName, secondToken, {
expires: expiryDate,
httpOnly: true,
secure: true,
domain: "example.com",
});
res.status(200).send(user).end();
return;

After some research, it appeared, that this is the problem here
This Set-Cookie was blocked because its Domain attribute is invalid with regards to the current host URL
This is seen as message in the response-headers.
But i do set all domains correcty. I tried with https as well as without it
Does any ever had the same problems?
PS : Both Front end and back end run on subdomains of a main domain
backend.maindomain.com - my backend
frontend.maindomain.com - my frontend
maindomain.com - landing page from witch you are rediirected to the app front end if you want to use it

Solved!
It appears you need to set the main domain name as domain and cookies are being set on each subdomain

Related

Port error when deploying express server with OIDC to Azure App Service

I am attempting to deploy a server to an azure app service. The server code can be found below.
The error I am getting from the log stream is:
2020-11-18T23:36:06.088Z ERROR - Container [container name] didn't respond to HTTP pings on port: 8080, failing site start. See container logs for debugging.
I have PORT set to 8080 and I know that config is picking up as I can see "Server listening on port 8080" in the logs. I have tried changing WEBSITES_PORT to 80 and 8080 as I saw that other posts, but I think my issue is different.
This site was working prior to my adding auth with OIDC libraries.
The app works locally with the server code below.
const https = require('https')
const express = require('express')
const path = require('path')
const app = express()
const fs = require('fs')
const key = fs.readFileSync('./key.pem')
const cert = fs.readFileSync('./cert.pem')
require('dotenv').config()
app.use(express.json())
app.use(express.urlencoded({
extended: true
}))
app.use(express.static('express'))
var cors = require('cors')
const OktaJwtVerifier = require('#okta/jwt-verifier')
const session = require('express-session')
const {
ExpressOIDC
} = require('#okta/oidc-middleware')
var getUserInfo = require('./getUserInfo')
// session support is required to use ExpressOIDC
app.use(
session({
secret: 'this should be secure',
resave: true,
saveUninitialized: false,
cookie: {
httpOnly: false,
secure: true,
},
})
)
const oidc = new ExpressOIDC({
issuer: process.env.ISSUER || 'https://[custom auth server domain].gov/oauth2/default',
client_id: process.env.CLIENT_ID || 'xxxxxxxxxxxxxxxxx',
client_secret: process.env.CLIENT_SECRET || 'xxxxxxxxxxxxxxxxxx',
redirect_uri: process.env.REDIRECT_URI ||
'https://localhost:3000/authorization-code/callback',
appBaseUrl: process.env.APP_BASE_URL || 'https://localhost:3000',
scope: 'openid profile',
})
// ExpressOIDC attaches handlers for the /login and /authorization-code/callback routes
app.use(oidc.router)
app.use(cors())
app.options('*', cors())
app.get('/userinfo', (req, res) => {
let domain = 'dev'
if (req.isAuthenticated()) {
getUserInfo.userRequest(res, req.userContext, domain)
}
})
app.get('/authStatus', (req, res) => {
if (req.isAuthenticated()) {
res.send(req.userContext.userinfo)
}
})
app.post('/forces-logout', oidc.forceLogoutAndRevoke(), (req, res) => {
// Nothing here will execute, after the redirects the user will end up wherever the `routes.logoutCallback.path` specifies (default `/`)
})
var linkObj = {not relevant links used hrefs on html based on env}
// default URL for website
app.get('/', function(req, res) {
res.sendFile(path.join(__dirname + '/express/index.html'))
//__dirname : It will resolve to your project folder.
})
// FAQ Path
app.get('/help', function(req, res) {
res.sendFile(path.join(__dirname + '/express/help.html'))
//__dirname : It will resolve to your project folder.
})
app.get('/links', (req, res) => {
res.json(linkObj)
})
app.post('/forces-logout', oidc.forceLogoutAndRevoke(), (req, res) => {
// Nothing here will execute, after the redirects the user will end up wherever the `routes.logoutCallback.path` specifies (default `/`)
})
// default URL for website
app.get('*', function(req, res) {
res.sendFile(path.join(__dirname + '/express/index.html'))
//__dirname : It will resolve to your project folder.
})
const port = normalizePort(process.env.PORT || '3000')
if (process.env.PORT) {
const server = https.createServer(app)
server.listen(port)
} else {
const server = https.createServer({
key: key,
cert: cert
}, app)
server.listen(port)
}
console.debug('Server listening on port ' + port)
function normalizePort(val) {
var port = parseInt(val, 10)
if (isNaN(port)) {
// named pipe
return val
}
if (port >= 0) {
// port number
return port
}
return false
}
I believe it's this line that could be giving you issues:
const port = normalizePort(process.env.PORT || '3000')
I'd try changing it to:
const port = normalizePort(process.env.PORT || '8080')
You'll also need to change these lines to have your public URL, not localhost:
redirect_uri: process.env.REDIRECT_URI ||
'https://localhost:3000/authorization-code/callback',
appBaseUrl: process.env.APP_BASE_URL || 'https://localhost:3000',
After you change these, you'll need to update your app on Okta to your production redirect URI.

Next.js, Express.js app's apis running in development but status 404 in production

I have deployed nextjs, expressjs application on production but I am getting 404 for all apis.
I have pages that works fine
var session = require('express-session');
var passport = require('passport');
const KnexSessionStore = require('connect-session-knex')(session);
const knex = require('./db/knex.js');
const authMid = require('./config/utils')
const app = express();
app.use(compression())
app.use(cookieParser());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: false}));
// sesstion
const store = new KnexSessionStore({
knex: knex,
tablename: 'sessions' // optional. Defaults to 'sessions'
});
app.use(session({
name: 'connect.sid',
store: store,
secret: 'somesupersecretkey',
resave: false,
saveUninitialized: false,
cookie: {
maxAge: 1000 * 60 * 60 * 24 * 7
},
// cookie: { secure: false }
}));
app.use(passport.initialize());
app.use(passport.session());
require("./config/passport");
require("./config/passport_fb");
require("./config/passport_google");
var routes = require('./routes/index');
routes.mp_routes(app);
// Server-side
const route = pathMatch();
// pages set up
// private & public
app.get('/', (req, res) => {
return nextApp.render(req, res, '/index', req.query)
})
app.get('/questions', (req, res) => {
return nextApp.render(req, res, '/questions', req.query)
})
And my routes/index.js file looks like
module.exports.mp_routes = (app) => {
app.use('/api/v1', questionsRoutes),
app.use('/api/v1', answersRoutes)
}
Everything nextjs pages and express apis do work fine in development. I don't have to do anything but on production pages do work but all apis say status 404. How do i get fixed

Express & Csurf rejects Vue (Axios) post request

I am currently dealing with forms and decided to test sending an email onchange mid registration to the server and give responsive feedback to users.
On Vue component creation I get the csrf token and store it for future posts. I attach it to the headers as 'X-CSRF-Token'. I send the token and still receive the invalid CSRF token error. I have verified the data in headers and the csrf token is in-fact being sent but just being rejected or the header is missing something.
Screenshot of error and response
//App.js
var createError = require('http-errors');
var cors = require('cors');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');
var csurf = require('csurf')
var Mongoose = require('mongoose')
var indexRouter = require('./routes/index');
var usersRouter = require('./routes/users');
var productRouter = require('./routes/products')
var app = express();
// DB things
var db
dbConnect();
app.use(cors())
app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser())
app.use(csurf({ cookie:true }))
app.use('/api/', indexRouter);
app.use('/api/users', usersRouter);
app.use('/api/products', productRouter)
//index routes
var express = require('express');
var router = express.Router();
/* GET home page. */
router.get('/', function(req, res, next) {
res.json({
message:'respond with a resource blank',
});
});
router.get('/getCSRF/', function(req, res, next) {
res.json({
csrf:req.csrfToken(),
});
});
module.exports = router;
Below is the route I try posting to
// Users Check Email Post Route
router.post('/checkEmail',function(req,res,next){
email = req.body.email
console.log(email)
User.findOne(function(err,user){
if (err) { return res.json({ err:err })}
else { return res.json({ user:true }) }
})
})
Here is the method used in Vue to post
checkEmail: function () {
var headers = {
'Accept': 'application/json',
'Access-Control-Allow-Origin': '*' ,
'Access-Control-Allow-Methods': 'GET, POST, OPTIONS, PUT, PATCH, DELETE',
'Access-Control-Allow-Headers': 'Origin, X-Requested-With, Content-Type,
Accept',
'Content-Type':'application/x-www-form-urlencoded',
'X-CSRF-Token':this.$store.getters.getCSRF,
}
axios.post('http://localhost:8000/api/users/checkEmail', {
email: this.user.Email
},headers )
.then(function (response) {
console.log(response)
})
.catch(function (error) {
console.log(error.response)
});
},
The issue is pretty common and I have gone through over 20 posts and tried their solutions but it did not help at all. I've tried using csurf independently on the same route(didn't work). I've tried all manner of headers. Your help will be greatly appreciated .
EDIT: Uploading to show proper headers pre-post. Pre-post logs of data
So I realized that csurf looks for two things . A cookie (this is not very apparent from the error but nevertheless) and the token to be sent via header . Axios by default apparently does not support cookies so I decided to move to a JWT header approach.
Your way of sending the token via router.get('/getCSRF/', ...) does not seem very secure to me, as any attacker could also easily get this token via this get request.
If you use app.use(csurf({ cookie:true })), then Express will validate every POST/PUT/DELETE request based on a cookie, but you need to set this cookie yourself.
(Csurf sets a cookie named _csrf but this is not the actual CSRF token)
I got it working this way:
Nodejs:
app.use(cookieParser());
app.use(
csrf({
// compare the XSRF-TOKEN cookie with the X-XSRF-TOKEN header on every post request
cookie: {
secure: true,
sameSite: 'strict',
},
})
);
app.use(function (req, res, next) {
// set the XSRF-TOKEN cookie so the client can send it back in the X-XSRF-TOKEN header
res.cookie('XSRF-TOKEN', req.csrfToken());
next();
});
I used Nuxt as my client. Nuxt.config.js:
modules: [
'#nuxtjs/axios',
],
axios: {
credentials: true, // this will take the XSRF-TOKEN from the cookie and set it in the X-XSRF-TOKEN header
baseURL: nuxtBaseUrl,
browserBaseURL: nuxtBrowserBaseUrl,
},
publicRuntimeConfig: {
axios: {
credentials: true, // this will take the XSRF-TOKEN from the cookie and set it in the X-XSRF-TOKEN header
baseURL: nuxtBaseUrl,
browserBaseURL: nuxtBrowserBaseUrl,
},
},
Alternative without nuxt:
const instance = axios.create({
baseURL: 'https://my-server/',
withCredentials: true, // this will take the XSRF-TOKEN from the cookie and set it in the X-XSRF-TOKEN header
});
instance.post('http://my-server/api/users/checkEmail', {
email: this.user.Email
}

Can't set a cookie w/ Nuxt.js, Express-Session

I'm new to NUXT and SSR and I've been researching this for a few hours now and I can't seem to figure it out. I'm using JWT to authenticate users in my Nuxt app with a Bearer Token, which is working great until I hit refresh and lose my session.
Now I'm looking to persist sessions using express-session and connect-mongo. I can't get the cookie to set on the client to be included on future requests.
When a user is authenticated:
router.post('/login', function(req, res) {
User.findOne({
username: req.body.username
}, function(err, user) {
if (err) throw err;
if (!user) {
res.status(401).send({success: false, msg: 'Authentication failed. User not found.'});
} else {
// check if password matches
user.comparePassword(req.body.password, function (err, isMatch) {
if (isMatch && !err) {
// if user is found and password is right create a token
var token = jwt.sign(user.toJSON(), config.secret, { expiresIn: 604800 });
req.session.authUser = { 'user': 'Test User' }
return res.json({success: true, token: token, user: user});
} else {
res.status(401).send({success: false, msg: 'Authentication failed. Wrong password.'});
}
});
}
The console.log above shows the authUser in the session.
Session {
cookie:
{ path: '/',
_expires: 2018-04-03T18:13:53.209Z,
originalMaxAge: 60000,
httpOnly: true },
authUser: { user: 'Test User' } }
When I look at my chrome devtools application cookies a connect.ssid hasn't been set and when I console.log(req.session) on future requests the authUser is missing.
My server code is:
// Passport
var passport = require('passport');
var passportJWT = require("passport-jwt");
var ExtractJwt = passportJWT.ExtractJwt;
var JwtStrategy = passportJWT.Strategy;
// Config File
let config = require('./config/settings.js')
// Initialize Express
var app = express();
// CORS-ENABLE
app.use(function(req, res, next) {
res.header("Access-Control-Allow-Origin", "http://127.0.0.1:1337");
res.header("Access-Control-Allow-Credentials", 'true');
next();
});
app.use(cors())
const dbPath = 'mongodb://blogUser:blogUserPassword#localhost:27017/blog'
// Express Session
app.use(session({
secret: 'super-secret-key',
resave: false,
saveUninitialized: false,
store: new MongoStore({ url: dbPath }),
cookie: { maxAge: 60000 }
}))
// File Upload
app.use(fileUpload());
// view engine setup
// app.set('views', path.join(__dirname, 'views'));
// app.set('view engine', 'jade');
// uncomment after placing your favicon in /public
//app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
// Routes
var index = require('./routes/index');
var users = require('./routes/users');
app.use('/api', index);
app.use('/users', users);
// Passport Config
app.use(passport.initialize());
app.use(passport.session())
// mongoose
const options = {
autoIndex: true, // Don't build indexes
reconnectTries: Number.MAX_VALUE, // Never stop trying to reconnect
reconnectInterval: 500, // Reconnect every 500ms
poolSize: 10, // Maintain up to 10 socket connections
// If not connected, return errors immediately rather than waiting for reconnect
bufferMaxEntries: 0
};
console.log(options);
// Localhost Connect
mongoose.connect(dbPath, options).then(
() => { console.log("connected !!!"); },
err => { console.log(err); }
);
Any and all help is appreciated.
If you want to use the server you create the problem with the session is the express router, because change res and req vars so like recommend in nuxt use this.
const express = require('express');
// Create express router
const router = express.Router()
// Transform req & res to have the same API as express
const app = express()
router.use((req, res, next) => {
Object.setPrototypeOf(req, app.request)
Object.setPrototypeOf(res, app.response)
req.res = res
res.req = req
next()
})
You are missing this step
// Create express router
const router = express.Router()
// Transform req & res to have the same API as express
// So we can use res.status() & res.json()
router.use((req, res, next) => {
Object.setPrototypeOf(req, app.request)
Object.setPrototypeOf(res, app.response)
req.res = res
res.req = req
next()
})
The req, res parameters need to be interchanged on the client side
Once you do router.post('/login') and logout
app.use('/api', router)
And that will work perfectly

In express when reading cookies, it returns an empty object as {}

I'm trying to set cookie using res.cookie or res.append('Set-Cookie',...) on express.js, but when I use req.cookies, it return {}(which is the default value of res.cookies). I've searched many web pages but it still can't return the right value. PS: Express is 4.15.2
in server.js:
var express = require('express');
var session = require('express-session');//设置session
var cookieParser = require('cookie-parser');//解析cookie
var bodyParser = require('body-parser');
var app = express();
app.use(express.static('public'));
app.use(session({
secret: 'keyboard cat',
cookie: { maxAge: 60000 },
resave: false,
saveUninitialized: true
}));
app.use(bodyParser.urlencoded({extended: true}));
app.use(bodyParser.json());
app.use(cookieParser());
//设置跨域访问
app.all('*', 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");
res.header("X-Powered-By",' 3.2.1')
res.header("Content-Type", "application/json;charset=utf-8");
next();
});
// 查询所有博客数据
app.get('/getAll',function(req,res){
var pageNum = req.query.pageNum;
//连接数据库
query("some sql",function(err,vals,fields) {
console.dir(req.session.id);
// Cookies that have not been signed
console.log('Cookies: ', req.cookies)
// Cookies that have been signed
console.log('Signed Cookies: ', req.signedCookies)
res.send(vals);
});
});
//登陆验证
app.post('/login',function(req,res){
var username = req.body.username;
var password = req.body.password;
//过滤
var sql = "some sql";
query(sql,function(err,vals,fields) {
if(vals.length == 1){
//登录成功
//res.cookie('mycookie', '1234567890', { domain:'localhost',secure:false, maxAage:120000, httpOnly: true });
res.append('Set-Cookie', 'foo=bar; Path=/; HttpOnly');
// req.cookie('sid',req.session.id,{ maxAge: 900000, httpOnly: false});
res.send("ok");
}else{
//登陆失败
res.send("false");
}
});
});
app.get('/', function (req, res) {
res.sendFile( __dirname + "/"+ "index_prod.html" );
})
var server = app.listen(8081, function () {
var host = server.address().address
var port = server.address().port
console.log("应用实例,访问地址为 http://%s:%s", host, port)
})
When I login in, the result is as following:
cookie
but when I request for /getAll ,the console for cookies are: { }. (the default value, NOT the value we just write).
If the cookies and session have been correctly used, the reasons may be :
Make sure the client and the server obey the Same-origin policy. If not, make some changes.
Make sure your websites have no errors. (In the code above, there exit other errors such as 404 (Not Found) in the process of images fetching)