Switched to promise based DB, now my login is completely broken - javascript

I can register just fine, however when I get directed to my game route I get a default Error page that's just white with [object Object] on the screen. Then I get the same in my console, [object Object] and it repeats every once in a while.
At first I thought it was something to do with socket.io, but it isn't even getting to that point. I think it might be something with passport and how I have it configured, not being setup good with the promise route I'm going, but I am at a complete loss. I don't know exactly where this error is occurring.
Here is the passport file:
/*jshint esversion: 6 */
const LocalStrategy = require('passport-local').Strategy;
const db = require('../config/db');
const bcrypt = require('bcryptjs');
let io = require('./io');
module.exports = (passport) => {
// Local Strategy login
passport.use('local-login',
new LocalStrategy((username, password, done) => {
console.log('username');
// Match Username
let sql = 'SELECT * FROM users WHERE username = ?';
db.query(sql, [username]).then(results => {
if (!results.length) {
return done(null, false, {
type: 'loginMessage',
message: 'Wrong Login',
});
}
console.log('password');
// Match Password
bcrypt.compare(password, results[0].password, (err, isMatch) => {
if (isMatch) {
console.log('Password is correct');
return done(null, results[0]);
} else {
return done(null, false, {
type: 'loginMessage',
message: 'Wrong Login',
});
}
});
});
}));
// =========================================================================
// passport session setup ==================================================
// =========================================================================
// used to serialize the user for the session
passport.serializeUser((user, done) => {
console.log(user.username + ' has been Serialized');
done(null, user.id);
});
// used to deserialize the user
passport.deserializeUser((id, done) => {
db.query('SELECT * FROM users WHERE id = ?', [id]).then(results => {
console.log(results[0].username + ' has been deserialized');
done(results[0]);
});
});
};
This seems to go off without a hitch, now here is my login redirect:
// Login Process
router.post('/login',
passport.authenticate('local-login', {
successRedirect: '/game',
failureRedirect: '/',
failureFlash: true,
}), (req, res) => {
console.log('login route test');
});
Again this seems to be doing well, it does in fact redirect me as intended. Now, here is some extra stuff I think might be causing it:
// Passport config
require('./config/passport')(passport);
app.use(passport.initialize());
app.use(passport.session());
app.get('*', function (req, res, next) {
res.locals.user = req.user || null;
next();
});
Then the game route:
//Route to game app
app.get('/game', function (req, res) {
console.log('Log before it checks req.user');
if (req.user) {
console.log('req.user is working');
res.render('game');
} else {
req.flash('error', 'You need to be signed in!');
res.redirect('/');
}
});
So here is the thing here: When I am not logged in and go to the /game route it will kick me back into my main route with the correct flash error. However, when I login, I can;t for the life of me get it to fire off a console.log() function. So I am thinking it is getting stuck with the req.user on login, but I am not sure why nor how. If more information is needed, I can give more... but this is mostly what all handles the login process (except socket.io, but it doesn't even get to that point yet, and all my socket.io file does is send the data client side for easy updates).
I will keep trying my luck, but since I am new to promises, this may be the reason why, and if it is, I may not be so lucky.
EDIT: Well, I've changed everything back to a normal callback for my DB (which is what I had it before). Weirdly though, I am getting the same result, and I've no idea why. This project had been put on hold for months, but I hadn't touched anything until I changed all the DB stuff. So something must have broken before I even touched anything when I left this project it was working just fine. But I did change it back to the promise method, because I'd rather stick to this message.
EDIT: Also, I am getting a 500 internal server error on the browser console.
EDIT: Updated code and added console.logs in more places to see where this is hanging up, and I'm still not sure. So here is the logging sequence when I click the login button:
username
password
Password is correct
Bloodmorphed has been Serialized
Bloodmorphed has been deserialized
[object Object]
Bloodmorphed has been deserialized
[object Object]
EDIT: So it seems like the login process is not working correctly. I am not sure why and I can't find a problem with anything I am doing. I have looked at multiple sources of how to set-up passport for MySQL and while some of them differ a tiny bit, they all seem to be the same where it matters. I do not know why this is happening and according to multiple sources of working logins, I am doing this right.

I am, well... simply an idiot. I forgot when I changed to a promise system, I handle errors inside the query itself, so there was no reason for me to pass it through.
so where I had done done(results[0]) in the passport.deserializeUser... I just had to add null before it like so: done(null, results[0]) so much wasted time on a simple matter. I feel like a dumby.

Related

Angular 2 404 error when attempting login

I have an angular 2 application which talks to a standalone backend nodejs api. Out of know where I am getting a 404 when I try to log in to my application. This just started happening and I went back to different versions of code in both front and backend to see if something in the recent code broke my app. I have verified the node backend is running by checking its existence in the activity monitor. There is absolutely no activity in the console for the backend when I try to hit the endpoint.
When I look at the network tab I am getting a 404 but the rest of the details seem to indicate that everything was sent as expected including the correct request url and request payload.
The only thing that I am not sure is normal is there seems to be two login attempts with the first one resulting in a 204. I actually do not know if this is a normal behavior.
After attempting to login I get the follow report in the network tab:
The details for both listings are below:
Any ideas would be greatly appreciated. Been wasting lots of precious time fighting with this. Thanks.
Update with some backend code per request:
The entry file is large but basically works like this:
Services and Models are instantiated and passed into the routes:
let authRouter = authCreateRouter(passport, services);
Auth Router:
module.exports = (passport, services) => {
let router = express.Router();
router.post('/login', (req, res, next) => {
passport.authenticate('login', (err, user, info) => {
if (err) {
logger.info({
err,
stack: err.stack
}, 'the error');
return next(err);
}
if (!user) {
logger.info({
user
}, 'this user could not be authenticated');
return res.status(401).json({
success: false
});
}
let token = jwt.sign({
id: user.id
}, process.env.SESSION_SECRET, {
expiresIn: '12h'
});
res.json({
success: true,
userId: user.id,
signup: user.signup,
avatarUrl: user.avatarUrl,
accountVerified: user.accountVerified,
user,
token
});
})(req, res, next);
});
};
The above code then interacts with the appropriate service. The thing is none of this code has changed so it is not likely the issue.

Node.js Passport not calling next function

I'm building an application using Node that uses Passport.js to handle user login using a local database.
So I have the following code that gets called when a user goes to /profile. After successfully logging in the user gets redirected to /profile. Which does happen according to morgan.
app.get('/profile', passport.authenticate('local-login', { session : false, failureRedirect : '/login' }), function(req, res) {
console.log("testnow");
res.render('profile.ejs', {
user : req.user // get the user out of session and pass to template
});
});
My local-login code is the following.
passport.use('local-login', new LocalStrategy({
// by default, local strategy uses username and password, we will override with email
usernameField : 'email',
passwordField : 'password',
passReqToCallback : true // allows us to pass back the entire request to the callback
},
function(req, email, password, done) { // callback with email and password from our form
// find a user whose email is the same as the forms email
// we are checking to see if the user trying to login already exists
User.findOne({ 'local.email' : email }, function(err, user) {
// if there are any errors, return the error before anything else
if (err)
return done(err);
// if no user is found, return the message
if (!user)
return done(null, false, req.flash('loginMessage', 'No user found.')); // req.flash is the way to set flashdata using connect-flash
// if the user is found but the password is wrong
if (!user.validPassword(password))
return done(null, false, req.flash('loginMessage', 'Oops! Wrong password.')); // create the loginMessage and save it to session as flashdata
// all is well, return successful user
console.log("testdone");
return done(null, user);
});
}));
When testing the code I login and get redirected to profile for a split second. The console prints "testdone" which is in my local-login code BUT doesn't print "testnow" as it is expected to. Meaning the second function in my /profile get method never seems to get called even tho local-login is calling the next function.
So from the end users standpoint you login (behind the scenes you get redirected to /profile for a split section) and /profile redirects you back to /login.
Any ideas on how to fix this so the second function in my /profile get method actually gets called?
Thanks so much in advance. I would also be more then happy to provide any additional information to help figure this out.
passport.authenticate() is meant to handle the actual authentication; in other words, to take the login credentials and pass them to the strategy. It's not meant to pass along requests if they are already authenticated, which is what you're trying to use it for.
Instead, you want to use something like connect-ensure-login to guard routes for which a user has to be logged in.
See also this Passport example project.

Passport-Google-OAuth Callback Not working when used in Web Service

I Have used Passport-Google-OAuth in Node.js web service project. I am using OAuth2Strategy.
The process i have used is i call the web service method to authenticate user from his Gmail account. Initially i serve the Raw HTMl which i receive from calling the Passport-google-OAuth. Which works fine.
Then i login with valid Gmail accounts. Once the Callback Url is called by google the server goes into infinite loop and calls the callback url again and again after fixed interval of time.
My Passport strategy configuration for Google is like this:
// Use the GoogleStrategy within Passport.
// Strategies in Passport require a `verify` function, which accept
// credentials (in this case, an accessToken, refreshToken, and Google
// profile), and invoke a callback with a user object.
passport.use(new GoogleStrategy({
clientID : "948630708036-2t6mestiv81gtv0s9n6iptoava4o1cpa.apps.googleusercontent.com",
clientSecret : "omugRnr7nad2yMmefiZdBaLL",
callbackURL : "http://localhost:4000/api/auth/google/callback"
},
function(token, refreshToken, profile, done) {
console.log('Inside global callback.');
// make the code asynchronous
// User.findOne won't fire until we have all our data back from Google
process.nextTick(function() {
// try to find the user based on their google id
User.findOne({ 'google.id' : profile.id }, function(err, user) {
if (err)
return done(err);
if (user) {
// if a user is found, log them in
return done(null, user);
} else {
// if the user isnt in our database, create a new user
var newUser = new User();
// set all of the relevant information
newUser.google.id = profile.id;
newUser.google.token = token;
newUser.google.name = profile.displayName;
newUser.google.email = profile.emails[0].value; // pull the first email
return done(null, newUser);
}
});
});
}));
Then i am calling the Passport from the endpoint in the service project:
passport.authenticate('google', { session:false,scope : ['profile', 'email'] });
And the Callback URL contains the following code where i am sending the returned Google account details of the user in JSON format to the client which accessed the web service intially.
function(req, res) {
console.log('Callback by Google:'+res.body+' || '+ res.headers);
console.log('Response Object:'+util.inspect(res));
passport.authenticate('google', { session : false }),function(req,res){
console.log('Callback authenticated.User: +req.user);
res.json(req.user);
}
In the Log i am getting "Callback by Google: undefined || undefined".
I am disabling sessions since this will be the API Server feeding data to various clients.
I dont know what mistake i am doing. Kindly point out any resource or example where the Passport-Google-OAuth(OAuth2Strategy) is used in a API(Web Service) server. Do i need to follow some other way. Thanks for ur help in advance.
There may be a problem in your routes. Look at the tutorial here
https://scotch.io/tutorials/easy-node-authentication-google
It's the best I have seen. And I have implemented something similar.

Express user authentication middleware, how much should it do?

I'm trying to learn Express session and authentication handling.
For example:
app.post('/login', authCredentials, function(req, res) {
console.log("second")
});
function authCredentials(req, res, next) {
//this happens first
console.log(req.body) // => { username: etc, password: etc }
next();
}
My question is just how much should my authCredentials function do?
For example if the credentials are correct, I can do something like
res.redirect('/index'). Once I do that, however, what purpose does the second function have?
Other questions:
How would I handle invalid credentials?
If I make authCredentials just return true or false depending on the credentials, doesn't that break the middleware flow because it would never invoke next()?
Is it possible to access anything in authCredentials in the anonymous callback after it? Basically in the function(req, res) { }?
The answer depends on your authentication strategy i.e. are you using session identifiers, access tokens, etc.
In either case I suggest that you break out the credential exchange (aka login) from the authentication.
function usernamePasswordExchange(req,res,next){
var username = req.body.username;
var password = req.body.password;
callToAuthService(username,password,function(err,user){
if(err){
next(err); // bad password, user doesn’t exist, etc
}else{
/*
this part depends on your application. do you use
sessions or access tokens? you need to send the user
something that they can use for authentication on
subsequent requests
*/
res.end(/* send something */);
}
});
}
function authenticate(req,res,next){
/*
read the cookie, access token, etc.
verify that it is legit and then find
the user that it’s associated with
*/
validateRequestAndGetUser(req,function(err,user){
if(err){
next(err); // session expired, tampered, revoked
}else{
req.user = user;
next();
}
});
}
app.post('/login',usernamePasswordExchange);
app.get('/protected-resource',authenticate,function(req,res,next){
/*
If we are here we know the user is authenticated and we
can know who the user is by referencing req.user
*/
});
Disclaimer: I work at Stormpath and we spend a lot of time writing
authentication code :) I just wrote our newest library, stormpath-sdk-express,
which has a concrete implementation of my suggestions
You want to add your authCredentials middleware to every end point that needs authentication. app.post('/login') usually does not need any as you want to access this end point to actually get credentials in the first place.
When credentials are correct/valid you simply invoke next() and the workflow will jump to the next middleware or the actual end point. If there was an error, invoke next() with an error object like next(new Error('could not authenticate'); for instance. Add an error route to your general routing and the error will be handled there:
app.use(function(err, req, res, next) {
res.render('error', err);
});
Should be answered by now.
A middleware does not return a value. It either calls next() or ends the process differently by calling res.send().
There are different approaches to pass variables from one middleware to another. The most common is probably to attach the desired value to the req parameter.
authenticate is an asychronous function in the following example:
function authCredentials(req, res, next) {
authenticate(req.body, function(err, user) {
if (err) {
return next(err);
}
req.user = user;
next();
});
}

Expressjs doesn't destroy session

I have an Backbone View which sends an Ajax call to the server to remove a session.
On the server following event is triggered:
app.delete('/session', function(req, res) {
if (req.session) {
req.session.destroy(function() {
res.clearCookie('connect.sid', { path: '/' });
res.send('removed session', 200);
});
} else {
res.send('no session assigned', 500);
}
});
The weird about this is that I can press the logout button multiple times without getting a HTTP 500 error code. Also chromium shows me that a cookie still exists.
What is going wrong?
Regards
EDIT:
I found out that this isn't directly a session issue but a cookie one.
I added res.clearCookie to the route. Unfortunatly the behaviour (cookie, session keep alive) didn't change
EDIT2:
I now gave res.clearCookie some parameters => res.clearCookie('connect.sid', { path: '/' });
Now at least the cookie is gone in the browser. But the session seems to be still available.
Or at least I can call the logout route how often I want even req.session should be false
EDIT3:
I now removed all sessions out of redis and restarted everything (redis, node, browser).
Than I have logged in again and logged out. This works so far but when I relaod the page with F5 I get a new session. WHY?
To concentrate all comments together I have written an answer:
Because express always creates a session and a cookie for a client we have to take a different approach than just to check if there is a session.
This parts handles logins
app.post('/session', function(req, res) {
User.findOne({ username: req.body.username })
.select('salt') // my mongoose schema doesn't fetches salt
.select('password') // and password by default
.exec(function(err, user) {
if (err || user === null) throw err; // awful error handling here
// mongoose schema methods which checks if the sent credentials
// are equal to the hashed password (allows callback)
user.hasEqualPassword(req.body.password, function(hasEqualPassword) {
if (hasEqualPassword) {
// if the password matches we do this:
req.session.authenticated = true; // flag the session, all logged-in check now check if authenticated is true (this is required for the secured-area-check-middleware)
req.session.user = user; // this is optionally. I have done this because I want to have the user credentials available
// another benefit of storing the user instance in the session is
// that we can gain from the speed of redis. If the user logs out we would have to save the user instance in the session (didn't tried this)
res.send(200); // sent the client that everything gone ok
} else {
res.send("wrong password", 500); // tells the client that the password was wrong (on production sys you want to hide what gone wronge)
}
});
});
});
That was the login part lets go to the logout:
app.delete('/session', function(req, res) {
// here is our security check
// if you use a isAuthenticated-middleware you could make this shorter
if (req.session.authenticated) {
// this destroys the current session (not really necessary because you get a new one
req.session.destroy(function() {
// if you don't want destroy the whole session, because you anyway get a new one you also could just change the flags and remove the private informations
// req.session.user.save(callback(err, user)) // didn't checked this
//delete req.session.user; // remove credentials
//req.session.authenticated = false; // set flag
//res.clearCookie('connect.sid', { path: '/' }); // see comments above res.send('removed session', 200); // tell the client everything went well
});
} else {
res.send('cant remove public session', 500); // public sessions don't containt sensible information so we leave them
}
});
Hope this helps

Categories

Resources