passport RESTAPI authentication - node.js

I am novice to Node passport authentication. I completely write an example of passport authentication as below:
var express = require('express');
var passport = require('passport');
var passportLocal = require('passport-local');
/*
Since express doesn't support sessions by
default you need following middlewares.
*/
// For storing session ID in browser
var cookieParser = require('cookie-parser');
// For rendering credentials from request bodies
var bodyParser = require('body-parser');
/*
For server side storage of session information.
All these session information is in server memory.
If the machine app reboot all sessions infromation
will disapear. The information coming with request
, thanks to cookies, with the session information
stored in the server used in deserializing the users.
Be careful properly handle session information
in server farms, round robbining and load balancing etc.
But it is easy to configure express-session middleware
to use external storage. In this case we use local machine.
*/
var expressSession = require('express-session');
var app = express();
app.set('view engine', 'ejs');
// For sessions. Need before Passport middleware
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(expressSession({
// This is the secret used to sign the session ID cookie.
secret: process.env.SESSION_SECRET || 'd7n5ihsxx9by8ydt',
resave: false,
saveUninitialized: false
}));
// Passport need 2 middlewares
app.use(passport.initialize());
app.use(passport.session());
/*
Strategies need to be told how to veryfy username and password
that is inside authorization header that is client is goint to send.
*/
function verifyCredentials(username, password, done) {
/*
Use crypto.pbkdf2(password, salt, iterations, keylen[, digest], callback)
For the real app.
*/
if (username === password) {
/*
done() first argument is Error
Second argument 'user' is any object required for your business logic.
But make it as much as smaller for fast serializing and deserializing.
*/
done(null, {
id : 123,
name: username,
role: 'Admin',
catogery : 'Operator'
});
} else {
done(null, null);
}
};
/*
Now we have to configure passport. Call the local strategy with
single function function(username, password, done) where done is callback
funciton.
*/
passport.use(new passportLocal.Strategy(verifyCredentials));
/*
passport.serializeUser serializes the 'user' object.
The callback function done, need a small piece of
'user' object which is required in deserializing.
In following case 'user.id' is saved to session
req.session.passport.user = {id:'..'}
*/
passport.serializeUser(function(user, done) {
done(null, user.id);
});
/*
In deserialize function you provide in first argument of
deserialize function that same key of user object that was
given to done function in serialize call. So your whole object is
retrieved with help of that key. that key here is id.
In deSerialize function that key is matched with in
memory array / database or any data resource.
*/
passport.deserializeUser(function(id, done){
/*
Query database or cache here! Example
User.findById(id, function(err, user) {
done(err, user);
});
*/
done(null, {
id : id,
name: id,
role: 'Admin',
catogery : 'Operator'
});
});
/*
Middleware for API's. 3rd party middlewares are available
to download and install if you want. Middleware is
nothing but a simple function with the following format.
'next' is callback function which is used to pass the request
to the next middleware inline.
*/
function ensureAuthenticated(req, res, next){
if(req.isAuthenticated()){
next();
} else {
res.status(403).json( {msg: '403 Forbidden'} );
}
}
app.get('/', function(req, res) {
res.render('index', {
/*
Express doesn't have isAuthenticated for req.
But passport add it to req. Also the 'user' object
added in done() callback is available through req.
*/
isAuthenticated : req.isAuthenticated(),
user : req.user
});
});
app.get('/login', function(req, res) {
res.render('login');
});
app.get('/logout', function(req, res) {
//Passport add logout method to request object
req.logout();
res.redirect('/');
});
/*
In this end point, the middleware passport.authenticate('local') is called.
passport.authenticate('local') returns a functon similar to ensureAuthenticated.
*/
app.post('/login', passport.authenticate('local'), function(req, res) {
res.redirect('/');
});
/*
Second endpoint for API's. The endpoint is authenticated
with a middleware which is between URI and function.
*/
app.get('/api/data', passport.authenticate('local'), function(req, res) {
res.json([
{name: 'My'},
{name: 'Kumara'}
]);
});
app.get('/api/data/me', function(req, res) {
res.json([
{name: 'My'},
{name: 'Kumara'}
]);
});
var port = process.env.PORT || 3000;
app.listen(port, function() {
console.log('Server is running on Port : ' + port);
})
My question:
I have two URI's:
/api/data
/api/data/me
I assumed that since I have authenticated /api/data it will automatically authenticate /api/data/me since /api/data part is already authenticated, /api/data/me is like a child of /api/data. But it's not. Does this mean do I have to authenticate each and every API's?
If not, how can I group set of API's in a single strategy?

