Passport.js: how to access user object after authentication? - node.js

I'm using Passport.js to login a user with username and password. I'm essentially using the sample code from the Passport site. Here are the relevant parts (I think) of my code:
app.use(passport.initialize());
app.use(passport.session());
passport.serializeUser(function(user, done) {
done(null, user);
});
passport.deserializeUser(function(obj, done) {
done(null, obj);
});
passport.use(new LocalStrategy(function(username, password, done) {
User.findOne({ username: username }, function(err, user) {
if (err) {
return done(err);
}
if (!user) {
return done(null, false, { message: 'Incorrect username.' });
}
if (!user.validPassword(password)) {
return done(null, false, { message: 'Incorrect password.' });
}
return done(null, user);
});
}
));
app.post('/login',
passport.authenticate('local', { failureRedirect: '/login/fail', failureFlash: false }),
function(req, res) {
// Successful login
//console.log("Login successful.");
// I CAN ACCESS req.user here
});
This seems to login correctly. However, I would like to be able to access the login user's information in other parts of the code, such as:
app.get('/test', function(req, res){
// How can I get the user's login info here?
console.log(req.user); // <------ this outputs undefined
});
I have checked other questions on SO, but I'm not sure what I'm doing wrong here. Thank you!

Late to the party but found this unanswered after googling the answer myself.
Inside the request will be a req.user object that you can work withr.
Routes like so:
app.get('/api/portfolio', passport.authenticate('jwt', { session: false }), stocks.buy);
Controller like this:
buy: function(req, res) {
console.log(req.body);
//res.json({lel: req.user._id});
res.json({lel: req.user});
}

In reference to the Passport documentation, the user object is contained in req.user. See below.
app.post('/login',
passport.authenticate('local'),function(req, res) {
// If this function gets called, authentication was successful.
// `req.user` contains the authenticated user.
res.redirect('/users/' + req.user.username);
});
That way, you can access your user object from the page you redirect to.
In case you get stuck, you can refer to my Github project where I implemented it clearly.

I'm pretty new to javascript but as I understand it from the tutorials you have to implement some session middleware first as indicated by 250R.
const session = require('express-session')
const app = express()
app.use(bodyParser.urlencoded({ extended: false }))
app.use(bodyParser.json())
let sess = {
genid: (req) => {
console.log('Inside the session middleware')
console.log(req.sessionID)
return uuid()
},
store: new FileStore(),
secret: 'keyboard cat', // password from environment
resave: false,
rolling: true,
saveUninitialized: true,
cookie: {
HttpOnly: true,
maxAge: 30 * 60 * 1000 // 30 minutes
}
}
app.use(session(sess))
// call passport after configuring the session with express-session
// as it rides on top of it
app.use(passport.initialize())
app.use(passport.session())
// then you will be able to use the 'user' property on the `req` object
// containing all your session details
app.get('/test', function (req, res) {
console.log(req.user)
})

res.render accepts an optional parameter that is an object containing local variables for the view.
If you use passport and already authenticated the user then req.user contains the authenticated user.
// app.js
app.get('/dashboard', (req, res) => {
res.render('./dashboard', { user: req.user })
})
// index.ejs
<%= user.name %>

You can define your route this way as follows.
router.post('/login',
passport.authenticate('local' , {failureRedirect:'/login', failureFlash: true}),
function(req, res) {
res.redirect('/home?' + req.user.username);
});
In the above code snippet, you can access and pass any field of the user object as "req.user.field_name" to the page you want to redirect. One thing to note here is that the base url of the page you want to redirect to should be followed by a question mark.

late to party but this worked for me
use this in your app.js
app.use(function(req,res,next){
res.locals.currentUser = req.user;
next();
})
get current user details in client side like ejs
<%= locals.currentUser.[parameter like name || email] %>

