Should I verify a JWT token information from a Database? - node.js

I'm currently trying to build a REST API using express, node, and MongoDB. Now for authentication, I'm using JWT.
Here is the code for checking JWT token
const token = req.headers['authorization'];
if (token){
const tokens = token.split(' ');
const key = tokens[1];
jwt.verify(key, config.jwtKey, (err, authData) => {
if (err){
res.status(403).json({
success: false,
message: "Authentication2 failed"
});
}
// User authenticated
// Do something
next();
});
} else {
res.status(403).json({
success: false,
message: "Authentication failed"
});
}
Now, this code is working perfectly.
For making the JWT, here is the code
........
........
const token = jwt.sign(
{
email: user[0]._email,
userId: user[0]._id
}, config.jwtKey,{ expiresIn: "1d" });
........
........
Now my question is should I also verify the user by checking the existence of the user's information in the database to make it more secure?
For example, searching the email and userId to my database.
I'm using node, express, MongoDB, Mongoose, and JWT for this project.

It's an old question but I want to leave an answer:
Yes! Everytime your client make an api request, backend should verify both the validity of the token and the presence somewhere in your backend (for example a db table).
Consider always the most dangerous scenario: bank account
What happens if someone steal your device?
You should be able to invalidate the token from another device and change the password.

The advantage of using a token is that the server can
verify it quickly without calling out to an external data store like MongoDB.
But if you're going to add a business login to your API authentication like a blacklist/whitelist of revoked tokens then you have to use a store to verify the token and user details, (will be slower than not doing a remote call for each token but you have to do it with low latency).
For low latency you have to use DB like Redis, Dynamodb would probably be fine and more secure without major latency between your DB and your API server.
Is not required to verify the signature with DB, and you can settle for JWT algorithm

Verifying (the signature of) the token using the selected algorithm is enough to ensure that this user exists (or existed) on the system because it was the system who generated the token in the first place.
But there are cases when that's not enough, for example, blacklists as mentioned in Roy G's answer, or if the users' claims have been changed or completely deleted from the system but they are still using an old token (not expired yet), they could still have access to the system, so checking against DB would prevent that access.
Setting a small expiry date in combination with refresh tokens is generally a good practice to prevent those kinds of leaks.

Related

Logout JWT with nestJS

I'm using JWT passaport to login module:
async validateUser(userEmail: string, userPassword: string) {
const user = await this.userService.findByEmail(userEmail);
if (user && user.password === userPassword) {
const { id, name, email } = user;
return { id: id, name, email };
}else {
throw new UnauthorizedException({
error: 'Incorrect username or password'
});
}
}
async login(user: any) {
const payload = { email: user.email, sub: user.id };
return {
access_token: this.jwtService.sign(payload),
};
}
This part is running.
My question is: how do the logout? I read about creating a blacklist and adding the token to it, but how do I get the user's access token?
Something you should know about token-based authentication is that it is stateless. This means that even the server does not keep track of which users are authenticated, like with session-based authentication. As such, you do not need to do anything on the server side to "log out" a user. You simply need to delete the t\JWT token on the client. If you make a request to the server app, without a valid JWT token, it will be treated as the user is not logged in.
Generally when a logout request would be sent the Authorization header should be present, so you can grab the token from there. Then you can save the token to the database's restrict list table.
When user click to "Log out" btn, you should sent a request which is attached Authorization header with bearer token. In the backend side, you need to extract header and push the token to the blacklist token (as your solution). Basically, you only need remove token in client side, it's so easy to do but in the worst case when the token was stolen by hacker, your token still valid. Using blacklist token is more secure but it can be lead to performance issue and scalable. What is the best solution? it's depend on you.
Read the Nestjs Execution context get the token from the request header and verify this token from JWT.
everything defines in the NESTJS link
//here we check the token from the header is valid or not expired
const tokenVarify = await this.jwtService.verify(token);
my idea is make whitelist every generate new token and remove it when user logout. so u need to check on guard also every user with token access its exist on whitelist or note
You must be refresh expire token to 1ms with:
https://webera.blog/how-to-implement-refresh-tokens-jwt-in-nestjs-b8093c5642a9
Actually there is a workaround for this, but not so straightforward!
The idea is to keep track of the tokens for logged out users (use some sort of black-list) and query provided token against that black-list.
To sum it up, you can follow these 4 steps:
Set a reasonable expiration time on tokens
Delete the stored token from client side upon log out
Have DB of no longer active tokens that still have some time to live
Query provided token against The Blacklist on every authorized request
For detailed explanations, you can check this post: medium
A guide in how to do the implementation is in this youtube video
Code with Vlad
, and there's as well the github source nestjs-jwts. I followed this video and implemented myself as well..