A wildcard could be used to match everything under a certain path. For example:
app.get('/api/data/*', passport.authenticate('local'), function(req, res, next) {
next(); // call next matching route
});
app.get('/api/data/me', function(req, res) {
res.json([
{name: 'My'},
{name: 'Kumara'}
]);
});
This enables the passport middleware for all requests under /api/data/ so that the user will need to authenticate using the passport local strategy. Logic for individual endpoints under /api/data/ can then be implemented without including the passport middleware.
There's more information about express route matching on the user guide. http://expressjs.com/en/guide/routing.html

Related

Passport req.user not persisting across requests -

Using passport.js local strategy I am trying to use the req.user to obtain current user id so that I can store recipes in the database with the users id. The problem seems to be around the deserialization part of the passport.js file I have in my config file in my app. Whenever I hit the /api/saveRecipe route for some reason it gets deserialized and the req user is then no longer available.
Notes: I am authenticating on my backend server using react on the front end.
Below is my server.js file
Problem: req.user is available after calling passport.authenticate('local') but once api/saveRecipe route is hit req.user is no longer available.
After researching this subject on S.O. it appears that it most often has to do with order in the server file setup but i have looked and reviewed and i believe my setup correct...
const express = require("express");
const bodyParser = require("body-parser");
const session = require("express-session");
const routes = require("./routes");
// Requiring passport as we've configured it
let passport = require("./config/passport");
const sequelize = require("sequelize");
// const routes = require("./routes");
const app = express();
var db = require("./models");
const PORT = process.env.PORT || 3001;
// Define middleware here
app.use(express.urlencoded({ extended: true }));
app.use(express.json());
// passport stuff
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
app.use(express.static("public"));
// We need to use sessions to keep track of our user's login status
// app.use(cookieParser('cookit'));
app.use(
session({
secret: "cookit",
name: "cookit_Cookie"
})
);
app.use(passport.initialize());
app.use(passport.session());
// Serve up static assets (usually on heroku)
if (process.env.NODE_ENV === "production") {
app.use(express.static("client/public"));
}
// the view files are JavaScript files, hence the extension
app.set('view engine', 'js');
// the directory containing the view files
app.set('pages', './');
// Add routes, both API and view
app.use(routes);
// Syncing our database and logging a message to the user upon success
db.connection.sync().then(function() {
console.log("\nDB connected\n")
// Start the API server
app.listen(PORT, function() {
console.log(`🌎 ==> API Server now listening on PORT ${PORT}!`);
});
});
module.exports = app;
my passport.js code
//we import passport packages required for authentication
var passport = require("passport");
var LocalStrategy = require("passport-local").Strategy;
//
//We will need the models folder to check passport against
var db = require("../models");
// Telling passport we want to use a Local Strategy. In other words, we want login with a username/email and password
passport.use(
new LocalStrategy(
// Our user will sign in using an email, rather than a "username"
{
usernameField: "email",
passwordField: "password",
passReqToCallback: true
},
function(req, username, password, done) {
// console.log(`loggin in with email: ${username} \n and password: ${password}`)
// When a user tries to sign in this code runs
db.User.findOne({
where: {
email: username
}
}).then(function(dbUser) {
// console.log(dbUser)
// If there's no user with the given email
if (!dbUser) {
return done(null, false, {
message: "Incorrect email."
});
}
// If there is a user with the given email, but the password the user gives us is incorrect
else if (!dbUser.validPassword(password)) {
return done(null, false, {
message: "Incorrect password."
});
}
// If none of the above, return the user
return done(null, dbUser);
});
}
)
);
// serialize determines what to store in the session data so we are storing email, ID and firstName
passport.serializeUser(function(user, done) {
console.log(`\n\n serializing ${user.id}\n`)
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
console.log(`\n\n DEserializing ${id}\n`)
db.User.findOne({where: {id:id}}, function(err, user) {
done(err, user);
});
});
// Exporting our configured passport
module.exports = passport;
const router = require("express").Router();
const controller = require("../../controllers/controller.js");
const passport = require("../../config/passport");
router.post(
"/login",
passport.authenticate("local", { failureRedirect: "/login" }),
function(req, res) {
console.log(`req body -${req.body}`);
res.json({
message: "user authenticated",
});
}
);
router.post("/saveRecipe", (req, res) => {
console.log(req.user)
if (req.isAuthenticated()) {
controller.saveRecipe;
} else {
res.json({ message: "user not signed in" });
}
});
module.exports = router;
The problem is in your router.post('login'). Try changing it to something like this:
app.post('/login', passport.authenticate('local-login', {
successRedirect: '/profile',
failureRedirect: '/login/failed'})
)
This will correctly set the req.user in your next requests!

