Unable to verify authorization state on Heroku - node.js

I'm running a nodejs/reactjs app on heroku. I implemented google login using Passport. I'm getting an error "Unable to verify authorization state" when people try to login.
I see here NodeJS Express Session isn't being restored between routes in kubernetes that I need to set the X-Forwarded-SSL header. How do I do that according to what the question says?
The solution outlined on that page also mentions Apache, but Heroku doesn't make use of Apache to forward requests to apps' web dynos.
Is anyone running into the same issue on Heroku?
So the weird thing is when I try to login, it works the second time but the first time, I get the error "Unable to verify authorization state".
here's my index.js
const session = require("express-session");
app.use (
session ({
secret: "ddd",
resave: false,
saveUninitialized: true,
cookie: {
expires: 60 * 60 * 24,
secure: (app.get('env') === 'production')
}
})
);
if (app.get('env') === 'production') {
app.set('trust proxy', 1); // trust first proxy
}

So as I mentioned your issue might not be related to the request headers because in my issue, session never persisted, whereas yours does in your second attempt. It might be an issue with your verify function or your deserializeUser function.
Here's an example. I don't use Google auth personally, but something else. My code looks similar, but I got some of the Google auth code from https://github.com/jaredhanson/passport-google/blob/master/examples/signon/app.js. Fill in your stuff where appropriate and debug/log what's coming in to see how your functions are being called.
passport.use(new GoogleStrategy({
returnURL: 'http://localhost:3000/auth/google/return', // replace with yours
realm: 'http://localhost:3000/' // replace with yours
}, function (identifier, profile, done) {
// console.log identifier and profile, or even better, use your debugger on this line and see what's happening
// Do whatever you need to do with identifier and then..
return done(null, profile);
}));
passport.serializeUser(async (user, done) => {
// console.log user or use your debugger on this line and see what's happening
const userInDb = await db.getUser(user) // Find your user in your db using based on whatever is in the user object
const serializedSessionUser = {id: userInDb.id, username: userInDb.username} // this is the object that'll appear in req.user. Customize it like you want
return done(null, serializedSessionUser); // Commit it to the session
});
passport.deserializeUser((user, done) => {
done(null, user);
});
Edit: Apparently there's 2 ways to use Google for passport. The first is OAuth / OAuth 2.0. The second is OpenID. I used OpenID in this example. Adjust accordingly!
Edit 2: Here's my own equivalent to your index.js:
app.use(session({
cookie: {
sameSite: 'lax',
secure: !['development', 'test'].includes(process.env.NODE_ENV),
maxAge: 1000 * 60 * 60 * 24 * 30, // 30 days
},
proxy: true,
resave: false,
saveUninitialized: true,
secret: process.env.COOKIE_SECRET,
store: 'your store',
rolling: true,
}));

Related

Firebase node.js application opens someone else's profile

I have a node.js application which I deployed as Firebase function. The issue that I am having is, if user1 is logged it from a window and user2 open the url in a seperate device+browser, User2 is automatically logged in as user1. I am not sure why this is happening.
Some things I have tried
Setting the Auth persistence to None.
Changing my session config in index.js which looks like
app.use(session({
name: '__session',
secret: 'random',
resave: true,
saveUninitialized: true,
cookie: {
maxAge: 5 * 24 * 60 * 60 * 1000, // 5 days
secure: false,
httpOnly: true
}
}));
app.use(flash());
app.use(async(req, res, next) => {
res.locals.success = req.flash('success');
res.locals.error = req.flash('error')
res.locals.currentUser = firebase.auth().currentUser;
next();
});
Anyone faced similar issue? Any idea how to fix this?
Thanks a lot in advance!
Session data is not saved in the cookie itself, just the session ID. Session data is stored server-side.

Staying logged in after logging out with Express and MongoDB