Solution for those using Next.js:
Oddly, —> the solution <— comes from a recently removed part of the README of next-connect, but works just as it should. You can ignore the typescript parts if you're using plain JS.
The key part is the getServerSideProps function in ./src/pages/index (or whichever file you want to get the user object for).
// —> ./src/authMiddleware.ts
// You'll need your session, initialised passport and passport with the session,
// so here's an example of how we've got ours setup, yours may be different
//
// Create the Passport middleware for SAML auth.
//
export const ppinit = passport.initialize();
//
// Set up Passport to work with expressjs sessions.
//
export const ppsession = passport.session();
//
// Set up expressjs session handling middleware
//
export const sess = session({
secret: process.env.sessionSecret as string,
resave: true,
saveUninitialized: true,
store: sessionStore,
});
// —> ./src/pages/index.ts
// update your user interface to match yours
export interface User {
id: string;
name: string;
}
interface ExtendedReq extends NextApiRequest {
user: User;
}
interface ServerProps {
req: ExtendedReq;
res: NextApiResponse;
}
interface ServerPropsReturn {
user?: User;
}
export async function getServerSideProps({ req, res }: ServerProps) {
const middleware = nc()
.use(sess, ppinit, ppsession)
.get((req: Express.Request, res: NextApiResponse, next) => {
next();
});
try {
await middleware.run(req, res);
} catch (e) {
// handle the error
}
const props: ServerPropsReturn = {};
if (req.user) props.user = req.user;
return { props };
}
interface Props {
user?: User;
}
//
// A trivial Home page - it should show minimal info if the user is not authenticated.
//
export default function Home({ user }: Props) {
return (
<>
<Head>
<title>My app</title>
<link rel="icon" href="/favicon.ico" />
</Head>
<main>
<h1>Welcome to My App {user?.name}</h1>
</main>
</>
);
}

You'll need to make sure that you register a middleware that populates req.session before registering the passport middlewares.
For example the following uses express cookieSession middleware
app.configure(function() {
// some code ...
app.use(express.cookieParser());
app.use(express.bodyParser());
app.use(express.cookieSession()); // Express cookie session middleware
app.use(passport.initialize()); // passport initialize middleware
app.use(passport.session()); // passport session middleware
// more code ...
});

Related

What's the difference between req.isAuthenticated() and passport.authenticate() in passport?

I am a beginner in programmation and experimenting the authentication process through node.js, express and mongoDB. I have used passport, passport-local and passport-local-mongoose to create a login/logout for users.
When my authentication succeed, user is redirect to my index page which show his/her name.
But I have a question… What is the difference between req.isAuthenticated() and passport.authenticate() ?
In my main.js, I have directly placed my req.user in the core of my session :
const passport = require('passport');
const expressSession =require('express-session');
const cookieParser = require('cookie-parser');
const connectFlash = require('connect-flash')
const localStrategy = require('passport-local').Strategy;
app.use(cookieParser("SecretStringForCookies"));
app.use(
expressSession({
secret : "SecretStringForCookies",
cookie : {
maxAge: 2000000
},
resave : false,
saveUninitialized : false
}))
app.use(passport.initialize());
app.use(passport.session());
//Serializing and deserializing user for checking login status in cookie
const User = require('./models/allUsers');
passport.use(User.createStrategy());
passport.serializeUser(User.serializeUser());
passport.deserializeUser(User.deserializeUser());
app.use(connectFlash());
app.use((req, res, next) => {
res.locals.flashMessages = req.flash();
res.locals.currentUser = req.user;
next();
});
For my authentication to succeed, I have used the following code in a UserController.js page :
module.exports = {
authentication : passport.authenticate("local", {
failureRedirect: "/login",
successRedirect: "/index",
successFlash : { type: 'success_msg', message: 'Welcome !' },
failureFlash : { type: 'error_msg', message: 'Your email and/or password are wrong, try again !' }
}),
isAuthenticatedUser : (req, res, next) => {
if(req.isAuthenticated()) {
next();
}
res.redirect = "/login";
},
}
My routes regarding the authentification :
const express = require('express');
const router = express.Router();
const userController = require('./userController');
router.post('/login', userController.authenticate, userController.isAuthenticatedUser);
router.get("/logout", userController.isAuthenticatedUser, (req, res)=> {req.logout(), res.redirect("/")});
router.get('/index');
My HTML :
<nav class="nav-links">
<% if(currentUser) { %>
<ul>
<li><%= currentUser.name %></li>
<li>Logout</li>
<li>Home</li>
</ul>
<% } %>
</nav>
However, my login authentication process seems to work fine with just only passport.authenticate() and my routes for login/logout doesn’t seem to need my function about req.isAuthenticated().
Sorry if my question seems dumb or weird but I am really confused about its purpose…
Could you please give me some advice ?
Thank you in advance for your help !
passport.authenticate() method extracts user credentials from request object and passes them to the authentication function which you use to authenticate the process,
passport.use(new LocalStrategy(
function(username, password, done) { // this is an authentication function
User.findOne({ username: username }, function (err, user) {
if (err) { return done(err); }
if (!user) { return done(null, false); }
if (!user.verifyPassword(password)) { return done(null, false); }
return done(null, user);
});
}
));
By default, when authentication succeeds, the req.user property is set to the authenticated user, a session is established, and the next function in the stack is called.
req.isAuthenticated() method checks if the user is already authenticated by the authentication function or not, for example, if you have an admin dashboard page and you want to ensure that only authenticated users can access this page, therefore you use req.isAuthenticated() method to ensure that the user who sends the request is already authenticated by the authentication function.
module.exports= (req,res, next)=>{
if(req.isAuthenticated()){ //checks whether user
//is authenticated by the passport.authenticate() method
next();
}
res.redirect('/login');
}
The req.isAuthenticated() command returns whether the user is logged in or not, and the other ensures that the user is logged in.