Web security. Am I protecting my REST api properly? (node.js express)

I have a MEAN stack app with a REST like api. I have two user types: user and admin. To sign the user in and keep the session i use jsonwebtoken jwt like this (simplified):
const jwt = require("jsonwebtoken");
//example user, normally compare pass, find user in db and return user
let user = { username: user.username, userType: user.userType };
const token = jwt.sign({ data: user }, secret, {
expiresIn: 604800 // 1 week
});
To protect my express routes from I do this:
in this example it is a "get user" route, the admin is ofc allowed to get information about any given user. The "normal" user is only allowed to get information about him/her -self, why i compare the requested username to the username decoded form the token.
let decodeToken = function (token) {
let decoded;
try {
decoded = jwt.verify(token, secret);
} catch (e) {
console.log(e);
}
return decoded;
}
// Get one user - admin full access, user self-access
router.get('/getUser/:username', (req, res, next) => {
let username = req.params.username;
if (req.headers.authorization) {
let token = req.headers.authorization.replace(/^Bearer\s/, '');
decoded = decodeToken(token);
if (decoded.data.userType == 'admin') {
//do something admin only
} else if (decoded.data.username == username) {
//do something user (self) only
} else{
res.json({ success: false, msg: 'not authorized' });
}
} else {
res.json({ success: false, msg: 'You are not logged in.' });
}
})
So my question is, how secure is this? Could someone manipulate the session token to swap the username to someone else's username? or even change the userType from user to admin?
My guess is. Only if they know the "secret" but is that enough security? the secret is after all just like a plain text password stored in the code. What is best practice?
the secret is after all just like a plain text password stored in the code.
That's right. If secret is not kept secret, then an attacker can forge user objects and sign them using that secret and verify would not be able to tell the difference.
Putting secrets in code risks the secret leaking. It could leak via your code repository, because a misconfigured server serves JS source files as static files, or because an attacker finds a way to exploit a child_process call to echo source files on the server. Your users' security shouldn't depend on noone making these kinds of common mistakes.
What is best practice?
Use a key management system (KMS) instead of rolling your own or embedding secrets in code. Wikipedia says
A key management system (KMS), also known as a cryptographic key management system (CKMS), is an integrated approach for generating, distributing and managing cryptographic keys for devices and applications.
Often, how you do this depends on your hosting. For example, both Google Cloud and AWS provide key management services.
https://www.npmjs.com/browse/keyword/kms might help you find something suitable to your stack.
This is extremely secure, especially if sent over HTTPS - then an attacker has no idea what your request payload looks like.
The only real danger is making sure you keep the SECRET safe, don't save on public git repos, lockdown access to any box on which the secret is stored. Use an encoded secret.
There are also other ways to harden your server. Consider using npm's popular helmet module.
This is secure if your data packets are sent over HTTPS. If you want to add another layer of security what you can do is you can first encrypt the user details with the help of iron which uses 'aes-256-cbc' for encryption and then use that encrypted text and generate a token through JWT. In this way if a user somehow gain access to your token and go the website of JWT. He won't be able to recognize what's inside this token.
Again this is just to add an extra layer of security and it makes technically infeasible for a person to extract information because it's very time consuming yet add some extra security to our application.
Also make sure that all of your secrets(secret keys) are private.
To answer your initial question "Could someone manipulate the session token to swap the username to someone else's username? or even change the userType from user to admin?"
JWT tokens are encrypted from the server-side and sent to the client in the form of a response. With that token, there are two things to keep in mind:
Token is encrypted with a private key that only the server knows about
The token contains, what is known as, a signature which validates the contents of the JWT payload (e.g. userType etc.)
For more information regarding the first point, please refer to the following answer.
For a better visual representation of JWT tokens and signatures, take a look at the following URL - https://jwt.io/
With this in mind, you must make sure that:
Your secret key is never exposed at any point in time to external services/clients. Ideally this key is not even hard-coded in your codebase (ask, should you want me to elaborate on this).
You don't have any logical flaws in your endpoints.
From a design perspective, I would much rather segregate any endpoints related to admin-space into their own endpoints however at a quick glance, your code seems to be fine. :-)
On a side-note that may assist you, if you don't have much experience in Web Application security, I would recommend checking out some automated scanners such as Acunetix, Burp (hybrid) and so on. Whilst not perfect by any means, they are quite capable of detecting a good amount of behavioral vulnerabilities (as in, ones a malicious actor would normally exploit).
Some potential vulnerabilities in the code above:
Variable username comes from the url, but usertype is asserted from the token in the router action. An audit log for example could be corrupted if it took the userid from the username variable, because that is not authentic and can be anything sent by the user.
Replay is an issue. As the token is valid for one week, it is impossible to revoke admin rights for example until the token expires. (A malicious user could replay the previous admin token.)
Similarly, you cannot terminate (force logout) any session until tokens expire. This is a common issue in such stateless designs.
Null reference after token decode as pointed out by others. In node.js that is more like a weakness than a vulnerability I'd say, I think it's not exploitable.
The secret on the server is extremely valuable and can be used to impersonate any user. It is very hard to protect such a secret in systems with high security requirements.
So contrary to another answer, this is far from being 'extremely secure', but can be reasonably secure for many purposes.

