This question is half about my code specifically, and half about the high-level view of actually getting data for a particular user from a node API.
So I have user signup/signin all working fine, and lets say I have a one-to-many mapping between Users and Widgets, so each User can create and delete Widgets from his or her profile page.
So here are my database models:
var User = new mongoose.Schema({
email: {
type: String,
unique: true,
required: true
},
hash: String,
salt: String
widgets : [{ type: Schema.Types.ObjectId,
ref: 'widget'
}],
});
var Widget = new mongoose.Schema({
name : String,
type : String,
description : String,
});
In the user login process, I generate a JWT and use that to save an authorization header on the front-end.
Now my question is two-fold. 1) How do I correctly save widgets so that they are associated with the currently logged in User? and 2) How do I get all widgets associated with a user?
My initial thoughts are something like this:
app.get('api/widgets', function(req, res, next){
// decode authorization header to get user email
// run database query to return all widgets associated with user email
});
app.post('api/widgets', function(req, res, next){
// decode authorization header to get user email
// create widget and update user with widget ID
});
Is this the correct idea? And if so, how would I go about decoding the authorization header to get the user's email? Do I need to go through this same process for every object associated with the User? Or is there a faster and more convenient way to do this in Node/Express?
Is this the correct idea?
In my opinion, yes, that is correct.
And if so, how would I go about decoding the authorization header to get the user's email?
If you're using jsonwebtoken package, there's a verify method that decodes the token. You can even store multiple values in a single token, like e-mail and name. verify will decode the token into an object with both properties.
Do I need to go through this same process for every object associated with the User? Or is there a faster and more convenient way to do this in Node/Express?
Loading those objects on every request is probably the most common method. If you find that this loading process is slowing down your application, you can cache it using node-cache or Redis (recommended when you have a distributed application cluster).
I also suggest that, instead of decoding the user's token into an email on every route, it's common to use a catch all route that decodes the token and store in the locals. DRY!
app.use(function (req, res, next) {
var token = req.signedCookies.token
res.locals.user = jwt.verify(token, 'my-secret')
next()
})
Related
I'm working on an Ionic application.
On the one hand I have an auth basic form in which people fill in their username and password. On the other hand I'd like to implement authentification with JSON Web Tokens and Node JS.
The workflow would be this one : as soon as a user fills in his credentials, they will be sent with a POST request. If these credentials are correct, the user can access to the application and gets an access token as a response.
The thing is that I'm a little bit lost with all that concepts. I built a form and sent informations with a POST request. I managed to create some APIs with Node JS and that's ok. I see how to build a authentified webservice too (e.g : https://github.com/jkasun/stack-abuse-express-jwt/blob/master/auth.js).
But I concretely don't understand the links between the html form and the authorisation check part..
To be clearer, how is it possible to make the html part and the Node JS scripts communicate together ?
Before posting that question I made many researches and found many stuff on building an authentified API. But there was very few advice on how to make it communicate with the client part (I mean the form), which is what I have to do.
If anyone has any ressources (document, Github examples..) on that, I'll greatly appreciate. But I would be very happy too if someone try to make me understand these concepts. I guess I have to improve my knowledge on all that so that I could test some POCs.
Many thanks in advance !
JWT General flow:
1- Authenticate using a strategy (You done it)
2- Deliver an accessToken along with response (You done it)
3- The client MUST store this accessToken (LocalStorage is the best place, not cookies: They are vulnerable to csrf attacks)
4- On every request you are going to make to a protected area (where user is supposed to be authenticated and authorized), make sure to send you accessToken along with it, you can put it on Authorization header, a custom header, directly in body of the request... Basicaly just make sure to send it properly.
5- On the server receiving client requests, you NEED to verify that token (You verify it by checking the signature of the accessToken).
6- If he is authorized, great, if not, send back an HTTP Unauthorized Error.
Here is my implementation using an accessToken on a header + passportjs-jwt:
Client code
To store token:
localStorage.setItem('accessToken', myAccessToken);
To send it:
const myAccessToken = localStorage.getItem('accessToken');
{
headers: {'Authorization', `Bearer ${myAccessToken}`}
}
Server code
1- Configure passport
passport.use('jwt', new JwtStrategy({
jwtFromRequest: jwtPassport.ExtractJwt.fromAuthHeaderAsBearerToken(),
secretOrKey: myAccessTokenSecret,
passReqToCallback: true
}, (req, payload, done: (err?, user?) => void): void {
User
.findOne({where: {id: req.params.id}})
.then((user: User) => {
if (!user) {
return done(new Error(`No user found with id: ${req.params.id}`), null);
}
return done(null, user);
})
.catch((e: Error) => done(e, null));
}));
Pay attention to callback: If your callback is called, it means that passport has successfuly verified the token (It is valid). In my example, i get the user details in database and this is the user that will be returned and put in req.user object passed to my controller below:
2- Finally, the controller route (protected area):
.get('/users/:id', passport.authenticate('jwt'), (req, res, next) => {
// do stuff in protected area.
}
And that's it. If you want more security, check refreshTokens implementation.
I used passport because i found it relevant in my case, but you can write your own handler, by using jsonwebtoken and just calling its "verify" function.
You can find documentation of passport jwt strategy here => http://www.passportjs.org/packages/passport-jwt/
What am i trying to do is a single todo app where you can create user and log in with that user.Every user can CRUD for single todo and can fetch all of the todos that are linked to that particular user.
this is the schema for the userSchema
const userSchema = new Schema({
firstName:{type:String,trim:true},
lastName:{type:String,trim:true},
userName:{type:String,required:true,unique:true,sparse:true,trim:true},
email:{type:String,required:true,unique:true,sparse:true,trim:true},
phoneNumber:{type:String,unique:true,sparse:true,required:true},
hash:{type:String,required:true}
})
and i have todoSchema
author:{type: Schema.Types.ObjectId},
title:{type:String, required:true, trim:true},
context:{type:String, trim:true,},
isFinished:{type:Boolean,required:true,default: false},
finishedAt:{type:Date}
Also i have login system that works with JWT,My question is how can i save todo with the id of the user that is currently logged in..And also how can i fetch all todos that are linked to the user that is currently logged in
I dont want todos to be stored in userSchema as array,rather as different schema where you can do CRUD for each todo
-I was reading something called populate that comes with mongoose but i dont know how to implement that
--It will be very nice if you can give me some ideas how to do that..I don't need full implementation..Thank you
-If you need more of the code here it is https://github.com/jkuzmanovik/todoApi
Once the user is logged in ( I guess you are using passport ) passport creates req.user which can be. req.user will contain all the details given in the user schema while creating passport plugin.
This can be used to populate your toDo schema like follows:
in the get request you do
toDo.find({})
.populate('author')
...
But if your goal is to find all toDos by the user you can do as follows
toDo.find({author:req.user._id})
This can be done only after authentication.
This will get you all the todos by the user.
PS: Once you populate only the author field will be replaced (not the proper word but understandable) by the user object itself, it does not help in filtering. Mongoose population should be used meticulously as it is database resource hungry and will reduce the server performance.
About JWT, whenever you make a req you need to pass the JWT to the server (by URL, authorization header etc.). There is a method called extract which will extract the user from the JWT token and thus authentication is possible.
More about that read documentation on passport-jwt and jsonwebtoken.
In my login module once I log in I pass my request to a header to store a session here is the code
var series = rand.generate(16);
var token = rand.generate(16);
var loginToken = new LoginTokens({
userId: req.user._id,
series: series,
token: token
});
loginToken.save(function(err, l) {
if (err) {
console.log(err);
} else {
console.log(l);
res.cookie('loginToken', JSON.stringify({
series: series,
token: passwordHash.generate(token)
}));
res.status(200);
res.set('Content-Type', 'application/json');
res.end(JSON.stringify({
'success': req.params.res
}));
}
});
Though this code was pre-written and I don't understand it much(I don't get the point of randomly generating 16 digit number and saving it if somebody does please explain)
I have been tasked with implementing log out and As I don't understand the rationale behind above code I want to implement my own session module such that even if the same user logs in from a different device, both sessions will be maintained and logging out from one device will not log out someone from all device.
So now the point comes that the session id will not be based on user id. Also there is a module called express-session which I don't know if I should use or not, personally I feel that after reading it up on GitHub that what it will do is create a session id for me, which again I will have to save in my database, so why to import a module, instead I will base64 encode a random number like above and use it.
So what I want to ask is the real question
How do you implement sessions in node js?
My understanding of life cycle
A user signs up you see if he/she exists in the database and if he does you raise an error message and if he/she doesn't you create a username for him, probably by email id.
You then via code log him in. In that process, you first check if he is there in the database. If he is you go ahead take the _id field and try and create a session object. Ques what does your session table should look like? What all parameters should be present in sessions table? Then how do save them into res.cookies? Then what do you do with all those cookies, like on client side?
While implementing logout. My thinking is using passport.logout will remove the user object from request header but, I will have to first remove the entry from sessions table somehow empty the cookies and then do req.logout?
Can somebody please answer the above doubts?
I asked a very specific question but realised after the comment that what I need is logical clarity and I did not find one on youtube videos nor on SO posts, so if someone can share their views mostly in code, with some explanation it would be great.
Cookies get deleted by sending a new cookie with the same name (some invalid data would be good) and an expiry in the past.
passport.logout() removes the session cookie that passport uses to maintain the current user, the next request will no longer have the cookie and will appear as an invalid request.
I'm not sure why there's a loginToken as well as passport authentication, apart from allowing you to invalidate sessions server side (by deleting the appropriate token from the database) Check if your passport strategy configuration has any checks against LoginToken during it's deserialize user step (this is where passport takes the session cookie and turns it into a user against the request)
I'm using passport to authenticate users at my site. Users can register orders, which have and foreignKey (ObjectId) to the User object.
Example-objects (written as mongoose schemas):
var orderSchema = new mongoose.Schema({
...
address: String,
_userID: {type: mongoose.Schema.Types.ObjectId, required: true, ref: 'User'}
});
var userSchema = new mongoose.Schema({
email: String,
});
Mongoose will create the primary key for each object.
My question is; is it enough to check if req.user._id === order._userID? Or can the req.user object be tampered with? Can I trust that req.user._id is the id of the logged in user?
I've found a couple of good resources, but it's not exactly what I'm asking of.
http://toon.io/articles/understanding-passportjs-authentication-flow/
http://passportjs.org/guide/authenticate/
So the question:
can the req.user object be tampered with?
Is difficult to answer, since you could have code within your application that will have access to your request object, and within it, modify the user. It's important to understand what code you have running within the flow of each request for anyone really, but especially those concerned about the security of their application. With that said, I can at least point you to where in the code this is established, and you can trace it with a debugger to assure yourself of the flow.
As you've mentioned, the passport documentation discusses authentication configuration options in their guide, and by default will process "logging in" the user when your strategy dictates successful authentication. You can provide a custom callback (mentioned in the referenced documentation above) to process this as well. In the end, it's important that req.logIn is called (which is done by default without any custom callbacks provided). Here's a link to the source. (Passport extends the request object via this code to provide helper functions which it later uses.)
The specific line you maybe interested in is here, which assigns to the req object the property user with a value of the authenticated user:
this[property] = user;
From there on, you have access to the logged in user under req.user, and their ID under req.user.id. Again note that this logIn function should only be called when the passport strategy states that successful authentication has occurred. But in this way, passport has provided you with a way of easily authenticating the user, and then getting access to this user via the request object.
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.