PassportJS authenticates user but returns 401 Unauthorized on subsequent requests

I'm writing one of my first applications in NodeJS so please bear with me. I've managed to successfully authenticate a user to our Active directory and I can see the connect.sid cookie being set and used on the subsequent requests.
Upon debugging the application by dumping the req object I can also see that the user variable has been set successfully. From the documentation I've read that seems to be a criteria for a successful session match?
However, the request is still getting a 401 Unauthorized.
To summarize:
The user is successfully authenticated after posting credentials /login.
Upon successful authentication the user is redirected to "/".
The "/" path replies with 401 Unauthorized.
Any ideas much appreciated. Code below.
const express = require('express');
var bodyParser = require('body-parser');
var session = require('express-session');
var passport = require('passport')
var ActiveDirectoryStrategy = require('passport-activedirectory')
// Setup the authentication strategy
passport.use(new ActiveDirectoryStrategy({
integrated: false,
ldap: {
url: 'ldap://myad.company.com',
baseDN: 'DC=domain,DC=company,DC=com',
username: 'user',
password: 'password'
}
}, function (profile, ad, done) {
ad.isUserMemberOf(profile._json.dn, 'Group', function (err, isMember) {
if (err) return done(err)
return done(null, profile)
})
}));
passport.serializeUser(function(user, done) {
done(null, JSON.stringify(user));
});
passport.deserializeUser(function(user, done) {
done(null, JSON.parse(user));
});
const app = express();
app.use(bodyParser.urlencoded({extended: true}));
app.use(session(
{ secret: "password" }
));
app.use(passport.initialize());
app.use(passport.session());
// For debugging purposes
app.use(function (req, res, next) {
console.log(req)
next()
})
// The login page posts a form containing user and password
app.get("/login", (req, res) => {
res.sendFile(__dirname + '/public/index.html');
})
// Handler for the login page. Receives user and password and redirects the user to /
app.post('/login',
passport.authenticate('ActiveDirectory', {
failWithError: true,
successRedirect: "/",
failureRedirect: "/login"
}
), function(req, res) {
res.json(req.user)
}, function (err) {
res.status(401).send('Not Authenticated')
}
)
// This is where the issue happens. The page returns "Unauthorized".
// Using console.log(req) shows that the user property has been set to the req object.
// However, for some reason it still fails.
app.get('/',
passport.authenticate('ActiveDirectory', {
failWithError: true,
}
), function(req, res) {
res.send("test")
}, function (err) {
res.status(401).send('Not Authenticated')
})
Found what I did wrong!
The .authenticate method is only used to validate credentials, not to validate a session.
So this:
app.get('/',
passport.authenticate('ActiveDirectory', {
failWithError: true,
}
), function(req, res) {
res.send("test")
}, function (err) {
res.status(401).send('Not Authenticated')
})
Should become:
app.get('/', function(req, res, next) {
// This is verifying that the user part has been populated,
// which means that the user has been authenticated.
if (req.user) {
res.send('Returning with some text');
} else {
// If the user property does no exist, redirect to /login
res.redirect('/login');
}
});
Another thing that I changed was the serialize/deserialize functions:
passport.serializeUser(function(user, done) {
done(null, user);
});
passport.deserializeUser(function(user, done) {
done(null, user);
});
This removes redundant serializing/deserializing.
These articles really helped me understand the flow:
http://toon.io/understanding-passportjs-authentication-flow/
https://www.airpair.com/express/posts/expressjs-and-passportjs-sessions-deep-dive
Hope it helps someone else!
/Patrik