I am having a problem with logging out of my application.
I am using MongoDB to store my sessions.
I log out by using session.destroy and the document gets removed from the collection in the database. However I still remained logged in. Also the cookie in the browser still exists, surely this should expire. I believe my authentication is using the cookie in the browser to check authentication and since that is still there, it keeps me logged in.
Here is my authentication code:
app.js
app.use(session({
secret: 'whatshouldmysecretkeybe',
cookie: {
maxAge: 86400000
},
resave: false,
unset: 'destroy',
saveUninitialized: false,
store: new MongoStore({
mongooseConnection: db
})
}));
loginController.js
// Authenticated Check
exports.requires_login = function (req, res, next) {
if (req.session && req.session.userId) {
return next();
} else {
return res.redirect('/cms/unauthenticated');
return;
}
}
exports.logout = function (req, res, next) {
if (req.session) {
// delete session object
req.session.destroy(function (err) {
if (err) {
return next(err);
} else {
return res.redirect('/cms/');
}
});
}
}
Route file
I put the login_controller.requires_login middleware on the route to check for authentication on every route that needs authentication. I hope this is the best way to do this.
/* GET Login authenticate */
router.get('/users/logout', login_controller.requires_login, login_controller.logout);
/* GET User listing page */
router.get('/users', login_controller.requires_login, user_controller.list);
Thank you.
Looks like the issue was due to mongoose 5 not being fully compatible with connect-mongo.
This github issue explains more:
https://github.com/jdesboeufs/connect-mongo/issues/277
To fix you have to use your mongo url in the url property instead of mongooseConnection.
Example:
app.use(session({
secret: 'whatshouldmysecretkeybe',
resave: false,
unset: 'destroy',
saveUninitialized: false,
store: new MongoStore({
url: YOUR_MONGO_URL
})
}));

Use session in Heroku scheduler