Passport js serializeUser & deserializeUser

I have read everything on the argument but still cannot understand it. The documentation on the Passport Js web site is very vague.
I am using Passport JS with the passport-ldapauth Strategy. I do not have a Database. I obviously don't want to hit the LDAP server on each request. I would like to authenticate the user the first time on the POST /login route using the passport strategy with LDAP, store the user in the session and on each subsequent requests I just want to check if the user is already logged in.
I am trying to use the session but I cannot understand how to use Passport + session with the serialize/deserialize flow. Every example I checked use a User.findOne in the deserializeUser function.
As of now I disabled the use of the session for Passport and I am using a custom middleware where I check if req.session.user != null. If that's the case the user is already logged in and I hit next(). Otherwise redirect to login.
Here is some code (for sake of simplicity I deleted the code not related to the question):
Passport configuration:
var express = require('express'),
session = require('express-session'),
passport = require('passport'),
LdapStrategy = require('passport-ldapauth');
var app = express();
var LdapStrategyOptions = {
server: {
url: '<url>',
bindDN: '<dn>',
bindCredentials: "<pwd>",
searchBase: '<searchBase>',
searchFilter: '<filter>'
}
};
passport.use(new LdapStrategy(LdapStrategyOptions));
app.use(cookieParser());
app.use(session({
store: new LokiStore({autosave: false}),
resave: false,
saveUninitialized: false
secret: env.get("SESSION_SECRET")
}));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(passport.initialize());
Routes:
// LOGIN ROUTE
app.get('/login',
function(req, res) {
res.render('login');
});
// LOGIN HANDLER ROUTE
app.post('/login',
passport.authenticate('ldapauth', { session: false }),
function(req, res) {
req.session.userId = req.user.cn;
req.session.user = {
"userId": req.user.cn,
"displayName": req.user.displayName
};
res.redirect('/');
});
// LOGOUT ROUTE
app.get('/logout',
function(req, res) {
req.session.destroy(function(err) {
req.logout();
res.redirect('/');
});
});
// HOME ROUTE
app.get('/', isLoggedIn, function(req, res) {
res.render('home');
});
IsLoggedIn Middleware:
var isLoggedIn = function(req, res, next) {
if (req.session.user != null){
console.log("is auth ok '" + req.session.user.userId +"'");
return next();
}
console.log("redirect to auth/login");
res.redirect('/auth/login');
}
What am I missing? Is there any security fault in my setup?
Any help is appreciated.
From passportjs docs:
In a typical web application, the credentials used to authenticate a user will only be transmitted during the login request. If authentication succeeds, a session will be established and maintained via a cookie set in the user's browser.
Each subsequent request will not contain credentials, but rather the unique cookie that identifies the session. In order to support login sessions, Passport will serialize and deserialize user instances to and from the session.
Basically serializeUser is supposed to return a unique user identifier so you can deserializeUser back into JSON later.
So for your implementation you should probably do something along these lines:
DISCLAIMER: I have no experience with LDAP.
passport.serializeUser(function(user, done) {
//We can identify the user uniquely by the CN,
//so we only serialize this into the session token.
done(null, user.cn);
});
passport.deserializeUser(function(cn, done) {
//Directly query LDAP.
//I'm not sure passport caches the result (only calls deserializeUser for new sessions)
//but worst case you can cache the result yourself.
somehowLoadUserFromLDAPByCN(cn, function(err, user) {
done(err, {
userId: user.cn,
displayName: user.displayName
});
});
});
If you only need an id and a display name, it's totally fine to keep them in session. You should only load the full user profile when you need more fields.

