Express: unable to access route from browser due to accept:application/javascript header missing - express

I'm new to express. I have a Vue application running on express. I have some API routes that I'm able to access using axios through the browser. To access those routes using postman I have to have the header:
accept: application/javascript
for it to return the result of the actual API. If I don't use this header, I get the generated index.html from webpack. I need to reuse one of these routes to return excel/pdf, based on a parameter and have it accessible via a link on the page.
Here's my server.js - based on https://github.com/southerncross/vue-express-dev-boilerplate
import express from 'express'
import path from 'path'
import favicon from 'serve-favicon'
import logger from 'morgan'
import cookieParser from 'cookie-parser'
import bodyParser from 'body-parser'
import webpack from 'webpack'
const argon2 = require('argon2');
const passport = require('passport')
const LocalStrategy = require ('passport-local')
const session = require('express-session')
import history from 'connect-history-api-fallback'
// Formal(Prod) environment, the following two modules do not need to be introduced
import webpackDevMiddleware from 'webpack-dev-middleware'
import webpackHotMiddleware from 'webpack-hot-middleware'
import config from '../../build/webpack.dev.conf'
const app = express()
app.set('trust proxy', true)
app.set("view engine", "pug")
app.set('views', path.join(__dirname, 'views'))
app.use ('/', require('./routes'))
app.use(session({
secret: process.env.SESSION_SECRET || 'secretsauce',
resave: false,
saveUninitialized: true
}))
app.use(history())
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')))
const compiler = webpack(config)
app.use(webpackDevMiddleware(compiler, {
publicPath: config.output.publicPath,
stats: {
colors: true
}
}))
app.use(webpackHotMiddleware(compiler))
////////// PASSPORT ///////////////////////
app.use (passport.initialize ());
app.use (passport.session ());
async function authenticateUser (username, password) {
//...
}
passport.use (
new LocalStrategy (async (username, password, done) => {
const user = await authenticateUser (username, password)
if (!user) {
return done (null, false, {
message: 'Username and password combination is wrong',
});
}
delete user.password;
return done (null, user)
})
);
// Serialize user in session
passport.serializeUser ((user, done) => {
done (null, user);
});
passport.deserializeUser (function(user, done) {
if(user === undefined || !user || Object.keys(user).length === 0)
return done(null, false)
else
done (null, user);
});
//////////// passport end ///////////////
app.set("view engine", "pug")
app.use(express.static(path.join(__dirname, 'views')))
app.get('/', function (req, res) {
res.sendFile('./views/index.html')
})
app.get('/success', function (req, res) {
res.render('./views/success')
})
app.use ('/api', require('./api'))
// catch 404 and forward to error handler
app.use(function (req, res, next) {
var err = new Error('Not Found')
err.status = 404
next(err)
})
app.use(function (err, req, res) {
res.status(err.status || 500)
res.send(err.message)
})
let server = app.listen(80)
export default app
And here's a bit of api.js
const {Router} = require ('express')
const router = Router()
router.get('/whome', function(req, res){
logger.info('whome', req.user)
return res.json(req.user)
})
router.get ('/hello', auth.isAuthenticated, async (req, res) => {
res.json ({text:'hello'})
})
module.exports = router
I can call http://localhost/api/hello from postman with the accept:application/javascript header and I get:
{
"text": "hello"
}
as expected. But if I call the same URL from the browser (and it's not sending that header), I get the created bundle index.html. How can I access these routes from the browser?

You have two options.
First one, try to add this in your server:
app.options('*', cors())
before to: app.set("view engine", "pug")
If that doesnt work, try to install this addon in your Google Chrome browser to test.
Allow-Control-Allow-Origin: *
And enable it. (The icon should be green instead of red).
Why this happens?
The request that's being made is called a preflight request.
Preflight requests are made by the browser, as CORS is a browser security restriction only - This is why it works in Postman, which is, of course, not a browser.
Reference: Preflight request

Related

req.isAuthenticated() returns FALSE (express-session, passport-local)

Edit: I got the cookie to be set by transferring app.use(session({...})) from register.js to app.js before I declare use of the router in app.js. However, the problem - req.isAuthenticated() returning false, still persists.
I'm trying to build a sign up flow using passport-local, express-session and mongodb for my backend.
When I register a new user, it seems to work fine and the user gets created in my database as desired. I can also access req.user in my passport.authenticate() callback post registration. However, when I redirect to a new route using res.redirect(), req.isAuthenticated() returns false on my new route.
Upon checking the cookies in my browser, it seems that express-session never saves the session as it is supposed to. There is no cookie from my localhost. I've done hours of research, and most answers I've come across just relate to double-checking the sequence of code when initializing. I've done this countless times, as well as tried to use both passport.serializeUser(User.serializeUser()); and passport.serializeUser(function(user, done) { done(null, user.id); }); to make sure I'm doing that step correctly. I can't seem to figure out why I'm having this issue. Any and all help will be deeply appreciated! Relevant snippets from my code -
Register.js (Router)
var express = require('express');
const session = require('express-session');
const passport = require('passport');
const LocalStrategy = require('passport-local').Strategy;
const passportLocalMongoose = require('passport-local-mongoose');
var router = express.Router();
const app = express();
app.use(bodyParser.urlencoded({extended: true}));
app.use(session({
secret:"Edit Later.",
resave:true,
saveUninitialized:true
}));
app.use(passport.initialize());
app.use(passport.session());
const userSchema = new mongoose.Schema({
name:String,
username:String,
contactNumber:String
});
userSchema.plugin(passportLocalMongoose);
passport.use(new LocalStrategy(User.authenticate()));
passport.serializeUser(User.serializeUser());
passport.deserializeUser(User.deserializeUser());
router.post('/', function(req, res, next) {
const reCaptchaKey = "<MYKEY>";
var userCaptchaToken = req.body['g-recaptcha-response'];
axios.post('https://www.google.com/recaptcha/api/siteverify', null, {params: {
secret: reCaptchaKey,
response: userCaptchaToken
}}).then(response => {
if (response.data.success === true) {
User.register({username: req.body.username, name: req.body.fullName}, req.body.password,
function(err, user) {
if (err) {
console.log(err);
// res.redirect('/views/register.html');
} else {
passport.authenticate('local', {session:true})(req, res, function() {
console.log(req.user);
console.log(req.session);
res.redirect('/profile');
console.log("Done!");
});
}
});
} else if (response.data.success === false) {
console.log("Captcha failed.");
}
}).catch(error => {
console.log(error);
});
});
module.exports = router;
Profile.js (Router)
const express = require('express');
const router = express.Router();
router.get('/', function(req, res) {
if (req.isAuthenticated()) {
res.render('profile')
} else {
res.redirect('/register')
console.log('Not a user.')
}
})
module.exports = router;
App.js
var registrationRouter = require('./routes/register');
var profileRouter = require('./routes/profile');
app.use('/register', registrationRouter);
app.use('/profile', profileRouter);
module.exports = app;
So after hours of trying, I figured it out. My issue was I used the following code in Register.js
app.use(session({
secret:"Edit Later.",
resave:true,
saveUninitialized:true
}));
app.use(passport.initialize());
app.use(passport.session());
This was a problem because Register.js was being used as a router, and I directed all requests to this route via App.js. This led to the app not being configured properly before the requests were handled by Register.js. The above lines are configuration code and MUST be written before app.use('/register', registrationRouter); in App.js. Thus, the fix was simply to move these lines over to App.js like so -
\\configure App
app.use(session({
secret:"Edit Later.",
resave:true,
saveUninitialized:true
}));
app.use(passport.initialize());
app.use(passport.session());
\\handle router
app.use('/register', registrationRouter);

Connect flash not displaying messages in Pug

I have an application with several routes, and I want to send flash messages of different kinds given the user interaction: either a success or failure message, or in some cases no message. Right now the messages are not displaying and I can't figure out how to get it to work. I'm using Node, Express and Pug.
I have a server.js file, routes.js file, message.pug file, and layout.pug file. Here are my files:
server.js
// init project
const express = require('express');
const app = express();
const bodyparser = require("body-parser");
const flash = require('connect-flash');
const passport = require("passport");
const session = require("express-session");
// http://expressjs.com/en/starter/static-files.html
app.use(express.static("public"));
app.set('view engine', 'pug');
// bodyparser middleware
app.use(bodyparser.urlencoded({ extended: false }));
app.use(bodyparser.json());
// express-session middleware
app.use(session({
secret: process.env.SECRET,
resave: true,
saveUninitialized: true
}));
// express-messages middleware
app.use(flash());
app.use((req, res, next) => {
res.locals.messages = require('express-messages')(req, res);
next();
});
app.use(express.json());
// import passport-config file
require("./passport-config")(passport);
// passport middleware
app.use(passport.initialize());
app.use(passport.session());
const routes = require('./routes.js');
routes(app);
// listen for requests :)
const listener = app.listen(process.env.PORT, () => {
console.log('Your app is listening on port ' + listener.address().port);
});
routes.js
app.get('/', (req, res) => {
req.flash("success", "your flash messages are working");
res.redirect("/admin");
});
app.get("/admin", (req, res) => {
res.render(process.cwd() + '/views/pug/admin');
});
message.pug
.messages
each type in Object.keys(messages)
each message in messages[type]
div(class="alert alert-" + type) #{ message }
// expected output
// div(class="alert alert-success") your flash messages are working
layout.pug
div.col-10.ml-sm-auto.px-4
!= messages('message', locals)
Since I had only two types of messages I was sending -- success or error -- I got rid of my messages.pug file and used the following code, which works.
server.js
// init project
const express = require('express');
const app = express();
const bodyparser = require("body-parser");
const flash = require('connect-flash');
const passport = require("passport");
const session = require("express-session");
// http://expressjs.com/en/starter/static-files.html
app.use(express.static("public"));
app.set('view engine', 'pug');
// bodyparser middleware
app.use(bodyparser.urlencoded({ extended: false }));
app.use(bodyparser.json());
// express-session middleware
app.use(session({
secret: process.env.SECRET,
resave: true,
saveUninitialized: true
}));
// express-messages middleware THIS IS WHAT I CHANGED
app.use(flash());
app.use((req, res, next) => {
res.locals.errors = req.flash("error");
res.locals.successes = req.flash("success");
next();
});
app.use(express.json());
// import passport-config file
require("./passport-config")(passport);
// passport middleware
app.use(passport.initialize());
app.use(passport.session());
const routes = require('./routes.js');
routes(app);
// listen for requests :)
const listener = app.listen(process.env.PORT, () => {
console.log('Your app is listening on port ' + listener.address().port);
});
routes.js (the same as before)
app.get('/', (req, res) => {
req.flash("success", "your flash messages are working");
res.redirect("/admin");
});
app.get("/admin", (req, res) => {
res.render(process.cwd() + '/views/pug/admin');
});
Then in layout, I use the following:
div.col-10.ml-sm-auto.px-4
if successes
for success in successes
div.alert.alert-success #{ success }
if errors
for error, i in errors
div.alert.alert-danger #{ error }
This works for displaying a message in a redirect.
Note: If you want to display a message directly (via res.render()), you have to pass it to the render method directly like this:
app.get('/admin', (req, res) => {
req.flash("success", "flash message for a render method");
res.render("/admin", { successes: req.flash("success") });
});

Google OAuth Broken After Heroku Deployment

My Google OAuth Strategy works perfectly on the client side. However after deploying the project to heroku the OAuth breaks once google attempts to redirect the user back to the specified redirect route. I am getting a Request Timed out error on the '/auth/google/callback/ route which is the route google is sending the user back to after authentication. It is defined in my authRoutes (Screenshot below). I am using PassportJS and Express for my backend. My development keys have been setup correctly as have my Google OAuth routes, the error only occurs upon the redirect from Google. Any help would be much appreciated
Here is my Passport Strategy:
const GoogleStrategy = require('passport-google-oauth20').Strategy
const keys = require('../config/keys')
const passport = require('passport')
const mongoose = require('mongoose')
const User = mongoose.model('users')
// This will store a cookie containing the user ID
// in our session after login is complete
passport.serializeUser((user, done) => {
done(null, user.id);
});
// This is called during every request
// It obtains a user object from the user id we serialized earlier
// the user object is stored in req.user
passport.deserializeUser((id, done) => {
User.findById(id).then(user => {
done(null, user);
});
});
passport.use (
new GoogleStrategy({
clientID: keys.googleClientID,
clientSecret: keys.googleClientSecret,
callbackURL: '/auth/google/callback',
proxy: true
},
async (accessToken, refreshToken, profile, done) => {
const existingUser = await User.findOne({googleID: profile.id});
if(!existingUser) {
const user = await User.create({googleID: profile.id}).save()
return done(null, user)
}
done(null, existingUser)
})
)
Here are my Routes:
const passport = require('passport')
module.exports = app => {
// This handles getting authentication details
// (gmail profile) from google
app.get("/auth/google", passport.authenticate('google', {
scope: ['profile', 'email']
}))
app.get("/auth/google/callback", passport.authenticate('google'), (req, res) => {
res.redirect('/surveys')
})
app.get("/api/logout", (req, res) => {
// Provided by passport
req.logout()
res.redirect('/')
})
app.get("/api/current_user", (req, res) => {
res.send(req.user)
})
}
And my Index.js:
const express = require('express');
const mongoose = require('mongoose');
const cookieSession = require('cookie-session');
const passport = require('passport');
const keys = require('./config/keys');
const bodyParser = require('body-parser')
require('./models/User');
require('./models/Survey');
require('./services/passport');
mongoose.connect(keys.mongoURI);
const app = express();
app.use(
cookieSession({
maxAge: 30 * 24 * 60 * 60 * 1000,
keys: [keys.cookieKey]
})
);
app.use(bodyParser.json())
app.use(passport.initialize());
// This middleware injects a cookie in every request
// to allow us to identify the user
app.use(passport.session());
require('./routes/authRoutes')(app);
require('./routes/billingRoutes')(app);
if(process.env.NODE_ENV == "production"){
// Express will serve up production assets
// like our main.js file or main.css file!
app.use(express.static('client/build'))
// Express will serve up the index.html file
// if it doesn't recognize the route
const path = require('path')
app.get('*', (req, res ) => {
res.sendFile(path.resolve(__dirname, 'client', 'build', 'index.html'))
})
}
const PORT = process.env.PORT || 5000;
app.listen(PORT);
Heroku Error:
at=error code=H12 desc="Request timeout" method=GET path="/auth/google/callback?code=4/tgDTZZu8osZZvvMGjX4qaazb46SqNukZU6kNARY7R2enmH21cX6IfkVYSVnVHIoQ_qHaUbLttS_VGiS81KYE3D0&scope=email+profile+https://www.googleapis.com/auth/userinfo.email+https://www.googleapis.com/auth/userinfo.profile" host=cryptic-citadel-17268.herokuapp.com request_id=e8eb956d-64e4-4217-b846-91c53953e0a7 fwd="193.203.134.47" dyno=web.1 connect=0ms service=30001ms status=503 bytes=0 protocol=https
It's how you connect to your DB. You've mentioned that everything is working on your localhost correctly, but your production is giving your errors, please check this bit on prod - your connection error is likely due to production DB key configuration.
passport.use (
new GoogleStrategy({
clientID: keys.googleClientID,
clientSecret: keys.googleClientSecret,
callbackURL: '/auth/google/callback',
proxy: true
},
async (accessToken, refreshToken, profile, done) => {
const existingUser = await User.findOne({googleID: profile.id});
if(!existingUser) {
const user = await User.create({googleID: profile.id}).save()
return done(null, user)
}
done(null, existingUser)
})

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

Heroku App Login works for HTTP but not HTTPS

I have passport.js set up with strategies for Google, Facebook and Github. They work fine over HTTP but not so in HTTPS.
When I'm on my site over HTTPS, and I click for example, Login with Google, I see in my URL bar the site has sent me to the relative URL '/auth/google'. This is the route I've wired in my backend to initiate the OAuth process for logging in. But in HTTPS, I simply end up at the page, there are no error messages in dev console. I have a catch all block of code that serves up index.html when the URL doesn't match any of my routes and I'm pretty sure this is what's happening i.e. the backend routes doesn't seemed to be recognised.
So to summarise, on HTTP, the app stays in a non-logged in state and there are no errors in the dev console.
What's worth noting is that I can also get it to stay in logged in state with the same behaviour as well. If I log in over HTTP and then immediately go to the HTTPS site, I'm logged in. If I try to log out, I get sent to '/auth/logout'. Again no error in console and I get served index.html as if I hadn't written a route for '/auth/logout' and I continue to be logged in.
Because there are no error messages, I don't know what part of the code to show here, I'll just show what I think could be possibly relevant.
Here are my auth routes:
const passport = require('passport')
const express = require('express')
const auth = express.Router()
auth.get(
'/google',
passport.authenticate('google', {
scope: ['profile']
})
)
auth.get('/google/callback', passport.authenticate('google'), (req, res) => {
res.redirect('/')
})
auth.get('/facebook', passport.authenticate('facebook'))
auth.get(
'/facebook/callback',
passport.authenticate('facebook'),
(req, res) => {
res.redirect('/')
}
)
auth.get('/github', passport.authenticate('github'))
auth.get('/github/callback', passport.authenticate('github'), (req, res) => {
res.redirect('/')
})
auth.get('/current_user', (req, res) => {
res.send(req.user)
})
auth.get('/logout', (req, res) => {
req.logout()
res.redirect('/')
})
module.exports = auth
Here is my passport strategies
const passport = require('passport')
const GoogleStrategy = require('passport-google-oauth20').Strategy
const FacebookStrategy = require('passport-facebook').Strategy
const GithubStrategy = require('passport-github').Strategy
const mongoose = require('mongoose')
const keys = require('../config/keys')
const User = mongoose.model('user')
passport.serializeUser((user, done) => {
done(null, user.id)
})
passport.deserializeUser((id, done) => {
User.findById(id).then(user => {
done(null, user)
})
})
const login = (accessToken, refreshToken, profile, done) => {
User.findOne({ profileID: profile.id }).then(existingUser => {
if (existingUser) {
done(null, existingUser)
} else {
new User({
profileID: profile.id
})
.save()
.then(user => done(null, user))
}
})
}
passport.use(
new GoogleStrategy(
{
clientID: keys.googleClientID,
clientSecret: keys.googleSecretKey,
callbackURL: '/auth/google/callback',
proxy: true
},
login
)
)
passport.use(
new FacebookStrategy(
{
clientID: keys.facebookClientID,
clientSecret: keys.facebookSecretKey,
callbackURL: '/auth/facebook/callback',
profileFields: ['id', 'name'],
proxy: true
},
login
)
)
passport.use(
new GithubStrategy(
{
clientID: keys.githubClientID,
clientSecret: keys.githubSecretKey,
callbackURL: '/auth/github/callback',
proxy: true
},
login
)
)
And here is my server index.js
const express = require('express')
const mongoose = require('mongoose')
const passport = require('passport')
const session = require('express-session')
const bodyParser = require('body-parser')
const keys = require('./config/keys')
const auth = require('./routes/authRoutes')
const poll = require('./routes/pollRoutes')
require('./models/User')
require('./services/passport')
mongoose.connect(keys.mongoURI, { useMongoClient: true })
mongoose.Promise = global.Promise
const app = express()
app.use(bodyParser.json())
app.use(
session({
secret: keys.cookieKey,
saveUninitialized: true,
resave: true
})
)
app.use(passport.initialize())
app.use(passport.session())
app.use('/auth', auth)
app.use('/poll', poll)
if (process.env.NODE_ENV === 'production') {
app.use(express.static('client/build'))
const path = require('path')
app.get('*', (req, res) => {
res.sendFile(path.resolve(__dirname, 'client', 'build', 'index.html'))
})
}
const PORT = process.env.PORT || 5000
app.listen(PORT, () => console.log(`Server started on port ${PORT}`))
I'll also provide a link to my Heroku app so you guys can see the buggy behaviour over HTTPS and a link to my Github repo
I'm pretty sure this doesn't have anything to do with how I've set up my Oauth on developer consoles of Google, Facebook and Github because all three login strategies behave exactly the same way so I would have to set up the Google+ API credentials, the Facebook Login settings and the Github Developers Settings all in a way to produce the same error which seems really unlikely. Also everything works correctly on LocalHost. Can someone please help me with this issue?