JWT Authentication system in webpage using nodejs

Recently I'm trying to build an JWT authentication system with an admin panel to manage login-ed users for simple purpose like RESTFUL API or realtime database(Socket.io) used in both mobile or web.But there are few issue that trips me over.
The status right now is i'm able to use nodejs to create a a JWT token and past it to the front end.
However after that i've no idea what to do with that token in the front end. And here are the questions
If i'm using React, Redux or ReactNative, is it alright to save the token in Redux status, and call it through ajax(Axios) request , if not where should it be store?
If i just wanna to use it HTML instead of any kind of single page app framework, where should i store the token (local storage, cookies, window sessionStorage, anything thing else ?)
I heard that session and cookies are not a good location to store the token due to they are vulnerable to different attack , how can i prevent that?
This is the biggest point where i'm stuck, I've create a Form for the user to login, after pressing the login button, i'll do an ajax request to ask for a token, after the token is received, how should i save the token (according to q2) and redirect the user to a protected route by setting the header {'x-access-token': token}?
if i would want to allow the user to logout, what is the proper way to do that? (just delete the token from client storage?)
I found quite a lot of tutorial about creating and refreshing the token, but i cant find any tutorial about what to do after obtaining the token? are there any good recommendations that i could follow along?
I know this is weird but i feel i'm missing some of the core concept on the whole authentication flow. Could anyone try to point it out according to the questions that I've asked above?
Sorry for my bad english, i've try my best to phrase it out in a correct way.
And this is my github repo for the questions
https://github.com/xylops/backend
And Thank you for the time to read this
Storing the Token:
Use localStorage for storing the token, So even when user
refreshes the page the token still be present., You can add your
token to axios header so it gets passed for every request you make.
Logging out User:
Yes just deleting works for simple apps.
You should specify
expiration while creating tokens and when a user logs out, store that
token in Database (usually Redis)
Every time a user makes a request, check if the exact same token is stored in Redis, if yes this means this is a logged out user.. return proper response back to the user.
Run a cron job which will keep on removing expired tokens from Redis so your redis database will not have expired tokens and at the same time your app itself will reject expired tokens.
After obtaining the Token
Do what you want to do, The token will contain the information you provide, Like user id, name and other details you choose, Based on this you can show different data in the frontend and find user specific records in the backend.
You're not missing much of anything, Logging out is not easy to implement in Token based authentication, The beauty of Token Based Authentication is your app doesn't depend on cookies, sessions and you can truly make an Stateless distributed application.
Code Example
This is how i use the token with localStorage and Axios
import axios from 'axios';
const setToken = (token) => {
// if token is provided, save it in localStorage and add it to axios header as well.
if ( token ) {
localStorage.token = token
axios.defaults.headers.common['Authorization'] = `Bearer ${token}`;
}
// if token is empty, just remove it from localStorage and axios..
// set empty token when you logout the user.
else {
delete localStorage.token;
delete axios.defaults.headers.common['Authorization'];
}
}
When the application loads for the first time and on every refresh do
if ( localStorage.token ) {
setToken( localStorage.token );
}
And to decode the token you can use, JWT
import jwt from 'jsonwebtoken';
const decodedToken = jwt.decode(localStorage.token);
Hope this helps a little.

