Related
I am trying to login an user (by a .js file, not via postman or browser) and maintain the session throughout the code. The login function seems to work fine, passport.authenticate, passportConfig and serializeUser get called, the response status is 200 OK, but when I try to call a function like sendFunds, the response is 'User is not authenticated'.
It works if I post the requests via postman. It seems like the login request gets a cookie and sends it automatically with the sendFunds request.
I guess that I need to do the same in my accountGenerator.js file, that means call the login method, get the cookies and send them with the sendFunds request, but I can't figure it out. How do I get them and do I need to manually add them to the express-session ?
Please :)
accountGenerator.js
async function loginUser(userLogin) {
return post('http://localhost:3002/api/user/login', userLogin)
}
function sendFunds(transferDetails) {
post('http://localhost:3002/api/user/sendFunds', transferDetails)
.then((res) => {
console.log(`Status: ${res.status}`);
}).catch((err) => {
console.error(err);
});
}
const loginResponse = await loginUser(userLogin);
export function loginUser(req, res) {
if (req.isAuthenticated()) {
res.status(200).send({
message: 'User is authenticated'
});
return;
}
passport.authenticate("local", {
successRedirect: "/",
failureRedirect: "/error"
// failureRedirect: "/login"
})(req, res, next);
}
export function sendFunds(req, res) {
if (!req.isAuthenticated()) {
res.status(401).send({
message: 'User is not authenticated'
});
return;
}
req.body.secretKey = AUTHENTICATOR_SECRET_KEY;
post(SEND_FUNDS_API_URL, req.body)
.then((response) => {
res.status(200).send(response.data);
}).catch((err) => {
console.error(err);
res.status(500).send(err);
});
}
export function passportConfig() {
passport.use('local', new LocalStrategy(
async (username, password, done) => {
const response = await User.findOne({ name: username });
if (!response) {
return done(null, false, { message: 'Incorrect username.' });
}
const isValidPassword = await compare(password, response.password);
if (!isValidPassword) {
return done(null, false, { message: 'Incorrect password.' });
}
return done(null, response);
}
))
}
passport.serializeUser((user, done) => {
done(null, user.id)
})
passport.deserializeUser((id, done) => {
User.findById(id, (err, user) => {
done(err, user)
})
})
app.use(expressSession({
store: new MongoStore({
mongoUrl: 'mongodb://127.0.0.1:27017/accountBankApp',
// mongooseConnection: mongoose,
ttl: 1 * 24 * 60 * 60, // = 365 days.
}),
secret: 'secret',
resave: true,
saveUninitialized: true,
}));
What worked for me was combining express-session with cookie-parser, even if cookie-parser is not required anymore in order for express-session to work.
Since version 1.5.0, the cookie-parser middleware no longer needs to be used for this module to work. This module now directly reads and writes cookies on req/res. Using cookie-parser may result in issues if the secret is not the same between this module and cookie-parser.
I want to store information in the database when user is authenticated. The information is coming form the client in the request. The following code throws error, saying req is not defined.
Controller:
exports.verifySession = async function(req, res, next) {
let responses = [];
passport.authenticate('jwt', async (error, result) => {
if (error) {
email.sendError(res, error);
} else if (result === false) {
responses.push(new CustomResponse(1).get());
return res.status(422).json({ data: { errors: responses } });
}
if (result.SessionToken) {
return res.status(200).json('valid');
} else {
return res.status(401).json();
}
})(req, res, next);
};
And passport.js:
passport.use(
new JWTstrategy(
{
// We expect the user to send the token as a query paramater with the name 'token'
jwtFromRequest: ExtractJWT.fromAuthHeaderAsBearerToken(),
// Secret we used to sign our JWT
secretOrKey: config.jwtkey
},
async (token, done) => {
console.log(req.body);
try {
const user = new User();
user.UserID = token.user.UserID;
user.SessionToken = token.user.SessionToken;
user.SessionDate = token.user.SessionDate;
user.ProviderID = token.user.ProviderID;
// Verify session token
await user.verifySessionToken(user, async (error, result) => {
if (error) {
return done(error);
} else if (result.returnValue === 0) {
return done(null, token.user);
} else if (result.returnValue !== 0) {
return done(null, result);
}
});
} catch (error) {
done(error);
}
}
)
);
You can use passReqToCallback feature of passport to pass your request body to passport.
From passport.js official docs :
The JWT authentication strategy is constructed as follows:
new JwtStrategy(options, verify)
options is an object literal
containing options to control how the token is extracted from the
request or verified.
...
...
passReqToCallback: If true the request will be passed to the verify
callback. i.e. verify(request, jwt_payload, done_callback).
You can try this:
passport.use(new JWTstrategy({
// We expect the user to send the token as a query paramater with the name 'token'
jwtFromRequest: ExtractJWT.fromAuthHeaderAsBearerToken(),
// Secret we used to sign our JWT
secretOrKey: config.jwtkey,
//this will help you to pass request body to passport
passReqToCallback: true
}, async (req, token,done) => {
//req becomes the first parameter
// now you can access req.body here
})
Note: req becomes the first parameter of callback function instead of token, when you use passReqToCallback
I am persistently getting 'unauthorized' error while authenticating using a JWT. Below is my controller code:
exports.loginPost = async (req, res) => {
winston.info('Calling loginPost()...');
passport.authenticate('local', { session: false }, (err, user, info) => {
if (err) {
return utils.errorHandler(res, err);
} else if (!user) {
return utils.errorHandler(res, {
statusCode: 403,
message: 'Incorrect username or password.'
});
}
const token = jwt.sign(user, sharedSecret, { expiresIn: '24h' });
//req.user = user;
return res.json({ user, token });
// req.login(user, { session: false }, (err) => {
// if (err) {
// res.send(err);
// }
// // generate a signed json web token with the contents of user object and return it in the response
// const token = jwt.sign(user, sharedSecret, { expiresIn: '24h' });
// //req.user = user;
// return res.json({ user, token });
// });
})(req, res);
};
exports.isUserLoggedIn = async (req, res) => {
let login = {"message": "all good !"}
console.log(req)
return res.status(200).json(login);
//return res.status(200).json(req.user);
};
and passport.js strategy script is as follows:
passport.use(new LocalStrategy({
usernameField: 'username',
passwordField: 'password'
}, async function (username, password, cb) {
//this one is typically a DB call. Assume that the returned user object is pre-formatted and ready for storing in JWT
try {
let user = await userService.getUserWithPassword(username, password);
console.log("passport.js: ",user);
if (!user || user.walletKey !== password) {
throw { statusCode: 403, message: 'Incorrect username or password.' };
}
// // purge password field
// delete user.currentPassword;
return cb(null, user, { message: 'Logged In Successfully' });
} catch (err) {
cb(err);
}
}));
passport.use(new JWTStrategy({
jwtFromRequest: ExtractJWT.fromAuthHeaderAsBearerToken(),
secretOrKey: sharedSecret,
passReqToCallback: true
},
async function (req, jwtPayload, cb) {
// Return user object from the JWTPayload
try {
let user = await userService.getUserWithPassword(jwtPayload.walletName, jwtPayload.walletKey);
console.log("passport.js: ",user);
req.user = user
return cb(null, user); //jwtPayload
} catch(err){
return cb(err,false);
}
}
));
I am able to generate token successfully, however, on calling isUserLoggedIn method using Bearer Token, it's prompting me unauthorized error. I am not making an traditional db call to login, instead I am just creating account in a Hyperledger-Indy pool nodes. Using swagger express middleware on a Node.js app.
Adding isUserLoggedIn method script below:
exports.isUserLoggedIn = async (req, res) => {
//let login = {"message": "all good !"}
console.log(req)
return res.status(200).json(req.user);
//return res.status(200).json(req.user);
};
I have an Express application running on port 3000. The front end runs on port 80, so this is a CORS application. Users are stored in an SQL server database. I'm using passport as the authentication method with Local Strategy as well as express session middleware. The application is a Single page application and all requests sent to server are done via ajax. Users log in on page and the credentials are sent and if authentication is successful, userID as well as username and FullNmae are supposed to be persisted to a session.
I have a lot of things wrong with this: The main thing is that after logging in, express saves the username and other data using passport on to a new session andsends back an html snippet to replace the body tag on the page. However, to test that the user object persists, I call the /create-user route and it says that the user object is not there. Additionally, a new session starts with every request (I check the logs and see that a different session ID is displayed each time). Not only that but at one point I was able to see the session cookie in the browser but I can no longer see it anymore. I tried to go back to the point where I could see the cookie but it still didn't appear!
I've been busting my head for hours and can't figure out why deserializeUser is not called nor why the data is not persisted. Where am I going wrong?
Note: some obvious code ommitted (app.listen(), require statements, etc.)
/* ------ CONFIGURATIONS ------ */
const app = express();
const mssqlConfig = JSON.parse(fs.readFileSync("mssql-config.json", "utf8"));
passport.use(new LocalStrategy(
function loginAuthentication(username, password, done) {
let connPool = new mssql.ConnectionPool(mssqlConfig);
connPool.connect(error => {
if (error) {console.log(error); return done(error);}
ps = new mssql.PreparedStatement(connPool);
ps.input('username', mssql.NVarChar(20));
ps.input('password', mssql.NVarChar(50));
ps.prepare('SELECT FullName, fldLoginName, fldEmployeeID, fldPassword FROM tblEmployees WHERE fldLoginName = #username AND fldPassword = #password;', error => {
if (error) {console.log(error); return done(error);}
ps.execute({username, password}, (error, result) => {
if (error) {console.log(error); return done(error);}
console.log(result);
if (result.recordset.length == 0) {
return done(null, false, {message: "There is no user with those credentials!"});
} else if (result.recordset[0].fldLoginName != username || result.recordset[0].fldPassword != password) {
return done(null, false, {message: "Username or password is incorrect!"})
} else {
return done(null, {
ID: result.recordset[0].fldEmployeeID,
username: result.recordset[0].fldLoginName,
fullName: result.recordset[0].FullName
});
}
ps.unprepare(error => console.log(error));
});
});
});
}
));
passport.serializeUser((user, done) => {
done(null, JSON.stringify(user));
})
passport.deserializeUser((user, done) => {
console.log(user);
done(null, JSON.parse(user));
});
/* ----- MIDDLEWARE ------ */
app.use(function allowCrossDomain(request, response, next) { // CORS
// intercept OPTIONS method
response.header('Access-Control-Allow-Credentials', true);
response.header('Access-Control-Allow-Origin', request.headers.origin);
response.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE');
response.header('Access-Control-Allow-Headers', 'X-Requested-With, X-HTTP-Method-Override, Content-Type, Accept');
response.header('Access-Control-Max-Age', '60');
if ('OPTIONS' == request.method) {
response.sendStatus(200);
} else {
next();
}
});
app.use(bodyParser.json());
app.use(session({
secret:"long string of characters",
name:'officetools-extensions',
saveUninitialized:false,
resave:false,
cookie:{secure:false, httpOnly:true, maxAge:86400000, domain:"http://officetools-extensions"},
store: new MemoryStore({checkPeriod:86400000})
}));
app.use(passport.initialize());
app.use(function checkRestrictedURL(request, response, next){
console.log(request.url);
if (!request.url.match(/^\/login$/g)) {
console.log("passed");
passport.session()(request, response, next);
} else {
next();
}
});
/* ------ ROUTES ------ */
app.post('/login', bodyParser.urlencoded({extended:false}), (request, response, next) => {
passport.authenticate('local', {session:true}, (error, user, info) => {
if (error) { error.status = 500; return next(error); }
if (info) { let err = new Error(info.message); err.status = 400; return next(err);}
if (!user) { return response.status(401).send("User could not be logged in!"); }
console.log(request.sessionID);
console.log(user);
console.log(request.session);
request.logIn(user, function loginCallBack(error) {
if (error) { error.status = 500; return next(error);}
console.log("after login", request.session);
console.log(request.isAuthenticated());
return response.sendFile(path.join(__dirname + "/templates/barcodes.html"));
})
})(request, response, next);
});
app.get("/current-user", (request, response, next) => {
console.log(request.user, request.session);
console.log(request.sessionID);
console.log(request.isAuthenticated());
if (request.user) {
response.header("Content-Type", "application/json");
return response.send(request.user);
}
else { return response.status(401).send("There is no user currently logged in!"); }
});
I figured it out. I just had to remove the domain property on the session settings. That made it work.
I need some help with setting up Passport Consumer Strategy and integrating it into "locals" (Right now, we have the local strategy working just fine). We have tried several approaches but with no luck on it working 100%. The below code is not the complete code, we have taken out some of it so this post doesn't get too long. Any help with this would greatly be appreciate. There could be compensation as well if someone can get us over this hurdle.
So one question is, if the user is authenticated by the consumer key and secret, how does Passport store the session variables so they are used throughout the site?
Second question, how do we handle the user after it passes the authentication process?
Both local and consumer need to be working.
Consumer key and secret using a POST by a Provider <- I can show some of the post if needed.
This needs to be OAuth1 Only, as of right now, OAuth2 isn't an option.
This is for a single sign-on authentication.
I can supply a consumer session output if needed.
Ultimately, we would like the local strategy and the consumer strategy working with the same "locals" global variables. As far as I can tell, we can authenticate the consumer, retrieve the user from our DB, create a session and can tell if user is "ensureAuthenticated".
Here is what we have working right now.
Local strategy is authenticating correctly.
We render the pages with these local variables:
"Omitted most of the code to save time."
//=================================================================
// The Authentication Module will bind to POST /login route
//=================================================================
authentication.initLocalStrategyRoutes(app);
passport.authenticate('local', {successReturnToOrRedirect: '/', failureRedirect: '/login'});
...
function renderPage(req, res, pageName, pageTitle){
res.render(pageName, {
pageName: pageTitle,
username: req.user ? req.user.username : '',
...
Consumer strategy is authenticating by a POST request from a "Provider"
We have tried adding the Consumer strategy to the authentication.
server.js
//=================================================================
// The Authentication Module will bind to POST /login route
//=================================================================
authentication.initLocalStrategyRoutes(app);
ADDED -> authentication.initConsumerStrategyRoutes(app);
passport.authenticate('local', {successReturnToOrRedirect: '/', failureRedirect: '/login'});
ADDED -> passport.authenticate('consumer', {successReturnToOrRedirect: '/', failureRedirect: '/login'});
authentication.js (omitted code)
module.exports = function(siteConfig, defaultRedirectPage, server, sessionStore, log) {
var passport = require('passport')
...
, ConsumerStrategy = require('passport-http-oauth').ConsumerStrategy
, TokenStrategy = require('passport-http-oauth').TokenStrategy
, LocalStrategy = require('passport-local').Strategy;
var auth = {};
var authenticationRedirects = { successRedirect: '/', failureRedirect: '/login' };
passport.serializeUser(function(user, done) {done(null, user);});
passport.deserializeUser(function(obj, done) {done(null, obj);});
auth.authenticate = function(email, password, callback) {
email = email.toLowerCase();
userController.findUserByUsernameWithPermissions(email,
function(err, user) {
if (err) return callback(err);
if (!user) return callback(null, null, 'Incorrect username.');
bcrypt.compare(password, user.password_hash, function(err, res) {
if(err){return callback(err);
} else if (!res) {return callback(null, null, 'Incorrect password.');
} else {if (user.account_state>0) {callback(null, user);} else {return callback(null, null, '/reset?rand='+user._id);}}
});
}
);
}
auth.initLocalStrategyRoutes = function(app){
passport.use(new LocalStrategy(auth.authenticate));
app.post('/login', function(req, res, next) {
passport.authenticate('local', function(err, user, info) {
if (err) return next(err);
if (!user) return res.send({success: false, message: info});
req.logIn(user, function(err) {
if (err) { return next(err); }
res.send(req.user);
});
}) (req, res, next);
});
}
auth.initConsumerStrategyRoutes = function(app){
// passport.use(new LocalStrategy(auth.authenticate));
console.log('app: ', app)
passport.use('consumer', new ConsumerStrategy(
function(key, done) { console.log('starting ConsumerStrategy');
dbConsumerKey.findByConsumerKey({consumerKey: key}, function(err, consumerKey) {
if (err) { return done(err); }
if (!consumerKey) {
var errCode = dbError.find({name:'no_resource_link_id'}, function(err, errorCodes) {
console.log('statusText: ', errorCodes[0]["statusText"]);
return errorCodes[0]["statusText"];
});
return done(null, errCode);
} else {
if (!consumerKey[0]["consumerKey"]) { return done(err); }
if (!consumerKey[0]["consumerSecret"]) { return done(err); }
// return done(null, consumerKey[0]["consumerKey"], consumerKey[0]["consumerSecret"]);
return done(null, consumerKey[0], consumerKey[0]["consumerSecret"]);
}
});
},
function(requestToken, done) {
dbRequestTokens.find(requestToken, function(err, token) {
console.log('inside requestToken');
if (err) { return done(err); }
var info = { verifier: token.verifier,
clientID: token.clientID,
userID: token.userID,
approved: token.approved
}
done(null, token.secret, info);
});
},
function(timestamp, nonce, done) {
done(null, true)
}
));
};
auth.initTokenStrategyRoutes = function(app){}
auth.addUser = function(username, email, password, callback){auth.authenticate(username, "pass", callback);}
return auth;
};
The authentication.js strategy does validate the consumer key and secret. but it doesn't create the session variable we are wanting. We would like the consumer strategy code to be in the authentication.js file.
Now here is another approach, we created a separate files called consumerkey.js
This direction works to a point. We can output the passport session either on the screen or on the command line.
var passport = require('passport')
exports.launchLti = [
passport.authenticate('consumer', { session: false/true [tried both] }),
function(req, res) {
db.findByStudentUserId({lis_person_contact_email_primary:
req.body.lis_person_contact_email_primary}, function(err, user) {
req.logIn(user, function(err) {
req.user.username = user[0].lis_person_contact_email_primary;
...
// req.session.save(function(){
// res.redirect('/classes');
res.redirect(200,'/');
// });
});
})
// res.render('launch', {launch: launch});
}
}]
I solved this issue by changing some of my code structure.
app.pst('/launch/lti/:id', function(req, res, next) {
passport.authenticate('consumer', {failureRedirect: '/login'}),
dbConsumerKey.findByStudentUserId({},
function(err, user) {
if (err) console.log(err, user);
req.logIn(user, function(err) {
if (err) return err;
ADDED -> req.session.valid = true;
ADDED -> res.redirect('/');
});
}
});
});
and modifying the render page function to adapt to the incoming information.
function renderPage(req, res, pageName, pageTitle){
...
this is where the locals are created
...
this allowed me to use my current local strategy as is and adding a totally different strategy route but making the session correctly.