I'm building a simple site with expressjs and passportjs and I'm running into the problem that I can't access session variables in my routes.
There are a lot of threads with this topic but the solutions don't work for me. It looks like my error is somewhere else.
My app is configured like this:
app.configure(function() {
app.set('port', process.env.PORT || 3000);
app.set('views', __dirname + '/views');
app.set('view engine', 'jade');
app.use(express.logger());
app.use(express.cookieParser());
app.use(express.bodyParser());
app.use(express.methodOverride());
app.use(express.session({
path: '/',
secret: 'very secret'
}));
app.use(passport.initialize());
app.use(passport.session());
app.use(app.router);
app.use(express.static(__dirname + '/public'));
});
Once passport verified the twitter account it's redirected to this site:
app.get('/auth/twitter/callback',
passport.authenticate('twitter', {
failureRedirect: '/login'
}),
function(req, res) {
res.redirect('/');
console.log("populate session")
populateSession(req, res);
});
This works, as I'm seeing the "populate session" output in the console if I'm logged in.
The populateSession() looks like this:
function populateSession(req, res) {
User.findOne({
twitterID: req.user.id
}, function(err, result) {
if (result) {
// Store data to session
req.session.twitterAccessToken = result.twitterAccessToken;
req.session.twitterAccessTokenSecret = result.twitterAccessTokenSecret;
req.session.lastfmAccountName = result.lastfmAccountName;
console.log("in session: " + req.session.lastfmAccountName)
}
})
}
"in session" is printed the right way. So the session itself works. Now my problem is that I want to have access to my session variables in routes because I want to pass them to my view templates like this:
app.get('/account', ensureAuthenticated, function(req, res) {
console.log(req.session)
console.log("lastfm nick " + req.session.lastfmAccountName)
res.render('account', {
user: req.user,
lastfmnick: req.session.lastfmAccountName
});
That's where I'm running into the problems. req.session contains all the twitter fields passport is populating it with but req.session.lastfmAccountName is undefined.
Any idea what's wrong there or is there a better way to pass variables to the view? I feel like it's not a good idea to have DB queries for the fields in all my routes if it could just be stored in the session.
Thanks!
The session will automatically be saved when the response ends, in this case by res.redirect(), which is done before the modifications are made to the session.
function(req, res) {
res.redirect('/'); // ends the response, saving the session
populateSession(req, res); // modifies the session without saving
});
Since .findOne() is asynchronous, if you revise populateSession() to take and call a callback when the find completes, you can control the order so the session is modified first:
function populateSession(req, res, next) {
User.findOne({
twitterID: req.user.id
}, function(err, result) {
if (result) {
// ...
}
if (next) next(err);
})
}
app.get('/auth/twitter/callback',
/* ... */,
function(req, res) {
populateSession(req, res, function () {
res.redirect('/');
});
});
It also allows you to use populateSession as middleware:
app.get('/auth/twitter/callback',
/* ... */,
populateSession,
function(req, res) {
res.redirect('/');
});
app.use(lib.express.cookieParser(lib.config.cookieSecret));
app.use(lib.express.session({
secret: 'very secret'
})
}));
This two line should be consecutive and in that order.
Related
I have been trying to implement authentication through passport-local-mongoose on my web app. When prompted to register, on entering the details, the "Bad request" window is served.
However, the data entered is saved to the database. I have been really frustrated since Morning and used almost every solution I could find on the internet, it neither returns an error nor refuses to save data in the database. just serves me the bad request.
I am a complete beginner so here is my code.
// Connecting to the database
mongoose.connect('mongodb://localhost:27017/secretsApp', {
useNewUrlParser: true,
});
const app = express();
app.use(bodyParser.urlencoded({ extended: true }));
app.use(express.static(__dirname + '/public/'));
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');
// Setting the session
app.use(
session({
secret: 'ThisIsMyLittleVerySmallSecret.',
resave: false,
saveUninitialized: false,
})
);
// Initialising the session
app.use(passport.initialize());
app.use(passport.session());
// defining database schema
const userSchema = new mongoose.Schema({
email: String,
password: String,
});
userSchema.plugin(passportLocalMongoose);
// Database encryption before mongoose model
// userSchema.plugin(encrypt, {secret: process.env.SECRET, encryptedFields: ["password"]})
const User = new mongoose.model('user', userSchema);
// use static authenticate method of model in LocalStrategy
passport.use(User.createStrategy());
// use static serialize and deserialize of model for passport session support
passport.serializeUser(User.serializeUser());
passport.deserializeUser(User.deserializeUser());
app.get('/register', (req, res) => {
res.render('register');
});
app.get('/secrets', (req, res) => {
if (req.isAuthenticated()) {
res.render('secrets');
} else {
res.redirect('/login');
}
});
app.post('/register', (req, res) => {
console.log(req.body.usermail);
User.register(
{ username: req.body.usermail },
req.body.password,
(err, user) => {
if (err) {
console.log(err);
res.redirect('/register');
} else {
passport.authenticate('local')(req, res, () => {
res.redirect('/secrets');
});
}
}
);
});
app.listen(3000, console.log('App started on port 3000'));
I have a Node.js server and I am using passport for session management. Planning on using it to implement remember me functionality for the server soon. But I have a problem where the normal sessions are basically acting as a remember me. Aka the session cookie connect.sid is not getting destroyed on browser close which I believe is not the correct behavior. I am using connect-bluesky as the session store (uses azure tables) and couchbase for storing default session information for future use.
Relevant code:
Server.js
app.configure(function()
{
app.use(express.logger('dev')); // log every request to the console
// set up our express application
app.use(express.cookieParser());
app.use(express.json())
.use(express.urlencoded());
app.use(express.static(__dirname));
var db = new couchbase.Connection({host: 'http://localhost:8091', bucket: 'default'}, function (err){
console.log("In cb connect");
console.log(err);
});
app.use(express.session({secret: 'SECRET',
store: new BlueskyStore({
account: 'ACCOUNT',
key: 'KEY',
table: 'sessionsTable',
cookie: { path: '/', httpOnly: true, maxAge: null }
})
}));
app.engine('html', engines.ejs);
//app.set('views', __dirname+'/views');
app.set('view engine', 'html');
var data = fs.readFileSync( __dirname+"/"+process.argv['2'],'utf8');
GLOBAL.infoJSON = JSON.parse(data);
require('./config/passport')(passport, infoJSON);
app.use(passport.initialize());
app.use(passport.session()); // persistent login sessions
app.use(flash()); // use connect-flash for flash messages stored in session
app.use(app.router);
});
Routes.js
function isLoggedIn(req, res, next) {
if (req.isAuthenticated())
return next();
res.redirect('/');
}
app.post('/login2', passport.authenticate('local-login'),
function(req, res)
{
var input = "irrelevant input string";
var loginInfo = {
input : input,
userName : req.user.id,
repoId : req.user.repoId
}
edgeMod.loginWF(loginInfo, req, res, function (req, res, result)
{
res.write(result);
res.end();
});
});
app.get('/logout', function(req, res)
{
req.logout();
res.redirect('/');
});
passport.js
passport.serializeUser(function(user, done) {
db.set(user.id, user, function(err, result) {
done(null, JSON.stringify(user));
});
});
passport.deserializeUser(function(json, done)
{
var user = JSON.parse(json);
db.get(user.id, function(err, result)
{
if (result)
{
done(null, result.value);
}
else
{
done(new Error("Bad JSON string in session"), null);
}
});
});
passport.use('local-login', new LocalStrategy({
usernameField : 'email',
passwordField : 'password',
passReqToCallback : true
},
function(req, email, password, done)
{
---login checks---
return done(null, serializeJson);
});
}));
How do I ensure sessions get destroyed on browser close? Any tips for doing this as its my 1st time doing something like this?
EDIT: It seems to be clearing the session on browser close in Firefox but not in chrome. Could this have something to do with chrome remembering pages on close?
Having "Continue where you left off" ticked in chrome options seems to be the problem. Doesn't clear the session on browser close with this ticked.
I am trying to do salseforce aouth in nodejs using passportjs butt getting error.
Cannot GET /callback?code=aPrxaSyVmC8fBbcSNDQ8G8UtoR.YZip2hdfAGpMSc2hf0798gD_UoDle1dqqC0HnPyewycgKMw%3D%3D
Here is my code
'use strict';
var express = require('express'),
passport = require('passport'),
util = require('util'),
ForceDotComStrategy = require('passport-forcedotcom').Strategy;
//----------------------------------------------------------------------------
// REPLACE THE BELOW SETTING TO MATCH YOUR SALESFORCE CONNECTED-APP'S SETTINGS
//----------------------------------------------------------------------------
var CF_CLIENT_ID = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';
var CF_CLIENT_SECRET = 'xxxxxxxxxxxxxxxxxxxxx';
var CF_CALLBACK_URL = 'http://localhost:3000/callback';
var SF_AUTHORIZE_URL = 'https://login.salesforce.com/services/oauth2/authorize';
var SF_TOKEN_URL = 'https://login.salesforce.com/services/oauth2/token';
passport.serializeUser(function(user, done) {
done(null, user);
});
passport.deserializeUser(function(obj, done) {
done(null, obj);
});
var sfStrategy = new ForceDotComStrategy({
clientID: CF_CLIENT_ID,
clientSecret: CF_CLIENT_SECRET,
callbackURL: CF_CALLBACK_URL,
authorizationURL: SF_AUTHORIZE_URL,
tokenURL: SF_TOKEN_URL
}, function(accessToken, refreshToken, profile, done) {
process.nextTick(function() {
delete profile._raw;
return done(null, profile);
});
});
passport.use(sfStrategy);
var app = express();
// configure Express
app.configure(function() {
app.set('views', __dirname + '/views');
app.set('view engine', 'jade');
app.use(express.logger());
app.use(express.cookieParser());
app.use(express.bodyParser());
app.use(express.methodOverride());
app.use(express.session({
secret: 'keyboard cat'
}));
// Initialize Passport! Also use passport.session() middleware, to support
// persistent login sessions (recommended).
app.use(passport.initialize());
app.use(passport.session());
app.use(app.router);
app.use(express.static(__dirname + '/public'));
});
app.get('/', function(req, res) {
console.log(req.user);
if(!req.user) {
req.session.destroy();
req.logout();
return res.redirect('/login');
}
res.render('index', {
user: req.user
});
});
app.get('/login', function(req, res) {
req.logout();
req.session.destroy();
res.render('login', {
user: req.user
});
});
app.get('/auth/forcedotcom', passport.authenticate('forcedotcom'), function(req, res) {
});
app.get('/auth/forcedotcom/callback', passport.authenticate('forcedotcom', {
successRedirect: '/',
failureRedirect: '/login'
}), function(req, res) {
res.redirect('/');
});
app.get('/logout', function(req, res) {
res.redirect('/login');
});
app.listen(3000);
console.log('localhost run in 3000');
function ensureAuthenticated(req, res, next) {
if(req.isAuthenticated()) {
return next();
}
res.redirect('/login');
}
I am trying to fix problem with already post a query in at there but that cannot resole Cannot GET /auth/twitter/callback while using twitter oauth in nodejs
looks like you've set the callback URL to http://yourdomain.com/callback in your salesforce oauth app, but have not define a handler for it correctly
either change the callback URL of your salesforce oauth app to
http://yourdomain.com/auth/forcedotcom/callback
or change the following in your code
app.get('/auth/forcedotcom/callback', passport.authenticate('...
to
app.get('/callback', passport.authenticate('forcedotcom', {
successRedirect: '/',
failureRedirect: '/login'
}), function(req, res) {
res.redirect('/');
});
I have a server in node.js using express and passport with the passport-local strategy.
I have the users in the database and through passport I'm able to authenticate them, unfortunately when a second request comes from the same client the req.isAuthenticated() method returns false.
There is also no user in the request (req.user = undefined).
I've also checked and when doing the authentication although I get back a user from passport.authenticate('local'... I do not get req.user populated then. If I try to set it up manually it just doesn't propagate for following requests.
I don't understand what I'm doing wrong, here is my code.
server.js
var express = require('express'),
compass = require('node-compass'),
routes = require('./server/routes')
http = require('http'),
path = require('path'),
passport = require('passport'),
LocalStrategy = require('passport-local').Strategy,
Database = require('./server/repositories/database'),
Configuration = require('./server/config').Config,
crypto = require('crypto');
var app = express();
app.enable("jsonp callback");
passport.use(new LocalStrategy(
function(email, password, done) {
process.nextTick(function () {
var userService = new UserService();
userService.login(email, crypto.createHash('md5').update(password).digest("hex"), function(error, user) {
if (error) done(error, user);
else if (!user) return done(null, false, { message: 'wrong credentials'});
return done(null, user);
});
});
}
));
passport.serializeUser(function(user, done) {
done(null, user._id);
});
passport.deserializeUser(function(id, done) {
var userService = new UserService();
userService.findById(id, function(err, user) {
done(err, user);
});
});
app.configure(function(){
app.set('port', Configuration.Port);
app.set('views', __dirname + '/app/views');
app.set('view engine', 'ejs');
app.use(express.favicon());
app.use(express.logger('dev'));
app.use(express.cookieParser());
app.use(express.bodyParser());
app.use(express.methodOverride());
app.use(compass({
project: path.join(__dirname, 'app'),
sass: 'styles'
}));
app.use(express.session({ secret: 'keyboard cat' }));
app.use(function(err, req, res, next){
console.error(err.stack);
res.send(500, 'Something broke!');
});
app.use(passport.initialize());
app.use(passport.session());
app.use(app.router);
app.use(express.static(path.join(__dirname, 'app')));
});
routes.configure(app);
Database.open(function() {
app.listen(Configuration.Port, function() {
console.log("Express server listening on port " + Configuration.Port);
});
});
routes.js
var Configuration = require('./config').Config;
var ApiResult = require('../model/apiResult').ApiResult;
var ApiErrorResult = require('../model/apiErrorResult').ApiErrorResult;
var ApiReturnCodes = require('../model/apiReturnCodes').ApiReturnCodes;
var passport = require('passport');
var usersController = require('./controllers/usersController');
exports.configure = function(app) {
function ensureAuthenticated(req, res, next) {
console.log(req.isAuthenticated());
if (req.isAuthenticated()) { return next(); }
else {res.send(new ApiErrorResult(ApiReturnCodes.NOT_LOGGED_IN, null));}
}
app.post('/login', function(req, res, next) {
passport.authenticate('local', function(err, user, info) {
if (err || !user) { console.log(info); res.send(new ApiErrorResult(ApiReturnCodes.ENTITY_NOT_FOUND, null)); }
// If this function gets called, authentication was successful.
// `req.user` contains the authenticated user
else res.send(new ApiResult(user));
})(req,res,next);
});
app.get('/anotherLink', ensureAuthenticated, function(req, res, next) {
res.json({Code:0});
});
}
When I hit the link /anotherLink after being authenticated I get res.isAuthenticated() as false.
Also when I see the req.session after the ensureAuthenticated is called I get:
{ cookie:
{ path: '/',
_expires: null,
originalMaxAge: null,
httpOnly: true },
passport: {} }
What am I missing for it to save the information that that user is authenticated?
On the client side I'm using Angular only doing a simple get with the url without parameters.
If I forgot to put something here just tell me, I'll update it.
Any help will be greatly appreciated. Thanks
So I found out what was wrong with my code.
My passport.deserializeUser method used the method userService.findById
And that called the repository... like this:
userRepository.findUnique({"_id": id}, callback);
because the id was generated by MongoDB the correct call needs to be:
userRepository.findUnique({"_id": new ObjectID(id)}, callback);
I hope this saves some time to the next person with the same problem.
With this detail, this code should work nicely for everyone wanting to use the LocalStrategy on the Passport framework.
I'm new to Express.js, Node.js, and Passport.js. I'm trying to develop an application where a user signs in with their Twitter account and then they can see their Mentions (Tweets in which other Twitter users have mentioned the logged in user's Twitter username). I came across Passport.js, and I have been able to use it successfully allow a user to sign in with their Twitter account.
However, I am not sure how to format a server-side HTTP GET request for the Twitter Mentions. I've reviewed the Twitter API numerous times at https://dev.twitter.com/docs/api/1/get/statuses/mentions, but since I'm unfamiliar with the Node/Express/Passport platform, I'm not sure how to perform this request server-side to return JSON-formatted Mentions. The application is set up to be read-only, as it only needs to be able to see relevant Tweets with the associated user.
The front-end is based on EJS. Below is what code I have that may be relevant. Thank you very much for your help.
-joshingmachine
/app.js
/**
* Module dependencies.
*/
var express = require('express')
, passport = require('passport')
, http = require('http')
, util = require('util')
, path = require('path')
, TwitterStrategy = require('passport-twitter').Strategy;
var TWITTER_CONSUMER_KEY = "theConsumerKeyForMyApp";
var TWITTER_CONSUMER_SECRET = "theConsumerSecretForMyApp";
var users = [];
// Passport session setup.
passport.serializeUser(function(user, done) {
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
var user = users[id];
done(null, user);
});
// Use the TwitterStrategy within Passport.
passport.use(new TwitterStrategy({
consumerKey: TWITTER_CONSUMER_KEY,
consumerSecret: TWITTER_CONSUMER_SECRET,
callbackURL: "http://127.0.0.1:3000/auth/twitter/callback"
},
function(token, tokenSecret, profile, done) {
//console.log(token);
//console.log(tokenSecret);
//console.log(profile);
// asynchronous verification, for effect...
process.nextTick(function () {
var user = users[profile.id] || (users[profile.id] = profile);
done(null, user);
});
}
));
var app = express();
// configure Express
app.configure(function(){
app.set('port', process.env.PORT || 3000);
app.set('views', __dirname + '/views');
app.set('view engine', 'ejs');
app.use(express.favicon());
app.use(express.logger('dev'));
app.use(express.bodyParser());
app.use(express.methodOverride());
app.use(express.cookieParser());
app.use(express.session({ secret:'secret'}));
app.use(passport.initialize());
app.use(passport.session());
app.use(app.router);
app.use(express.static(__dirname + '/public'));
});
app.get('/', function(req, res){
res.render('index', { user: req.user });
});
app.get('/account', ensureAuthenticated, function(req, res){
res.render('account', { user: req.user });
});
app.get('/login', function(req, res){
res.render('login', { user: req.user });
});
// GET /auth/twitter
app.get('/auth/twitter',
passport.authenticate('twitter'),
function(req, res){
// The request will be redirected to Twitter for authentication, so this
// function will not be called.
});
// GET /auth/twitter/callback
app.get('/auth/twitter/callback',
passport.authenticate('twitter', { failureRedirect: '/login' }),
function(req, res) {
res.redirect('/');
});
app.get('/logout', function(req, res){
req.logout();
res.redirect('/');
});
// Create server
http.createServer(app).listen(app.get('port'), function(){
console.log("Express server listening on port " + app.get('port'));
});
// Simple route middleware to ensure user is authenticated.
function ensureAuthenticated(req, res, next) {
if (req.isAuthenticated()) { return next(); }
res.redirect('/login');
}
app.use(function(req, res, next){
res.send(404, '404 Not Found');
});
/routes/index.js
/*
* GET home page.
*/
exports.index = function(req, res){
res.render('index', { title: 'Express' });
};
Install the module:
npm install oauth
Then use this code to create your GET request(replace the variables as needed).
var oauth = new OAuth.OAuth(
'https://api.twitter.com/oauth/request_token',
'https://api.twitter.com/oauth/access_token',
'your application consumer key',
'your application secret',
'1.0A',
null,
'HMAC-SHA1'
);
oauth.get(
'https://api.twitter.com/1.1/trends/place.json?id=23424977',
'your user toke for this app', //test user token
'your user secret for this app', //test user secret
function (e, data, res){
if (e) console.error(e);
console.log(require('util').inspect(data));
done();
});
And reference here if you need more info. Good luck!
https://github.com/ciaranj/node-oauth
I created something similar to what you're trying to do. Since you've signed in succesfully, I'm assuming that you're able to get the access token and secret. Once you have those, use an external library like mtwitter. I made the call like this (editing out the parts where i get the tokens from my db):
app.get('/twitter/userMentions', function (req, res) {
var twit = new mtwitter({
consumer_key: consumerKey,
consumer_secret: consumerSecret,
access_token_key: token,
access_token_secret: secret
});
twit.get("/statuses/mentions_timeline", { "include_entities": false },
function (err, data) {
if (err) {
res.write(err.toString());
}
else res.write(JSON.stringify(data));
res.end('\n');
});
}
I think you are looking for Request node module !!
https://npmjs.org/package/request