How to destroy JWT Tokens on logout?

I am using jwt plugin and strategy in hapijs.
I am able to create jwt token while login user and authenticate other API using the same token through 'jwt' strategy.
I am setting the token in request.state.USER_SESSION as a cookie where USER_SESSION is a token name. Also, I am not saving these token in the database.
But how can I destroy jwt token at the time of logout?
Please suggest a way.
The JWT is stored on browser, so remove the token deleting the cookie at client side
If you need also to invalidate the token from server side before its expiration time, for example account deleted/blocked/suspended, password changed, permissions changed, user logged out by admin, take a look at Invalidating JSON Web Tokens for some commons techniques like creating a blacklist or rotating tokens
You cannot manually expire a token after it has been created. Thus, you cannot log out with JWT on the server-side as you do with sessions.
JWT is stateless, meaning that you should store everything you need in the payload and skip performing a DB query on every request. But if you plan to have a strict log out functionality, that cannot wait for the token auto-expiration, even though you have cleaned the token from the client-side, then you might need to neglect the stateless logic and do some queries. so what's a solution?
Set a reasonable expiration time on tokens
Delete the stored token from client-side upon log out
Query provided token against The Blacklist on every authorized request
Blacklist
“Blacklist” of all the tokens that are valid no more and have not expired yet. You can use a DB that has a TTL option on documents which would be set to the amount of time left until the token is expired.
Redis
Redis is a good option for blacklist, which will allow fast in-memory access to the list. Then, in the middleware of some kind that runs on every authorized request, you should check if the provided token is in The Blacklist. If it is you should throw an unauthorized error. And if it is not, let it go and the JWT verification will handle it and identify if it is expired or still active.
For more information, see How to log out when using JWT. by Arpy Vanyan(credit and reference)
On Logout from the Client Side, the easiest way is to remove the token from the storage of browser.
But, What if you want to destroy the token on the Node server -
The problem with JWT package is that it doesn't provide any method or way to destroy the token.
So in order to destroy the token on the serverside you may use jwt-redis package instead of JWT
This library (jwt-redis) completely repeats the entire functionality of the library jsonwebtoken, with one important addition. Jwt-redis allows you to store the token label in redis to verify validity. The absence of a token label in redis makes the token not valid. To destroy the token in jwt-redis, there is a destroy method
it works in this way :
1) Install jwt-redis from npm
2) To Create -
var redis = require('redis');
var JWTR = require('jwt-redis').default;
var redisClient = redis.createClient();
var jwtr = new JWTR(redisClient);
jwtr.sign(payload, secret)
.then((token)=>{
// your code
})
.catch((error)=>{
// error handling
});
3) To verify -
jwtr.verify(token, secret);
4) To Destroy -
jwtr.destroy(token)
Note : you can provide expiresIn during signin of token in the same as it is provided in JWT.
If you just want to remove the token, it will be simple as removing it from the front end application, In you case clear the cookies that stores the token
On the other hand if you mean to invalidate the token, there is couple of ways to do it, below are some ways
(1) If all the token ever generated is stored in backend, It will be just simple as clearing that storage, if tokens have been mapped to users you can just clear tokens for a particular user.
(2) You can add a date field like "invalidate_before" along with user which should be updated at a event of changing password, logout from all devices etc.
Simply update the invalidate_before to currentTime() on such events.
Every time a new token is created, add the created time in token payload,
to validate the token on incoming request just check if the created time in payload is greater than invalidate_before time for that user in db
(3) When you create a new user, create a secret for just that user, then you can sign every user token with that specific secret, and just like in (2) events like changing password, logout from all devices etc, Should create a new secret.
This way also you can invalidate by checking the token signature.
overhead with (2) and (3) is that, validation will be a 2 step process and it involves db reading
EDIT: For (3) you may use a salt instead (final secret will be common secret + salt for particular user), So that you hava a way to invalidate either a single user's token by changing salt or the all user's token by changing common secret
You can add "issue time" to token and maintain "last logout time" for each user on the server. When you check token validity, also check "issue time" be after "last logout time".
While other answers provide detailed solutions for various setups, this might help someone who is just looking for a general answer.
There are three general options, pick one or more:
On the client side, delete the cookie from the browser using javascript.
On the server side, set the cookie value to an empty string or something useless (for example "deleted"), and set the cookie expiration time to a time in the past.
On the server side, update the refreshtoken stored in your database. Use this option to log out the user from all devices where they are logged in (their refreshtokens will become invalid and they have to log in again).
OK so I tried something that I wanna share I think it's a really easy and effective method so basically instead of destroying your token or blacklist it we can simply append a random value to it in the middle in a random index or even in the end of it like a random number (or a random hashed number) to make it harder for anyone to reverse it and obtain the previously valid token, Doing so makes this token invalid so the user won't go anywhere and from the front-end you can redirect the user to login again (or even from the back-end however I prefer if the front-end did it) so the user logs out they get redirected to the login page and it's all good, Here's my code. first of all I have an auth middleware that if the token(password & username) is OK it appends the token to req.token so whenever I call this middleware the user's token will be save to req.token
router.post('/logout', auth, async(req, res) => {
try{
let randomNumberToAppend = toString(Math.floor((Math.random() * 1000) + 1));
let randomIndex = Math.floor((Math.random() * 10) + 1);
let hashedRandomNumberToAppend = await bcrypt.hash(randomNumberToAppend, 10);
// now just concat the hashed random number to the end of the token
req.token = req.token + hashedRandomNumberToAppend;
return res.status(200).json('logout');
}catch(err){
return res.status(500).json(err.message);
}
});
right now it will concat the the hashed random number to the end of the token which means it's no longer valid so the user will have to login again as they will be redirected to the login page

