express node how to avoid sending user data on every request? - javascript

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.

Related

How to get userId on client without storing the jwt on client?

Im using jwt in my react native/node app. I do not store it on the client and dont want/need to. In one case I need to know the userId on client but only have the token (that i use to create jwt). I need it because I need to establish a connection only with specific users so I need to get the userId.
How can I do that without storing the jwt on client? Or do I just use the token on client, send that, and in my backend I verify the token to get the actual userId and then compare?
client:
const instance = axios.create({
baseURL: 'example.com'
});
instance.interceptors.request.use(
async (config) => {
const token = await AsyncStorage.getItem("token");
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
},
(err) => {
return Promise.reject(err);
}
backend
app.use(router.get('/stream', requireAuth));
in requireAuth create the jwt
jwt.verify(token, ...
and attach it to req.user.
The best way to transfer a token from the BE to FE will be using HTTP-only cookies, with this you will not be able to read the token in FE using JS but it will automatically send with every request and you will be able to read it in the BE and get the userId and do relevant things with it.
Using HTTP cookies

I don't want to send form data back with express, but it does, or does it?

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.

How can I get a value from the JWT header into a variable in nodejs?

I am making an API server where I can login and see some information. Whenever I log in I get a JWT token which contains my username. I want to store this username in a variable whenever I want to use it. This is the code I use to verify my token when I want to enter certain webpages.
import jwt from "jsonwebtoken";
function authenticateToken(req, res, next) {
const authHeader = req.header("access-token");
if (!authHeader) return res.sendStatus(401);
jwt.verify(authHeader, process.env.ACCESS_TOKEN_SECRET, (err, user) => {
if (err) return res.sendStatus(403);
req.user = user;
next();
});
}
export default authenticateToken;
It checks if the header contains a JWT token. If so it verifies it. Then at the bottom the user gets retrieved, but I dont know how to get this value.
On my routes I secure them like this:
router.get("/", authenticateToken, getData);
within the getData method im displaying data from a mysql database. I want to add a check to see if my username is equal to the data in getData (since you can only see certain data).
In getData i get my data like this:
connection.query(
"SELECT * FROM data WHERE userid = ?", [user.username],
(err, rows) =>
On the [user.username] spot I want to retrieve the username within the JWT token. I want to do this using a method, but I cant seem to get it. How can I get my username?
You could try to change your code a little bit to pass the request to the getData function, as you store the authenticated user within the request.
Try something like this:
router.get("/", authenticateToken, async (req, res) => {
getData(req);
});

Implement jwt-permissions on NodeJS - UnauthorizedError

I am new to the use of Nodejs but I confess that I am loving, I am using tokens Jwt for authentication of users, simple thing, they log and win a token. but I want to put permissions on the routes, I've been reading about express-jwt-permissions, but using it in my application I'm getting the following error at POSTMAN: UnauthorizedError: user object "user" was not found. Check your configuration.
customerRoutes.js
const router = require('express').Router();
var jwt = require('jsonwebtoken');
var guard = require('express-jwt-permissions')()
const customerController = require('../controllers/customerController');
function verifyToken(req, res, next) {
// Get auth header value
const bearerHeader = req.headers['authorization'];
// Check if bearer is undefined
if(typeof bearerHeader !== 'undefined') {
// Split at the space
const bearer = bearerHeader.split(' ');
// Get token from array
const bearerToken = bearer[1];
// Set the token
req.token = bearerToken;
// Next middleware
next();
} else {
// Forbidden
res.sendStatus(403);
}
}
router.post('/posts',verifyToken,guard.check('admin'),
customerController.posts);
module.exports = router;
this is how my token looks : eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJwZXJtaXNzaW9ucyI6WyJhZG1pbiJdLCJpYXQiOjE1Mzc0MDg0NzMsImV4cCI6MTUzNzQ0ODQ3M30.8l1vezWWz3Gmb5M3N0DgsmCl-nZHK2c3GP-dKzYDLRU
From the documentation of express-jwt-permissions
This middleware assumes you already have a JWT authentication middleware such as express-jwt.
And then tracing towards express-jwt documentation, with the first example:
var jwt = require('express-jwt');
app.get('/protected',
jwt({secret: 'shhhhhhared-secret'}),
function(req, res) {
if (!req.user.admin) return res.sendStatus(401);
res.sendStatus(200);
});
And associate with your encountered error, it's safe to say that express-jwt-permissions middleware is expecting a populated req.user field with the value of an object (then I actually checked their source code https://github.com/MichielDeMey/express-jwt-permissions/blob/master/index.js#L11 and proves that it is the case).
So my suggestion would be either use express-jwt which does the work for you and is known to work with express-jwt-permissions, or populate a req.user object within your verifyToken middleware by yourself (requires decoding of the JWT), something like:
// decode JWT
// get decoded token.permissions
req.user = {
permissions: token.permissions
};
Before invoking next() to the next middleware.
Note that you can also tweak express-jwt-permissions to expect a different field than user (but still need to live in the req namespace of express middleware life-cycle), as well as a different name than permissions field inside the token payload. See https://www.npmjs.com/package/express-jwt-permissions#configuration for details. But either way, the bottom line is you'll need to decode the token first.

Updating Mongoose schema with JSON object via REST endpoint

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.

Categories