what does req.login do in passport - node.js

I am using multiple passport stratergy across my app.
Now, since I am using multiple passport strategy to connect (and not to just sign-in), I decided to Google things on how to do it.
This is where I stumbled upon this code
passport.authenticate('meetup', (err, user, info) => {
if (err) { return next(err); }
if (!user) { return res.redirect(process.env.CLIENT_ADDRESS); }
req.logIn(user, function(err) {
if (err) { return next(err); }
return res.redirect(process.env.CLIENT_ADDRESS);
});
Here I am unable to comprehend what is happening, like for first question, what is if (!user), Does it mean req.user
Second, there is req.logIn()
According to passport docs,
Passport exposes a login() function on req (also aliased as logIn())
that can be used to establish a login session.
and
When the login operation completes, user will be assigned to req.user.
Then what is the difference between using serializer/deserializer when compared with req.login?
Also in the callback, we can always do this
passReqToCallback: true
}, (req, accessToken, refreshToken, params, profile, cb) => {
to get req
To summarize can someone please help me comprehend the above code snippet?

At a high level Passport.js is a middleware that "serializes" a user identity in a request/response header (usually a session cookie). This serializing step means that it's taking the login information that identifies a user and produces a new object that represents the user. Think of this object as a key 🔑 card that only Passport will know how to interpret.
When a user makes additional API requests they pass that same identification header back. Passport auths the request by "deserializing" it to identify what user is making that request.
req.login() is the magic that is generating a session for a user. This session represents how long a login is good for without having to re-authenticate.
Let's take a look at the beginning of your snippet:
passport.authenticate('meetup', (err, user, info) => {
...
if (!user) { return...
In this snippet, passport is being set up as middleware. When a request comes through, passport behind the scenes has already interpreted the request header by deserializing the cookie and determines if it represents a user. If there is not a user or the request header does not represent a user, the request is not authorized.

req.login aliased as req.logIn
Passport exposes a login() function on req (also aliased as logIn()) that can be used to establish a login
session.
When the login operation completes, user will be assigned to req.user
Note: passport.authenticate() middleware invokes req.login() automatically.

Use a lower version of passport for this feature v0.4.1
You can install this with
npm install passport#^0.4.1
Your req.login function should work with that version.

Related

How to send a flash error when passport.deserializeUser() fails?

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', {});
(...)
});

How is passport receiving the profile?

I have implemented passport with GitHub-Strategy. Here's the Glith.
It works wonderfully and I'm receiving the user-profile on redirect from GitHub.
Now, I'm only trying to understand how this actually works 'under the hood'.
I did not find any similar question here on stackoverflow, neither on Passport.
So if I open Chrome Developer Tools during the Auth-Flow, the following seems to be going on when I click on Login with GitHub:
the node route /auth/github is called
node redirects to https://github.com/login/oauth/authorize?response_type=code&redirect_uri=https%3A%2F%2Frightful-exclusive-carriage.glitch.me%2Fauth%2Fgithub%2Fcallback&client_id=ccfcc73fac8223317176
the user is presented with GitHub-Login-Page
User types in GitHub-credentials and clicks 'Login'
GitHub checks the credentials
If valid credentials are provided, user is authenticated and GitHub redirects to the registered callback-endpoint, which is in my case: https://rightful-exclusive-carriage.glitch.me/auth/github/callback
The callback-url has a url-parameter, e.g. ?code=02337a951c242b9202fd. It's interesting to note, that it's a GET-method and nothing else is provided.
On the server, the passport.authenticate('github', ...) method is called inside of the /auth/github/callback-route.
When the GithubStrategy is instanciated, a callback-function is passed with the signature function(accessToken, refreshToken, profile, cb). Somehow magically, the accessToken and profile are fully available here. And I don't understand how this happens.
How is passport receiving the profile?
Is node.js making a server-side call to GitHub? Maybe with the ?code=<id> ?
Yeah that is exactly what NodeJS is doing. This doesn't have anything to do with Passport.JS or Node.JS. It is the OAuth mechanism of how authorizations work.
Whenever a the Identity provider like twitter/facebook calls your /callback with a ?code= query param It then hits another url and gets the AccessToken, RefreshToken and Idtoken(which is basically the user profile).
You can check out in the source code as well:
In this strategy.js#L157 and strategy.js#L173 of passports oauth strategy:
if (req.query && req.query.code) {
....
self._oauth2.getOAuthAccessToken(code, params,
function(err, accessToken, refreshToken, params) {
....
}
....
}
And the oauth2.js#L177 and oauth2.js#L190 of node-oauth package can see that:
exports.OAuth2.prototype.getOAuthAccessToken= function(code, params, callback) {
...
this._request("POST", this._getAccessTokenUrl(), post_headers, post_data, null, function(error, data, response) {
....
})
}
Bascially a POST request is being sent to the accessTokenUrl.

Should req.user store all user information in Express application

I am working on setting up logins with session using Express, express-session, and passport.
In a passport documentation as I go I see this example: http://www.passportjs.org/docs/profile/
passport.serializeUser(function(user, done) {
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
User.findById(id, function(err, user) {
done(err, user);
});
});
Also, I was following this tutorial: https://github.com/bradtraversy/node_passport_login
Now according to these examples deserializeUser stores the user information in req.user. However, I see that the whole user is stored which puzzles me, as this also stores the passwords to the object.
Isn't that risky?
Or maybe it is not possible to access req.user from front end?
Assuming your backend server is not compromised, req.user will stay only in your backend, which should be a trusted environment, and will not be sent back to your client via res by default.
Also, anything stored within req will only be available in the request itself, another request will have its own req instance, so data is not shared and should not leak to another request unless purposely made to do so.
However, you should always be staying on the ball, keep in mind to test and make sure all data sent back to your client does not have any sensitive info contained within them (e.g. password, tokens).
If you are not comfortable with storing that in req.user, you can always add a layer of middleware to strip the sensitive info before reaching your controller. This way, routes that use the middleware will not have sensitive info exposed.

Passport.js strategy fails when not using session

I'm trying to figure out how to integrate a Oauth strategy(github) to my application which uses express and websockets.
I'm following this guide which explains how to use JWT tokens instead of using the default passport sessions
https://blog.hyphe.me/token-based-authentication-with-node/
this is the code i have so far
app.use(passport.initialize())
app.get('/auth/github',passport.authenticate('github',{session:false}),serialize, generateToken, respond)
app.get('/auth/github/callback',passport.authenticate('github',{failureRedirect:'/'}),
function(req,res){
res.redirect('/')
}
)
When i try to login via github - i get the below error
Error: Failed to serialize user into session
at pass (/home/avernus/Desktop/experiments/oauth/node_modules/passport/lib/authenticator.js:271:19)
at Authenticator.serializeUser (/home/avernus/Desktop/experiments/oauth/node_modules/passport/lib/authenticator.js:289:5)
at IncomingMessage.req.login.req.logIn (/home/avernus/Desktop/experiments/oauth/node_modules/passport/lib/http/request.js:50:29)
at Strategy.strategy.success (/home/avernus/Desktop/experiments/oauth/node_modules/passport/lib/middleware/authenticate.js:235:13)
at verified (/home/avernus/Desktop/experiments/oauth/node_modules/passport-oauth2/lib/strategy.js:177:20)
at Strategy._verify (/home/avernus/Desktop/experiments/oauth/passport.js:13:12)
at /home/avernus/Desktop/experiments/oauth/node_modules/passport-oauth2/lib/strategy.js:193:24
at /home/avernus/Desktop/experiments/oauth/node_modules/passport-github/lib/strategy.js:174:7
at passBackControl (/home/avernus/Desktop/experiments/oauth/node_modules/oauth/lib/oauth2.js:125:9)
at IncomingMessage.<anonymous> (/home/avernus/Desktop/experiments/oauth/node_modules/oauth/lib/oauth2.js:143:7)
I'm not sure where exactly the problem is
this is my github strategy
passport.use(new githubStrategy({
clientID:'********',
clientSecret:'*******',
callbackURL:'http://localhost:3000/auth/github/callback'
},
function(accessToken,refreshToken,profile,done){
console.log('accessToken: ',accessToken,' refreshToken: ',refreshToken,' profile: ',profile)
return done(null,profile)
}
))
I'm able to successfully get the profile from github
the serialize function
function serialize(req, res, next) {
db.updateOrCreate(req.user, function(err, user){
if(err) {return next(err);}
// we store the updated information in req.user again
req.user = {
id: user.id
};
next();
});
}
from my experience passportjs with oauth always requires sessions to operate, despite the session: false option.
i believe the underlying oauth library dependencies look for sessions no matter what. its quite frustrating.
edit: to add more detail to this, the example you are linking to uses the default strategy, which is not oauth based. in this instance you could opt out of using sessions. you are using the github strategy which uses oauth thus requires sessions
Aren't you missing the {session:false} option in your callback?
app.get('/auth/github/callback',passport.authenticate('github',{failureRedirect:'/', session: false}),
function(req,res){
res.redirect('/')
})
Im guessing right here because I've never worked with Strategies that requires a callback. But i would imagine that passport tries to serialize the user in the callback as thats the point where you receive the profile from Github.

Passport.js: passport-facebook-token strategy, login through JS SDK and THEN authenticate passport?

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.

Resources