Node.js - How to use access / auth tokens?

I have built my first Node.js app that is supposed to be installed on a Shopify store. If you want to see what my actual code looks like (app.js) you can view it here. It's really basic so reading through won't be hard.
I know how to authenticate the installation of the app (following the Shopify instructions) but I don't how to authenticate all subsequent requests using the permanent access token that a successful installation provides me with.
By subsequent requests I'm referring to requests to either render the app or requests to install the app, even though the app is already installed.
Right now, I'm storing the shop's name (which is unique) along with the permanent token that Shopify sends me in my database. But I don't know if that's necessary. If I'm not mistaken, simply using the browser's session will do ? But how do I do that ? And how do I use this token every time a request comes through to check if it is a valid one?
Thank you for any help/suggestions!
The code below is sort of a representation of what my actual code looks like in order to give you an idea of what my issues are :
db.once('open', function(callback)
{
app.get('/', function (req, res)
{
var name = getNameFrom(req);
if (existsInDB(name) && tokenExistsInDBfor(name))
{
res.redirect('/render');
/*
Is checking that the shop (along with a permanent token)
exists in my DB enough ?
Shouldn't I check whether the current request comes with
a token that is equal to the one in my DB ?
What if the token received with this request is different
from the one stored in my DB ?
*/
}
else res.redirect('/auth');
});
app.get('/auth', function (req, res)
{
if (authenticated(req))
{
var token = getPermanentToken();
storeItInDB(nameFrom(req), token);
res.redirect('/render');
/*
aren't I supposed to do anything more
with the token I've received ? send it
back/store it in the browser session as well maybe?
is storing it in the db necessary ?
*/
}
});
app.get('/render', function (req, res)
{
/*
How do I check that this request is coming
from an authorised shop that has the necessary token ?
Simply checking my DB will not do
because there might be some inconsistency correct ?
*/
res.sendFile(*file that will build app on the client*);
});
});
Getting access token from Shopify is once time process.
Save access token and shop's name in your DB, and also generate and save 'auth token' based on some algorithm. Return generated auth token to Client. Make sure client sends this auth token in every request.
Now when client hit your server verify auth token; once verified make call to Shopify API using appropriate 'access token' and shop name.
Authentication flow could be as follows:
Get Access token from Shopify
Generate token(i am refering this as auth token) for the Shopify Shop, refer this
Now save shopify's access token, shopify store name and your generated token into DB
Now send your generated token to client(save it in cookie or local storage)
Validation flow:
Clients hits your server to get data with your auth token
Verify this auth token in your DB, and get access token and shop name for that auth token
Now make calls to Shopify API using this access token and shop name
Hope this method helps

Resources