I have the following code
var user = function(req,res,next) {
db.findOne({ username: req.params.uid }, function (err, docs) {
//error handaling
if(err){console.log(err)}
//check if user is real
if(docs === null){
res.end('404 user not found');
}else{
//IMPORTANT PART res.sendFile(__dirname + '/frontend/user.html');
}
});
}
app.get('/user/:uid',user);
Don't worry about the database stuff.
I want to know how to get req.params.uid sent to the client side and how to get it from there.
Thanks so much.
If your user is configured correctly every request will have a user:
var user = function(req,res) {
db.User.findOne({ _id: req.user._id }, function (err, docs) {
//error handaling
if(err){console.log(err)}
//check if user is real
if(docs === null){
res.end('404 user not found');
}else{
res.json(docs)
}
});
and then your api endpoint is just '/user/
In your client just make a GET request to this endpoint (maybe using AJAX) and your response will be any user that makes that given request.
Note: You don't need to pass in next unless you are defining middleware.
This is just a more complete answer based on my comment.
If you want to store a string of information about the user with each request they make, then you want to use cookies.
When the user first makes a request to the page, you would set the cookie via the res.cookie. So, in your code, the final if statement would look something like:
if(docs === null) {
res.end('404 user not found');
} else {
res.cookie('uid', req.params.uid, { httpOnly: true });
//IMPORTANT PART res.sendFile(__dirname + '/frontend/user.html');
}
Then, in the next request, and futures requests before the cookie expires, you can access it using:
req.cookies.uid
However, you need the cookie-parser middleware somewhere in your app beforehand:
var cookieParser = require('cookie-parser');
app.use(cookieParser());
If you need to access the value of uid on the clientside, you could either use a template, or set the httpOnly value to false when setting it using res.cookie. Then you could access the cookie using document.cookies.
Check out this W3Schools page for accessing cookies on the clientside.
Related
I know the title of this question might sound confusing, but my problem is actually simple. I have these two handlers for /login get and post requests:
loginRender(req, res) {
let options = { title: 'Login', layout: 'auth.hbs' }
res.render('login', options)
}
login (req,res){
let user = Routes.findUser(req.body.username)
let passwordCorrect = Routes.hashCompare(
req.body.password,
user.password
)
if (passwordCorrect) {
let token = Routes.jwtsign(req.body.username)
let refreshToken = Routes.jwtRefreshToken(req.body.username)
Routes.authRedirect(res, token, refreshToken)
} else {
Routes.badRequestRedirect(res, '/login')
}
}
authRedirect(res, token, refreshToken )
{
let options = {
cssPath: 'styles/querystyle.css',
}
res.cookie('access_token', `${token}`, { httpOnly: true })
res.cookie('refresh_token', `${refreshToken}`, { httpOnly: true })
res.status(200).render('query', options)
}
// app.use(urlencoded)
// app.use(cookieParser)
// app.post('/login', login)';
// app.get('/login', loginRender)
Please, ignore all unrelated stuff.
So, everytime I complete login, I get my webpage rendered and I can actually open inspector and see this:
Page Inspector
Address line
How can I fix that? I want my user to be redirected to dashboard-like page and not to receive his sensitive data in insecure form.
UPD
there's also auth middleware that only appends req.username in case we did parse jwt successfully, and there's a little bit of interaction with it, but it does not appear on page until I go to this page manually by writing the address in address line.
If you don't send the data to the Express server, then you can't read it in you login function and you can't authenticate the user.
It is not a problem is the user can use the tools in their own browser to inspect the data that they entered.
You need it to be encrypted in transport (i.e. use HTTPS and not plain HTTP, at least in production) but you don't need to worry about the user finding out their own password.
i was wandering what is the best way to avoid sending the user data on every request,
lets say i want to add product from user's account, i have to send the user. or i want to order something, i have to send the user.
i thought about something like this:
app.use(async (req, res, next) => {
if (!req.body.userId) {
return next();
}
const user = await enteties.User.findByPk(req.body.userId);
req.user = user;
next();
});
but it also requires me to send the user on evey request..
there must be a way to avoid sending the user data to the server on almost every request.
also, it will make all my requests of type "post" since i have to send the user, and even "get" requests are now become "posts", for sure this is not correct
If you implement your JWT token correctly you don't need to send the logged in user id.
JWT tokens contain a payload section that is basically any JSON data you want to set. This is basically your decentralized session stored in the user's machine. When creating a JWT token you'd normally do something like:
const jwt = require('jsonwebtoken');
const config = require('./config');
function generateToken(user) {
let payload = {
sub: user.id
};
return jwt.sign(payload, config.secret, {
algorithm: 'HS512', // choose algorithm appropriate for you
expiresIn: config.expires
})
}
That payload part allows you to send user identifying information. In the case above, the user id. To get that id from a request simply verify it:
app.use((req, res, next) => {
const token = req.get('Authorization');
jwt.verify(token, config.secret, (err, payload) => {
if (err) {
next(err);
}
else {
req.user = payload; // user.sub is the user id
next();
}
});
});
Or you can use a library such as express-jwt to do it for you:
const expressJwt = require('express-jwt');
const express = require('express');
const config = require('./config');
const app = express();
app.use(expressJwt({ secret: config.secret }); // use express-jwt like any
// middleware, you can even install
// it on specific routes.
Now in your controller/route you can simply extract the payload in the req.user object. Invalid tokens or requests without tokens will completely skip your handler and immediately return an error or unauthorized response:
app.get('/some/endpoint', (req, res) => {
console.log('user is', req.user.sub); // note: req.user is our payload
});
Additional tricks:
As I mentioned, the payload is basically user defined. If you need to keep track of other user information such as roles or permissions you can store them in the JWT token:
// Example payload
let payload = {
sub: user.id,
admin: user.role === 'admin',
gender: user.gender
};
This reduces the number of database requests needed to process the user. Making the authentication system completely decentralized. For example you may have a service that consumes this JWT token that is not connected to your user database but need to check if user is admin. With the right payload that service does not even need to have access to the user database.
Note however that the payload is not encrypted. It is just base64 encoded. This means that the information in the token can be easily read by anyone with access to it (normally the user but beware of 3rd party scripts). So ideally you shouldn't store dox-able information in the payload if you have 3rd party scripts on your website (then again, it is highly unusual these days for anyone to write the entire front-end from scratch without any libraries or frameworks)
Also note that the more you put in your payload the larger your token will be.
I have a REST endpoint that is being submitted with a JSON object (User) and I just set the corresponding mongo record to that JSON object. This saves me the trouble of updating schema changes in the service method and the endpoint leaving just the Mongoose model to update.
What would be a more secure way of doing this, if any?
Example User JSON
{
'fname': 'Bill',
'lname': 'Williams',
'email': 'bill#billwilliams.com',
'settings': {
'strokeColor': '#FF0000'
}
}
From my Angular service
Update: function(my_user) {
return $http.put('http://api.domain.com/v1/api/users/' + _user.id, {
user: my_user,
token: window.localStorage['token']
});
}
My REST endpoint in Node
api.route('/users/:user_id')
.put(function(req, res) {
User.findById(req.params.user_id, function(err, user) {
userData = req.body.user;
if (user) {
//-- This is potential trouble area?
User.update({'_id': user._id}, {$set: userData});
user.save(function(err) {
res.json({
success: true,
message: 'User updated'
});
}); //-- end findById()
}); //-- end /users/:user_id put() route
Have a look at Jsonwebtoken.
It basically works like this:
Create a REST endpoint that lets users aquire a token with a certain
payload (id for example)
Secure the relevant part of your api with the Jsonwebtoken middleware (if using express as the webserver)
User adds the token to every request header (by using $httpInterceptor)
Token is checked on the server side before the request reaches your API
Tokens may expire after a certain time (useful when users needs to register first) which adds additional security.
I have a NodeJS Express app that uses express-session. This works great, as long as session cookies are supported.
Unfortunately it also needs to work with a PhoneGap app that does not support cookies of any kind.
I am wondering: Is it possible to get an express session and access the data in that session, using the sessionID?
I am thinking I could append the sessionID as a querystring parameter for every request sent by the PhoneGap app like so:
https://endpoint.com/dostuff?sessionID=whatever
But I don't know how to tell express to retrieve the session.
You can certainly create an express route/middleware that tricks express-session that the incoming request contains the session cookie. Place something like this before the session middleware:
app.use(function getSessionViaQuerystring(req, res, next) {
var sessionId = req.query.sessionId;
if (!sessionId) return res.send(401); // Or whatever
// Trick the session middleware that you have the cookie;
// Make sure you configure the cookie name, and set 'secure' to false
// in https://github.com/expressjs/session#cookie-options
req.cookies['connect.sid'] = req.query.sessionId;
next();
});
Seems like req.cookies isn't accessible in my case. Here's another solution that recreates the session using the 'x-connect.sid' header (you may use any name or even a query param if you like).
Put this middleware after the session middleware
// FIRST you set up your default session like: app.use(session(options));
// THEN you recreate it using your/custom session ID
app.use(function(req, res, next){
var sessionId = req.header('x-connect.sid');
function makeNew(next){
if (req.sessionStore){
req.sessionStore.get(sessionId, function(err, session){
if (err){
console.error("error while restoring a session by id", err);
}
if (session){
req.sessionStore.createSession(req, session);
}
next();
});
} else {
console.error("req.sessionStore isn't available");
next();
}
}
if (sessionId) {
if (req.session){
req.session.destroy(function(err){
if (err) {
console.error('error while destroying initial session', err);
}
makeNew(next);
});
} else {
makeNew(next);
}
} else {
next();
}
});
I'm trying to build a simple application with parse.com as my user manager.
I would like to make a login call to parse.com from my client side, and call my node.js server with the user's session token (I'll add it as a cookie). In the server side, I'll validate the session (using https://parse.com/docs/rest#users-validating) and allow access only if the session is valid.
For example (in my server):
app.get('/api', function(req, res, next) {
var token = getTokenFromRequest(req);
if(tokenIsValid(token)) {
next();
} else { // Redirect... }
});
app.get('/api/doSomething', function(req, res) {
// Do something....
});
the tokenIsValid(token) function should be implemented using https://parse.com/docs/rest#users-validating.
However, it seems that the REST API user validation returns the user even if the user is logged out (expected to return 'invalid session').
Is this a bug in the REST API user validation? What am I doing wrong? Is there a better way for doing that?
Thanks!
Via REST there's no concept of sessions really. REST calls are meant to be stateless meaning that the (current) user at /me will be serialized from the token provided. If the token is associated to a user it will return the JSON representation of that user otherwise in returns an error.
One way or another that call is asynchronous so you can't really use it in and if statement.
You can do:
app.get('/api', function(req, res, next) {
var token = getTokenFromRequest(req);
serializeUserFromToken(token,function(err,parseResponse) {
if(err) return next(err)
if(parseResponse.code && parseResponse.code === 101){
// called to parse succedded but the token is not valid
return next(parseResponse);
}
// parseResponse is the current User.
next();
});
});
Where serializeUserFromToken makes a request to Parse with the token in the X-Parse-Session-Token header field.