how to use passport.js in a proper way

I am trying passport library to authenticate api request. To start I have created a NodeJS application with the express framework. The project contains some apis that serve some data. In public folder it contains index.html page having username and password field.
Index.html
<form action="/login" method="post">
<div>
<label>Username:</label>
<input type="text" name="name"/>
</div>
<div>
<label>Password:</label>
<input type="password" name="password"/>
</div>
<div>
<input type="submit" value="Log In"/>
</div>
</form>
Created a server.ts that create a http server and listen on some port and created apis using express framework.
Server.ts
let userList: User[] = [new User(1, "Sunil"), new User(2, "Sukhi")];
let app = express();
// passport library
let passport = require('passport');
let LocalStrategy = require('passport-local').Strategy;
// middlewares
app.use(express.static("public"));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(session({ resave: false, saveUninitialized: true, secret: "secretKey123!!" }));
// passport middleware invoked on every request to ensure session contains passport.user object
app.use(passport.initialize());
// load seriliazed session user object to req.user
app.use(passport.session());
// Only during the authentication to specify what user information should be stored in the session.
passport.serializeUser(function (user, done) {
console.log("Serializer : ", user)
done(null, user.userId);
});
// Invoked on every request by passport.session
passport.deserializeUser(function (userId, done) {
let user = userList.filter(user => userId === user.userId);
console.log("D-serializer : ", user);
// only pass if user exist in the session
if (user.length) {
done(null, user[0]);
}
});
// passport strategy : Only invoked on the route which uses the passport.authenticate middleware.
passport.use(new LocalStrategy({
usernameField: 'name',
passwordField: 'password'
},
function (username, password, done) {
console.log("Strategy : Authenticating if user is valid :", username)
let user = userList.filter(user => username === user.userName)
if (!user) {
return done(null, false, { message: 'Incorrect username.' });
}
return done(null, user);
}
));
app.post('/login', passport.authenticate('local', {
successRedirect: '/done',
failureRedirect: '/login'
}));
app.get('/done', function (req, res) {
console.log("Done")
res.send("done")
})
app.get('/login', function (req, res) {
console.log("login")
res.send("login")
})
// http server creation
let server = http.createServer(app);
server.listen(7000, () => {
console.log('Up and running on port 7000');
});
Now when I hit localhost:7000 it opens the login page and when I click submit with username from userList it returns the done otherwise login. This is fine.
Now every call goes through deserializeUser method.
The problem is when I call other URLs directly without hitting /login (authenticates the user) they also work fine and return data.
I was expecting that if the request is not authenticated all other calls will fail as deserializeUser is intercepting every request but in this case, no passport method is called.
Is this how it works? or I am missing something?
You need to add a middleware, for check if your user is authenticated:
isAuthenticated = (req, res, next) => {
if (req.isAuthenticated()) {
//if user is logged in, req.isAuthenticated() will return true
return next();
}
res.redirect('/login');
};
And you have to use that middleware like that:
//if user not authenticated, he will be redirect on /login
app.get('/done', isAuthenticated, (req, res) => {
res.send("done")
});
I was missing middleware to authenticate all subsequent requests. So I have created isAuthenticated method (thanks #Sombrero).
// request interceptor that will check user authentication
private static isAuthenticated = (req, res, next) => {
console.log("Authenticating :", req.originalUrl)
if (req.isAuthenticated()) {
return next();
}
res.redirect('/login');
};
and then in every request
app.get('/done', isAuthenticated, (req, res) => {
res.send("done")
});
but this was tough to use isAuthenticated method in every request. So I created an array of API list that is public and added middleware to intercept every request and updated isAuthenticated method to ignore public apis
// list of apis for which authentication is not required
private static publicApiList: string[] = ["/login"];
// request interceptor that will check user authentication
private static isAuthenticated = (req, res, next) => {
console.log("Authenticating :", req.originalUrl)
if (req.isAuthenticated() || Server.publicApiList.indexOf(req.originalUrl) > -1) {
return next();
}
res.redirect('/login');
};
and then used this method as middleware
app.use(Server.isAuthenticated)

Passport/ExpressJS - How to Properly Maintain Session State with Cookies?

I am new to using Passport authentication with express between my
server and client (on two different ports), and am having great
trouble understanding both the concept of maintaining session state
with Passport and also trying to retrieve req.session.passport.user
in order to determine if a request is sent from a user that has
successfully logged on.
Checking Persistent Session State with Passport
In my code, I have a route to my profile page that uses my own
authenticated function. My login route uses
passport.authenticate('local'), however. Should I be applying this
to ALL subsequent routes (like for my profile), or is my separate
authentication expected?
req.session.passport undefined
For some reason, the cookie that is sent with the /profile GET request is missing the passport object after a successful login (by
passport.use('local-login')) It looks like the following if I
console.log(req.session) in authenticated. Why is this?
Session {
cookie:
{ path: '/',
_expires: 2017-07-11T01:38:30.087Z,
originalMaxAge: 14400000,
httpOnly: true,
secure: false } }
Server Code
// server.js
let app = express();
app.use(cors());
import passportConfig from '../config/passport';
passportConfig(passport); //apply passport configuration
var jsonParser = bodyParser.json();
app.use(jsonParser);
app.use(cookieParser('mySecret'));
app.use(session({
secret: 'mySecret',
resave: false,
saveUninitialized: true,
cookie: {secure: false, maxAge: 4*60*60*1000 }
}));
app.use(passport.initialize());
app.use(passport.session());
function authenticated (req, res, next) {
// req.session.passport is undefined
if (req.session.passport.user) {
next();
}
else {
res.redirect('/signup');
}
}
app.get('/profile', authenticated, (req, res) => {
// server code handling route
}
app.post('/login', passport.authenticate('local-login', {
session: true
}), (req, res) => {
res.json({
email: req.user.local.email
});
});
// passport.js
import User from '../models/User';
export default function passportConfig(passport) {
passport.serializeUser((user, done) => {
console.log('serializing user');
done(null, user.id);
});
// used to deserialize the user
passport.deserializeUser((id, done) => {
console.log('deserializing user');
User.findById(id, (err, user) => {
done(err, user);
});
});
passport.use('local-login', new LocalStrategy({
usernameField : 'email',
passwordField : 'password',
passReqToCallback : true
}, (req, email, password, done) => {
// if verification is successful
var newUser = new User();
// set credentials of newUser...
return done(null, newUser);
}));
}
Client Code
// profile.js (client action code with react-redux)
export function profile(terms, location) {
return (dispatch) => {
return fetch('profile',
{credentials: 'include'})
.then(response => response.json())
.then(json => { dispatch(profileSuccess(json)); })
.catch(error => dispatch(profileError(error)));
}
}
I am aware there are actually quite a lot of articles/posts available
of similar issues, but applying many of the different solutions have
all failed for me. Any help to my problem specifically is greatly
appreciated.

passport-github how to extract session cookie to know that the user already logged in

I am building a passport-github auth to my application. but I think currently I don't know how to extract the cookie from request that would say user is already logged in. so everytime When i go to home page i get redirected to /login.
My code roughly looks like this:
passport.use(new GitHubStrategy({
clientID: authConfig.GITHUB_CLIENT_ID,
clientSecret: authConfig.GITHUB_CLIENT_SECRET,
callbackURL: "http://127.0.0.1:8080/auth/github/callback"
},
function(accessToken, refreshToken, profile, done) {
// asynchronous verification, for effect...
return db.user.findOne({where:{github_id:profile.id}})
.then(data=>{
if (data) {
return done(null,data);
} else {
return db.user.build({ github_id: profile.id }).save()
.then(()=>{
return db.user.findOne({where:{github_id:profile.id}})
})
.then(data=>{
return done(null,data);
})
}
});
}
));
// 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
passport.serializeUser(function(user, done) {
console.log("serialize>>>>>", user.github_id);
done(null, user.github_id);
});
passport.deserializeUser(function(id, done) {
console.log("deserialize>>>>", id);
db.user.findOne({where:{github_id: id}})
.then(user=>{
done(null, user.toJSON());
})
});
I have established the session :
app.use(session({ secret: 'keyboard cat', resave: false, saveUninitialized: false }));
app.use(passport.initialize());
app.use(passport.session());
And I have an isAuthenticated function that checks for req info:
function isAuthenticated (req, res, next) {
// If the user is logged in, continue with the request to the restricted route
console.log("req.user is>>>>", req);
if (req.isAuthenticated()) {
return next();
}
// If the user isnt' logged in, redirect them to the login page
return res.redirect("/index");
}
I am using this passport-github lib. I cannot get some useful information from reqseems
updated to include routes:
Here is the routes:
const isAuthenticated = require('./middleware/isAuthenticated.js');
router
.get('/index', query.renderIndex)
.get('/', isAuthenticated, query.displayRepos)
.post('/', query.queryRepoTopic)
.post('/trending', query.addRepo)
.post('/addTopic', query.addTopic)
.get('trending', query.updateScore);
router.get('/login', auth.loginPage)
.get('/auth/github',
passport.authenticate('github', { scope: [ 'user:email' ] }),
function(req, res){}
)
.get('/auth/github/callback',
passport.authenticate('github', { failureRedirect: '/login' }),
auth.signInRedirect
)
.get('/logout', auth.logout);
Here is the controller function that does the logic:
const loginPage = (req, res) => {
res.render('index');
}
// signin a user in
const signInRedirect = (req, res) => {
console.log("here in callback>>>");
console.log("req.user is>>>>", req.user);
//res.json("you have successfully logged in!");
res.redirect('/');
}
const logout = (req, res) => {
req.logout();
res.redirect('/index');
}
I see you have this route configuration:
const isAuthenticated = require('./middleware/isAuthenticated.js');
router
.get('/index', query.renderIndex)
.get('/', isAuthenticated, query.displayRepos)
...
If you want to call localhost:3000, and be redirected to auth/github when you are not logged in, you could change isAuthenticated function like this:
function isAuthenticated (req, res, next) {
// If the user is logged in, continue with the request to the restricted route
console.log("req.user is>>>>", req);
if (req.isAuthenticated()) {
return next();
}
// If the user isnt' logged in, redirect them to the github login page.
return res.redirect("/auth/github");
}
Wich means, when you try to call the '/', the isAuthenticated will check if the req.user was set (if (req.isAuthenticated())), if not, redirect to the /auth/github route.
Have you tried this?
Have it can help!

Resources