I want to be able to delay send email on my heroku/nodejs app.
I know how to use the Heroku Scheduler to delay tasks.
I can then retrieve which emails need to be sent for which user.
But to use the Gmail API I need to retrieve the authenticated user session, and I don't know how to do that.
I am using pg to store the session, but I don't believe it would be specific:
app.use(session({
secret: 'xxx',
store: new pgSession({
conString: config.db.url
}),
resave: false,
saveUninitialized: false,
cookie: { maxAge: 24192000000 }
}));
High Level
At some point there is a function along your route chain that is triggering the delayed email, you should just include the session (from req.session) in the task register. From there just make sure the proper user data is available for you to send the email (aka, user_id or user_email in the session data).
More Detailed
You need to think about how your middleware is handling this for a moment.
app.use(session({ etc. is going to add a session object to your req object. Somewhere in your routes, you are processing another function (such as login or registration) after the app.use
(session) portion is called. So in the case of Gmail, you are calling an Oauth request to the Gmail api. They are going to return a user access token to you, which you can use to fetch their user data (such as email address). From there, you can attach their user data to your req.session object.
A Simple Example (modify for your use)
app.use(session({
secret: 'xxx',
store: new pgSession({
conString: config.db.url
}),
resave: false,
saveUninitialized: false,
cookie: { maxAge: 24192000000 }
}));
app.post('/register', function(req, res, next) {
// You'll have to write your own google client implementation :)
var token = getGoogleAccessToken();
// Again, left to you to write.
var user_email = fetchUserGoogleEmail( token );
var session = req.session;
session.email = user_email;
// Register heroku delayed task with session data.
// Save user data and create account, etc.
});
I use the below code to do something similar
var email = require("emailjs");
var server = email.server.connect({
user: "user#gmail.com",
password: "password",
host: "smtp.gmail.com",
ssl: true
});
var cron = require('node-schedule');
cron.scheduleJob({hour: 2, minute: 30, dayOfWeek: 0}, function() {
server.send({
text: "i hope this works",
from: "you <user#gmail.com>",
to: "reciever <reciever#gmail.com>",
subject: "My Email Subject"
}, function(err, message) {
//error handling
});
});
hope this helps

Renewing/Refreshing Express Session

In my app I restrict some access to some actions and pages if a user is not logged in. I have:
var restrict = function(req, res, next) {
if (!req.user) {
console.log("USER isn't logged in.")
return res.status(403).send('Access or action denied, please log in');
}
next();
}
app.get('/stocks', restrict, MainHandler.findAllStocksFromUser);
app.get('/stocks/:id', MainHandler.findStockByIdAndDates);
app.put('/stocks/:id/stockActions', restrict, MainHandler.handleStockAction);
I'm essentially trying to refresh a session everytime the client makes a request to the server so that the server doesn't logout the user/destroy the session when it shouldn't. For testing, I want the session to expire/the user to be logged out if 20 seconds go by without the user making an requests to the server. I have:
app.use(session({secret: 'secret', saveUninitialized: true, resave: true, expires: new Date(Date.now() + (20000))}));
Then I try to use middleware to refresh the expiration date every time the use makes a request:
// Session-persisted message middleware
app.use(function(req, res, next){
req.session.cookie.expires = new Date(Date.now() + 20000);
next();
});
But if I log in from the client, and click around, causing requests to the server, I still get the log-in error on the client after 20 seconds, despite trying to "refresh" the session in the middleware. I have also tried using maxAge using the same strategy with the middleware. Any ideas? Thanks!
You can try define your session as follows
app.use (
session ({
secret: "secret",
saveUninitialized: true,
resave: true,
cookie: {
expires: 20 * 1000
}
})
);
and then refresh the session using
req.session.touch()
or you could define your session as
app.use (
session ({
secret: "secret",
saveUninitialized: false,
resave: true,
rolling: true,
cookie: {
expires: 20 * 1000
}
})
);
and it will renew the session automatically and it will only expire when it has been idle for the value in the expires variable
express-session supports a duration-based maxAge setting, which will work better than setting a fixed date for all sessions. So your middleware usage should instead look like:
app.use(session({
secret: 'secret',
saveUninitialized: true,
resave: true,
maxAge: 20000
}));
Next, to update the expiration of the session, you can just call req.session.touch(); if that is all you're doing to the session and its contents.
The documentation has a lot of other good information on controlling session expiration and related topics.

Passport.js / Express.js Creating New Session on Every Network Request

I have a working login function that properly authenticates and saves. However, express never remembers the old session, it always creates a new one for every network request.
Evidently, Passport is exceedingly sensitive to the order that express middleware is initialized. (Example: https://www.airpair.com/express/posts/expressjs-and-passportjs-sessions-deep-dive). I checked my config against a number of examples and rearranged it to so if it would help, but it hasn't moved my bug. Either that isn't the issue or I just haven't found the config holy grail yet. Here's my current config:
Express Config
app.engine('html', require('ejs').renderFile);
app.set('view engine', 'html');
app.use(express.static(path.join(config.root, '/views')));
app.set('views', config.root + '/views');
var sessionOpts = {
saveUninitialized: true,
resave: false,
store: new RedisStore({
host: 'localhost',
port: 6379
}),
secret: 'keyboard',
cookie: {
httpOnly: true,
maxAge: 1000
}
}
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({
extended: false
}));
app.use(cookieParser('keyboard'));
app.use(session(sessionOpts));
app.use(passport.initialize());
app.use(passport.session());
app.use(cors());
require('./routes/router.js')(app, passport);
Passport Config
passport.use('local-login', new LocalStrategy({
usernameField: 'email',
passwordField: 'password',
passReqToCallback: true
},
function(req, username, password, done) {
client.hgetall(username, function(err, reply) {
if (err) {
return done(err);
}
if (!reply) {
return done(null, false, {
message: 'Incorrect username.'
})
}
if (reply.password !== password) {
return done(null, false, {
message: 'Incorrect password.'
})
}
return done(null, reply)
})
}));
Does Passport need handholding for Redis? Redis sessions are stored in a 'sess' folder with a key like so: sess:RhodmaK2V2wDNLglV5j1B6rC. All of the tutorials I've found have been about Mongo so I'm not sure if I need to somehow include the session key when trying to look it up. Within the session entry, it's properly stored in standard cookie form for passport though: req.session.passport.user
Is there any way to see what is happening inside of passport initialize? On subsequent requests it is supposed to do this: "The general passport middleware we setup (passport.initialize) is invoked on the request, it finds the passport.user attached to the session. If is doesn't (user is not yet authenticated) it creates it like req.passport.user = {}." See 'SUBSEQUENT AUTHENTICATED REQUESTS FLOW' - http://toon.io/understanding-passportjs-authentication-flow/ I strongly suspect my problem lies at that step, so it would be nice to be able to see inside of it.
Some interesting tidbits:
Passport has never once called deserializeUser. I assume it's never
reached that point.
I have checked the other StackOverflow questions about this problem,
but none of those solutions worked for me.
I've checked to see if the new sessions are generated by any static resources but they are not. It's only for intentional server requests. On the pages without them, no new sessions are made.
All sessions have either a populated or empty req.session.passport property.
Your session ID might be different for every request because you have your cookie set to expire after 1 second. cookie.maxAge here is in ms:
cookie: {
httpOnly: true,
maxAge: 1000
}
Edit: I also have to nag you about storing passwords in plaintext. Don't do this ;)

Resources