Isomorphic react App can't find req.login after logging in

I log in with the user at the login, and the user object gets saved for req.user (or passports user) however after I go to a different route/state it doesnt hold the user object there anymore. To demonstrate I just try to console.log(req.user) and it returns undefined, for a POST method (updateUserProfile) in my controller. I used PostMan to test the POST method, the GET method worked on Postman for grabbing all users. If I refresh the page the app.get(*) will load the req.user and print it fine, its just in the calls. What could be the reason? Could it be my express setup? Example:
My routes:
/**
* Routes for express app
*/
var express = require('express');
var users = require('../controllers/users');
var feedback = require("../controllers/feedbackapi");
var problem = require("../controllers/problemapi");
var pair = require("../controllers/userproblempairapi");
var mongoose = require('mongoose');
var _ = require('lodash');
var Header = require('../../public/assets/header.server');
var App = require('../../public/assets/app.server');
module.exports = function(app, passport) {
// user routes
app.post('/login', users.postLogin);
app.post('/signup', users.postSignUp);
app.get('/logout', users.getLogout);
app.post('/updateUserProfile', users.updateUserProfile);
// google auth
// Redirect the user to Google for authentication. When complete, Google
// will redirect the user back to the application at
// /auth/google/return
// Authentication with google requires an additional scope param, for more info go
// here https://developers.google.com/identity/protocols/OpenIDConnect#scope-param
app.get('/auth/google', passport.authenticate('google', { scope: [
'https://www.googleapis.com/auth/userinfo.profile',
'https://www.googleapis.com/auth/userinfo.email'
] }));
// Google will redirect the user to this URL after authentication. Finish the
// process by verifying the assertion. If valid, the user will be logged in.
// Otherwise, the authentication has failed.
app.get('/auth/google/callback',
passport.authenticate('google', {
successRedirect: '/',
failureRedirect: '/login'
}));
//Important** on refresh we look at our wildcard call to find out if we're still logged in.
// Retrieves all topics on any endpoint for demonstration purposes
// If you were indeed doing this in production, you should instead only
// query the Topics on a page that has topics
app.get('*', function(req, res, next) {
// We don't want to be seeding and generating markup with user information
var user = req.user ? { authenticated: true, isWaiting: false } : { authenticated: false, isWaiting: false };
// An object that contains response local variables scoped to the request, and therefore available only to the view(s) rendered during
// that request/response cycle (if any). Otherwise, this property is identical to app.locals
// This property is useful for exposing request-level information such as request path name, authenticated user, user settings, and so on.
// pass in data to be seeded into the TopicStore
res.locals.data = {
UserStore: { user: user }
};
next();
});
// This is where the magic happens. We take the locals data we have already
// fetched and seed our stores with data.
// App is a function that requires store data and url to initialize and return the React-rendered html string
app.get('*', function (req, res, next) {
var html = App(JSON.stringify(res.locals.data || {}), req, res);
html = html.replace("TITLE", Header.title)
.replace("META", Header.meta);
if(process.env.NODE_ENV === 'devhotloader') {
html = html.replace("LINK", '');
} else {
html = html.replace("LINK", Header.link);
}
res.contentType = "text/html; charset=utf8";
res.end(html);
});
};;
My users controller:
var _ = require('lodash');
var User = require('../models/user');
var passport = require('passport');
var ParsonsProblem = require('../models/parsonsproblem');
var Feedback = require('../models/feedback');
var UserProblemPair = require('../models/userproblempair');
/**
* POST /login
*/
exports.postLogin = function(req, res, next) {
// Do email and password validation for the server
/*var Feed = new Feedback({
description: 'Supsuop'
});
var Problem = new ParsonsProblem({
description: 'Test',
feedback: Feed
});
var Pair = new UserProblemPair({
problem_id: Problem,
attempt_quantity: 0,
completed: true
});
*/
console.log(req.body);
passport.authenticate('local', function(err, user, info) {
if(err) return next(err);
if(!user) {
req.flash('errors', {msg: info.message});
}
// Passport exposes a login() function on req (also aliased as logIn()) that can be used to establish a login session
req.logIn(user, function(err) {
if(err) return next(err);
req.flash('success', { msg: 'Success! You are logged in'});
res.end('Success');
console.log(req.user);
});
})(req, res, next);
/*
Feed.save(function(err) {console.log('Feedback saved');});
Problem.save(function(err) {console.log('Problem saved');});
Pair.save(function(err) {console.log('ProblemPair saved');});
console.log(Feed);
*/
};
/**
* POST UpdateUser Profile
*/
exports.updateUserProfile = function(req, res) {
var id = req.user._id;
if (req.body.firstName == "") {
req.body.firstName = req.user.profile.firstName;
}
if (req.body.lastName == "") {
req.body.lastName = req.user.profile.lastName;
}
if (req.body.gender == "") {
req.body.gender = req.user.profile.gender;
}
if (req.body.section == "") {
req.body.section = req.user.profile.section;
}
User.findById(id, function(err, user) {
console.log("ID: " + id);
user.profile.firstName = req.body.firstName;
user.profile.lastName = req.body.lastName;
user.profile.gender = req.body.gender;
user.profile.section = req.body.section;
user.save();
res.end();
});
}
/**
* GET /logout
*/
exports.getLogout = function(req, res, next) {
// Do email and password validation for the server
console.log("User has been logged out");
req.logout();
res.redirect('/');
//res.end():
};
/**
* POST /signup
* Create a new local account
*/
exports.postSignUp = function(req, res, next) {
var user = new User({
email: req.body.email,
password: req.body.password,
profile: {
firstName : req.body.firstName,
lastName : req.body.lastName,
section : req.body.section
}
});
//user.profile.firstName = req.body.firstName;
//user.profile.lastName = req.body.lastName;
//user.profile.section = req.body.section;
User.findOne({email: req.body.email}, function(err, existingUser) {
if(existingUser) {
req.flash('errors', { msg: 'Account with that email address already exists' });
res.redirect('/sign');
}
user.save(function(err) {
if(err) return next(err);
req.logIn(user, function(err) {
if(err) return next(err);
console.log('Successfully created');
console.log('Printing user');
console.log(user);
console.log('Print our body from our request');
console.log(req.body);
res.redirect('/');
});
});
});
};
Express Setup:
app.disable('x-powered-by');
app.set('views', path.join(__dirname, '..', 'views'));
app.set('view cache', false);
app.use(bodyParser.json({limit: '100mb'}));
app.use(bodyParser.urlencoded({extended: true})); // for parsing application/x-www-form-urlencoded
app.use(methodOverride());
app.use(express.static(path.join(__dirname, '../..', 'public')));
// I am adding this here so that the Heroku deploy will work
// Indicates the app is behind a front-facing proxy,
// and to use the X-Forwarded-* headers to determine the connection and the IP address of the client.
// NOTE: X-Forwarded-* headers are easily spoofed and the detected IP addresses are unreliable.
// trust proxy is disabled by default.
// When enabled, Express attempts to determine the IP address of the client connected through the front-facing proxy, or series of proxies.
// The req.ips property, then, contains an array of IP addresses the client is connected through.
// To enable it, use the values described in the trust proxy options table.
// The trust proxy setting is implemented using the proxy-addr package. For more information, see its documentation.
app.enable('trust proxy');
// Cookie parser should be above session
// cookieParser - Parse Cookie header and populate req.cookies with an object keyed by cookie names
// Optionally you may enable signed cookie support by passing a secret string, which assigns req.secret
// so it may be used by other middleware
app.use(cookieParser());
// Create a session middleware with the given options
// Note session data is not saved in the cookie itself, just the session ID. Session data is stored server-side.
// Options: resave: forces the session to be saved back to the session store, even if the session was never
// modified during the request. Depending on your store this may be necessary, but it can also
// create race conditions where a client has two parallel requests to your server and changes made
// to the session in one request may get overwritten when the other request ends, even if it made no
// changes(this behavior also depends on what store you're using).
// saveUnitialized: Forces a session that is uninitialized to be saved to the store. A session is uninitialized when
// it is new but not modified. Choosing false is useful for implementing login sessions, reducing server storage
// usage, or complying with laws that require permission before setting a cookie. Choosing false will also help with
// race conditions where a client makes multiple parallel requests without a session
// secret: This is the secret used to sign the session ID cookie.
// name: The name of the session ID cookie to set in the response (and read from in the request).
// cookie: Please note that secure: true is a recommended option.
// However, it requires an https-enabled website, i.e., HTTPS is necessary for secure cookies.
// If secure is set, and you access your site over HTTP, the cookie will not be set.
var sess = {
resave: true,
saveUninitialized: true,
// Use generic cookie name for security purposes
key: 'sessionId',
secret: secrets.sessionSecret,
// Add HTTPOnly, Secure attributes on Session Cookie
// If secure is set, and you access your site over HTTP, the cookie will not be set
cookie: {
expires: false,
httpOnly: false,
//secure: false
},
store: new MongoStore({ url: secrets.db, autoReconnect: true})
};
var node_env = process.env.NODE_ENV;
console.log('Environment: ' + node_env);
//if(node_env === 'production') {
//sess.cookie.secure = false; // Serve secure cookies
//}
app.use(session(sess));
app.use(passport.initialize());
app.use(passport.session());
app.use(flash());
edit: Printing out the req.session and req.user only display when logging in and signing in/out, when transitioning to another view with react-router it doesn't have that info anymore. ("tried with console logs")
I think you have to user react-router to handle internal redirects.

