I'm working on a node express app, and I've got it working well with the Facebook authentication already. I'm now attempting to enable our own email/password login and running it to a roadblock. With Facebook I am able to access and write to the session during auth:
everyauth.faceboook
// Basic stuff
.findOrCreateUser( function( sess, accessToken, extra, fbUser) {
var promise = this.Promise();
sess.myvar = myvar
// Find user in DB, etc
promise.fulfill(fbUser);
return promise()
This works great as I can save some stuff I need later to the session right in this method. But I'm not sure how to do the same thing when using everyauth password login:
everyauth.password
// Basic stuff
.authenticate( function(email, password) {
var promise = this.Promise();
// Create new user in DB, etc
// Now I need to save stuff to the session but I'm not sure how to access
// it in here...
promise.fulfill(newUser)
return promise
So is there any way to access the session in the authenticate and login methods (which use the same API) of everyauth.password?
You can't access the session from your .authenticate function, and what you're trying to do here feels wrong. Your .authenticate function should simply be looking up the user by the login parameter, validating that the password parameter matches the user's password and then returning the user object for that user via the callback. If you want to use everyauth to also create users you should be using the .validateRegistration and .registerUser functions.
Related
Using passport in an express app. For reasons, the session tokens expire after one hour.
If the user is active when the session expired, the deserialize function "fails", i.e., user is undefined.
passport.deserializeUser(function (id, done) {
const user = sessionManager.userLookup(id);
done(null, user);
});
The trouble is that when user is undefined, then there is no req.user for subsequent middleware. So to the code it simply appears that the user is not signed in, with no breadcrumbs to indicate that the session just expired. The app simply redirects all request from unauthenticated users to /login.
For a user in the middle of a workflow, this experience is sub-optimal.
The expiration can be detected within passport.deserializeUser() like this:
passport.deserializeUser(function (id, done) {
const user = sessionManager.userLookup(id);
const errorInfo = ( expire logic check ) ? 'session has expired' : null;
done(errorInfo, user);
});
I can get the logic check right with the sessionManager. The trouble with this solution is that passport sends the user a 500 Internal Server Error, which is also sub-optimal.
What I would like is for the app to send a flash error saying the session has expired. But passport.deserialize() has no visibility to the req object for calling req.flash().
At this point the only way I can think to resolve the issue is to insert a middleware before passport, where the code would lookup the user in the session manager and call req.flash() if the session has expired. It seems like passport should provide a better way to handle such errors.
Answers would be extra-helpful if they include a link to documentation for passport.deserialize(). The only docs I have found here make no mention of how passport handles errors or if it is possible to configure or override the behavior.
UPDATE
After some reflection, flash is not the best mechanism for reporting the session expiration. The app should instead redirect to a "session expired" page. However, the main question still stands. The call changes from req.flash() to res.redirect(), but neither of these objects is available in passport.deserialize().
you can add req as a first parameter in the function, like this:
(also, recommend you use arrow function)
passport.deserializeUser(async (req, id, done) => {
req.flash('error', {});
(...)
});
I'm using active directory to authenticate users, so I thought I didn't need to use Passport and that all I would need to do is after the password checks out is to create a global(?) boolean with res.locals to indicate that the user has been authenticated.
I've tried something like this in a controller function:
ad.authenticate(username,password, function(err,auth) {
//some of the things I tried unsuccessfully -- should be true after logged in
res.locals.auth = auth
app.locals.auth = auth //app not defined
})
However, I've discovered that when I call a later function checking if the user is logged in as part of middleware for a diff route, res.locals.auth and app.locals.auth are either false or undefined. I've tried setting both vars in my server.js file at the beg with the code below but that didn't work either...
app.use((req, res, next) => {
app.locals.auth = false;
res.locals.auth = false;
next();
});
So my question is, what var/where should I be saving the authenticated status? Or should I just use passport instead because there's some security concern that I was unaware of? What is the point of the isMemberOf in passport setup example?
https://www.npmjs.com/package/passport-activedirectory
All I want to do is just check user credentials and basically recreate req.isAuthenticated in Passport because I couldn't figure out how to use it because of the isMemberOf.
Usually the server sends back a token containing some useful data (user or session id, expiration date) either by cookies or by JWT (json web token).
Then a client puts the token into every request to the server . The server validates expiration date and handles requests.
Cookies will be put into a request by the browser automatically. JWT should be put into a request by your client code.
I have created node js app using express framework.
I have created middleware for restricting access to some routes.
Middleware actually works fine. but i have difficulties in displaying data.
Suppose In My app i have created route for display list of countries('/country/master')i.e html page which is using internally different/default route ('/country/') to get data from mongoDB.
In this case user will not able to see data cause i have not given permission to "/" routes. but i want to display data but not allow him to make use of "/" route to check data.
How can i deal with this case ????
The answer depends on your authentication strategy i.e. are you using session identifiers, access tokens, etc.
In either case I suggest that you break out the credential exchange (aka login) from the authentication. They should be separate middleware functions. Below is an example of what this looks like.
While this answers your question, specific to ExpressJS, it does leave out a lot of other details that matter when you are building an authentication system (like how to securely store passwords). I work at Stormpath, we provide user management as an API so that you don't have to worry about all the security details! It's very easy to integrate our API into your application, using the express-stormpath module. You'll have a fully featured user database in minutes, without having to setup mongo or a user table.
All that said, here's the example:
/* pseudo example of building your own authentication middleware */
function usernamePasswordExchange(req,res,next){
var username = req.body.username;
var password = req.body.password;
callToAuthService(username,password,function(err,user){
if(err){
next(err); // bad password, user doesn’t exist, etc
}else{
/*
this part depends on your application. do you use
sessions or access tokens? you need to send the user
something that they can use for authentication on
subsequent requests
*/
res.end(/* send something */);
}
});
}
function authenticate(req,res,next){
/*
read the cookie, access token, etc.
verify that it is legit and then find
the user that it’s associated with
*/
validateRequestAndGetUser(req,function(err,user){
if(err){
next(err); // session expired, tampered, revoked
}else{
req.user = user;
next();
}
});
}
app.post('/login',usernamePasswordExchange);
app.get('/protected-resource',authenticate,function(req,res,next){
/*
If we are here we know the user is authenticated and we
can know who the user is by referencing req.user
*/
});
You can positioning of middleware in you app.for example:-
app.get('/country/master',function(req,res){
})
app.use(function(req,res){
your middle ware for providing authentication
})
// other routes where authentication should be enabled
app.get('other urls')
I was looking for a way to let my client authorize with the facebook JS SDK and then somehow transfer this authorization to my node server (so it can verify requests with the fb graph api)
I stumbled across:
https://github.com/jaredhanson/passport-facebook/issues/26
&
https://github.com/drudge/passport-facebook-token
what seems to be an entirely different strategy from passport-facebook.
Am I correct when assuming that:
One logs in with the fb JS SDK, and then the facebook-token strategy somehow extracts the token and fb id from the document or body object?
Or is there any other decent way to achieve this? I'm namely trying to avoid the redirects enforced by the server SDKs
I've spent a couple of days this week trying to figure out the best way to use Facebook Authentication for a private API, using passport.js — passport-facebook-token is perfect for this.
You are correct in assuming these are two separate authentication strategies. You don't need passport-facebook installed to use passport-facebook-token.
If you have Facebook authentication implemented in the client-side JS (or iOS etc.), and are looking for a way to then authenticate API requests using your user's Facebook authToken, passport-facebook-token is a really elegant solution.
passport-facebook-token works totally independently of passport-facebook, and basically handles the redirects required by Facebook internally, before passing the request along to your controller.
So to authenticate an API route using passport-facebook-token, you'll need to set up a passport strategy like so:
passport.use('facebook-token', new FacebookTokenStrategy({
clientID : "123-your-app-id",
clientSecret : "ssshhhhhhhhh"
},
function(accessToken, refreshToken, profile, done) {
// console.log(profile);
var user = {
'email': profile.emails[0].value,
'name' : profile.name.givenName + ' ' + profile.name.familyName,
'id' : profile.id,
'token': accessToken
}
// You can perform any necessary actions with your user at this point,
// e.g. internal verification against a users table,
// creating new user entries, etc.
return done(null, user); // the user object we just made gets passed to the route's controller as `req.user`
}
));
It's worth noting that the User.findOrCreate method used in the passport-facebook-token Readme is not a default mongo/mongoose method, but a plugin that you'll have to install if you want it.
To use this auth strategy as middleware for any of your routes you'll need to pass it an access_token object either as a URL parameter, or as a property of the request body.
app.get('/my/api/:access_token/endpoint',
passport.authenticate(['facebook-token','other-strategies']),
function (req, res) {
if (req.user){
//you're authenticated! return sensitive secret information here.
res.send(200, {'secrets':['array','of','top','secret','information']});
} else {
// not authenticated. go away.
res.send(401)
}
}
NB. the access_token property is case-sensitive and uses an underscore.
The documentation for passport-facebook-token isn't extensive, but the source is really well commented and pretty easy to read, so I'd encourage you to take a look under the hood there. It certainly helped me wrap my head around some of the more general ways that passport works.
I'm rather new to node, I have started writing a small app and am working on my user auth and profile. I'm using express, passport and ejs. I've got passport working and have my user redirecting to a profile page that requires additional data before the user begin's to use the application. My question is:
What's the proper way to update the user's session (created at passport.authentication) when a user updates their profile data? I would like to update the user session in order to hit it rather than the data base for basic user data.
If your using express you have middleware that looks like:
function (res, req, next){}
When you get session data you can add it to the req object.
function (res, req, next){
// get the session here...
req.session = session;
}
Then any other middleware will have access to the req.session object which will contain the user's session.