Google-oauth2 passport not working

I am trying to implement sign-in with google and passport but I am running into a bit of a problem. I successfully authenticate with google, but my data isn't being passed to the front end. I Haven't changed anything from the original code except for the URI and necessary client id and secret. Can anyone tell me what I am missing?
var express = require( 'express' )
, app = express()
, server = require( 'http' ).createServer( app )
, passport = require( 'passport' )
, util = require( 'util' )
, bodyParser = require( 'body-parser' )
, cookieParser = require( 'cookie-parser' )
, session = require( 'express-session' )
, RedisStore = require( 'connect-redis' )( session )
, GoogleStrategy = require( 'passport-google-oauth2' ).Strategy;
// API Access link for creating client ID and secret:
// https://code.google.com/apis/console/
var GOOGLE_CLIENT_ID = "307841191614-1shiak514mrjugtbon3dm2if8hbhnvdv.apps.googleusercontent.com"
, GOOGLE_CLIENT_SECRET = "fgViegEgHWuoc1X-p63iPmpF";
// Passport session setup.
// To support persistent login sessions, Passport needs to be able to
// serialize users into and deserialize users out of the session. Typically,
// this will be as simple as storing the user ID when serializing, and finding
// the user by ID when deserializing. However, since this example does not
// have a database of user records, the complete Google profile is
// serialized and deserialized.
passport.serializeUser(function(user, done) {
done(null, user);
console.log("User: "+ user.displayName); // If there is a persistent session, the console logs out the displayName
});
passport.deserializeUser(function(obj, done) {
done(null, obj);
});
// 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: GOOGLE_CLIENT_ID,
clientSecret: GOOGLE_CLIENT_SECRET,
//NOTE :
//Carefull ! and avoid usage of Private IP, otherwise you will get the device_id device_name issue for Private IP during authentication
//The workaround is to set up thru the google cloud console a fully qualified domain name such as http://mydomain:3000/
//then edit your /etc/hosts local file to point on your private IP.
//Also both sign-in button + callbackURL has to be share the same url, otherwise two cookies will be created and lead to lost your session
//if you use it.
callbackURL: "http://127.0.0.1:3000/oauth2callback",
passReqToCallback : true
},
function(request, accessToken, refreshToken, profile, done) {
// asynchronous verification, for effect...
process.nextTick(function () {
// To keep the example simple, the user's Google profile is returned to
// represent the logged-in user. In a typical application, you would want
// to associate the Google account with a user record in your database,
// and return that user instead.
console.log(profile); //logs google profile successfully
return done(null, profile);
});
}
));
// configure Express
app.set('views', __dirname + '/views');
app.set('view engine', 'ejs');
app.use( express.static(__dirname + '/public'));
app.use( cookieParser());
app.use( bodyParser.json());
app.use( bodyParser.urlencoded({
extended: true
}));
app.use( session({
secret: 'cookie_secret',
name: 'kaas',
store: new RedisStore({
host: '127.0.0.1',
port: 6379
}),
proxy: true,
resave: true,
saveUninitialized: true
}));
app.use( passport.initialize());
app.use( passport.session());
/*
===
===
===
Here is where the data is not being read.
*/
app.get('/', function(req, res){
res.render('index', { user: req.user });
console.log(req.user); //Output: undefined
});
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/google
// Use passport.authenticate() as route middleware to authenticate the
// request. The first step in Google authentication will involve
// redirecting the user to google.com. After authorization, Google
// will redirect the user back to this application at /auth/google/callback
app.get('/auth/google', passport.authenticate('google', { scope: [
'https://www.googleapis.com/auth/plus.login',
'https://www.googleapis.com/auth/plus.profile.emails.read']
}));
// GET /auth/google/callback
// Use passport.authenticate() as route middleware to authenticate the
// request. If authentication fails, the user will be redirected back to the
// login page. Otherwise, the primary route function function will be called,
// which, in this example, will redirect the user to the home page.
app.get( '/oauth2callback',
passport.authenticate( 'google', {
successRedirect: '/',
failureRedirect: '/login'
}));
app.get('/logout', function(req, res){
req.logout();
res.redirect('/');
});
server.listen( 3000 );
// Simple route middleware to ensure user is authenticated.
// Use this route middleware on any resource that needs to be protected. If
// the request is authenticated (typically via a persistent login session),
// the request will proceed. Otherwise, the user will be redirected to the
// login page.
function ensureAuthenticated(req, res, next) {
if (req.isAuthenticated()) { return next(); }
res.redirect('/login');
}
`
Here is the simple layout that doesn't seem to be receiving any data.
<% if (!user) { %>
<h2>Welcome! Please log in.</h2>
<% } else { %>
<h2>Hello, <%= user.displayName %>.</h2>
<% } %>
Your code works, I just used same example on my app.
I had the same problem and I realized that I'm not using a valid account in my tests.
This API retrieves data from Google+ profile. Are you using a valid Google account with linked Google+ profile to authenticate?

How do I persist form data on redirect if Passport authentication fails?

I am trying to use passport to authenticate users on the web page. Everything works fine, except when authentication fails and the passport redirects the user to the same rout, all the data on the form is lost. Is there a way to persist the data and pass them back to the form.
I have the following in routes.js
// =====================================
// SIGNUP ==============================
// =====================================
// show the signup form
app.get('/signup', function(req, res) {
// render the page and pass in any flash data if it exists
signup.isAuthenticated = req.isAuthenticated();
signup.user = req.user;
signup.message = req.flash('signupMessage');
res.render('signup', signup);
});
// process the signup form
app.post('/signup', passport.authenticate('local-signup', {
successRedirect : '/', // redirect to the secure section
failureRedirect : '/signup', // redirect back to the signup page if there is an error
failureFlash : true // allow flash messages
}));
on my passport.js I have the following:
// =========================================================================
// LOCAL SIGNUP ============================================================
// =========================================================================
// we are using named strategies since we have one for login and one for signup
// by default, if there was no name, it would just be called 'local'
passport.use('local-signup', 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
},
// this function is used when signing up
function(req, email, password, done) {
// TODO: get the user from data
if(email == 'myemail#gmail.com') {
// user email already exists
console.log('user already exists !');
return done(null, false, req.flash('signupMessage', 'That email is already taken.'));
}
else {
// if there is no user with that email
// create the user
var newUser = { username : 'myemail#gmail.com', name : 'Name Surname' };
newUser.local.email = email;
newUser.local.password = newUser.generateHash(password);
return done(null, newUser);
}
}));
and my server.js has the following:
// server.js
// set up ======================================================================
// get all the tools we need
var express = require('express');
var path = require('path');
var app = express();
var port = process.env.PORT || 3000;
// var mongoose = require('mongoose');
var passport = require('passport');
var flash = require('connect-flash');
var morgan = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
var session = require('express-session');
var multer = require('multer');
var configDB = require('./config/database.js');
// configuration ===============================================================
// mongoose.connect(configDB.url); // connect to our database
require('./config/passport')(passport); // pass passport for configuration
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');
// set up our express application
app.use(morgan('dev')); // log every request to the console
app.use(bodyParser.json()); // get information from html forms
app.use(bodyParser.urlencoded({ extended: false }));
// use multer to process multi-part requests and multer to save our files by default to /uploads/ directory
app.use(multer({
dest : path.join(__dirname, '/uploads/'),
limits : {
fieldNameSize : 200, // 200 bytes
files : 5, // 5 files
fileSize : 5194304000000, // 5 GB
fields : 50 // 50 fields on the form
}
}))
app.use(cookieParser()); // read cookies (needed for auth)
app.use(express.static(path.join(__dirname, 'public')));
// required for passport
app.use(session({
secret: 'mylongsecretpassphrase',
resave : true,
saveUninitialized : true
})); // session secret
app.use(passport.initialize());
app.use(passport.session()); // persistent login sessions
app.use(flash()); // use connect-flash for flash messages stored in session
// routes ======================================================================
require('./app/routes.js')(app, passport); // load our routes and pass in our app and fully configured passport
// show error page if the resource is not found
app.use('*', function(req, res) {
res.render('page-error', {
title : 'myWeb - Page Error',
description : 'my web page',
keywords : 'keywords1, keywords2, keywords3'
});
});
// launch ======================================================================
app.listen(port);
console.log('Node listens on port ' + port);
any help would be greatly appreciated !
If you don't want to lose form data, you could use AJAX to send your form, and send a status 401 Unauthorized in case the auth fails. Passport sends the 401 by default so the following should work (untested, may contain typos) :
app.post('/login', function(req, res, next) {
passport.authenticate('local-signup',
function(req, res) {
// If this function gets called, authentication was successful. If not, your ajax call gets a 401 status and you can handle it in .fail()
res.redirect('/');
});
});
A bit of explanation from the passport website :
By default, if authentication fails, Passport will respond with a 401
Unauthorized status, and any additional route handlers will not be
invoked. If authentication succeeds, the next handler will be invoked
and the req.user property will be set to the authenticated user.
Instead of using the default callbacks like
passport.authenticate('local-signup', {
successRedirect : '/', // redirect to the secure section
failureRedirect : '/signup', // redirect back to the signup page if there is an error
failureFlash : true // allow flash messages
})
you can use a custom callback, and pass whatever input vars you want via flash messages like so
router.post('/signup', function(request, response, next) {
passport.authenticate('local-signup', function(err, user, info) {
if (err)
return next(err);
if (!user) {
// Attach flash messages to keep user input
request.flash('emailInput', request.body.email);
request.flash('usernameInput', request.body.username);
return response.redirect('/signup');
}
// Note that when using a custom callback, it becomes the application's responsibility
// to establish a session (by calling req.login()) and send a response.
request.logIn(user, function(err) {
if (err)
return next(err);
return response.redirect('/profile');
});
})(request, response, next);
});
then, when redirected, you can send the flash messages to your view templates like normal
response.render('signup.ejs', {
signupMessage: request.flash('signupMessage'),
emailInput: request.flash('emailInput'),
usernameInput: request.flash('usernameInput')
});

Resources